From 9280ad9c33c878b77bd607d77d2188896456f243 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 12 Apr 2019 07:40:15 +0200 Subject: [PATCH 001/632] make plethysm with tensor return result as element of second parent --- src/sage/combinat/sf/sfa.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index 78853299d0d..dc8edecdfea 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -2879,13 +2879,15 @@ def raise_c(n): return lambda c: c.subs(**{str(g): g ** n for g in degree_one}) if tensorflag: - tparents = x.parent()._sets - return tensor([parent]*len(tparents))(sum(d*prod(sum(raise_c(r)(c) - * tensor([p[r].plethysm(base(la)) - for (base,la) in zip(tparents,trm)]) - for (trm,c) in x) - for r in mu) - for (mu, d) in p(self))) + parent = x.parent() + tparents = parent._sets + elt = sum(d*prod(sum(raise_c(r)(c) + * tensor([p[r].plethysm(base(la)) + for (base,la) in zip(tparents, trm)]) + for (trm, c) in x) + for r in mu) + for (mu, d) in p(self)) + return parent(elt) # Takes in n, and returns a function which takes in a partition and # scales all of the parts of that partition by n From 6e94b833158997cbf4503bd2e26a2291cae5fe42 Mon Sep 17 00:00:00 2001 From: dwbmscz <75940445+dwbmscz@users.noreply.github.com> Date: Fri, 19 Feb 2021 14:16:40 -0800 Subject: [PATCH 002/632] Willie's code implementing a cythonized version of the F-matrix code --- src/sage/combinat/root_system/all.py | 1 + src/sage/combinat/root_system/f_matrix.py | 753 ++++++++++++++++++ .../root_system/map_reduce_engine.pxd | 7 + .../root_system/map_reduce_engine.pyx | 118 +++ .../combinat/root_system/poly_tup_engine.pxd | 25 + .../combinat/root_system/poly_tup_engine.pyx | 390 +++++++++ 6 files changed, 1294 insertions(+) create mode 100644 src/sage/combinat/root_system/f_matrix.py create mode 100644 src/sage/combinat/root_system/map_reduce_engine.pxd create mode 100644 src/sage/combinat/root_system/map_reduce_engine.pyx create mode 100644 src/sage/combinat/root_system/poly_tup_engine.pxd create mode 100644 src/sage/combinat/root_system/poly_tup_engine.pyx diff --git a/src/sage/combinat/root_system/all.py b/src/sage/combinat/root_system/all.py index dfd959e9697..b8e06e4ff3d 100644 --- a/src/sage/combinat/root_system/all.py +++ b/src/sage/combinat/root_system/all.py @@ -20,6 +20,7 @@ lazy_import('sage.combinat.root_system.weyl_characters', ['WeylCharacterRing', 'WeightRing']) lazy_import('sage.combinat.root_system.fusion_ring', ['FusionRing']) +lazy_import('sage.combinat.root_system.f_matrix', ['FMatrix']) from .branching_rules import BranchingRule, branching_rule_from_plethysm, branching_rule lazy_import('sage.combinat.root_system.non_symmetric_macdonald_polynomials', 'NonSymmetricMacdonaldPolynomials') diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py new file mode 100644 index 00000000000..50261cd4583 --- /dev/null +++ b/src/sage/combinat/root_system/f_matrix.py @@ -0,0 +1,753 @@ +""" +F-Matrix Factory for FusionRings +""" +# **************************************************************************** +# Copyright (C) 2019 Daniel Bump +# Guillermo Aboumrad +# Travis Scrimshaw +# Galit Anikeeva +# +# Distributed under the terms of the GNU General Public License (GPL) +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.misc.misc import inject_variable +from sage.matrix.constructor import matrix +from sage.rings.polynomial.all import PolynomialRing +from sage.rings.ideal import Ideal +from sage.combinat.root_system.fusion_ring import FusionRing +import sage.graphs +from sage.graphs.generators.basic import EmptyGraph +from itertools import product +from sage.misc.misc import inject_variable + +#Import pickle for checkpointing and loading certain variables +try: + import cPickle as pickle +except: + import pickle + +from sage.rings.polynomial.polydict import ETuple +# from sage.rings import AlgebraicField as QQbar + +class FMatrix(): + r"""Return an F-Matrix factory for a FusionRing. + + INPUT: + + - ``FR`` -- a FusionRing. + + We only undertake to compute the F-matrix if the + FusionRing is *multiplicity free* meaning that + the Fusion coefficients `N^{ij}_k` are bounded + by 1. For Cartan Types `X_r` and level `k`, + the multiplicity-free cases are given by the + following table. + ++------------------------+----------+ +| Cartan Type | `k` | ++========================+==========+ +| `A_1` | any | ++------------------------+----------+ +| `A_r, r\geq 2` | `\leq 2` | ++------------------------+----------+ +| `B_r, r\geq 2` | `\leq 2` | ++------------------------+----------+ +| `C_2` | `\leq 2` | ++------------------------+----------+ +| `C_r, r\geq 3` | `\leq 1` | ++------------------------+----------+ +| `D_r, r\geq 4` | `\leq 2` | ++------------------------+----------+ +| `G_2,F_4,E_r` | `\leq 2` | ++------------------------+----------+ + + Beyond this limitation, computation of the F-matrix + can involve very large systems of equations. A + rule of thumb is that this code can compute the + F-matrix for systems with `\leq 4` primary fields, + with the exception of `G_2` at level `2`. + + The FusionRing and its methods capture much + of the structure of the underlying tensor category. + But an important aspect that is not encoded in the + fusion ring is the associator, which is a homomorphism + `(A\otimes B)\otimes C\to A\otimes(B\otimes C)` + requires an additional tool, the F-matrix or 6j-symbol. + To specify this, we fix a simple object `D` + and represent the transformation + + .. MATH:: + + \text{Hom}(D,(A\otimes B)\otimes C) \to \text{Hom}(D,A\otimes(B\otimes C)) + + by a matrix `F^{ABC}_D`. This depends on a pair of + additional simple objects `X` and `Y`. Indeed, we can + get a basis for `\text{Hom}(D,(A\otimes B)\otimes C)` + indexed by simple objects `X` in which the corresponding + homomorphism factors through `X\otimes C`, and similarly + `\text{Hom}(D,A\otimes(B\otimes C))` has a basis indexed + by `Y`, in which the basis vector factors through `A\otimes Y`. + + See [TTWL2009]_ for an introduction to this topic, + [EGNO2015]_ Section 4.9 for a precise mathematical + definition and [Bond2007]_ Section 2.5 for a discussion + of how to compute the F-matrix. In addition to + [Bond2007]_ worked out F-matrices may be found in + [RoStWa2009]_ and [CuWa2015]_. + + The F-matrix is only determined up to a gauge. This + is a family of embeddings `C\to A\otimes B` for + simple objects `A,B,C` such that `\text{Hom}(C,A\otimes B)` + is nonzero. Changing the gauge changes the F-matrix though + not in a very essential way. By varying the gauge it is + possible to make the F-matrices unitary, or it is possible + to make them cyclotomic. We choose the latter. + + Due to the large number of equations we may fail to find a + Groebner basis if there are too many variables. + + EXAMPLES:: + + sage: I=FusionRing("E8",2,conjugate=True) + sage: I.fusion_labels(["i0","p","s"],inject_variables=True) + sage: f = FMatrix(I,inject_variables=True); f + creating variables fx1..fx14 + F-Matrix factory for The Fusion Ring of Type E8 and level 2 with Integer Ring coefficients + + We've exported two sets of variables to the global namespace. + We created three variables ``i0, p, s`` to represent the + primary fields (simple elements) of the FusionRing. Creating + the FMatrix factory also created variables ``fx1,fx2, ... , fx14`` + in order to solve the hexagon and pentagon equations describing + the F-matrix. Since we called ``FMatrix`` with the parameter ``inject_variables`` + set true, these have been exported into the global namespace. This + is not necessary for the code to work but if you want to + run the code experimentally you may want access to these + variables. + + EXAMPLES:: + + sage: f.fmatrix(s,s,s,s) + [fx10 fx11] + [fx12 fx13] + + The F-matrix has not been computed at this stage, so + the F-matrix `F^{sss}_s` is filled with variables + ``fx10``, ``fx11``, ``fx12``, ``fx13``. The task is + to solve for these. + + As explained above The F-matrix `(F^{ABC}_D)_{X,Y}` + two other variables `X` and `Y`. We have methods to + tell us (depending on `A,B,C,D`) what the possibilities + for these are. In this example with `A=B=C=D=s` + both `X` and `Y` are allowed to be `i_0` or `s`. + + EXAMPLES:: + + sage: f.f_from(s,s,s,s), f.f_to(s,s,s,s) + ([i0, p], [i0, p]) + + The last two statments show that the possible values of + `X` and `Y` when `A=B=C=D=s` are `i_0` and `p`. + + The F-matrix is computed by solving the so-called + pentagon and hexagon equations. The *pentagon + equations* reflect the Mac Lane pentagon axiom in the + definition of a monoidal category. The hexagon relations + reflect the axioms of a *braided monoidal category*, + which are constraints on both the F-matrix and on + the R-matrix. + + EXAMPLES:: + + sage: f.pentagon()[1:3] + equations: 41 + [-fx0*fx1 + fx1, -fx1*fx2^2 + fx1] + sage: f.hexagon()[1:3] + equations: 14 + [fx1*fx5 + fx2, fx2 + 1] + + You may solve these 41+14=55 equations to compute the F-matrix. + + EXAMPLES:: + + sage: f.get_solution(output=True) + Setting up hexagons and pentagons... + equations: 14 + equations: 37 + Finding a Groebner basis... + Solving... + Fixing the gauge... + adding equation... fx1 - 1 + adding equation... fx11 - 1 + Done! + {(s, s, s, s, i0, i0): (-1/2*zeta128^48 + 1/2*zeta128^16), + (s, s, s, s, i0, p): 1, + (s, s, s, s, p, i0): 1/2, + (s, s, s, s, p, p): (1/2*zeta128^48 - 1/2*zeta128^16), + (s, s, p, i0, p, s): (-1/2*zeta128^48 + 1/2*zeta128^16), + (s, s, p, p, i0, s): (-zeta128^48 + zeta128^16), + (s, p, s, i0, s, s): 1, + (s, p, s, p, s, s): -1, + (s, p, p, s, s, i0): 1, + (p, s, s, i0, s, p): (-zeta128^48 + zeta128^16), + (p, s, s, p, s, i0): (-1/2*zeta128^48 + 1/2*zeta128^16), + (p, s, p, s, s, s): -1, + (p, p, s, s, i0, s): 1, + (p, p, p, p, i0, i0): 1} + + We now have access to the values of the F-mstrix using + the methods :meth:`fmatrix` and :meth:`fmat`. + + EXAMPLES:: + + sage: f.fmatrix(s,s,s,s) + [(-1/2*zeta128^48 + 1/2*zeta128^16) 1] + [ 1/2 (1/2*zeta128^48 - 1/2*zeta128^16)] + sage: f.fmat(s,s,s,s,p,p) + (1/2*zeta128^48 - 1/2*zeta128^16) + + """ + def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variables=False): + self.FR = fusion_ring + if self.FR._fusion_labels is None: + self.FR.fusion_labels(fusion_label, inject_variables=True) + #Set up F-symbols entry by entry + n_vars = self.findcases() + self._poly_ring = PolynomialRing(self.FR.field(),n_vars,var_prefix) + if inject_variables: + print ("creating variables %s%s..%s%s"%(var_prefix,1,var_prefix,n_vars)) + for i in range(self._poly_ring.ngens()): + inject_variable("%s%s"%(var_prefix,i),self._poly_ring.gens()[i]) + self._var_to_sextuple, self._fvars = self.findcases(output=True) + self._singles = self.singletons() + + #Initialize list of defining equations + self.ideal_basis = list() + + #Initialize empty set of solved F-symbols + self.solved = set() + + #New attributes of the FMatrix class + self._var_to_idx = { var : idx for idx, var in enumerate(self._poly_ring.gens()) } + self._known_vals = dict() + self.field = self.FR.field() + self.temp_eqns = [] + + ####################### + ### Class utilities ### + ####################### + + def __repr__(self): + """ + EXAMPLES:: + + sage: FMatrix(FusionRing("B2",1)) + F-Matrix factory for The Fusion Ring of Type B2 and level 1 with Integer Ring coefficients + """ + return "F-Matrix factory for %s"%self.FR + + def remaining_vars(self): + """ + Return a list of unknown F-symbols (reflects current stage of computation) + """ + return [var for var in self._poly_ring.gens() if var not in self.solved] + + def clear_equations(self): + """ + Clear the set of equations to be solved. + """ + self.ideal_basis = list() + + def clear_vars(self): + """ + Clear the set of variables. + """ + self._fvars = { self._var_to_sextuple[key] : key for key in self._var_to_sextuple } + self.solved = set() + + def fmat(self, a, b, c, d, x, y, data=True): + """ + Return the F-Matrix coefficient `(F^{a,b,c}_d)_{x,y}` + + EXAMPLES:: + + sage: f=FMatrix(FusionRing("G2",1),fusion_label=["i0","t"]) + sage: [f.fmat(t,t,t,t,x,y) for x in f.FR.basis() for y in f.FR.basis()] + [fx1, fx2, fx3, fx4] + sage: f.get_solution(output=True) + Setting up hexagons and pentagons... + equations: 5 + equations: 10 + Finding a Groebner basis... + Solving... + Fixing the gauge... + adding equation... fx2 - 1 + Done! + {(t, t, t, i0, t, t): 1, + (t, t, t, t, i0, i0): (-zeta60^14 + zeta60^6 + zeta60^4 - 1), + (t, t, t, t, i0, t): 1, + (t, t, t, t, t, i0): (-zeta60^14 + zeta60^6 + zeta60^4 - 1), + (t, t, t, t, t, t): (zeta60^14 - zeta60^6 - zeta60^4 + 1)} + sage: [f.fmat(t,t,t,t,x,y) for x in f.FR.basis() for y in f.FR.basis()] + [(-zeta60^14 + zeta60^6 + zeta60^4 - 1), + 1, + (-zeta60^14 + zeta60^6 + zeta60^4 - 1), + (zeta60^14 - zeta60^6 - zeta60^4 + 1)] + + """ + if self.FR.Nk_ij(a,b,x) == 0 or self.FR.Nk_ij(x,c,d) == 0 or self.FR.Nk_ij(b,c,y) == 0 or self.FR.Nk_ij(a,y,d) == 0: + return 0 + + #Some known zero F-symbols + if a == self.FR.one(): + if x == b and y == d: + return 1 + else: + return 0 + if b == self.FR.one(): + if x == a and y == c: + return 1 + else: + return 0 + if c == self.FR.one(): + if x == d and y == b: + return 1 + else: + return 0 + if data: + #Better to use try/except for speed. Somewhat trivial, but worth + #hours when method is called ~10^11 times + try: + return self._fvars[a,b,c,d,x,y] + except KeyError: + return 0 + else: + return (a,b,c,d,x,y) + + def fmatrix(self,a,b,c,d): + """ + Return the F-Matrix `F^{a,b,c}_d`. + + INPUT: + + - ``a,b,c,d`` -- basis elements of the FusionRing + + EXAMPLES:: + + sage: f=FMatrix(FusionRing("A1",2),fusion_label="c") + sage: f.get_solution(verbose=False); + equations: 14 + equations: 37 + adding equation... fx4 - 1 + adding equation... fx10 - 1 + sage: f.f_from(c1,c1,c1,c1) + [c0, c2] + sage: f.f_to(c1,c1,c1,c1) + [c0, c2] + sage: f.fmatrix(c1,c1,c1,c1) + [ (1/2*zeta32^12 - 1/2*zeta32^4) (-1/2*zeta32^12 + 1/2*zeta32^4)] + [ (1/2*zeta32^12 - 1/2*zeta32^4) (1/2*zeta32^12 - 1/2*zeta32^4)] + """ + + X = self.f_from(a,b,c,d) + Y = self.f_to(a,b,c,d) + return matrix([[self.fmat(a,b,c,d,x,y) for y in Y] for x in X]) + + def findcases(self,output=False): + """ + Return unknown F-matrix entries. If run with output=True, + this returns two dictionaries; otherwise it just returns the + number of unknown values. + + EXAMPLES:: + + sage: f=FMatrix(FusionRing("G2",1),fusion_label=["i0","t"]) + sage: f.findcases() + 5 + sage: f.findcases(output=True) + ({fx4: (t, t, t, t, t, t), + fx3: (t, t, t, t, t, i0), + fx2: (t, t, t, t, i0, t), + fx1: (t, t, t, t, i0, i0), + fx0: (t, t, t, i0, t, t)}, + {(t, t, t, i0, t, t): fx0, + (t, t, t, t, i0, i0): fx1, + (t, t, t, t, i0, t): fx2, + (t, t, t, t, t, i0): fx3, + (t, t, t, t, t, t): fx4}) + + """ + i = 0 + if output: + idx_map = dict() + ret = dict() + for (a,b,c,d) in list(product(self.FR.basis(), repeat=4)): + for x in self.f_from(a, b, c, d): + for y in self.f_to(a, b, c, d): + fm = self.fmat(a, b, c, d, x, y, data=False) + if fm is not None and fm not in [0,1]: + if output: + v = self._poly_ring.gens()[i] + ret[(a,b,c,d,x,y)] = v + idx_map[v] = (a, b, c, d, x, y) + i += 1 + if output: + return idx_map, ret + else: + return i + + def singletons(self): + """ + Find x_i that are automatically nonzero, because their F-matrix is 1x1 + """ + ret = [] + for (a, b, c, d) in list(product(self.FR.basis(), repeat=4)): + (ff,ft) = (self.f_from(a,b,c,d),self.f_to(a,b,c,d)) + if len(ff) == 1 and len(ft) == 1: + v = self._fvars.get((a,b,c,d,ff[0],ft[0]), None) + if v in self._poly_ring.gens(): + ret.append(v) + return ret + + def f_from(self,a,b,c,d): + r""" + Return the possible `x` such that there are morphisms + `d\to x\otimes c\to (a\otimes b)\otimes c`. + + INPUT: + + - ``a,b,c,d`` -- basis elements of the FusionRing. + + EXAMPLES:: + + sage: f=FMatrix(FusionRing("A1",3),fusion_label="a") + sage: f.fmatrix(a1,a1,a2,a2) + [fx6 fx7] + [fx8 fx9] + sage: f.f_from(a1,a1,a2,a2) + [a0, a2] + sage: f.f_to(a1,a1,a2,a2) + [a1, a3] + """ + + return [x for x in self.FR.basis() if self.FR.Nk_ij(a,b,x) != 0 and self.FR.Nk_ij(x,c,d) != 0] + + def f_to(self,a,b,c,d): + r""" + Return the possible `y` such that there are morphisms + `d\to a\otimes y\to a\otimes(b\otimes c)`. + + INPUT: + + - ``a,b,c,d`` -- basis elements of the FusionRing. + + EXAMPLES:: + + sage: B=FMatrix(FusionRing("B2",2),fusion_label="b") + sage: B.fmatrix(b2,b4,b4,b2) + [fx278 fx279 fx280] + [fx281 fx282 fx283] + [fx284 fx285 fx286] + sage: B.f_from(b2,b4,b4,b2) + [b1, b3, b5] + sage: B.f_to(b2,b4,b4,b2) + [b0, b1, b5] + + """ + + return [y for y in self.FR.basis() if self.FR.Nk_ij(b,c,y) != 0 and self.FR.Nk_ij(a,y,d) != 0] + + #################### + ### Data getters ### + #################### + + def get_fmats_in_alg_field(self): + return { sextuple : self._qqbar_embedding(fvar) for sextuple, fvar in self._fvars.items() } + + #Return radical expression for fmats for easy visualization + def get_radical_expression(self): + return { sextuple : val.radical_expression() for sextuple, val in get_fmats_in_alg_field().items() } + + #Construct a dictionary of idx, known_val pairs for equation substitution + def get_known_vals(self): + return { var_idx : self._fvars[self._var_to_sextuple[self._poly_ring.gen(var_idx)]] for var_idx in self.solved } + + #Construct an ETuple indicating positions of known nonzero variables. + #MUST be called after fmats._ks = get_known_sq() + def get_known_nonz(self): + nonz = { self._var_to_idx[var] : 100 for var in self._singles } + for idx in self._ks: + nonz[idx] = 100 + return ETuple(nonz, fmats._poly_ring.ngens()) + + ############################## + ### Variables partitioning ### + ############################## + + #Get the size of the largest F-matrix F^{abc}_d + def largest_fmat_size(self): + return max(self.fmatrix(*tup).nrows() for tup in product(self.FR.basis(),repeat=4)) + + #Partition the F-symbols according to the size of the F-matrix F^{abc}_d + #they belong to + def get_fmats_by_size(self,n): + fvars_copy = deepcopy(self._fvars) + self.clear_vars() + var_set = set() + for quadruple in product(self.FR.basis(),repeat=4): + F = self.fmatrix(*quadruple) + #Discard trivial 1x1 F-matrix, if applicable + if F.nrows() == n and F.coefficients() != [1]: + var_set.update(self._var_to_idx[fx] for fx in F.coefficients()) + self._fvars = fvars_copy + return var_set + + ############################ + ### Checkpoint utilities ### + ############################ + + #Auto-generate filename string + def get_fr_str(self): + return self.FR.cartan_type()[0] + str(self.FR.cartan_type()[1]) + str(self.FR.fusion_level()) + + def save_fvars(self,filename): + with open(filename, 'wb') as f: + pickle.dump(self._fvars, f) + # pickle.dump([fmats._fvars, fmats.solved], f) + + #If provided, optional param save_dir should have a trailing forward slash + def load_fvars(self,save_dir=""): + with open(save_dir + "saved_fvars_" + self.get_fr_str() + ".pickle", 'rb') as f: + self._fvars = pickle.load(f) + # fmats._fvars, fmats.solved = pickle.load(f) + + ######################## + ### Equations set up ### + ######################## + + def poly_to_tup(self,poly): + return tuple(poly.dict().items()) + + def tup_to_poly(self,eq_tup,parent=None): + if parent is None: + parent = self._poly_ring + return parent({ exp : coeff for exp, coeff in eq_tup }) + + def get_orthogonality_constraints(self,output=True): + eqns = list() + for tup in product(self.FR.basis(), repeat=4): + mat = self.fmatrix(*tup) + eqns.extend((mat.T * mat - matrix.identity(mat.nrows())).coefficients()) + if output: + return eqns + self.ideal_basis.extend([self.poly_to_tup(eq) for eq in eqns]) + + def feq(self, a, b, c, d, e, f, g, k, l, prune=False): + """ + Return True if the Pentagon axiom ([Bond2007]_ (2.77)) is satisfied. + """ + lhs = self.fmat(f,c,d,e,g,l)*self.fmat(a,b,l,e,f,k) + rhs = sum(self.fmat(a,b,c,g,f,h)*self.fmat(a,h,d,e,g,k)*self.fmat(b,c,d,k,h,l) for h in self.FR.basis()) + if lhs != 0 or not prune: # it is believed that if lhs=0, the equation carries no new information + return lhs - rhs + else: + return 0 + + def req(self, a, b, c, d, e, g, side="left"): + """ + Return A hexagon equation (Bond[2007]_ (2.78) or (2.79)). + + INPUT: + + - ``a,b,c,d,e,f`` -- basis elements of the FusionRing + - ``side`` -- (default left) which hexagon axiom to use + + """ + if side == "left": + lhs = self.FR.r_matrix(a,c,e)*self.fmat(a,c,b,d,e,g)*self.FR.r_matrix(b,c,g) + rhs = sum(self.fmat(c,a,b,d,e,f)*self.FR.r_matrix(f,c,d)*self.fmat(a,b,c,d,f,g) for f in self.FR.basis()) + elif side == "right": + # r(a,b,x) is a root of unity, so its inverse is its complex conjugate + lhs = self.FR.r_matrix(c,a,e).conjugate()*self.fmat(a,c,b,d,e,g)*self.FR.r_matrix(c,b,g).conjugate() + rhs = sum(self.fmat(c,a,b,d,e,f)*self.FR.r_matrix(c,f,d).conjugate()*self.fmat(a,b,c,d,f,g) for f in self.FR.basis()) + return lhs-rhs + + def pentagon(self, verbose=False, output=True, factor=False, prune=False): + """ + Return generators of the ideal of Pentagon equations. + + INPUT: + + - ``verbose`` -- (optional) set True for verbose. Default False + - ``output`` -- (optional) set True to output a set of equations. Default True + - ``factor`` -- (optional) set True for sreduce simplified equations. + + In contrast with the hexagon equations, where setting ``factor`` True + is a big improvement, for the pentagon equations this option produces + little or no simplification. So the default is False. + + EXAMPLES:: + + sage: p = FMatrix(FusionRing("A2",1),fusion_label="c") + sage: p.pentagon()[-3:] + equations: 16 + [-fx5*fx6 + fx1, -fx4*fx6*fx7 + fx2, -fx5*fx7^2 + fx3*fx6] + sage: p.pentagon(factor=True)[-3:] + equations: 16 + [fx5*fx6 - fx1, fx4*fx6*fx7 - fx2, fx5*fx7^2 - fx3*fx6] + + """ + if output: + ret = [] + for (a,b,c,d,e,f,g,k,l) in list(product(self.FR.basis(), repeat=9)): + pd = self.feq(a,b,c,d,e,f,g,k,l,prune=prune) + if pd != 0: + if factor: + pd = self.sreduce(pd) + if output: + ret.append(pd) + if verbose: + print ("%s,%s,%s,%s,%s,%s,%s,%s,%s : %s"%(a,b,c,d,e,f,g,k,l,pd)) + print ("equations: %s"%len(ret)) + if output: + return ret + + ##################### + ### Graph methods ### + ##################### + + def equations_graph(self,eqns=None): + """ + Return a graph whose vertices are variables + and whose edges correspond to equations + relating two variables. + + This graph may contain isolated vertices... + + INPUT: + + - ``equations`` -- a list of equations + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("A1",3)) + sage: G = f.equation_graph(f.hexagon(factor=True)) + equations: 71 + sage: G.connected_components_number() + 14 + sage: G1=G.connected_components_subgraphs()[0] + sage: G1.size() + 60 + sage: G1.is_regular() + True + + """ + if eqns is None: + eqns = self.ideal_basis + + G = sage.graphs.generators.basic.EmptyGraph() + G.add_vertices([x for eq_tup in eqns for x in variables(eq_tup)]) + for eq_tup in eqns: + s = [v for v in variables(eq_tup)] + for x in s: + for y in s: + if y!=x: + G.add_edge(x,y) + return G + + ####################### + ### Solution method ### + ####################### + + def get_solution(self, equations=None, factor=True, verbose=True, prune=True, algorithm='', output=False): + """ + Solve the the hexagon and pentagon relations to evaluate the F-matrix. + + INPUT: + + - ``equations`` -- (optional) a set of equations to be + solved. Defaults to the hexagon and pentagon equations. + - ``factor`` -- (default: ``True``). Set true to use + the sreduce method to simplify the hexagon and pentagon + equations before solving them. + - ``algorithm`` -- (optional). Algorithm to compute Groebner Basis. + - ``output`` -- (optional, default False). Output a dictionary of + F-matrix values. This may be useful to see but may be omitted + since this information will be available afterwards via the + :meth:`fmatrix` and :meth:`fmat` methods. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("A2",1),fusion_label="a") + sage: f.get_solution(verbose=False,output=True) + equations: 8 + equations: 16 + adding equation... fx4 - 1 + {(a2, a2, a2, a0, a1, a1): 1, + (a2, a2, a1, a2, a1, a0): 1, + (a2, a1, a2, a2, a0, a0): 1, + (a2, a1, a1, a1, a0, a2): 1, + (a1, a2, a2, a2, a0, a1): 1, + (a1, a2, a1, a1, a0, a0): 1, + (a1, a1, a2, a1, a2, a0): 1, + (a1, a1, a1, a0, a2, a2): 1} + + After you successfully run ``get_solution`` you may check + the correctness of the F-matrix by running :meth:`hexagon` + and :meth:`pentagon`. These should return empty lists + of equations. In this example, we turn off the factor + and prune optimizations to test all instances. + + EXAMPLES:: + + sage: f.hexagon(factor=False) + equations: 0 + [] + sage: f.hexagon(factor=False,side="right") + equations: 0 + [] + sage: f.pentagon(factor=False,prune=False) + equations: 0 + [] + + """ + if equations is None: + if verbose: + print("Setting up hexagons and pentagons...") + equations = self.hexagon(verbose=False, factor=factor)+self.pentagon(verbose=False, factor=factor, prune=prune) + if verbose: + print("Finding a Groebner basis...") + self.ideal_basis = set(Ideal(equations).groebner_basis(algorithm=algorithm)) + if verbose: + print("Solving...") + self.substitute_degree_one() + if verbose: + print("Fixing the gauge...") + self.fix_gauge(algorithm=algorithm) + if verbose: + print("Done!") + if output: + return self._fvars + + ##################### + ### Verifications ### + ##################### + + #Ensure the hexagon equations are satisfied + def verify_hexagons(self): + hex = [] + for a,b,c,d,e,g in product(self.FR.basis(),repeat=6): + lhs = self.field(self.FR.r_matrix(a,c,e))*self.fmat(a,c,b,d,e,g)*self.field(self.FR.r_matrix(b,c,g)) + rhs = sum(self.fmat(c,a,b,d,e,f)*self.field(self.FR.r_matrix(f,c,d))*self.fmat(a,b,c,d,f,g) for f in self.FR.basis()) + hex.append(lhs-rhs) + return list(set(hex)) + + #Verify that all F-matrices are real and unitary (orthogonal) + def fmats_are_orthogonal(self): + is_orthog = [] + for a,b,c,d in product(self.FR.basis(),repeat=4): + mat = self.fmatrix(a,b,c,d) + is_orthog.append(mat.T * mat == matrix.identity(mat.nrows())) + return all(is_orthog) diff --git a/src/sage/combinat/root_system/map_reduce_engine.pxd b/src/sage/combinat/root_system/map_reduce_engine.pxd new file mode 100644 index 00000000000..15b5aeb0607 --- /dev/null +++ b/src/sage/combinat/root_system/map_reduce_engine.pxd @@ -0,0 +1,7 @@ +cdef class MapReduceEngine(): + cdef public list worker_results + cdef public object input_iter + cdef public int mp_thresh + + cpdef map_caller(self,mp_params,mapper,extra_args=*) + diff --git a/src/sage/combinat/root_system/map_reduce_engine.pyx b/src/sage/combinat/root_system/map_reduce_engine.pyx new file mode 100644 index 00000000000..3bd01615aac --- /dev/null +++ b/src/sage/combinat/root_system/map_reduce_engine.pyx @@ -0,0 +1,118 @@ +############################ +### Parlallel processing ### +############################ +from functools import partial +cimport cython + +#Rudimentary framework for performing MapReduce style computations +#DISCLAIMER: does not +cdef class MapReduceEngine(): + def __init__(self, mp_thresh=2500): + self.worker_results = list() + self.input_iter = None + #Multiprocess size parameter + self.mp_thresh = mp_thresh + + ################ + ### Reducers ### + ################ + + #Helper function for returning processed results back to parent process + #Trivial reducer: simply collects objects with the same key in the worker + def reduce_single_proc(self,proc): + #Discard the zero polynomial + reduced = set(self.worker_results)-set([tuple()]) + self.worker_results = list() + return list(reduced) + + #All-to-one communication step + def reduce_multi_process(self,reducer,worker_pool): + collected_eqns = set() + for child_eqns in worker_pool.imap_unordered(reducer,range(worker_pool._processes)): + collected_eqns.update(child_eqns) + return list(collected_eqns) + + ################# + ### MapReduce ### + ################# + + #Map fn across input_iter and reduce output to polynomial + #If worker_pool is not provided, function maps and reduces on a single process. + #If worker_pool is provided, the function attempts to determine whether it should + #use multiprocessing based on the length of the input iterable. If it can't determine + #the length of the input iterable then it uses multiprocessing with the default chunksize of 1 + #if chunksize is not explicitly provided. + def map_triv_reduce(self,mapper,input_iter,reducer=None,worker_pool=None,chunksize=None,mp_thresh=None): + #Compute multiprocessing parameters + if worker_pool is not None: + try: + n = len(input_iter) + except: + n = mp_thresh + 1 + if chunksize is None: + chunksize = n // (worker_pool._processes**2) + 1 + if mp_thresh is None: + mp_thresh = self.mp_thresh + no_mp = worker_pool is None or n < mp_thresh + #Map phase. Casting Async Object blocks execution... Each process holds results + #in its copy of fmats.temp_eqns + if no_mp: + list(map(mapper,input_iter)) + else: + list(worker_pool.imap_unordered(mapper,input_iter,chunksize=chunksize)) + + #Early termination in case no reducer is provided + if reducer is None: + return + + #Reduce phase + if no_mp: + results = reducer(0) + else: + results = self.reduce_multi_process(reducer,worker_pool) + return results + + #Map the given mapper across the input iterable creted using + #input_iter_setter, and then reduce using the provided reducer. + #The input iterable is created in each worker process with minimal communication: + #the parent only tells woker how to create iterable, but does NOT send an + #iterator object or its contents + + ###INPUT: + #input_iter_setter is a function that takes as a single argument an integer + #0 <= proc <= worker_pool._processes. If worker_pool is None, then + #input_iter_setter(0) is called + #The function input_iter_setter should set mr_eng.input_iter to a desired value + #mapper is a function that calls mr_eng.map_caller with appropriate arguments, + #that are bound to the function inside each worker process + #mapper does NOT return; it appends results to mr_eng.worker_results + #reducer is a function that instructs the engine how to collect results + #corresponding to the same key in each worker process + #input_iter_setter, mapper, AND reducer are instructions that will be + #processed inside each worker in the provided pool + + ###NOTES: set_input_iter MUST be called BEFORE + def emap_no_comm(self,input_iter_setter,mapper,reducer,worker_pool=None): + n_proc = worker_pool._processes if worker_pool is not None else 1 + params = [(child_id, n_proc) for child_id in range(n_proc)] + self.map_triv_reduce(input_iter_setter,params,worker_pool=worker_pool,chunksize=1,mp_thresh=0) + return self.map_triv_reduce(mapper,params,reducer,worker_pool=worker_pool,chunksize=1,mp_thresh=0) + + @cython.wraparound(False) + @cython.nonecheck(False) + @cython.cdivision(True) + cpdef map_caller(self,mp_params,mapper,extra_args=None): + cdef int child_id, i, n_proc + child_id, n_proc = mp_params + #Plug in additional arguments, if any + if extra_args is not None: + mapper = partial(mapper,*extra_args) + if self.input_iter is None: + raise ValueError("set_input_iter must be used to create input iterable in each worker process") + for i, input in enumerate(self.input_iter): + if i % n_proc == child_id: + res = mapper(input) + if res: + self.worker_results.append(res) + #Re-set iterator + self.input_iter = None diff --git a/src/sage/combinat/root_system/poly_tup_engine.pxd b/src/sage/combinat/root_system/poly_tup_engine.pxd new file mode 100644 index 00000000000..28f5d9138bc --- /dev/null +++ b/src/sage/combinat/root_system/poly_tup_engine.pxd @@ -0,0 +1,25 @@ +from sage.rings.polynomial.polydict cimport ETuple + +cpdef tuple resize(tuple eq_tup, dict idx_map, int nvars) +cdef ETuple degrees(tuple poly_tup) +cpdef ETuple get_variables_degrees(list eqns) +cpdef list variables(tuple eq_tup) +cpdef constant_coeff(tuple eq_tup) +cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map) +cpdef bint tup_fixes_sq(tuple eq_tup) +cpdef dict subs_squares(dict eq_dict, dict known_sq) +cdef dict remove_gcf(dict eq_dict, ETuple nonz) +cdef tuple to_monic(dict eq_dict) +cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq) +cpdef dict compute_known_powers(ETuple max_deg, dict val_dict) +cpdef dict subs(tuple poly_tup, dict known_powers) +cdef tuple tup_mul(tuple p1, tuple p2) +cpdef update_reduce(tuple eq_tup, factory, mr_eng) +cpdef int poly_tup_cmp(tuple tleft, tuple tright) +cpdef tuple req_cy(factory, tuple sextuple, side=*) +cpdef tuple feq_cy(factory, tuple nonuple, bint prune=*) +cpdef feq_verif(factory, tuple nonuple, float tol=*) +cpdef pent_verify(tuple mp_params, factory) +cpdef tuple rationalify(factory, tuple eq_tup) +cpdef tuple cyclotomify(factory, tuple rat_tup) + diff --git a/src/sage/combinat/root_system/poly_tup_engine.pyx b/src/sage/combinat/root_system/poly_tup_engine.pyx new file mode 100644 index 00000000000..02c7a6f7c2e --- /dev/null +++ b/src/sage/combinat/root_system/poly_tup_engine.pyx @@ -0,0 +1,390 @@ +from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular, MPolynomialRing_libsingular +from sage.rings.polynomial.polydict cimport ETuple, PolyDict +from sage.rings.polynomial.term_order import TermOrder +from sage.rings.rational_field import RationalField as QQ + +###NOTE: issues with importing NumberFieldElement_absolute and FMatrix +#from sage.rings.number_field.number_field_element cimport NumberFieldElement +#from sage.combinat.root_system.f_matrix import FMatrix + +################################################### +### Arithmetic Engine for polynomials as tuples ### +################################################### + +########### +### API ### +########### + +#Convert a polynomial object into the internal representation as tuple of +#(ETuple exp, NumberFieldElement coeff) pairs +cpdef tuple poly_to_tup(MPolynomial_libsingular poly): + return tuple(poly.dict().items()) + +#Return a polynomial object from its tuple representation. Inverse of poly_to_tup. +#poly_to_tup(tup_to_poly(eq_tup, ring)) == eq_tup && tup_to_poly(poly_to_tup(eq), eq.parent()) == eq +#Assumes parent.ngens() == len(exp_tup) for exp_tup, c in eq_tup +cpdef MPolynomial_libsingular tup_to_poly(tuple eq_tup, MPolynomialRing_libsingular parent): + # Maybe the following is faster but we need to ensure all coefficients are + # already in fmats._poly_ring.base_ring() so that implicit casting is avoided + # (this is pretty slow) + # return parent._element_constructor_({ exp : c for exp, c in tup_of_pairs }, check=False) + return parent(dict(eq_tup)) + +###################### +### "Change rings" ### +###################### + +#Given a tuple of pairs representing a univariate polynomial and a univariate +#polynomial ring generator, return a univariate polynomial object +def tup_to_univ_poly(eq_tup, gen): + univ_tup = tuple((exp.nonzero_values()[0] if exp.nonzero_values() else 0, c) for exp, c in eq_tup) + return sum(c * gen ** p for p, c in univ_tup) + +#Return a tuple representing a polynomial in a ring with len(sorted_vars) generators +#This method is used for creating polynomial objects with the "right number" of +#variables for computing Groebner bases of the partitioned equations graph +#and for adding constraints ensuring certain F-symbols are nonzero +cpdef tuple resize(tuple eq_tup, dict idx_map, int nvars): + cdef ETuple new_e + cdef list resized = list() + for exp, c in eq_tup: + new_e = ETuple({ idx_map[pos] : d for pos, d in exp.sparse_iter() }, nvars) + resized.append((new_e,c)) + return tuple(resized) + +########################### +### Convenience methods ### +########################### + +#Return the maximal degree of each variable in the polynomial +cdef ETuple degrees(tuple poly_tup): + #Deal with the empty tuple, representing the zero polynomial + if not poly_tup: return ETuple() + cdef ETuple max_degs, exp + max_degs = poly_tup[0][0] + for exp, c in poly_tup[1:]: + max_degs = max_degs.emax(exp) + return max_degs + +#Find maximum degrees for each variable in equations +cpdef ETuple get_variables_degrees(list eqns): + cdef tuple eq_tup + cdef ETuple max_deg + max_deg = degrees(eqns[0]) + for eq_tup in eqns[1:]: + max_deg = max_deg.emax(degrees(eq_tup)) + return max_deg + +#Return indices of all variables appearing in eq_tup +cpdef list variables(tuple eq_tup): + return degrees(eq_tup).nonzero_positions() + +#Return the constant coefficient of the polynomial represented by given tuple +cpdef constant_coeff(tuple eq_tup): + for exp, coeff in eq_tup: + if exp.is_constant(): + return coeff + return 0 + +cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map): + cdef list new_tup = list() + for exp, coeff in eq_tup: + new_tup.append((exp, coeff_map(coeff))) + return tuple(new_tup) + +#Determine if given equation fixes the square of a variable. An equation +#fixes the sq of a variable if it is of the form a*x^2 + c for NONZERO constants a, c +cpdef bint tup_fixes_sq(tuple eq_tup): + return len(eq_tup) == 2 and len(variables(eq_tup)) == 1 and eq_tup[0][0].nonzero_values() == [2] + +###################### +### Simplification ### +###################### + +#Substitutes for known squares in a given polynomial. +#The parameter known_sq is a dictionary of (int i, NumberFieldElement a) pairs such that x_i^2 - a == 0 +#Returns a dictionary of (ETuple, coeff) pairs representing polynomial +cpdef dict subs_squares(dict eq_dict, dict known_sq): + cdef dict subbed, new_e + cdef ETuple exp, lm + cdef int idx, power + subbed = dict() + for exp, coeff in eq_dict.items(): + new_e = dict() + for idx, power in exp.sparse_iter(): + if idx in known_sq: + coeff *= known_sq[idx] ** (power // 2) + #New power is 1 if power is odd + if power & True: + new_e[idx] = 1 + else: + new_e[idx] = power + exp = ETuple(new_e, len(exp)) + #If exponent tuple is already present in dictionary, coefficients are added + if exp in subbed: + subbed[exp] += coeff + else: + subbed[exp] = coeff + return { exp : a for exp, a in subbed.items() } + +#Returns a dictionary of (ETuple, coeff) pairs describing the polynomial eq / GCF(eq) +#The input nonz is an ETuple indicating the positions of variables known to be nonzero. +#The entries of nonz are assumed to be some relatively large number, like 100 +cdef dict remove_gcf(dict eq_dict, ETuple nonz): + #Find common variables, filtered according to known nonzeros + cdef ETuple common_powers, exp + common_powers = nonz + for exp, c in eq_dict.items(): + common_powers = common_powers.emin(exp) + return { exp.esub(common_powers) : c for exp, c in eq_dict.items() } + +#Return tuple of pairs (ETuple, coeff) describing the monic polynomial associated to eq_dict +#Here, the leading coefficient is chosen according to the degree reverse lexicographic ordering +#(default for multivariate polynomial rings) +cdef tuple to_monic(dict eq_dict): + if not eq_dict: return tuple() + it = reversed(sorted(eq_dict, key=TermOrder().sortkey_degrevlex)) + lm = next(it) + lc = eq_dict[lm] + if not lc: return tuple() + cdef ETuple exp + cdef list ret = [(lm, QQ().one())] + inv_lc = lc.inverse_of_unit() + for exp in it: + ret.append((exp, inv_lc * eq_dict[exp])) + return tuple(ret) + +# Return a dictionary describing a monic polynomial with no known nonzero gcd and +# no known squares +cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq): + if not eq_dict: return tuple() + return to_monic(remove_gcf(subs_squares(eq_dict, known_sq), nonz)) + +#################### +### Substitution ### +#################### + +#Pre-compute powers of known values for efficiency +cpdef dict compute_known_powers(ETuple max_deg, dict val_dict): + assert max(max_deg.nonzero_values(sort=False)) <= 100, "NotImplementedError: Cannot substitute for degree larger than 100" + max_deg = max_deg.emin(ETuple({ idx : 100 for idx in val_dict }, len(max_deg))) + cdef dict known_powers + #Get polynomial unit as tuple to initialize list elements + cdef tuple one = tuple(((ETuple({},len(max_deg)),QQ().one()),)) + cdef int d, var_idx + known_powers = { var_idx : [one]*(d+1) for var_idx, d in max_deg.sparse_iter() } + for var_idx, d in max_deg.sparse_iter(): + for power in range(d): + known_powers[var_idx][power+1] = tup_mul(known_powers[var_idx][power],val_dict[var_idx]) + return known_powers + +#Substitute given variables into a polynomial tuple +cpdef dict subs(tuple poly_tup, dict known_powers): + cdef dict subbed = {} + cdef ETuple exp, m + cdef int var_idx, power + cdef tuple temp + for exp, coeff in poly_tup: + #Get polynomial unit as tuple + temp = tuple(((ETuple({},len(exp)),QQ().one()),)) + for var_idx, power in exp.sparse_iter(): + if var_idx in known_powers: + exp = exp.eadd_p(-power,var_idx) + temp = tup_mul(temp,known_powers[var_idx][power]) + for m, c in temp: + if exp.eadd(m) in subbed: + subbed[exp.eadd(m)] += coeff*c + else: + subbed[exp.eadd(m)] = coeff*c + return subbed + +#Multiplication of two tuples... may have to make this faster +cdef tuple tup_mul(tuple p1, tuple p2): + cdef dict prod = dict() + cdef ETuple xi, yj + for xi, ai in p1: + for yj, bj in p2: + if xi.eadd(yj) in prod: + prod[xi.eadd(yj)] += ai*bj + else: + prod[xi.eadd(yj)] = ai*bj + return tuple(prod.items()) + +#Substitute known values, known squares, and reduce! +cpdef update_reduce(tuple eq_tup, factory, mr_eng): + cdef dict eq_dict = subs(eq_tup,factory._kp) + cdef reduced + if tup_fixes_sq(tuple(eq_dict.items())): + reduced = to_monic(eq_dict) + else: + reduced = reduce_poly_dict(eq_dict,factory._nnz,factory._ks) + mr_eng.worker_results.append(reduced) + +############### +### Sorting ### +############### + +#Determine which polynomial is larger with respect to the degrevlex ordering +cpdef int poly_tup_cmp(tuple tleft, tuple tright): + cdef int i, ret, sf, sg, val + cdef ETuple f, g + ret = 0 + for i in range(min(len(tleft),len(tright))): + f, g = tleft[i][0], tright[i][0] + if f == g: + if tleft[i][1] != tright[i][1]: + ret = -1 + 2*(tleft[i][1] > tright[i][1]) + else: + sf, sg = 0, 0 + for val in f.nonzero_values(sort=False): + sf += val + for val in g.nonzero_values(sort=False): + sg += val + ret = -1 + 2*(sf > sg or ( sf == sg and f.reversed() < g.reversed() )) + if ret != 0: + return ret + return len(tleft) - len(tright) + +############################ +### Fast FMatrix methods ### +############################ +from itertools import product + +#Given an FMatrix factory and a sextuple, return a pentagon equation as a polynomial object +cpdef tuple req_cy(factory, tuple sextuple, side="left"): + a, b, c, d, e, g = sextuple + #To add typing we need to ensure all fmats.fmat are of the same type? + #Return fmats._poly_ring.zero() and fmats._poly_ring.one() instead of 0 and 1? + lhs = factory.FR.r_matrix(a,c,e)*factory.fmat(a,c,b,d,e,g)*factory.FR.r_matrix(b,c,g) + rhs = 0 + for f in factory.FR.basis(): + rhs += factory.fmat(c,a,b,d,e,f)*factory.FR.r_matrix(f,c,d)*factory.fmat(a,b,c,d,f,g) + he = lhs - rhs + if he: + return reduce_poly_dict(he.dict(),factory._nnz,factory._ks) + else: + return tuple() + +#Set up and reduce the hexagon equations corresponding to this worker +# cpdef get_reduced_hexagons(factory, int child_id, int n_proc): +# cdef int i +# cdef tuple sextuple +# for i, sextuple in enumerate(product(factory.FR.basis(),repeat=6)): +# if i % n_proc == child_id: +# he = req_cy(factory,sextuple) +# if he: +# factory.temp_eqns.append(reduce_poly_dict(he.dict(),factory._nnz,factory._ks)) + +#Given an FMatrix factory and a nonuple, return a pentagon equation as a polynomial object + +cpdef tuple feq_cy(factory, tuple nonuple, bint prune=False): + a, b, c, d, e, f, g, k, l = nonuple + cdef lhs = factory.fmat(f,c,d,e,g,l)*factory.fmat(a,b,l,e,f,k) + if lhs == 0 and prune: # it is believed that if lhs=0, the equation carries no new information + return factory._poly_ring.zero() + cdef MPolynomial_libsingular rhs = factory._poly_ring.zero() + for h in factory.FR.basis(): + rhs += factory.fmat(a,b,c,g,f,h)*factory.fmat(a,h,d,e,g,k)*factory.fmat(b,c,d,k,h,l) + return reduce_poly_dict((lhs - rhs).dict(),factory._nnz,factory._ks) + +#Set up and reduce the pentagon equations corresponding to this worker +# cpdef get_reduced_pentagons(factory, int child_id, int n_proc, bint prune=True): +# cdef int i +# cdef tuple nonuple +# cdef MPolynomial_libsingular pe +# for i, nonuple in enumerate(product(factory.FR.basis(),repeat=9)): +# if i % n_proc == child_id: +# pe = feq_cy(factory,nonuple,prune=prune) +# if pe: +# factory.temp_eqns.append(reduce_poly_dict(pe.dict(),factory._nnz,factory._ks)) + +#################### +### Verification ### +#################### + +#Check the pentagon equation corresponding to the given nonuple +cpdef feq_verif(factory, tuple nonuple, float tol=5e-8): + a, b, c, d, e, f, g, k, l = nonuple + cdef float diff, lhs, rhs + lhs = factory.fmat(f,c,d,e,g,l)*factory.fmat(a,b,l,e,f,k) + rhs = 0.0 + for h in factory.FR.basis(): + rhs += factory.fmat(a,b,c,g,f,h)*factory.fmat(a,h,d,e,g,k)*factory.fmat(b,c,d,k,h,l) + diff = lhs - rhs + if diff > tol or diff < -tol: + factory.temp_eqns.append(diff) + +#Generate all the pentagon equations assigned to this process, and reduce them +from time import time +from os import getpid +cimport cython +@cython.wraparound(False) +@cython.nonecheck(False) +@cython.cdivision(True) +cpdef pent_verify(tuple mp_params, factory): + child_id, n_proc = mp_params + cdef float t0 + cdef tuple nonuple + cdef long i + t0 = time() + for i, nonuple in enumerate(product(factory.FR.basis(),repeat=9)): + if i % n_proc == child_id: + feq_verif(factory,nonuple) + if i % 50000000 == 0 and i: + print("{:5d}m equations checked in {:8.2f}... {} misses so far...".format(i // 1000000,time()-t0,len(factory.temp_eqns))) + print("Ran through {} pentagons in {:8.2f}... Worker {} with {} reporting {} total misses...".format(i,time()-t0,child_id,getpid(),len(factory.temp_eqns))) + +################################ +### Well, this was a bust... ### +################################ + +#Given a tuple representation of an n-variate polynomial over a cyclotomic field, +#return a tuple representation of an (n+1)-variate polynomial over the rationals, +#with the cyclotomic field generator treated as an indeterminate and used as the +#last variable in the parent polynomial ring +cpdef tuple rationalify(factory, tuple eq_tup): + F = factory.FR.field() + cdef list rat_tup = list() + cdef ETuple exp + for exp, coeff in eq_tup: + #In the future we should avoid casting by ensuring all coeffs created are + #in fmats.FR.field (eg. cython reducers, FMatrix.fmat) + for cyc_power, rat_coeff in F(coeff).polynomial().dict().items(): + nnz = { len(exp) : cyc_power } + for idx, val in exp.sparse_iter(): + nnz[idx] = val + new_e = ETuple(nnz, len(exp)+1) + rat_tup.append((new_e, rat_coeff)) + return tuple(rat_tup) + +#Given a tuple representation of an n-variate polynomial over the rationals, +#return a tuple repn of the polynomial with integer coefficients, obtained by +#multiplying all coefficients by the least common multiple of denominators +# cpdef tuple integralify(tuple rat_tup): +# cdef int cdenom = LCM(coeff.denominator() for exp, coeff in rat_tup) +# cdef list int_tup = list() +# for exp, coeff in rat_tup: +# int_tup.append((exp, int(cdenom * coeff))) +# return tuple(int_tup) + +#Left inverse of rationalify... +#cylcotomify(factory, rationalify(factory, eq_tup)) == eq_tup +cpdef tuple cyclotomify(factory, tuple rat_tup): + gen = factory.FR.field().gen() + cdef cyc_dict = dict() + cdef ETuple exp + for exp, coeff in rat_tup: + cyc_pow = exp.get_exp(len(exp)-1) + new_e = ETuple({ pos : val for pos, val in exp.sparse_iter() if pos != len(exp)-1 }, len(exp)-1) + if new_e in cyc_dict: + cyc_dict[new_e] += gen ** cyc_pow * coeff + else: + cyc_dict[new_e] = gen ** cyc_pow * coeff + return tuple(cyc_dict.items()) + +# def clear_denom(eq): +# for var in eq.variables(): +# if int(str(var)[1:]) >= fmats._poly_ring.ngens(): +# d = eq.degree(var) +# eq *= var ** d +# return eq From e129b942d5245c7af8cf0b55e94d5995a3db7ff9 Mon Sep 17 00:00:00 2001 From: dwbmscz <75940445+dwbmscz@users.noreply.github.com> Date: Fri, 19 Feb 2021 16:13:01 -0800 Subject: [PATCH 003/632] work on f_matrix.py --- src/sage/combinat/root_system/all.py | 1 + src/sage/combinat/root_system/f_matrix.py | 459 ++++++++++++++++++---- 2 files changed, 392 insertions(+), 68 deletions(-) diff --git a/src/sage/combinat/root_system/all.py b/src/sage/combinat/root_system/all.py index b8e06e4ff3d..7c620b9cac2 100644 --- a/src/sage/combinat/root_system/all.py +++ b/src/sage/combinat/root_system/all.py @@ -21,6 +21,7 @@ 'WeightRing']) lazy_import('sage.combinat.root_system.fusion_ring', ['FusionRing']) lazy_import('sage.combinat.root_system.f_matrix', ['FMatrix']) +lazy_import('sage.combinat.root_system.map_reduce_engine', ['MapReduceEngine']) from .branching_rules import BranchingRule, branching_rule_from_plethysm, branching_rule lazy_import('sage.combinat.root_system.non_symmetric_macdonald_polynomials', 'NonSymmetricMacdonaldPolynomials') diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 50261cd4583..b99aa1e2c3a 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -21,14 +21,18 @@ from itertools import product from sage.misc.misc import inject_variable + #Import pickle for checkpointing and loading certain variables try: import cPickle as pickle except: import pickle +from functools import cmp_to_key, partial from sage.rings.polynomial.polydict import ETuple -# from sage.rings import AlgebraicField as QQbar +from sage.combinat.root_system.poly_tup_engine import * +from sage.rings.qqbar import QQbar, number_field_elements_from_algebraics +import numpy as np class FMatrix(): r"""Return an F-Matrix factory for a FusionRing. @@ -230,9 +234,13 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab self.solved = set() #New attributes of the FMatrix class + self.field = self.FR.field() + r = self.field.defining_polynomial().roots(ring=QQbar,multiplicities=False)[0] + self._qqbar_embedding = self.field.hom([r],QQbar) self._var_to_idx = { var : idx for idx, var in enumerate(self._poly_ring.gens()) } + self._ks = self.get_known_sq() + self._nnz = self.get_known_nonz() self._known_vals = dict() - self.field = self.FR.field() self.temp_eqns = [] ####################### @@ -474,13 +482,31 @@ def get_radical_expression(self): def get_known_vals(self): return { var_idx : self._fvars[self._var_to_sextuple[self._poly_ring.gen(var_idx)]] for var_idx in self.solved } + #Construct a dictionary of known squares. Keys are variable indices and corresponding values are the squares + def get_known_sq(self,eqns=None): + if eqns is None: + eqns = self.ideal_basis + return { variables(eq_tup)[0] : -eq_tup[-1][1] for eq_tup in eqns if tup_fixes_sq(eq_tup) } + #Construct an ETuple indicating positions of known nonzero variables. - #MUST be called after fmats._ks = get_known_sq() + #MUST be called after self._ks = get_known_sq() def get_known_nonz(self): nonz = { self._var_to_idx[var] : 100 for var in self._singles } for idx in self._ks: nonz[idx] = 100 - return ETuple(nonz, fmats._poly_ring.ngens()) + return ETuple(nonz, self._poly_ring.ngens()) + + ######################### + ### Useful predicates ### + ######################### + + #Determine if monomial exponent is univariate in a still unknown F-symbol + def is_univariate_in_unknown(self,monom_exp): + return len(monom_exp.nonzero_values()) == 1 and monom_exp.nonzero_positions()[0] not in self.solved + + #Determine if monomial exponent is univariate and linear in a vstill unknown F-symbol + def is_uni_linear_in_unkwown(self,monom_exp): + return monom_exp.nonzero_values() == [1] and monom_exp.nonzero_positions()[0] not in self.solved ############################## ### Variables partitioning ### @@ -515,13 +541,13 @@ def get_fr_str(self): def save_fvars(self,filename): with open(filename, 'wb') as f: pickle.dump(self._fvars, f) - # pickle.dump([fmats._fvars, fmats.solved], f) + # pickle.dump([self._fvars, self.solved], f) #If provided, optional param save_dir should have a trailing forward slash def load_fvars(self,save_dir=""): with open(save_dir + "saved_fvars_" + self.get_fr_str() + ".pickle", 'rb') as f: self._fvars = pickle.load(f) - # fmats._fvars, fmats.solved = pickle.load(f) + # self._fvars, self.solved = pickle.load(f) ######################## ### Equations set up ### @@ -544,75 +570,114 @@ def get_orthogonality_constraints(self,output=True): return eqns self.ideal_basis.extend([self.poly_to_tup(eq) for eq in eqns]) - def feq(self, a, b, c, d, e, f, g, k, l, prune=False): - """ - Return True if the Pentagon axiom ([Bond2007]_ (2.77)) is satisfied. - """ - lhs = self.fmat(f,c,d,e,g,l)*self.fmat(a,b,l,e,f,k) - rhs = sum(self.fmat(a,b,c,g,f,h)*self.fmat(a,h,d,e,g,k)*self.fmat(b,c,d,k,h,l) for h in self.FR.basis()) - if lhs != 0 or not prune: # it is believed that if lhs=0, the equation carries no new information - return lhs - rhs - else: - return 0 - - def req(self, a, b, c, d, e, g, side="left"): - """ - Return A hexagon equation (Bond[2007]_ (2.78) or (2.79)). - - INPUT: + def get_hexagons(self,worker_pool=None,output=True): + he = mr_eng.emap_no_comm(hex_iter_setter,hex_caller,trivial_reducer_caller,worker_pool) + if output: + return [self.tup_to_fpoly(h) for h in he] + self.ideal_basis.extend(he) - - ``a,b,c,d,e,f`` -- basis elements of the FusionRing - - ``side`` -- (default left) which hexagon axiom to use + #If a worker_pool is passed, then we use multiprocessing + def get_pentagons(self,worker_pool=None,output=True): + pe = mr_eng.emap_no_comm(pent_iter_setter,pent_caller,trivial_reducer_caller,worker_pool) + if output: + return [self.tup_to_fpoly(p) for p in pe] + self.ideal_basis.extend(pe) - """ - if side == "left": - lhs = self.FR.r_matrix(a,c,e)*self.fmat(a,c,b,d,e,g)*self.FR.r_matrix(b,c,g) - rhs = sum(self.fmat(c,a,b,d,e,f)*self.FR.r_matrix(f,c,d)*self.fmat(a,b,c,d,f,g) for f in self.FR.basis()) - elif side == "right": - # r(a,b,x) is a root of unity, so its inverse is its complex conjugate - lhs = self.FR.r_matrix(c,a,e).conjugate()*self.fmat(a,c,b,d,e,g)*self.FR.r_matrix(c,b,g).conjugate() - rhs = sum(self.fmat(c,a,b,d,e,f)*self.FR.r_matrix(c,f,d).conjugate()*self.fmat(a,b,c,d,f,g) for f in self.FR.basis()) - return lhs-rhs - - def pentagon(self, verbose=False, output=True, factor=False, prune=False): - """ - Return generators of the ideal of Pentagon equations. + ############################ + ### Equations processing ### + ############################ - INPUT: + def tup_to_fpoly(eq_tup): + return tup_to_poly(eq_tup,parent=self._poly_ring) - - ``verbose`` -- (optional) set True for verbose. Default False - - ``output`` -- (optional) set True to output a set of equations. Default True - - ``factor`` -- (optional) set True for sreduce simplified equations. + #Solve for a linear term occurring in a two-term equation. + def solve_for_linear_terms(self,eqns=None): + if eqns is None: + eqns = self.ideal_basis - In contrast with the hexagon equations, where setting ``factor`` True - is a big improvement, for the pentagon equations this option produces - little or no simplification. So the default is False. + linear_terms_exist = False + for eq_tup in eqns: + if len(eq_tup) == 1: + m = eq_tup[0][0] + if self.is_univariate_in_unknown(m): + var = m.nonzero_positions()[0] + self._fvars[self._var_to_sextuple[self._poly_ring.gen(var)]] = tuple() + self.solved.add(var) + linear_terms_exist = True + if len(eq_tup) == 2: + monomials = [m for m, c in eq_tup] + max_var = monomials[0].emax(monomials[1]).nonzero_positions()[0] + for this, m in enumerate(monomials): + other = (this+1)%2 + if self.is_uni_linear_in_unkwown(m) and m.nonzero_positions()[0] == max_var and monomials[other][m.nonzero_positions()[0]] == 0: + var = m.nonzero_positions()[0] + rhs_key = monomials[other] + rhs_coeff = -eq_tup[other][1] / eq_tup[this][1] + self._fvars[self._var_to_sextuple[self._poly_ring.gen(var)]] = ((rhs_key,rhs_coeff),) + self.solved.add(var) + linear_terms_exist = True + return linear_terms_exist + + #Backward substitution step. Traverse variables in reverse lexicographical order. + def backward_subs(self): + for var in reversed(self._poly_ring.gens()): + sextuple = self._var_to_sextuple[var] + rhs = self._fvars[sextuple] + d = { var_idx : self._fvars[self._var_to_sextuple[self._poly_ring.gen(var_idx)]] for var_idx in variables(rhs) if var_idx in self.solved } + if d: + kp = compute_known_powers(get_variables_degrees([rhs]), d) + self._fvars[sextuple] = tuple(subs_squares(subs(rhs,kp),self._ks).items()) + + def update_reduction_params(self,eqns=None,worker_pool=None,children_need_update=False): + if eqns is None: + eqns = self.ideal_basis + self._ks, self._var_degs = self.get_known_sq(eqns), get_variables_degrees(eqns) + self._nnz = self.get_known_nonz() + self._kp = compute_known_powers(self._var_degs,self.get_known_vals()) + if worker_pool is not None and children_need_update: + #self._nnz and self._kp are computed in child processes to reduce IPC overhead + n_proc = worker_pool._processes + new_data = [(self._fvars,self.solved,self._ks,self._var_degs)]*n_proc + mr_eng.map_triv_reduce(update_child_fmats,new_data,worker_pool=worker_pool,chunksize=1,mp_thresh=0) + + #Perform triangular elimination of linear terms in two-term equations until no such terms exist + #For optimal usage of TRIANGULAR elimination, pass in a SORTED list of equations + #Returns a list of equations + def triangular_elim(self,required_vars=None,eqns=None,worker_pool=None,verbose=True): + ret = True + if eqns is None: + eqns = self.ideal_basis + ret = False + if required_vars is None: + required_vars = self._poly_ring.gens() + poly_sortkey = cmp_to_key(poly_tup_cmp) + + #Testing new _fvars representation... Unzip polynomials + self._fvars = { sextuple : poly_to_tup(rhs) for sextuple, rhs in self._fvars.items() } + + while True: + linear_terms_exist = self.solve_for_linear_terms(eqns) + if not linear_terms_exist: break + self.backward_subs() + + #Support early termination in case only some F-symbols are needed + req_vars_known = all(self._fvars[self._var_to_sextuple[var]] in self.FR.field() for var in required_vars) + if req_vars_known: return 1 + + #Compute new reduction params, send to child processes if any, and update eqns + # FIXME: replace 2500 below + self.update_reduction_params(eqns=eqns,worker_pool=worker_pool,children_need_update=len(eqns)>2500) + eqns = sorted(mr_eng.map_triv_reduce(update_reduce_caller,eqns,trivial_reducer_caller,worker_pool=worker_pool), key=poly_sortkey) + if verbose: + print("Elimination epoch completed... {} eqns remain in ideal basis".format(len(eqns))) - EXAMPLES:: + #Zip up _fvars before exiting + tzip = time() + self._fvars = { sextuple : self.tup_to_fpoly(rhs_tup) for sextuple, rhs_tup in self._fvars.items() } - sage: p = FMatrix(FusionRing("A2",1),fusion_label="c") - sage: p.pentagon()[-3:] - equations: 16 - [-fx5*fx6 + fx1, -fx4*fx6*fx7 + fx2, -fx5*fx7^2 + fx3*fx6] - sage: p.pentagon(factor=True)[-3:] - equations: 16 - [fx5*fx6 - fx1, fx4*fx6*fx7 - fx2, fx5*fx7^2 - fx3*fx6] - - """ - if output: - ret = [] - for (a,b,c,d,e,f,g,k,l) in list(product(self.FR.basis(), repeat=9)): - pd = self.feq(a,b,c,d,e,f,g,k,l,prune=prune) - if pd != 0: - if factor: - pd = self.sreduce(pd) - if output: - ret.append(pd) - if verbose: - print ("%s,%s,%s,%s,%s,%s,%s,%s,%s : %s"%(a,b,c,d,e,f,g,k,l,pd)) - print ("equations: %s"%len(ret)) - if output: - return ret + if ret: + return eqns + self.ideal_basis = eqns ##################### ### Graph methods ### @@ -657,10 +722,244 @@ def equations_graph(self,eqns=None): G.add_edge(x,y) return G + #Partition equations corresponding to edges in a disconnected graph + def partition_eqns(graph,eqns=None,verbose=True): + if eqns is None: + eqns = self.ideal_basis + partition = { tuple(c) : [] for c in graph.connected_components() } + for eq_tup in eqns: + partition[tuple(graph.connected_component_containing_vertex(variables(eq_tup)[0]))].append(eq_tup) + if verbose: + print("Partitioned {} equations into {} components of size:".format(len(eqns),len(graph.connected_components()))) + print(graph.connected_components_sizes()) + return partition + + #Compute a Groebner basis for a set of equations partitioned according to their corresponding graph + def par_graph_gb(worker_pool=None,eqns=None,term_order="degrevlex",verbose=True): + if eqns is None: eqns = self.ideal_basis + graph = self.equations_graph(eqns) + small_comps = list() + + #For informative print statement + nmax = largest_fmat_size() + vars_by_size = list() + for i in range(nmax+1): + vars_by_size.append(self.get_fmats_by_size(i)) + + for comp, comp_eqns in self.partition_eqns(graph,verbose=verbose).items(): + #Check if component is too large to process + if len(comp) > 60: + fmat_size = 0 + #For informative print statement + for i in range(1,nmax+1): + if set(comp).issubset(vars_by_size[i]): + fmat_size = i + print("Component of size {} with vars in F-mats of size {} is too large to find GB".format(len(comp),fmat_size)) + self.temp_eqns.extend(comp_eqns) + else: + small_comps.append(comp_eqns) + gb_calculator = partial(compute_gb,term_order=term_order) + small_comp_gb = mr_eng.map_triv_reduce(gb_calculator,small_comps,trivial_reducer_caller,worker_pool=worker_pool,chunksize=1,mp_thresh=50) + ret = small_comp_gb + self.temp_eqns + self.temp_eqns = [] + return ret + + #Translate equations in each connected component to smaller polynomial rings + #so we can call built-in variety method. + def get_component_variety(var, eqns): + #Define smaller poly ring in component vars + R = PolynomialRing(self.FR.field(),len(var),'a',order='lex') + + #Zip tuples into R and compute Groebner basis + idx_map = { old : new for new, old in enumerate(sorted(var)) } + nvars = len(var) + polys = [tup_to_poly(resize(eq_tup,idx_map,nvars),parent=R) for eq_tup in eqns] + var_in_R = Ideal(sorted(polys)).variety(ring=AA) + + #Change back to fmats poly ring and append to temp_eqns + inv_idx_map = { v : k for k, v in idx_map.items() } + return [{ inv_idx_map[i] : value for i, (key, value) in enumerate(sorted(soln.items())) } for soln in var_in_R] + + ############### + ### Mappers ### + ############### + + #Map callers are called in the worker processes. Map callers are used to supply + #the worker's local copy of forked variables to the relevant function + + #Set the required input iterable for consumption + def hex_iter_setter(proc): + mr_eng.input_iter = product(self.FR.basis(),repeat=6) + + def pent_iter_setter(proc): + mr_eng.input_iter = product(self.FR.basis(),repeat=9) + + #These callers should be cythonized + def hex_caller(mp_params): + mr_eng.map_caller(mp_params,req_cy,(fmats,)) + + def pent_caller(mp_params): + mr_eng.map_caller(mp_params,feq_cy,(fmats,)) + + #Substitute known values, known squares, and reduce a given equation + def update_reduce_caller(eq_tup): + update_reduce(eq_tup,fmats,mr_eng) + + #Compute Groebner basis for given equations iterable + def compute_gb(eqns,term_order="degrevlex"): + #Define smaller poly ring in component vars + sorted_vars = sorted(set(fx for eq_tup in eqns for fx in variables(eq_tup))) + R = PolynomialRing(self.FR.field(),len(sorted_vars),'a',order=term_order) + + #Zip tuples into R and compute Groebner basis + idx_map = { old : new for new, old in enumerate(sorted_vars) } + nvars = len(sorted_vars) + polys = [tup_to_poly(resize(eq_tup,idx_map,nvars),parent=R) for eq_tup in eqns] + gb = Ideal(sorted(polys)).groebner_basis(algorithm="libsingular:slimgb") + + #Change back to fmats poly ring and append to temp_eqns + inv_idx_map = { v : k for k, v in idx_map.items() } + nvars = self._poly_ring.ngens() + for p in gb: + mr_eng.worker_results.append(resize(poly_to_tup(p),inv_idx_map,nvars)) + + #One-to-all communication used to update fvars after triangular elim step. + def update_child_fmats(data_tup): + #fmats is assumed to be global before forking used to create the Pool object, + #so each child has a global fmats variable. So it's enough to update that object + self._fvars, self.solved, self._ks, self._var_degs = data_tup + self._nnz = self.get_known_nonz() + self._kp = compute_known_powers(self._var_degs,self.get_known_vals()) + + ################ + ### Reducers ### + ################ + + #Trivial reducer: simply collects objects with the same key in the worker + def trivial_reducer_caller(proc): + return mr_eng.reduce_single_proc(0) + ####################### ### Solution method ### ####################### + #Get a numeric solution for given equations by using the partitioning the equations + #graph and selecting an arbitrary point on the discrete degrees-of-freedom variety, + #which is computed as a Cartesian product + def get_numeric_solution(eqns=None,verbose=True): + if eqns is None: + eqns = self.ideal_basis + eqns_partition = self.partition_eqns(self.equations_graph(eqns),verbose=verbose) + + x = self.FR.field()['x'].gen() + numeric_fvars = dict() + non_cyclotomic_roots = list() + must_change_base_field = False + phi = self.FR.field().hom([self.field.gen()],self.FR.field()) + for comp, part in eqns_partition.items(): + #If component have only one equation in a single variable, get a root + if len(comp) == 1 and len(part) == 1: + #Attempt to find cyclotomic root + univ_poly = tup_to_univ_poly(part[0],x) + real_roots = univ_poly.roots(ring=AA,multiplicities=False) + assert real_roots, "No real solution exists... {} has no real roots".format(univ_poly) + roots = univ_poly.roots(multiplicities=False) + if roots: + numeric_fvars[comp[0]] = roots[0] + else: + non_cyclotomic_roots.append((comp[0],real_roots[0])) + must_change_base_field = True + #Otherwise, compute the component variety and select a point to obtain a numerical solution + else: + sols = self.get_component_variety(comp,part) + assert len(sols) > 1, "No real solution exists... component with variables {} has no real points".format(comp) + for fx, rhs in sols[0].items(): + non_cyclotomic_roots.append((fx,rhs)) + must_change_base_field = True + + if must_change_base_field: + #If needed, find a NumberField containing all the roots + roots = [self.FR.field().gen()]+[r[1] for r in non_cyclotomic_roots] + self.field, nf_elts, self._qqbar_embedding = number_field_elements_from_algebraics(roots,minimal=True) + #Embed cyclotomic field into newly constructed NumberField + cyc_gen_as_nf_elt = nf_elts.pop(0) + phi = self.FR.field().hom([cyc_gen_as_nf_elt], self.field) + numeric_fvars = { k : phi(v) for k, v in numeric_fvars.items() } + for i, elt in enumerate(nf_elts): + numeric_fvars[non_cyclotomic_roots[i][0]] = elt + + #Do some appropriate conversions + new_poly_ring = self._poly_ring.change_ring(self.field) + nvars = self._poly_ring.ngens() + self._var_to_idx = { new_poly_ring.gen(i) : i for i in range(nvars) } + self._var_to_sextuple = { new_poly_ring.gen(i) : self._var_to_sextuple[self._poly_ring.gen(i)] for i in range(nvars) } + self._poly_ring = new_poly_ring + self._fvars = { sextuple : apply_coeff_map(poly_to_tup(rhs),phi) for sextuple, rhs in self._fvars.items() } + + #Backward substitution step. Traverse variables in reverse lexicographical order. (System is in triangular form) + self.solved.update(numeric_fvars) + nvars = self._poly_ring.ngens() + for fx, rhs in numeric_fvars.items(): + self._fvars[self._var_to_sextuple[self._poly_ring.gen(fx)]] = ((ETuple({},nvars),rhs),) + assert len(self.solved) == nvars, "Some F-symbols are still missing...{}".format([self._poly_ring.gen(fx) for fx in set(range(nvars)).difference(self.solved)]) + self.clear_equations() + backward_subs() + self._fvars = { sextuple : constant_coeff(rhs) for sextuple, rhs in self._fvars.items() } + + #Solver + #If provided, optional param save_dir (for saving computed _fvars) should have a trailing forward slash + #Supports "warm" start. Use load_fvars to re-start computation from checkpoint + def find_real_unitary_solution(use_mp=True,save_dir="",verbose=True): + #Set multiprocessing parameters. Context can only be set once, so we try to set it + try: + set_start_method('fork') + except RuntimeError: + pass + pool = Pool(processes=max(cpu_count()-1,1)) if use_mp else None + print("Computing F-symbols for {} with {} variables...".format(FR, len(self._fvars))) + + #Set up hexagon equations and orthogonality constraints + self.get_orthogonality_constraints(output=False) + self.get_hexagons(worker_pool=pool,output=False) + self.ideal_basis = sorted(self.ideal_basis, key=poly_sortkey) + if verbose: + print("Set up {} hex and orthogonality constraints...".format(len(self.ideal_basis))) + + #Set up equations graph. Find GB for each component in parallel. Eliminate variables + self.ideal_basis = sorted(self.par_graph_gb(worker_pool=pool), key=poly_sortkey) + print("GB is of length",len(self.ideal_basis)) + self.triangular_elim(worker_pool=pool,verbose=verbose,mp_thresh=mp_thresh) + + #Report progress and checkpoint! + if verbose: + print("Hex elim step solved for {} / {} variables".format(len(self.solved), len(self._poly_ring.gens()))) + filename = save_dir + "saved_fvars_" + self.get_fr_str() + ".pickle" + self.save_fvars(filename) + + #Update reduction parameters, also in children if any + self.update_reduction_params(worker_pool=pool,children_need_update=True) + + #Set up pentagon equations in parallel, simplify, and eliminate variables + self.get_pentagons(worker_pool=pool,output=False) + if verbose: + print("Set up {} reduced pentagons...".format(len(self.ideal_basis))) + self.ideal_basis = sorted(self.ideal_basis, key=poly_sortkey) + self.triangular_elim(worker_pool=pool,verbose=verbose,mp_thresh=mp_thresh) + + #Report progress and checkpoint! + if verbose: + print("Pent elim step solved for {} / {} variables".format(len(self.solved), len(self._poly_ring.gens()))) + self.save_fvars(filename) + + #Close worker pool to free resources + if pool is not None: pool.close() + + #Set up new equations graph and compute variety for each component + self.ideal_basis = sorted(self.par_graph_gb(term_order="lex"), key=poly_sortkey) + self.triangular_elim(verbose=verbose) + self.get_numeric_solution(verbose=verbose) + self.save_fvars(filename) + def get_solution(self, equations=None, factor=True, verbose=True, prune=True, algorithm='', output=False): """ Solve the the hexagon and pentagon relations to evaluate the F-matrix. @@ -744,6 +1043,30 @@ def verify_hexagons(self): hex.append(lhs-rhs) return list(set(hex)) + #Some abstract nonsense to ensure the pentagon verifier is called with the fmats + #object living in each child process + def pent_verify_caller(mp_params): + pent_verify(mp_params, fmats) + + def par_pent_verify(use_mp=True,prune=False): + print("Testing F-symbols for {}".format(self.FR)) + treal = time() + self._fvars = { sextuple : float(RDF(rhs)) for sextuple, rhs in self.get_fmats_in_alg_field().items() } + print("Converted to floats in {}".format(time()-treal)) + if use_mp: + pool = Pool(processes=cpu_count()) + else: + pool = None + n_proc = pool._processes if pool is not None else 1 + params = [(child_id,n_proc) for child_id in range(n_proc)] + pe = mr_eng.map_triv_reduce(pent_verify_caller,params,trivial_reducer_caller,worker_pool=pool,chunksize=1,mp_thresh=0) + if np.all(np.isclose(np.array(pe),0)): + print("Success!!! Found valid F-symbols for {}".format(self.FR)) + else: + print("Ooops... something went wrong... These pentagons remain:") + print(pe) + return pe + #Verify that all F-matrices are real and unitary (orthogonal) def fmats_are_orthogonal(self): is_orthog = [] From 2963d485ce1459372da08d9b48b0d38f08560343 Mon Sep 17 00:00:00 2001 From: dwbmscz <75940445+dwbmscz@users.noreply.github.com> Date: Wed, 24 Feb 2021 13:38:51 -0800 Subject: [PATCH 004/632] now working to compute F-matrices --- src/sage/combinat/root_system/f_matrix.py | 393 ++++++++---------- .../fast_parallel_fmats_methods.pxd | 5 + .../fast_parallel_fmats_methods.pyx | 153 +++++++ src/sage/combinat/root_system/fusion_ring.py | 228 ++++++++-- .../combinat/root_system/poly_tup_engine.pxd | 12 +- .../combinat/root_system/poly_tup_engine.pyx | 165 +------- 6 files changed, 534 insertions(+), 422 deletions(-) create mode 100644 src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd create mode 100644 src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index b99aa1e2c3a..862cfc3514e 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -11,28 +11,29 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.misc.misc import inject_variable -from sage.matrix.constructor import matrix -from sage.rings.polynomial.all import PolynomialRing -from sage.rings.ideal import Ideal -from sage.combinat.root_system.fusion_ring import FusionRing +from functools import cmp_to_key, partial +from itertools import product +import sage.combinat.root_system.fusion_ring as FusionRing import sage.graphs from sage.graphs.generators.basic import EmptyGraph -from itertools import product +from sage.matrix.constructor import matrix from sage.misc.misc import inject_variable +from sage.rings.polynomial.all import PolynomialRing +from sage.rings.ideal import Ideal - +from copy import deepcopy #Import pickle for checkpointing and loading certain variables try: import cPickle as pickle except: import pickle - -from functools import cmp_to_key, partial -from sage.rings.polynomial.polydict import ETuple -from sage.combinat.root_system.poly_tup_engine import * -from sage.rings.qqbar import QQbar, number_field_elements_from_algebraics +from multiprocessing import cpu_count, Pool, set_start_method import numpy as np +from sage.combinat.root_system.fast_parallel_fmats_methods import * +from sage.combinat.root_system.poly_tup_engine import * +from sage.rings.polynomial.polydict import ETuple +from sage.rings.qqbar import AA, QQbar, number_field_elements_from_algebraics +from sage.rings.real_double import RDF class FMatrix(): r"""Return an F-Matrix factory for a FusionRing. @@ -111,12 +112,14 @@ class FMatrix(): Due to the large number of equations we may fail to find a Groebner basis if there are too many variables. + EXAMPLES:: sage: I=FusionRing("E8",2,conjugate=True) sage: I.fusion_labels(["i0","p","s"],inject_variables=True) sage: f = FMatrix(I,inject_variables=True); f creating variables fx1..fx14 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 F-Matrix factory for The Fusion Ring of Type E8 and level 2 with Integer Ring coefficients We've exported two sets of variables to the global namespace. @@ -165,9 +168,10 @@ class FMatrix(): EXAMPLES:: - sage: f.pentagon()[1:3] - equations: 41 - [-fx0*fx1 + fx1, -fx1*fx2^2 + fx1] + sage: f.get_pentagons()[1:3] + [fx1*fx5 - fx7^2, fx5*fx8*fx13 - fx2*fx12] + + sage: f.hexagon()[1:3] equations: 14 [fx1*fx5 + fx2, fx2 + 1] @@ -217,13 +221,14 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab self.FR = fusion_ring if self.FR._fusion_labels is None: self.FR.fusion_labels(fusion_label, inject_variables=True) - #Set up F-symbols entry by entry + #Set up F-symbols entry by entry n_vars = self.findcases() self._poly_ring = PolynomialRing(self.FR.field(),n_vars,var_prefix) if inject_variables: print ("creating variables %s%s..%s%s"%(var_prefix,1,var_prefix,n_vars)) - for i in range(self._poly_ring.ngens()): - inject_variable("%s%s"%(var_prefix,i),self._poly_ring.gens()[i]) + self._poly_ring.inject_variables() + # for i in range(self._poly_ring.ngens()): + # inject_variable("%s%s"%(var_prefix,i),self._poly_ring.gens()[i]) self._var_to_sextuple, self._fvars = self.findcases(output=True) self._singles = self.singletons() @@ -241,7 +246,11 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab self._ks = self.get_known_sq() self._nnz = self.get_known_nonz() self._known_vals = dict() - self.temp_eqns = [] + self.symbols_known = False + + #Multiprocessing attributes + self.mp_thresh = 7500 + ####################### ### Class utilities ### @@ -520,6 +529,7 @@ def largest_fmat_size(self): #they belong to def get_fmats_by_size(self,n): fvars_copy = deepcopy(self._fvars) + solved_copy = deepcopy(self.solved) self.clear_vars() var_set = set() for quadruple in product(self.FR.basis(),repeat=4): @@ -528,6 +538,7 @@ def get_fmats_by_size(self,n): if F.nrows() == n and F.coefficients() != [1]: var_set.update(self._var_to_idx[fx] for fx in F.coefficients()) self._fvars = fvars_copy + self.solved = solved_copy return var_set ############################ @@ -540,27 +551,98 @@ def get_fr_str(self): def save_fvars(self,filename): with open(filename, 'wb') as f: - pickle.dump(self._fvars, f) - # pickle.dump([self._fvars, self.solved], f) + pickle.dump([self._fvars, self.solved], f) #If provided, optional param save_dir should have a trailing forward slash def load_fvars(self,save_dir=""): with open(save_dir + "saved_fvars_" + self.get_fr_str() + ".pickle", 'rb') as f: - self._fvars = pickle.load(f) - # self._fvars, self.solved = pickle.load(f) + self._fvars, self.solved = pickle.load(f) + + ################# + ### MapReduce ### + ################# + + #Map fn across input_iter and reduce output to polynomial + #If worker_pool is not provided, function maps and reduces on a single process. + #If worker_pool is provided, the function attempts to determine whether it should + #use multiprocessing based on the length of the input iterable. If it can't determine + #the length of the input iterable then it uses multiprocessing with the default chunksize of 1 + #if chunksize is not explicitly provided. + def map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_thresh=None): + if mp_thresh is None: + mp_thresh = self.mp_thresh + #Compute multiprocessing parameters + if worker_pool is not None: + try: + n = len(input_iter) + except: + n = mp_thresh + 1 + if chunksize is None: + chunksize = n // (worker_pool._processes**2) + 1 + no_mp = worker_pool is None or n < mp_thresh + #Map phase. Casting Async Object blocks execution... Each process holds results + #in its copy of fmats.temp_eqns + if no_mp: + list(map(mapper,input_iter)) + else: + list(worker_pool.imap_unordered(mapper,input_iter,chunksize=chunksize)) + #Reduce phase + if no_mp: + results = collect_eqns(0) + else: + results = self.reduce_multi_process(collect_eqns,worker_pool) + return results + + ###################### + ### Mapper callers ### + ###################### + + #Map callers are called in the worker processes. Map callers are used to supply + #the worker's local copy of forked variables to the relevant function + + #Substitute known values, known squares, and reduce a given equation + def update_reduce_caller(self,eq_tup): + update_reduce(self,eq_tup) + + #Compute Groebner basis for ideal defined by the given equations + def compute_gb_caller(self,eqns,term_order="degrevlex"): + compute_gb(self,eqns,term_order) + + #Construct reduced hexagons in a worker using the worker's copy of fmats + def reduced_hex_caller(self,mp_params): + get_reduced_hexagons(self,*mp_params) + + #Construct reduced pentagons in a worker using the worker's copy of fmats + def reduced_pent_caller(self,mp_params,prune=True): + get_reduced_pentagons(self,*mp_params) + + #One-to-all communication used to update fvars after triangular elim step. + def update_child_fmats(self,data_tup): + #fmats is assumed to be global before forking used to create the Pool object, + #so each child has a global fmats variable. So it's enough to update that object + self._fvars, self.solved, self._ks, self._var_degs = data_tup + self._nnz = self.get_known_nonz() + self._kp = compute_known_powers(self._var_degs,self.get_known_vals()) + + #Verify satisfaction of pentagon equations + def pent_verify_caller(self,mp_params): + pent_verify(mp_params,self) + + ################ + ### Reducers ### + ################ + + #All-to-one communication step + def reduce_multi_process(self,reducer,worker_pool): + collected_eqns = set() + for child_eqns in worker_pool.imap_unordered(reducer,range(worker_pool._processes)): + collected_eqns.update(child_eqns) + return list(collected_eqns) ######################## ### Equations set up ### ######################## - def poly_to_tup(self,poly): - return tuple(poly.dict().items()) - - def tup_to_poly(self,eq_tup,parent=None): - if parent is None: - parent = self._poly_ring - return parent({ exp : coeff for exp, coeff in eq_tup }) - def get_orthogonality_constraints(self,output=True): eqns = list() for tup in product(self.FR.basis(), repeat=4): @@ -568,26 +650,32 @@ def get_orthogonality_constraints(self,output=True): eqns.extend((mat.T * mat - matrix.identity(mat.nrows())).coefficients()) if output: return eqns - self.ideal_basis.extend([self.poly_to_tup(eq) for eq in eqns]) + self.ideal_basis.extend([poly_to_tup(eq) for eq in eqns]) + #If a worker_pool is passed, then we use multiprocessing def get_hexagons(self,worker_pool=None,output=True): - he = mr_eng.emap_no_comm(hex_iter_setter,hex_caller,trivial_reducer_caller,worker_pool) + n_proc = worker_pool._processes if worker_pool is not None else 1 + params = [(child_id, n_proc) for child_id in range(n_proc)] + he = self.map_triv_reduce(self.reduced_hex_caller,params,worker_pool=worker_pool,chunksize=1,mp_thresh=0) if output: return [self.tup_to_fpoly(h) for h in he] self.ideal_basis.extend(he) #If a worker_pool is passed, then we use multiprocessing def get_pentagons(self,worker_pool=None,output=True): - pe = mr_eng.emap_no_comm(pent_iter_setter,pent_caller,trivial_reducer_caller,worker_pool) + n_proc = worker_pool._processes if worker_pool is not None else 1 + params = [(child_id, n_proc) for child_id in range(n_proc)] + pe = self.map_triv_reduce(self.reduced_pent_caller,params,worker_pool=worker_pool,chunksize=1,mp_thresh=0) if output: - return [self.tup_to_fpoly(p) for p in pe] + return [self.tup_to_fpoly(h) for h in pe] self.ideal_basis.extend(pe) ############################ ### Equations processing ### ############################ - def tup_to_fpoly(eq_tup): + #Assemble a polynomial object from its tuple representation + def tup_to_fpoly(self,eq_tup): return tup_to_poly(eq_tup,parent=self._poly_ring) #Solve for a linear term occurring in a two-term equation. @@ -628,6 +716,7 @@ def backward_subs(self): kp = compute_known_powers(get_variables_degrees([rhs]), d) self._fvars[sextuple] = tuple(subs_squares(subs(rhs,kp),self._ks).items()) + #Update reduction parameters in all processes def update_reduction_params(self,eqns=None,worker_pool=None,children_need_update=False): if eqns is None: eqns = self.ideal_basis @@ -638,7 +727,7 @@ def update_reduction_params(self,eqns=None,worker_pool=None,children_need_update #self._nnz and self._kp are computed in child processes to reduce IPC overhead n_proc = worker_pool._processes new_data = [(self._fvars,self.solved,self._ks,self._var_degs)]*n_proc - mr_eng.map_triv_reduce(update_child_fmats,new_data,worker_pool=worker_pool,chunksize=1,mp_thresh=0) + self.map_triv_reduce(self.update_child_fmats,new_data,worker_pool=worker_pool,chunksize=1,mp_thresh=0) #Perform triangular elimination of linear terms in two-term equations until no such terms exist #For optimal usage of TRIANGULAR elimination, pass in a SORTED list of equations @@ -665,16 +754,13 @@ def triangular_elim(self,required_vars=None,eqns=None,worker_pool=None,verbose=T if req_vars_known: return 1 #Compute new reduction params, send to child processes if any, and update eqns - # FIXME: replace 2500 below - self.update_reduction_params(eqns=eqns,worker_pool=worker_pool,children_need_update=len(eqns)>2500) - eqns = sorted(mr_eng.map_triv_reduce(update_reduce_caller,eqns,trivial_reducer_caller,worker_pool=worker_pool), key=poly_sortkey) + self.update_reduction_params(eqns=eqns,worker_pool=worker_pool,children_need_update=len(eqns)>self.mp_thresh) + eqns = sorted(self.map_triv_reduce(self.update_reduce_caller,eqns,worker_pool=worker_pool), key=poly_sortkey) if verbose: print("Elimination epoch completed... {} eqns remain in ideal basis".format(len(eqns))) #Zip up _fvars before exiting - tzip = time() self._fvars = { sextuple : self.tup_to_fpoly(rhs_tup) for sextuple, rhs_tup in self._fvars.items() } - if ret: return eqns self.ideal_basis = eqns @@ -684,31 +770,6 @@ def triangular_elim(self,required_vars=None,eqns=None,worker_pool=None,verbose=T ##################### def equations_graph(self,eqns=None): - """ - Return a graph whose vertices are variables - and whose edges correspond to equations - relating two variables. - - This graph may contain isolated vertices... - - INPUT: - - - ``equations`` -- a list of equations - - EXAMPLES:: - - sage: f = FMatrix(FusionRing("A1",3)) - sage: G = f.equation_graph(f.hexagon(factor=True)) - equations: 71 - sage: G.connected_components_number() - 14 - sage: G1=G.connected_components_subgraphs()[0] - sage: G1.size() - 60 - sage: G1.is_regular() - True - - """ if eqns is None: eqns = self.ideal_basis @@ -723,7 +784,7 @@ def equations_graph(self,eqns=None): return G #Partition equations corresponding to edges in a disconnected graph - def partition_eqns(graph,eqns=None,verbose=True): + def partition_eqns(self,graph,eqns=None,verbose=True): if eqns is None: eqns = self.ideal_basis partition = { tuple(c) : [] for c in graph.connected_components() } @@ -735,13 +796,14 @@ def partition_eqns(graph,eqns=None,verbose=True): return partition #Compute a Groebner basis for a set of equations partitioned according to their corresponding graph - def par_graph_gb(worker_pool=None,eqns=None,term_order="degrevlex",verbose=True): + def par_graph_gb(self,worker_pool=None,eqns=None,term_order="degrevlex",verbose=True): if eqns is None: eqns = self.ideal_basis graph = self.equations_graph(eqns) small_comps = list() + temp_eqns = list() #For informative print statement - nmax = largest_fmat_size() + nmax = self.largest_fmat_size() vars_by_size = list() for i in range(nmax+1): vars_by_size.append(self.get_fmats_by_size(i)) @@ -755,18 +817,17 @@ def par_graph_gb(worker_pool=None,eqns=None,term_order="degrevlex",verbose=True) if set(comp).issubset(vars_by_size[i]): fmat_size = i print("Component of size {} with vars in F-mats of size {} is too large to find GB".format(len(comp),fmat_size)) - self.temp_eqns.extend(comp_eqns) + temp_eqns.extend(comp_eqns) else: small_comps.append(comp_eqns) - gb_calculator = partial(compute_gb,term_order=term_order) - small_comp_gb = mr_eng.map_triv_reduce(gb_calculator,small_comps,trivial_reducer_caller,worker_pool=worker_pool,chunksize=1,mp_thresh=50) - ret = small_comp_gb + self.temp_eqns - self.temp_eqns = [] + gb_calculator = partial(self.compute_gb_caller,term_order=term_order) + small_comp_gb = self.map_triv_reduce(gb_calculator,small_comps,worker_pool=worker_pool,chunksize=1,mp_thresh=50) + ret = small_comp_gb + temp_eqns return ret #Translate equations in each connected component to smaller polynomial rings #so we can call built-in variety method. - def get_component_variety(var, eqns): + def get_component_variety(self,var,eqns): #Define smaller poly ring in component vars R = PolynomialRing(self.FR.field(),len(var),'a',order='lex') @@ -780,65 +841,6 @@ def get_component_variety(var, eqns): inv_idx_map = { v : k for k, v in idx_map.items() } return [{ inv_idx_map[i] : value for i, (key, value) in enumerate(sorted(soln.items())) } for soln in var_in_R] - ############### - ### Mappers ### - ############### - - #Map callers are called in the worker processes. Map callers are used to supply - #the worker's local copy of forked variables to the relevant function - - #Set the required input iterable for consumption - def hex_iter_setter(proc): - mr_eng.input_iter = product(self.FR.basis(),repeat=6) - - def pent_iter_setter(proc): - mr_eng.input_iter = product(self.FR.basis(),repeat=9) - - #These callers should be cythonized - def hex_caller(mp_params): - mr_eng.map_caller(mp_params,req_cy,(fmats,)) - - def pent_caller(mp_params): - mr_eng.map_caller(mp_params,feq_cy,(fmats,)) - - #Substitute known values, known squares, and reduce a given equation - def update_reduce_caller(eq_tup): - update_reduce(eq_tup,fmats,mr_eng) - - #Compute Groebner basis for given equations iterable - def compute_gb(eqns,term_order="degrevlex"): - #Define smaller poly ring in component vars - sorted_vars = sorted(set(fx for eq_tup in eqns for fx in variables(eq_tup))) - R = PolynomialRing(self.FR.field(),len(sorted_vars),'a',order=term_order) - - #Zip tuples into R and compute Groebner basis - idx_map = { old : new for new, old in enumerate(sorted_vars) } - nvars = len(sorted_vars) - polys = [tup_to_poly(resize(eq_tup,idx_map,nvars),parent=R) for eq_tup in eqns] - gb = Ideal(sorted(polys)).groebner_basis(algorithm="libsingular:slimgb") - - #Change back to fmats poly ring and append to temp_eqns - inv_idx_map = { v : k for k, v in idx_map.items() } - nvars = self._poly_ring.ngens() - for p in gb: - mr_eng.worker_results.append(resize(poly_to_tup(p),inv_idx_map,nvars)) - - #One-to-all communication used to update fvars after triangular elim step. - def update_child_fmats(data_tup): - #fmats is assumed to be global before forking used to create the Pool object, - #so each child has a global fmats variable. So it's enough to update that object - self._fvars, self.solved, self._ks, self._var_degs = data_tup - self._nnz = self.get_known_nonz() - self._kp = compute_known_powers(self._var_degs,self.get_known_vals()) - - ################ - ### Reducers ### - ################ - - #Trivial reducer: simply collects objects with the same key in the worker - def trivial_reducer_caller(proc): - return mr_eng.reduce_single_proc(0) - ####################### ### Solution method ### ####################### @@ -846,7 +848,7 @@ def trivial_reducer_caller(proc): #Get a numeric solution for given equations by using the partitioning the equations #graph and selecting an arbitrary point on the discrete degrees-of-freedom variety, #which is computed as a Cartesian product - def get_numeric_solution(eqns=None,verbose=True): + def get_numeric_solution(self,eqns=None,verbose=True): if eqns is None: eqns = self.ideal_basis eqns_partition = self.partition_eqns(self.equations_graph(eqns),verbose=verbose) @@ -894,31 +896,34 @@ def get_numeric_solution(eqns=None,verbose=True): self._var_to_idx = { new_poly_ring.gen(i) : i for i in range(nvars) } self._var_to_sextuple = { new_poly_ring.gen(i) : self._var_to_sextuple[self._poly_ring.gen(i)] for i in range(nvars) } self._poly_ring = new_poly_ring - self._fvars = { sextuple : apply_coeff_map(poly_to_tup(rhs),phi) for sextuple, rhs in self._fvars.items() } - #Backward substitution step. Traverse variables in reverse lexicographical order. (System is in triangular form) + #Ensure all F-symbols are known self.solved.update(numeric_fvars) nvars = self._poly_ring.ngens() + assert len(self.solved) == nvars, "Some F-symbols are still missing...{}".format([self._poly_ring.gen(fx) for fx in set(range(nvars)).difference(self.solved)]) + + #Backward substitution step. Traverse variables in reverse lexicographical order. (System is in triangular form) + self._fvars = { sextuple : apply_coeff_map(poly_to_tup(rhs),phi) for sextuple, rhs in self._fvars.items() } for fx, rhs in numeric_fvars.items(): self._fvars[self._var_to_sextuple[self._poly_ring.gen(fx)]] = ((ETuple({},nvars),rhs),) - assert len(self.solved) == nvars, "Some F-symbols are still missing...{}".format([self._poly_ring.gen(fx) for fx in set(range(nvars)).difference(self.solved)]) - self.clear_equations() - backward_subs() + self.backward_subs() self._fvars = { sextuple : constant_coeff(rhs) for sextuple, rhs in self._fvars.items() } + self.clear_equations() #Solver #If provided, optional param save_dir (for saving computed _fvars) should have a trailing forward slash #Supports "warm" start. Use load_fvars to re-start computation from checkpoint - def find_real_unitary_solution(use_mp=True,save_dir="",verbose=True): + def find_real_orthogonal_solution(self,use_mp=True,save_dir="",verbose=True): #Set multiprocessing parameters. Context can only be set once, so we try to set it try: set_start_method('fork') except RuntimeError: pass pool = Pool(processes=max(cpu_count()-1,1)) if use_mp else None - print("Computing F-symbols for {} with {} variables...".format(FR, len(self._fvars))) + print("Computing F-symbols for {} with {} variables...".format(self.FR, len(self._fvars))) #Set up hexagon equations and orthogonality constraints + poly_sortkey = cmp_to_key(poly_tup_cmp) self.get_orthogonality_constraints(output=False) self.get_hexagons(worker_pool=pool,output=False) self.ideal_basis = sorted(self.ideal_basis, key=poly_sortkey) @@ -928,7 +933,7 @@ def find_real_unitary_solution(use_mp=True,save_dir="",verbose=True): #Set up equations graph. Find GB for each component in parallel. Eliminate variables self.ideal_basis = sorted(self.par_graph_gb(worker_pool=pool), key=poly_sortkey) print("GB is of length",len(self.ideal_basis)) - self.triangular_elim(worker_pool=pool,verbose=verbose,mp_thresh=mp_thresh) + self.triangular_elim(worker_pool=pool,verbose=verbose) #Report progress and checkpoint! if verbose: @@ -944,7 +949,7 @@ def find_real_unitary_solution(use_mp=True,save_dir="",verbose=True): if verbose: print("Set up {} reduced pentagons...".format(len(self.ideal_basis))) self.ideal_basis = sorted(self.ideal_basis, key=poly_sortkey) - self.triangular_elim(worker_pool=pool,verbose=verbose,mp_thresh=mp_thresh) + self.triangular_elim(worker_pool=pool,verbose=verbose) #Report progress and checkpoint! if verbose: @@ -959,76 +964,7 @@ def find_real_unitary_solution(use_mp=True,save_dir="",verbose=True): self.triangular_elim(verbose=verbose) self.get_numeric_solution(verbose=verbose) self.save_fvars(filename) - - def get_solution(self, equations=None, factor=True, verbose=True, prune=True, algorithm='', output=False): - """ - Solve the the hexagon and pentagon relations to evaluate the F-matrix. - - INPUT: - - - ``equations`` -- (optional) a set of equations to be - solved. Defaults to the hexagon and pentagon equations. - - ``factor`` -- (default: ``True``). Set true to use - the sreduce method to simplify the hexagon and pentagon - equations before solving them. - - ``algorithm`` -- (optional). Algorithm to compute Groebner Basis. - - ``output`` -- (optional, default False). Output a dictionary of - F-matrix values. This may be useful to see but may be omitted - since this information will be available afterwards via the - :meth:`fmatrix` and :meth:`fmat` methods. - - EXAMPLES:: - - sage: f = FMatrix(FusionRing("A2",1),fusion_label="a") - sage: f.get_solution(verbose=False,output=True) - equations: 8 - equations: 16 - adding equation... fx4 - 1 - {(a2, a2, a2, a0, a1, a1): 1, - (a2, a2, a1, a2, a1, a0): 1, - (a2, a1, a2, a2, a0, a0): 1, - (a2, a1, a1, a1, a0, a2): 1, - (a1, a2, a2, a2, a0, a1): 1, - (a1, a2, a1, a1, a0, a0): 1, - (a1, a1, a2, a1, a2, a0): 1, - (a1, a1, a1, a0, a2, a2): 1} - - After you successfully run ``get_solution`` you may check - the correctness of the F-matrix by running :meth:`hexagon` - and :meth:`pentagon`. These should return empty lists - of equations. In this example, we turn off the factor - and prune optimizations to test all instances. - - EXAMPLES:: - - sage: f.hexagon(factor=False) - equations: 0 - [] - sage: f.hexagon(factor=False,side="right") - equations: 0 - [] - sage: f.pentagon(factor=False,prune=False) - equations: 0 - [] - - """ - if equations is None: - if verbose: - print("Setting up hexagons and pentagons...") - equations = self.hexagon(verbose=False, factor=factor)+self.pentagon(verbose=False, factor=factor, prune=prune) - if verbose: - print("Finding a Groebner basis...") - self.ideal_basis = set(Ideal(equations).groebner_basis(algorithm=algorithm)) - if verbose: - print("Solving...") - self.substitute_degree_one() - if verbose: - print("Fixing the gauge...") - self.fix_gauge(algorithm=algorithm) - if verbose: - print("Done!") - if output: - return self._fvars + self.symbols_known = True ##################### ### Verifications ### @@ -1041,31 +977,32 @@ def verify_hexagons(self): lhs = self.field(self.FR.r_matrix(a,c,e))*self.fmat(a,c,b,d,e,g)*self.field(self.FR.r_matrix(b,c,g)) rhs = sum(self.fmat(c,a,b,d,e,f)*self.field(self.FR.r_matrix(f,c,d))*self.fmat(a,b,c,d,f,g) for f in self.FR.basis()) hex.append(lhs-rhs) - return list(set(hex)) - - #Some abstract nonsense to ensure the pentagon verifier is called with the fmats - #object living in each child process - def pent_verify_caller(mp_params): - pent_verify(mp_params, fmats) + if all(h == self.field.zero() for h in hex): + print("Success!!! Found valid F-symbols for {}".format(self.FR)) + else: + print("Ooops... something went wrong... These pentagons remain:") + print(hex) + return hex - def par_pent_verify(use_mp=True,prune=False): - print("Testing F-symbols for {}".format(self.FR)) - treal = time() + def verify_pentagons(self,use_mp=True,prune=False): + print("Testing F-symbols for {}...".format(self.FR)) + fvars_copy = deepcopy(self._fvars) self._fvars = { sextuple : float(RDF(rhs)) for sextuple, rhs in self.get_fmats_in_alg_field().items() } - print("Converted to floats in {}".format(time()-treal)) if use_mp: pool = Pool(processes=cpu_count()) else: pool = None n_proc = pool._processes if pool is not None else 1 params = [(child_id,n_proc) for child_id in range(n_proc)] - pe = mr_eng.map_triv_reduce(pent_verify_caller,params,trivial_reducer_caller,worker_pool=pool,chunksize=1,mp_thresh=0) - if np.all(np.isclose(np.array(pe),0)): + pe = self.map_triv_reduce(self.pent_verify_caller,params,worker_pool=pool,chunksize=1,mp_thresh=0) + if np.all(np.isclose(np.array(pe),0,atol=1e-7)): print("Success!!! Found valid F-symbols for {}".format(self.FR)) + pe = None else: print("Ooops... something went wrong... These pentagons remain:") print(pe) - return pe + self._fvars = fvars_copy + return pe #Verify that all F-matrices are real and unitary (orthogonal) def fmats_are_orthogonal(self): diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd new file mode 100644 index 00000000000..bcebff6c376 --- /dev/null +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd @@ -0,0 +1,5 @@ +cpdef get_reduced_hexagons(factory, int child_id, int n_proc) +cpdef get_reduced_pentagons(factory, int child_id, int n_proc, bint prune=*) +cpdef update_reduce(factory, tuple eq_tup) +cpdef compute_gb(factory,eqns,term_order=*) +cpdef pent_verify(tuple mp_params, factory) diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx new file mode 100644 index 00000000000..fb8409b33c0 --- /dev/null +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -0,0 +1,153 @@ +############################ +### Fast FMatrix methods ### +############################ +from itertools import product +from os import getpid +from sage.combinat.root_system.poly_tup_engine cimport * +from sage.rings.ideal import Ideal +from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +cimport cython + +#Define a global temporary worker results repository +worker_results = list() + +############### +### Mappers ### +############### + +#Given an FMatrix factory and a sextuple, return a pentagon equation as a polynomial object +cdef req_cy(factory, tuple sextuple, side="left"): + a, b, c, d, e, g = sextuple + #To add typing we need to ensure all fmats.fmat are of the same type? + #Return fmats._poly_ring.zero() and fmats._poly_ring.one() instead of 0 and 1? + lhs = factory.FR.r_matrix(a,c,e)*factory.fmat(a,c,b,d,e,g)*factory.FR.r_matrix(b,c,g) + rhs = 0 + for f in factory.FR.basis(): + rhs += factory.fmat(c,a,b,d,e,f)*factory.FR.r_matrix(f,c,d)*factory.fmat(a,b,c,d,f,g) + return lhs-rhs + +#Set up and reduce the hexagon equations corresponding to this worker +@cython.wraparound(False) +@cython.nonecheck(False) +@cython.cdivision(True) +cpdef get_reduced_hexagons(factory, int child_id, int n_proc): + global worker_results + cdef int i + cdef tuple sextuple + for i, sextuple in enumerate(product(factory.FR.basis(),repeat=6)): + if i % n_proc == child_id: + he = req_cy(factory,sextuple) + if he: + worker_results.append(reduce_poly_dict(he.dict(),factory._nnz,factory._ks)) + +#Given an FMatrix factory and a nonuple, return a pentagon equation as a polynomial object +cdef MPolynomial_libsingular feq_cy(factory, tuple nonuple, bint prune=False): + a, b, c, d, e, f, g, k, l = nonuple + cdef lhs = factory.fmat(f,c,d,e,g,l)*factory.fmat(a,b,l,e,f,k) + if lhs == 0 and prune: # it is believed that if lhs=0, the equation carries no new information + return factory._poly_ring.zero() + cdef rhs = factory._poly_ring.zero() + for h in factory.FR.basis(): + rhs += factory.fmat(a,b,c,g,f,h)*factory.fmat(a,h,d,e,g,k)*factory.fmat(b,c,d,k,h,l) + return lhs - rhs + +#Set up and reduce the pentagon equations corresponding to this worker +@cython.wraparound(False) +@cython.nonecheck(False) +@cython.cdivision(True) +cpdef get_reduced_pentagons(factory, int child_id, int n_proc, bint prune=True): + cdef int i + cdef tuple nonuple + cdef MPolynomial_libsingular pe + for i, nonuple in enumerate(product(factory.FR.basis(),repeat=9)): + if i % n_proc == child_id: + pe = feq_cy(factory,nonuple,prune=prune) + if pe: + worker_results.append(reduce_poly_dict(pe.dict(),factory._nnz,factory._ks)) + +#Substitute known values, known squares, and reduce! +cpdef update_reduce(factory, tuple eq_tup): + global worker_results + cdef dict eq_dict = subs(eq_tup,factory._kp) + cdef reduced + if tup_fixes_sq(tuple(eq_dict.items())): + reduced = to_monic(eq_dict) + else: + reduced = reduce_poly_dict(eq_dict,factory._nnz,factory._ks) + worker_results.append(reduced) + +#Compute Groebner basis for given equations iterable +cpdef compute_gb(factory,eqns,term_order="degrevlex"): + global worker_results + #Define smaller poly ring in component vars + sorted_vars = list() + for eq_tup in eqns: + for fx in variables(eq_tup): + sorted_vars.append(fx) + sorted_vars = sorted(set(sorted_vars)) + R = PolynomialRing(factory.FR.field(),len(sorted_vars),'a',order=term_order) + + #Zip tuples into R and compute Groebner basis + cdef idx_map = dict() + for new, old in enumerate(sorted_vars): + idx_map[old] = new + nvars = len(sorted_vars) + cdef list polys = list() + for eq_tup in eqns: + polys.append(tup_to_poly(resize(eq_tup,idx_map,nvars),parent=R)) + gb = Ideal(sorted(polys)).groebner_basis(algorithm="libsingular:slimgb") + + #Change back to fmats poly ring and append to temp_eqns + cdef dict inv_idx_map = dict() + for k, v in idx_map.items(): + inv_idx_map[v] = k + nvars = factory._poly_ring.ngens() + for p in gb: + worker_results.append(resize(poly_to_tup(p),inv_idx_map,nvars)) + +################ +### Reducers ### +################ + +#Helper function for returning processed results back to parent process +#Trivial reducer: simply collects objects with the same key in the worker +def collect_eqns(proc): + #Discard the zero polynomial + global worker_results + reduced = set(worker_results)-set([tuple()]) + worker_results = list() + return list(reduced) + +#################### +### Verification ### +#################### + +#Check the pentagon equation corresponding to the given nonuple +cdef feq_verif(factory, tuple nonuple, float tol=5e-8): + global worker_results + a, b, c, d, e, f, g, k, l = nonuple + cdef float diff, lhs, rhs + lhs = factory.fmat(f,c,d,e,g,l)*factory.fmat(a,b,l,e,f,k) + rhs = 0.0 + for h in factory.FR.basis(): + rhs += factory.fmat(a,b,c,g,f,h)*factory.fmat(a,h,d,e,g,k)*factory.fmat(b,c,d,k,h,l) + diff = lhs - rhs + if diff > tol or diff < -tol: + worker_results.append(diff) + +#Generate all the pentagon equations assigned to this process, and reduce them +@cython.wraparound(False) +@cython.nonecheck(False) +@cython.cdivision(True) +cpdef pent_verify(tuple mp_params, factory): + child_id, n_proc = mp_params + cdef float t0 + cdef tuple nonuple + cdef long i + for i, nonuple in enumerate(product(factory.FR.basis(),repeat=9)): + if i % n_proc == child_id: + feq_verif(factory,nonuple) + if i % 50000000 == 0 and i: + print("{:5d}m equations checked... {} potential misses so far...".format(i // 1000000,len(worker_results))) + print("Ran through {} pentagons... Worker {} with pid {} reporting {} potential misses...".format(i,child_id,getpid(),len(worker_results))) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 667b71760d4..811935103b0 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -3,8 +3,9 @@ """ # **************************************************************************** # Copyright (C) 2019 Daniel Bump -# Nicolas Thiery # Guillermo Aboumrad +# Travis Scrimshaw +# Nicolas Thiery # # Distributed under the terms of the GNU General Public License (GPL) # https://www.gnu.org/licenses/ @@ -19,6 +20,11 @@ from sage.rings.number_field.number_field import CyclotomicField from sage.misc.cachefunc import cached_method +from itertools import product +import sage.combinat.root_system.f_matrix as FMatrix +from sage.misc.lazy_attribute import lazy_attribute +from sage.matrix.special import diagonal_matrix + class FusionRing(WeylCharacterRing): r""" Return the Fusion Ring (Verlinde Algebra) of level ``k``. @@ -116,7 +122,7 @@ class FusionRing(WeylCharacterRing): The fusion ring has a number of methods that reflect its role as the Grothendieck ring of a *modular tensor category* (MTC). These include twist methods :meth:`Element.twist` and :meth:`Element.ribbon` - for its elements related to the ribbon structure, and the + for its elements related to the ribbon structure, and the S-matrix :meth:`s_ij`. There are two natural normalizations of the S-matrix. Both @@ -128,7 +134,7 @@ class FusionRing(WeylCharacterRing): The unitary S-matrix is `s=D^{-1/2}\tilde{s}` where .. MATH:: - + D = \sum_V d_i(V)^2. The sum is over all simple objects `V` with @@ -546,7 +552,7 @@ def virasoro_central_charge(self): field theory associated with the Fusion Ring. If `\mathfrak{g}` is the corresponding semisimple Lie algebra, this is - + .. MATH:: \frac{k\dim\mathfrak{g}}{k+h^\vee}, @@ -561,7 +567,7 @@ def virasoro_central_charge(self): is computed in :meth:`D_plus` and `D = \sum d_i^2 > 0` is computed by :meth:`global_q_dimension`. Squaring this identity and remembering that `D_+ D_- = D` gives - + .. MATH:: D_+ / D_- = e^{i\pi c/2}. @@ -706,7 +712,7 @@ def s_matrix(self, unitary=False): - ``unitary`` -- (default: ``False``) set to ``True`` to obtain the unitary S-matrix - Without the ``unitary`` parameter, this is the matrix denoted + Without the ``unitary`` parameter, this is the matrix denoted `\widetilde{s}` in [BaKi2001]_. EXAMPLES:: @@ -734,12 +740,19 @@ def s_matrix(self, unitary=False): return S / self.total_q_order() else: return S - + + @cached_method def r_matrix(self, i, j, k): r""" Return the R-matrix entry corresponding to the subobject ``k`` in the tensor product of ``i`` with ``j``. + .. WARNING:: + + This method only gives complete information when `N_{ij}^k = 1` + (an important special case). Tables of MTC including R-matrices + may be found in Section 5.3 of [RoStWa2009]_ and in [Bond2007]_. + The R-matrix is a homomorphism `i \otimes j \rightarrow j \otimes i`. This may be hard to describe since the object `i \otimes j` may be reducible. However if `k` is a simple subobject of @@ -749,21 +762,15 @@ def r_matrix(self, i, j, k): R-matrix. This method computes that scalar. It is possible to adjust the set of embeddings `k \rightarrow i \otimes j` (called a *gauge*) so that this scalar equals - + .. MATH:: \pm \sqrt{\frac{ \theta_k }{ \theta_i \theta_j }}. If `i \neq j`, the gauge may be used to control the sign of the square root. But if `i = j` then we must be careful - about the sign. This sign is `+` if `k` is a subobject of - the symmetric square of `i` and `-` if it is a subobject of - the exterior square. See [LR1997]_ Corollary 2.22 - (actually due to Reshetikhin). - - This method only gives complete information when `N_{ij}^k = 1` - (an important special case). Tables of MTC including R-matrices - may be found in Section 5.3 of [RoStWa2009]_ and in [Bond2007]_. + about the sign. These cases are computed by a formula + of [BDGRTW2019]_, Proposition 2.3. EXAMPLES:: @@ -782,14 +789,14 @@ def r_matrix(self, i, j, k): """ if self.Nk_ij(i, j, k) == 0: return 0 - r = self.root_of_unity((k.twist(reduced=False) - i.twist(reduced=False) - j.twist(reduced=False)) / 2) if i != j: - return r - wt = k.weight() - if wt in i.symmetric_power(2).monomial_coefficients(): - return r - # We instead have wt in i.exterior_power(2).monomial_coefficients(): - return -r + return self.root_of_unity((k.twist(reduced=False) - i.twist(reduced=False) - j.twist(reduced=False)) / 2) + i0 = self.one() + B = self.basis() + return sum(y.ribbon()**2 / (i.ribbon() * x.ribbon()**2) + * self.s_ij(i0,y) * self.s_ij(i,z) * self.s_ij(x,z).conjugate() + * self.s_ij(k,x).conjugate() * self.s_ij(y,z).conjugate() / self.s_ij(i0,z) + for x in B for y in B for z in B) / (self.total_q_order()**4) def global_q_dimension(self): r""" @@ -864,6 +871,176 @@ def D_minus(self): """ return sum((x.q_dimension())**2 / x.ribbon() for x in self.basis()) + ################################### + ### Braid group representations ### + ################################### + + #Recursively enumerate all the admissible trees with given top row and root. + #Returns a list of tuples (l1,...,lk) such that + #root -> lk # m[-1], lk -> l_{k-1} # m[-2], ..., l1 -> m[0] # m[1], + #with top_row = m + def get_trees(self,top_row,root): + if len(top_row) == 2: + m1, m2 = top_row + return [[]] if self.Nk_ij(m1,m2,root) else [] + else: + m1, m2 = top_row[:2] + return [tuple([l,*b]) for l in self.basis() for b in self.get_trees([l]+top_row[2:],root) if self.Nk_ij(m1,m2,l)] + + #Get the so-called computational basis for Hom(b, a^n). The basis is a list of + #(n-2)-tuples (m_1,...,m_{n//2},l_1,...,l_{(n-3)//2}) such that + #each m_i is a monomial in a^2 and l_{j+1} \in l_j # a, and l[-1] \in a # b + def get_comp_basis(self,a,b,n_strands): + comp_basis = list() + for top in product((a*a).monomials(),repeat=n_strands//2): + #If the n_strands is odd, we must extend the top row by a fusing anyon + top_row = list(top)+[a]*(n_strands%2) + comp_basis.extend(tuple([*top,*levels]) for levels in self.get_trees(top_row,b)) + return comp_basis + + #Compute the (xi,yi), (xj,yj) entry of generator braiding the middle two strands + #in the tree b -> xi # yi -> (a # a) # (a # a), which results in a sum over j + #of trees b -> xj # yj -> (a # a) # (a # a) + @cached_method + def mid_sig_ij(self,row,col,a,b): + xi, yi = row + xj, yj = col + entry = 0 + for c in self.basis(): + for d in self.basis(): + ##Warning: We assume F-matrices are orthogonal!!! (using transpose for inverse) + f1 = self.fmats.fmat(a,a,yi,b,xi,c) + f2 = self.fmats.fmat(a,a,a,c,d,yi) + f3 = self.fmats.fmat(a,a,a,c,d,yj) + f4 = self.fmats.fmat(a,a,yj,b,xj,c) + r = self.r_matrix(a,a,d) + entry += f1 * f2 * r * f3 * f4 + return entry + + #Compute the xi, xj entry of the braid generator on the right-most strands, + #corresponding to the tree b -> (xi # a) -> (a # a) # a, which results in a + #sum over j of trees b -> xj -> (a # a) # (a # a) + @cached_method + def odd_one_out_ij(self,xi,xj,a,b): + entry = 0 + for c in self.basis(): + ##Warning: We assume F-matrices are orthogonal!!! (using transpose for inverse) + f1 = self.fmats.fmat(a,a,a,b,xi,c) + f2 = self.fmats.fmat(a,a,a,b,xj,c) + r = self.r_matrix(a,a,c) + entry += f1 * r * f2 + return entry + + @lazy_attribute + def fmats(self): + return FMatrix.FMatrix(self) + + #Compute generators of the Artin braid group on n_strands strands. If + #fusing_anyon = a and total_topological_charge = b, the generators are + #endomorphisms of Hom(a^n_strands, b). + ###NOTE: For now, we assume existence of fmats with relevant F-symbols ready. + #In the future this method will call an appropriate F-matrix solver... + #For useful group calculations, the F-symbols should lie in a NumberField. + def get_braid_generators(self,fusing_anyon,total_topological_charge,n_strands): + assert n_strands > 2, "The number of strands must be an integer greater than 2" + a, b = fusing_anyon, total_topological_charge + + #Construct associated FMatrix object and solve for F-symbols + if not self.fmats.symbols_known: + self.fmats.find_real_orthogonal_solution() + + #Set up the computational basis + comp_basis = self.get_comp_basis(a,b,n_strands) + basis_dict = { elt : i for i, elt in enumerate(comp_basis) } + dim = len(comp_basis) + print("Computing an {}-dimensional representation of the Artin braid group on {} strands...".format(dim,n_strands)) + + #Compute diagonal odd-indexed generators using the 3j-symbols + gens = { 2*i+1 : diagonal_matrix(self.r_matrix(a,a,c[i]) for c in comp_basis) for i in range(n_strands//2) } + + #Compute even-indexed generators using F-matrices + for k in range(1,n_strands//2): + entries = dict() + for i in range(dim): + for f,e,q in product(self.basis(),repeat=3): + #Compute appropriate possible nonzero row index + nnz_pos = list(comp_basis[i]) + nnz_pos[k-1:k+1] = f,e + #Handle the special case k = 1 + if k > 1: + nnz_pos[n_strands//2+k-2] = q + nnz_pos = tuple(nnz_pos) + + #Skip repeated entries when k = 1 + if nnz_pos in comp_basis and (basis_dict[nnz_pos],i) not in entries: + m, l = comp_basis[i][:n_strands//2], comp_basis[i][n_strands//2:] + #A few special cases + top_left = m[0] + if k >= 3: + top_left = l[k-3] + root = b + if k - 1 < len(l): + root = l[k-1] + + #Handle the special case k = 1 + if k == 1: + entries[basis_dict[nnz_pos],i] = self.mid_sig_ij(m[:2],(f,e),a,root) + continue + + entry = 0 + for p in self.basis(): + f1 = self.fmats.fmat(top_left,m[k-1],m[k],root,l[k-2],p) + f2 = self.fmats.fmat(top_left,f,e,root,q,p) + entry += f1 * self.mid_sig_ij((m[k-1],m[k]),(f,e),a,p) * f2 + entries[basis_dict[nnz_pos],i] = entry + gens[2*k] = matrix(entries) + + #If n_strands is odd, we compute the final generator + if n_strands % 2: + entries = dict() + for i in range(dim): + for f, q in product(self.basis(),repeat=2): + #Compute appropriate possible nonzero row index + nnz_pos = list(comp_basis[i]) + nnz_pos[n_strands//2-1] = f + #Handle small special case + if n_strands > 3: + nnz_pos[-1] = q + nnz_pos = tuple(nnz_pos) + + if nnz_pos in comp_basis: + m, l = comp_basis[i][:n_strands//2], comp_basis[i][n_strands//2:] + + #Handle a couple of small special cases + if n_strands == 3: + entries[basis_dict[nnz_pos],i] = self.odd_one_out_ij(m[-1],f,a,b) + continue + top_left = m[0] + if n_strands > 5: + top_left = l[-2] + root = b + + #Compute relevant entry + entry = 0 + for p in self.basis(): + f1 = self.fmats.fmat(top_left,m[-1],a,root,l[-1],p) + f2 = self.fmats.fmat(top_left,f,a,root,q,p) + entry += f1 * self.odd_one_out_ij(m[-1],f,a,p) * f2 + entries[basis_dict[nnz_pos],i] = entry + gens[n_strands-1] = matrix(entries) + + return comp_basis, [gens[k] for k in sorted(gens)] + + #A useful sanity check + #Determine if given iterable of n matrices defines a representation of + #the Artin braid group on (n+1) strands + def gens_satisfy_braid_gp_rels(self,sig): + n = len(sig) + braid_rels = all(sig[i] * sig[i+1] * sig[i] == sig[i+1] * sig[i] * sig[i+1] for i in range(n-1)) + far_comm = all(sig[i] * sig[j] == sig[j] * sig[i] for i, j in product(range(n),repeat=2) if abs(i-j) > 1 and i > j) + singular = any(s.is_singular() for s in sig) + return braid_rels and far_comm and not singular + class Element(WeylCharacterRing.Element): """ A class for FusionRing elements. @@ -904,7 +1081,7 @@ def weight(self): def twist(self, reduced=True): r""" - Return a rational number `h` such that `\theta = e^{i \pi h}` + Return a rational number `h` such that `\theta = e^{i \pi h}` is the twist of ``self``. The quantity `e^{i \pi h}` is also available using :meth:`ribbon`. @@ -913,7 +1090,7 @@ def twist(self, reduced=True): `h = \langle \lambda, \lambda+2\rho \rangle`, where `\rho` is half the sum of the positive roots. As in [Row2006]_, this requires normalizing - the invariant bilinear form so that + the invariant bilinear form so that `\langle \alpha, \alpha \rangle = 2` for short roots. INPUT: @@ -1026,4 +1203,3 @@ def q_dimension(self): expr = expr.substitute(q=q**4) / (q**(2*expr.degree())) zet = P.field().gen() ** (P._cyclotomic_order/P._l) return expr.substitute(q=zet) - diff --git a/src/sage/combinat/root_system/poly_tup_engine.pxd b/src/sage/combinat/root_system/poly_tup_engine.pxd index 28f5d9138bc..78d4dffa4e5 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pxd +++ b/src/sage/combinat/root_system/poly_tup_engine.pxd @@ -1,5 +1,9 @@ from sage.rings.polynomial.polydict cimport ETuple +from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular, MPolynomialRing_libsingular + +cpdef tuple poly_to_tup(MPolynomial_libsingular poly) +cpdef MPolynomial_libsingular tup_to_poly(tuple eq_tup, MPolynomialRing_libsingular parent) cpdef tuple resize(tuple eq_tup, dict idx_map, int nvars) cdef ETuple degrees(tuple poly_tup) cpdef ETuple get_variables_degrees(list eqns) @@ -14,12 +18,4 @@ cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq) cpdef dict compute_known_powers(ETuple max_deg, dict val_dict) cpdef dict subs(tuple poly_tup, dict known_powers) cdef tuple tup_mul(tuple p1, tuple p2) -cpdef update_reduce(tuple eq_tup, factory, mr_eng) cpdef int poly_tup_cmp(tuple tleft, tuple tright) -cpdef tuple req_cy(factory, tuple sextuple, side=*) -cpdef tuple feq_cy(factory, tuple nonuple, bint prune=*) -cpdef feq_verif(factory, tuple nonuple, float tol=*) -cpdef pent_verify(tuple mp_params, factory) -cpdef tuple rationalify(factory, tuple eq_tup) -cpdef tuple cyclotomify(factory, tuple rat_tup) - diff --git a/src/sage/combinat/root_system/poly_tup_engine.pyx b/src/sage/combinat/root_system/poly_tup_engine.pyx index 02c7a6f7c2e..beacc925e4d 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pyx +++ b/src/sage/combinat/root_system/poly_tup_engine.pyx @@ -1,12 +1,9 @@ +from functools import cmp_to_key from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular, MPolynomialRing_libsingular -from sage.rings.polynomial.polydict cimport ETuple, PolyDict +from sage.rings.polynomial.polydict cimport ETuple from sage.rings.polynomial.term_order import TermOrder from sage.rings.rational_field import RationalField as QQ -###NOTE: issues with importing NumberFieldElement_absolute and FMatrix -#from sage.rings.number_field.number_field_element cimport NumberFieldElement -#from sage.combinat.root_system.f_matrix import FMatrix - ################################################### ### Arithmetic Engine for polynomials as tuples ### ################################################### @@ -36,7 +33,7 @@ cpdef MPolynomial_libsingular tup_to_poly(tuple eq_tup, MPolynomialRing_libsingu #Given a tuple of pairs representing a univariate polynomial and a univariate #polynomial ring generator, return a univariate polynomial object -def tup_to_univ_poly(eq_tup, gen): +def tup_to_univ_poly(tuple eq_tup, gen): univ_tup = tuple((exp.nonzero_values()[0] if exp.nonzero_values() else 0, c) for exp, c in eq_tup) return sum(c * gen ** p for p, c in univ_tup) @@ -210,20 +207,12 @@ cdef tuple tup_mul(tuple p1, tuple p2): prod[xi.eadd(yj)] = ai*bj return tuple(prod.items()) -#Substitute known values, known squares, and reduce! -cpdef update_reduce(tuple eq_tup, factory, mr_eng): - cdef dict eq_dict = subs(eq_tup,factory._kp) - cdef reduced - if tup_fixes_sq(tuple(eq_dict.items())): - reduced = to_monic(eq_dict) - else: - reduced = reduce_poly_dict(eq_dict,factory._nnz,factory._ks) - mr_eng.worker_results.append(reduced) - ############### ### Sorting ### ############### +#Implement richcmp comparator object that can be passed in as key to sorted method + #Determine which polynomial is larger with respect to the degrevlex ordering cpdef int poly_tup_cmp(tuple tleft, tuple tright): cdef int i, ret, sf, sg, val @@ -244,147 +233,3 @@ cpdef int poly_tup_cmp(tuple tleft, tuple tright): if ret != 0: return ret return len(tleft) - len(tright) - -############################ -### Fast FMatrix methods ### -############################ -from itertools import product - -#Given an FMatrix factory and a sextuple, return a pentagon equation as a polynomial object -cpdef tuple req_cy(factory, tuple sextuple, side="left"): - a, b, c, d, e, g = sextuple - #To add typing we need to ensure all fmats.fmat are of the same type? - #Return fmats._poly_ring.zero() and fmats._poly_ring.one() instead of 0 and 1? - lhs = factory.FR.r_matrix(a,c,e)*factory.fmat(a,c,b,d,e,g)*factory.FR.r_matrix(b,c,g) - rhs = 0 - for f in factory.FR.basis(): - rhs += factory.fmat(c,a,b,d,e,f)*factory.FR.r_matrix(f,c,d)*factory.fmat(a,b,c,d,f,g) - he = lhs - rhs - if he: - return reduce_poly_dict(he.dict(),factory._nnz,factory._ks) - else: - return tuple() - -#Set up and reduce the hexagon equations corresponding to this worker -# cpdef get_reduced_hexagons(factory, int child_id, int n_proc): -# cdef int i -# cdef tuple sextuple -# for i, sextuple in enumerate(product(factory.FR.basis(),repeat=6)): -# if i % n_proc == child_id: -# he = req_cy(factory,sextuple) -# if he: -# factory.temp_eqns.append(reduce_poly_dict(he.dict(),factory._nnz,factory._ks)) - -#Given an FMatrix factory and a nonuple, return a pentagon equation as a polynomial object - -cpdef tuple feq_cy(factory, tuple nonuple, bint prune=False): - a, b, c, d, e, f, g, k, l = nonuple - cdef lhs = factory.fmat(f,c,d,e,g,l)*factory.fmat(a,b,l,e,f,k) - if lhs == 0 and prune: # it is believed that if lhs=0, the equation carries no new information - return factory._poly_ring.zero() - cdef MPolynomial_libsingular rhs = factory._poly_ring.zero() - for h in factory.FR.basis(): - rhs += factory.fmat(a,b,c,g,f,h)*factory.fmat(a,h,d,e,g,k)*factory.fmat(b,c,d,k,h,l) - return reduce_poly_dict((lhs - rhs).dict(),factory._nnz,factory._ks) - -#Set up and reduce the pentagon equations corresponding to this worker -# cpdef get_reduced_pentagons(factory, int child_id, int n_proc, bint prune=True): -# cdef int i -# cdef tuple nonuple -# cdef MPolynomial_libsingular pe -# for i, nonuple in enumerate(product(factory.FR.basis(),repeat=9)): -# if i % n_proc == child_id: -# pe = feq_cy(factory,nonuple,prune=prune) -# if pe: -# factory.temp_eqns.append(reduce_poly_dict(pe.dict(),factory._nnz,factory._ks)) - -#################### -### Verification ### -#################### - -#Check the pentagon equation corresponding to the given nonuple -cpdef feq_verif(factory, tuple nonuple, float tol=5e-8): - a, b, c, d, e, f, g, k, l = nonuple - cdef float diff, lhs, rhs - lhs = factory.fmat(f,c,d,e,g,l)*factory.fmat(a,b,l,e,f,k) - rhs = 0.0 - for h in factory.FR.basis(): - rhs += factory.fmat(a,b,c,g,f,h)*factory.fmat(a,h,d,e,g,k)*factory.fmat(b,c,d,k,h,l) - diff = lhs - rhs - if diff > tol or diff < -tol: - factory.temp_eqns.append(diff) - -#Generate all the pentagon equations assigned to this process, and reduce them -from time import time -from os import getpid -cimport cython -@cython.wraparound(False) -@cython.nonecheck(False) -@cython.cdivision(True) -cpdef pent_verify(tuple mp_params, factory): - child_id, n_proc = mp_params - cdef float t0 - cdef tuple nonuple - cdef long i - t0 = time() - for i, nonuple in enumerate(product(factory.FR.basis(),repeat=9)): - if i % n_proc == child_id: - feq_verif(factory,nonuple) - if i % 50000000 == 0 and i: - print("{:5d}m equations checked in {:8.2f}... {} misses so far...".format(i // 1000000,time()-t0,len(factory.temp_eqns))) - print("Ran through {} pentagons in {:8.2f}... Worker {} with {} reporting {} total misses...".format(i,time()-t0,child_id,getpid(),len(factory.temp_eqns))) - -################################ -### Well, this was a bust... ### -################################ - -#Given a tuple representation of an n-variate polynomial over a cyclotomic field, -#return a tuple representation of an (n+1)-variate polynomial over the rationals, -#with the cyclotomic field generator treated as an indeterminate and used as the -#last variable in the parent polynomial ring -cpdef tuple rationalify(factory, tuple eq_tup): - F = factory.FR.field() - cdef list rat_tup = list() - cdef ETuple exp - for exp, coeff in eq_tup: - #In the future we should avoid casting by ensuring all coeffs created are - #in fmats.FR.field (eg. cython reducers, FMatrix.fmat) - for cyc_power, rat_coeff in F(coeff).polynomial().dict().items(): - nnz = { len(exp) : cyc_power } - for idx, val in exp.sparse_iter(): - nnz[idx] = val - new_e = ETuple(nnz, len(exp)+1) - rat_tup.append((new_e, rat_coeff)) - return tuple(rat_tup) - -#Given a tuple representation of an n-variate polynomial over the rationals, -#return a tuple repn of the polynomial with integer coefficients, obtained by -#multiplying all coefficients by the least common multiple of denominators -# cpdef tuple integralify(tuple rat_tup): -# cdef int cdenom = LCM(coeff.denominator() for exp, coeff in rat_tup) -# cdef list int_tup = list() -# for exp, coeff in rat_tup: -# int_tup.append((exp, int(cdenom * coeff))) -# return tuple(int_tup) - -#Left inverse of rationalify... -#cylcotomify(factory, rationalify(factory, eq_tup)) == eq_tup -cpdef tuple cyclotomify(factory, tuple rat_tup): - gen = factory.FR.field().gen() - cdef cyc_dict = dict() - cdef ETuple exp - for exp, coeff in rat_tup: - cyc_pow = exp.get_exp(len(exp)-1) - new_e = ETuple({ pos : val for pos, val in exp.sparse_iter() if pos != len(exp)-1 }, len(exp)-1) - if new_e in cyc_dict: - cyc_dict[new_e] += gen ** cyc_pow * coeff - else: - cyc_dict[new_e] = gen ** cyc_pow * coeff - return tuple(cyc_dict.items()) - -# def clear_denom(eq): -# for var in eq.variables(): -# if int(str(var)[1:]) >= fmats._poly_ring.ngens(): -# d = eq.degree(var) -# eq *= var ** d -# return eq From c229fb1e07ee75f296e7305987f7b258dab7e497 Mon Sep 17 00:00:00 2001 From: dwbmscz <75940445+dwbmscz@users.noreply.github.com> Date: Wed, 24 Feb 2021 13:40:18 -0800 Subject: [PATCH 005/632] remove unneeded map_reduce_engine --- .../root_system/map_reduce_engine.pxd | 7 -- .../root_system/map_reduce_engine.pyx | 118 ------------------ 2 files changed, 125 deletions(-) delete mode 100644 src/sage/combinat/root_system/map_reduce_engine.pxd delete mode 100644 src/sage/combinat/root_system/map_reduce_engine.pyx diff --git a/src/sage/combinat/root_system/map_reduce_engine.pxd b/src/sage/combinat/root_system/map_reduce_engine.pxd deleted file mode 100644 index 15b5aeb0607..00000000000 --- a/src/sage/combinat/root_system/map_reduce_engine.pxd +++ /dev/null @@ -1,7 +0,0 @@ -cdef class MapReduceEngine(): - cdef public list worker_results - cdef public object input_iter - cdef public int mp_thresh - - cpdef map_caller(self,mp_params,mapper,extra_args=*) - diff --git a/src/sage/combinat/root_system/map_reduce_engine.pyx b/src/sage/combinat/root_system/map_reduce_engine.pyx deleted file mode 100644 index 3bd01615aac..00000000000 --- a/src/sage/combinat/root_system/map_reduce_engine.pyx +++ /dev/null @@ -1,118 +0,0 @@ -############################ -### Parlallel processing ### -############################ -from functools import partial -cimport cython - -#Rudimentary framework for performing MapReduce style computations -#DISCLAIMER: does not -cdef class MapReduceEngine(): - def __init__(self, mp_thresh=2500): - self.worker_results = list() - self.input_iter = None - #Multiprocess size parameter - self.mp_thresh = mp_thresh - - ################ - ### Reducers ### - ################ - - #Helper function for returning processed results back to parent process - #Trivial reducer: simply collects objects with the same key in the worker - def reduce_single_proc(self,proc): - #Discard the zero polynomial - reduced = set(self.worker_results)-set([tuple()]) - self.worker_results = list() - return list(reduced) - - #All-to-one communication step - def reduce_multi_process(self,reducer,worker_pool): - collected_eqns = set() - for child_eqns in worker_pool.imap_unordered(reducer,range(worker_pool._processes)): - collected_eqns.update(child_eqns) - return list(collected_eqns) - - ################# - ### MapReduce ### - ################# - - #Map fn across input_iter and reduce output to polynomial - #If worker_pool is not provided, function maps and reduces on a single process. - #If worker_pool is provided, the function attempts to determine whether it should - #use multiprocessing based on the length of the input iterable. If it can't determine - #the length of the input iterable then it uses multiprocessing with the default chunksize of 1 - #if chunksize is not explicitly provided. - def map_triv_reduce(self,mapper,input_iter,reducer=None,worker_pool=None,chunksize=None,mp_thresh=None): - #Compute multiprocessing parameters - if worker_pool is not None: - try: - n = len(input_iter) - except: - n = mp_thresh + 1 - if chunksize is None: - chunksize = n // (worker_pool._processes**2) + 1 - if mp_thresh is None: - mp_thresh = self.mp_thresh - no_mp = worker_pool is None or n < mp_thresh - #Map phase. Casting Async Object blocks execution... Each process holds results - #in its copy of fmats.temp_eqns - if no_mp: - list(map(mapper,input_iter)) - else: - list(worker_pool.imap_unordered(mapper,input_iter,chunksize=chunksize)) - - #Early termination in case no reducer is provided - if reducer is None: - return - - #Reduce phase - if no_mp: - results = reducer(0) - else: - results = self.reduce_multi_process(reducer,worker_pool) - return results - - #Map the given mapper across the input iterable creted using - #input_iter_setter, and then reduce using the provided reducer. - #The input iterable is created in each worker process with minimal communication: - #the parent only tells woker how to create iterable, but does NOT send an - #iterator object or its contents - - ###INPUT: - #input_iter_setter is a function that takes as a single argument an integer - #0 <= proc <= worker_pool._processes. If worker_pool is None, then - #input_iter_setter(0) is called - #The function input_iter_setter should set mr_eng.input_iter to a desired value - #mapper is a function that calls mr_eng.map_caller with appropriate arguments, - #that are bound to the function inside each worker process - #mapper does NOT return; it appends results to mr_eng.worker_results - #reducer is a function that instructs the engine how to collect results - #corresponding to the same key in each worker process - #input_iter_setter, mapper, AND reducer are instructions that will be - #processed inside each worker in the provided pool - - ###NOTES: set_input_iter MUST be called BEFORE - def emap_no_comm(self,input_iter_setter,mapper,reducer,worker_pool=None): - n_proc = worker_pool._processes if worker_pool is not None else 1 - params = [(child_id, n_proc) for child_id in range(n_proc)] - self.map_triv_reduce(input_iter_setter,params,worker_pool=worker_pool,chunksize=1,mp_thresh=0) - return self.map_triv_reduce(mapper,params,reducer,worker_pool=worker_pool,chunksize=1,mp_thresh=0) - - @cython.wraparound(False) - @cython.nonecheck(False) - @cython.cdivision(True) - cpdef map_caller(self,mp_params,mapper,extra_args=None): - cdef int child_id, i, n_proc - child_id, n_proc = mp_params - #Plug in additional arguments, if any - if extra_args is not None: - mapper = partial(mapper,*extra_args) - if self.input_iter is None: - raise ValueError("set_input_iter must be used to create input iterable in each worker process") - for i, input in enumerate(self.input_iter): - if i % n_proc == child_id: - res = mapper(input) - if res: - self.worker_results.append(res) - #Re-set iterator - self.input_iter = None From 8767fe249873a2a7e36b3f3436e644121cdaace4 Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Fri, 26 Feb 2021 06:38:08 -0800 Subject: [PATCH 006/632] updated f_matrix to include executor code --- src/sage/combinat/root_system/f_matrix.py | 126 +++++++----------- .../fast_parallel_fmats_methods.pxd | 9 +- .../fast_parallel_fmats_methods.pyx | 83 ++++++++++-- 3 files changed, 123 insertions(+), 95 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 862cfc3514e..c93ca689779 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -35,6 +35,8 @@ from sage.rings.qqbar import AA, QQbar, number_field_elements_from_algebraics from sage.rings.real_double import RDF +from itertools import zip_longest + class FMatrix(): r"""Return an F-Matrix factory for a FusionRing. @@ -112,14 +114,12 @@ class FMatrix(): Due to the large number of equations we may fail to find a Groebner basis if there are too many variables. - EXAMPLES:: sage: I=FusionRing("E8",2,conjugate=True) sage: I.fusion_labels(["i0","p","s"],inject_variables=True) sage: f = FMatrix(I,inject_variables=True); f creating variables fx1..fx14 - Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 F-Matrix factory for The Fusion Ring of Type E8 and level 2 with Integer Ring coefficients We've exported two sets of variables to the global namespace. @@ -168,10 +168,9 @@ class FMatrix(): EXAMPLES:: - sage: f.get_pentagons()[1:3] - [fx1*fx5 - fx7^2, fx5*fx8*fx13 - fx2*fx12] - - + sage: f.pentagon()[1:3] + equations: 41 + [-fx0*fx1 + fx1, -fx1*fx2^2 + fx1] sage: f.hexagon()[1:3] equations: 14 [fx1*fx5 + fx2, fx2 + 1] @@ -249,7 +248,7 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab self.symbols_known = False #Multiprocessing attributes - self.mp_thresh = 7500 + self.mp_thresh = 10000 ####################### @@ -562,7 +561,13 @@ def load_fvars(self,save_dir=""): ### MapReduce ### ################# - #Map fn across input_iter and reduce output to polynomial + #Apply the given mapper to each element of the given input iterable and + #return the results (with no duplicates) in a list. This method applies the + #mapper in parallel if a worker_pool is provided. + ## INPUT: + #mapper is a string specifying the name of a function defined in the + #fast_parallel_fmats_methods module. + ###NOTES: #If worker_pool is not provided, function maps and reduces on a single process. #If worker_pool is provided, the function attempts to determine whether it should #use multiprocessing based on the length of the input iterable. If it can't determine @@ -582,10 +587,11 @@ def map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_th no_mp = worker_pool is None or n < mp_thresh #Map phase. Casting Async Object blocks execution... Each process holds results #in its copy of fmats.temp_eqns + input_iter = zip_longest([],input_iter,fillvalue=(mapper,id(self))) if no_mp: - list(map(mapper,input_iter)) + list(map(executor,input_iter)) else: - list(worker_pool.imap_unordered(mapper,input_iter,chunksize=chunksize)) + list(worker_pool.imap_unordered(executor,input_iter,chunksize=chunksize)) #Reduce phase if no_mp: results = collect_eqns(0) @@ -593,41 +599,6 @@ def map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_th results = self.reduce_multi_process(collect_eqns,worker_pool) return results - ###################### - ### Mapper callers ### - ###################### - - #Map callers are called in the worker processes. Map callers are used to supply - #the worker's local copy of forked variables to the relevant function - - #Substitute known values, known squares, and reduce a given equation - def update_reduce_caller(self,eq_tup): - update_reduce(self,eq_tup) - - #Compute Groebner basis for ideal defined by the given equations - def compute_gb_caller(self,eqns,term_order="degrevlex"): - compute_gb(self,eqns,term_order) - - #Construct reduced hexagons in a worker using the worker's copy of fmats - def reduced_hex_caller(self,mp_params): - get_reduced_hexagons(self,*mp_params) - - #Construct reduced pentagons in a worker using the worker's copy of fmats - def reduced_pent_caller(self,mp_params,prune=True): - get_reduced_pentagons(self,*mp_params) - - #One-to-all communication used to update fvars after triangular elim step. - def update_child_fmats(self,data_tup): - #fmats is assumed to be global before forking used to create the Pool object, - #so each child has a global fmats variable. So it's enough to update that object - self._fvars, self.solved, self._ks, self._var_degs = data_tup - self._nnz = self.get_known_nonz() - self._kp = compute_known_powers(self._var_degs,self.get_known_vals()) - - #Verify satisfaction of pentagon equations - def pent_verify_caller(self,mp_params): - pent_verify(mp_params,self) - ################ ### Reducers ### ################ @@ -643,6 +614,10 @@ def reduce_multi_process(self,reducer,worker_pool): ### Equations set up ### ######################## + #Get constraints making F-matrices orthogonal + #If output=True, equations are returned as polynomial objects. Otherwise, + #polynomial generators (stored in the internal tuple representation) are + #appended to self.ideal_basis def get_orthogonality_constraints(self,output=True): eqns = list() for tup in product(self.FR.basis(), repeat=4): @@ -652,23 +627,20 @@ def get_orthogonality_constraints(self,output=True): return eqns self.ideal_basis.extend([poly_to_tup(eq) for eq in eqns]) + #Get the equations defining the ideal generated by the hexagon or + #pentagon relations. Use option='hexagons' or option='pentagons' + #to specify desired equations. + #If output=True, equations are returned as polynomial objects. Otherwise, + #polynomial generators (stored in the internal tuple representation) are + #appended to self.ideal_basis #If a worker_pool is passed, then we use multiprocessing - def get_hexagons(self,worker_pool=None,output=True): - n_proc = worker_pool._processes if worker_pool is not None else 1 - params = [(child_id, n_proc) for child_id in range(n_proc)] - he = self.map_triv_reduce(self.reduced_hex_caller,params,worker_pool=worker_pool,chunksize=1,mp_thresh=0) - if output: - return [self.tup_to_fpoly(h) for h in he] - self.ideal_basis.extend(he) - - #If a worker_pool is passed, then we use multiprocessing - def get_pentagons(self,worker_pool=None,output=True): + def get_defining_equations(self,option,worker_pool=None,output=True): n_proc = worker_pool._processes if worker_pool is not None else 1 params = [(child_id, n_proc) for child_id in range(n_proc)] - pe = self.map_triv_reduce(self.reduced_pent_caller,params,worker_pool=worker_pool,chunksize=1,mp_thresh=0) + eqns = self.map_triv_reduce('get_reduced_'+option,params,worker_pool=worker_pool,chunksize=1,mp_thresh=0) if output: - return [self.tup_to_fpoly(h) for h in pe] - self.ideal_basis.extend(pe) + return [self.tup_to_fpoly(p) for p in eqns] + self.ideal_basis.extend(eqns) ############################ ### Equations processing ### @@ -727,7 +699,7 @@ def update_reduction_params(self,eqns=None,worker_pool=None,children_need_update #self._nnz and self._kp are computed in child processes to reduce IPC overhead n_proc = worker_pool._processes new_data = [(self._fvars,self.solved,self._ks,self._var_degs)]*n_proc - self.map_triv_reduce(self.update_child_fmats,new_data,worker_pool=worker_pool,chunksize=1,mp_thresh=0) + self.map_triv_reduce('update_child_fmats',new_data,worker_pool=worker_pool,chunksize=1,mp_thresh=0) #Perform triangular elimination of linear terms in two-term equations until no such terms exist #For optimal usage of TRIANGULAR elimination, pass in a SORTED list of equations @@ -755,7 +727,7 @@ def triangular_elim(self,required_vars=None,eqns=None,worker_pool=None,verbose=T #Compute new reduction params, send to child processes if any, and update eqns self.update_reduction_params(eqns=eqns,worker_pool=worker_pool,children_need_update=len(eqns)>self.mp_thresh) - eqns = sorted(self.map_triv_reduce(self.update_reduce_caller,eqns,worker_pool=worker_pool), key=poly_sortkey) + eqns = sorted(self.map_triv_reduce('update_reduce',eqns,worker_pool=worker_pool), key=poly_sortkey) if verbose: print("Elimination epoch completed... {} eqns remain in ideal basis".format(len(eqns))) @@ -802,26 +774,26 @@ def par_graph_gb(self,worker_pool=None,eqns=None,term_order="degrevlex",verbose= small_comps = list() temp_eqns = list() - #For informative print statement - nmax = self.largest_fmat_size() - vars_by_size = list() - for i in range(nmax+1): - vars_by_size.append(self.get_fmats_by_size(i)) + # #For informative print statement + # nmax = self.largest_fmat_size() + # vars_by_size = list() + # for i in range(nmax+1): + # vars_by_size.append(self.get_fmats_by_size(i)) for comp, comp_eqns in self.partition_eqns(graph,verbose=verbose).items(): #Check if component is too large to process if len(comp) > 60: - fmat_size = 0 - #For informative print statement - for i in range(1,nmax+1): - if set(comp).issubset(vars_by_size[i]): - fmat_size = i - print("Component of size {} with vars in F-mats of size {} is too large to find GB".format(len(comp),fmat_size)) + # fmat_size = 0 + # #For informative print statement + # for i in range(1,nmax+1): + # if set(comp).issubset(vars_by_size[i]): + # fmat_size = i + # print("Component of size {} with vars in F-mats of size {} is too large to find GB".format(len(comp),fmat_size)) temp_eqns.extend(comp_eqns) else: small_comps.append(comp_eqns) - gb_calculator = partial(self.compute_gb_caller,term_order=term_order) - small_comp_gb = self.map_triv_reduce(gb_calculator,small_comps,worker_pool=worker_pool,chunksize=1,mp_thresh=50) + input_iter = zip_longest(small_comps,[],fillvalue=term_order) + small_comp_gb = self.map_triv_reduce('compute_gb',input_iter,worker_pool=worker_pool,chunksize=1,mp_thresh=50) ret = small_comp_gb + temp_eqns return ret @@ -913,7 +885,7 @@ def get_numeric_solution(self,eqns=None,verbose=True): #Solver #If provided, optional param save_dir (for saving computed _fvars) should have a trailing forward slash #Supports "warm" start. Use load_fvars to re-start computation from checkpoint - def find_real_orthogonal_solution(self,use_mp=True,save_dir="",verbose=True): + def find_real_unitary_solution(self,use_mp=True,save_dir="",verbose=True): #Set multiprocessing parameters. Context can only be set once, so we try to set it try: set_start_method('fork') @@ -925,7 +897,7 @@ def find_real_orthogonal_solution(self,use_mp=True,save_dir="",verbose=True): #Set up hexagon equations and orthogonality constraints poly_sortkey = cmp_to_key(poly_tup_cmp) self.get_orthogonality_constraints(output=False) - self.get_hexagons(worker_pool=pool,output=False) + self.get_defining_equations('hexagons',worker_pool=pool,output=False) self.ideal_basis = sorted(self.ideal_basis, key=poly_sortkey) if verbose: print("Set up {} hex and orthogonality constraints...".format(len(self.ideal_basis))) @@ -945,7 +917,7 @@ def find_real_orthogonal_solution(self,use_mp=True,save_dir="",verbose=True): self.update_reduction_params(worker_pool=pool,children_need_update=True) #Set up pentagon equations in parallel, simplify, and eliminate variables - self.get_pentagons(worker_pool=pool,output=False) + self.get_defining_equations('pentagons',worker_pool=pool,output=False) if verbose: print("Set up {} reduced pentagons...".format(len(self.ideal_basis))) self.ideal_basis = sorted(self.ideal_basis, key=poly_sortkey) @@ -994,7 +966,7 @@ def verify_pentagons(self,use_mp=True,prune=False): pool = None n_proc = pool._processes if pool is not None else 1 params = [(child_id,n_proc) for child_id in range(n_proc)] - pe = self.map_triv_reduce(self.pent_verify_caller,params,worker_pool=pool,chunksize=1,mp_thresh=0) + pe = self.map_triv_reduce('pent_verify',params,worker_pool=pool,chunksize=1,mp_thresh=0) if np.all(np.isclose(np.array(pe),0,atol=1e-7)): print("Success!!! Found valid F-symbols for {}".format(self.FR)) pe = None diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd index bcebff6c376..63dde1f57cf 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd @@ -1,5 +1,6 @@ -cpdef get_reduced_hexagons(factory, int child_id, int n_proc) -cpdef get_reduced_pentagons(factory, int child_id, int n_proc, bint prune=*) +cpdef get_reduced_hexagons(factory, tuple mp_params) +cpdef get_reduced_pentagons(factory, tuple mp_params, bint prune=*) cpdef update_reduce(factory, tuple eq_tup) -cpdef compute_gb(factory,eqns,term_order=*) -cpdef pent_verify(tuple mp_params, factory) +cpdef compute_gb(factory, tuple args) +cpdef update_child_fmats(factory, tuple data_tup) +cpdef pent_verify(factory, tuple mp_params) diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index fb8409b33c0..ad8ef9d5fee 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -1,40 +1,84 @@ ############################ ### Fast FMatrix methods ### ############################ +cimport cython +import ctypes from itertools import product from os import getpid +import sage from sage.combinat.root_system.poly_tup_engine cimport * from sage.rings.ideal import Ideal from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -cimport cython #Define a global temporary worker results repository worker_results = list() +############################## +### Parallel code executor ### +############################## + +#Execute a function defined in this module (sage.combinat.root_system.fast_parallel_fmats_methods) +#in a worker process, and supply the factory parameter by constructing a reference +#to the FMatrix object in the worker's memory adress space from its id +### NOTE: When the parent process is forked, each worker gets a copy of +#every global variable. The virtual memory address of object X in the parent +#process equals the VIRTUAL memory address of the copy of object X in each +#worker, so we may construct references to forked copies of X +def executor(params): + (fn_name, fmats_id), args = params + #Construct a reference to global FMatrix object in this worker's memory + fmats_obj = ctypes.cast(fmats_id, ctypes.py_object).value + mod = sage.combinat.root_system.fast_parallel_fmats_methods + #Bind module method to FMatrix object in worker process, and call the method + getattr(mod,fn_name)(fmats_obj,args) + ############### ### Mappers ### ############### +#Cython version of fmat class method. Using cdef for fastest dispatch +cdef _fmat(factory, a, b, c, d, x, y): + if factory.FR.Nk_ij(a,b,x) == 0 or factory.FR.Nk_ij(x,c,d) == 0 or factory.FR.Nk_ij(b,c,y) == 0 or factory.FR.Nk_ij(a,y,d) == 0: + return 0 + #Some known zero F-symbols + if a == factory.FR.one(): + if x == b and y == d: + return 1 + else: + return 0 + if b == factory.FR.one(): + if x == a and y == c: + return 1 + else: + return 0 + if c == factory.FR.one(): + if x == d and y == b: + return 1 + else: + return 0 + return factory._fvars[a,b,c,d,x,y] + #Given an FMatrix factory and a sextuple, return a pentagon equation as a polynomial object cdef req_cy(factory, tuple sextuple, side="left"): a, b, c, d, e, g = sextuple #To add typing we need to ensure all fmats.fmat are of the same type? #Return fmats._poly_ring.zero() and fmats._poly_ring.one() instead of 0 and 1? - lhs = factory.FR.r_matrix(a,c,e)*factory.fmat(a,c,b,d,e,g)*factory.FR.r_matrix(b,c,g) + lhs = factory.FR.r_matrix(a,c,e)*_fmat(factory,a,c,b,d,e,g)*factory.FR.r_matrix(b,c,g) rhs = 0 for f in factory.FR.basis(): - rhs += factory.fmat(c,a,b,d,e,f)*factory.FR.r_matrix(f,c,d)*factory.fmat(a,b,c,d,f,g) + rhs += _fmat(factory,c,a,b,d,e,f)*factory.FR.r_matrix(f,c,d)*_fmat(factory,a,b,c,d,f,g) return lhs-rhs #Set up and reduce the hexagon equations corresponding to this worker @cython.wraparound(False) @cython.nonecheck(False) @cython.cdivision(True) -cpdef get_reduced_hexagons(factory, int child_id, int n_proc): - global worker_results - cdef int i +cpdef get_reduced_hexagons(factory, tuple mp_params): + cdef int child_id, n_proc, i + child_id, n_proc = mp_params cdef tuple sextuple + global worker_results for i, sextuple in enumerate(product(factory.FR.basis(),repeat=6)): if i % n_proc == child_id: he = req_cy(factory,sextuple) @@ -44,22 +88,24 @@ cpdef get_reduced_hexagons(factory, int child_id, int n_proc): #Given an FMatrix factory and a nonuple, return a pentagon equation as a polynomial object cdef MPolynomial_libsingular feq_cy(factory, tuple nonuple, bint prune=False): a, b, c, d, e, f, g, k, l = nonuple - cdef lhs = factory.fmat(f,c,d,e,g,l)*factory.fmat(a,b,l,e,f,k) + cdef lhs = _fmat(factory,f,c,d,e,g,l)*_fmat(factory,a,b,l,e,f,k) if lhs == 0 and prune: # it is believed that if lhs=0, the equation carries no new information return factory._poly_ring.zero() cdef rhs = factory._poly_ring.zero() for h in factory.FR.basis(): - rhs += factory.fmat(a,b,c,g,f,h)*factory.fmat(a,h,d,e,g,k)*factory.fmat(b,c,d,k,h,l) + rhs += _fmat(factory,a,b,c,g,f,h)*_fmat(factory,a,h,d,e,g,k)*_fmat(factory,b,c,d,k,h,l) return lhs - rhs #Set up and reduce the pentagon equations corresponding to this worker @cython.wraparound(False) @cython.nonecheck(False) @cython.cdivision(True) -cpdef get_reduced_pentagons(factory, int child_id, int n_proc, bint prune=True): - cdef int i +cpdef get_reduced_pentagons(factory, tuple mp_params, bint prune=True): + cdef int child_id, n_proc, i + child_id, n_proc = mp_params cdef tuple nonuple cdef MPolynomial_libsingular pe + global worker_results for i, nonuple in enumerate(product(factory.FR.basis(),repeat=9)): if i % n_proc == child_id: pe = feq_cy(factory,nonuple,prune=prune) @@ -78,7 +124,8 @@ cpdef update_reduce(factory, tuple eq_tup): worker_results.append(reduced) #Compute Groebner basis for given equations iterable -cpdef compute_gb(factory,eqns,term_order="degrevlex"): +cpdef compute_gb(factory, tuple args): + eqns, term_order = args global worker_results #Define smaller poly ring in component vars sorted_vars = list() @@ -106,6 +153,14 @@ cpdef compute_gb(factory,eqns,term_order="degrevlex"): for p in gb: worker_results.append(resize(poly_to_tup(p),inv_idx_map,nvars)) +#One-to-all communication used to update fvars after triangular elim step. +cpdef update_child_fmats(factory, tuple data_tup): + #fmats is assumed to be global before forking used to create the Pool object, + #so each child has a global fmats variable. So it's enough to update that object + factory._fvars, factory.solved, factory._ks, factory._var_degs = data_tup + factory._nnz = factory.get_known_nonz() + factory._kp = compute_known_powers(factory._var_degs,factory.get_known_vals()) + ################ ### Reducers ### ################ @@ -128,10 +183,10 @@ cdef feq_verif(factory, tuple nonuple, float tol=5e-8): global worker_results a, b, c, d, e, f, g, k, l = nonuple cdef float diff, lhs, rhs - lhs = factory.fmat(f,c,d,e,g,l)*factory.fmat(a,b,l,e,f,k) + lhs = _fmat(factory,f,c,d,e,g,l)*_fmat(factory,a,b,l,e,f,k) rhs = 0.0 for h in factory.FR.basis(): - rhs += factory.fmat(a,b,c,g,f,h)*factory.fmat(a,h,d,e,g,k)*factory.fmat(b,c,d,k,h,l) + rhs += _fmat(factory,a,b,c,g,f,h)*_fmat(factory,a,h,d,e,g,k)*_fmat(factory,b,c,d,k,h,l) diff = lhs - rhs if diff > tol or diff < -tol: worker_results.append(diff) @@ -140,7 +195,7 @@ cdef feq_verif(factory, tuple nonuple, float tol=5e-8): @cython.wraparound(False) @cython.nonecheck(False) @cython.cdivision(True) -cpdef pent_verify(tuple mp_params, factory): +cpdef pent_verify(factory, tuple mp_params): child_id, n_proc = mp_params cdef float t0 cdef tuple nonuple From d8fb553cd17e9793c93088affd700f51c4a8764b Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Fri, 26 Feb 2021 06:48:07 -0800 Subject: [PATCH 007/632] work on docstrings --- src/sage/combinat/root_system/f_matrix.py | 58 +++++---- .../fast_parallel_fmats_methods.pyx | 99 +++++++++------ .../combinat/root_system/poly_tup_engine.pyx | 115 ++++++++++++------ 3 files changed, 179 insertions(+), 93 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index c93ca689779..b521b88c3d7 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -103,23 +103,25 @@ class FMatrix(): [Bond2007]_ worked out F-matrices may be found in [RoStWa2009]_ and [CuWa2015]_. - The F-matrix is only determined up to a gauge. This + The F-matrix is only determined up to a *gauge*. This is a family of embeddings `C\to A\otimes B` for simple objects `A,B,C` such that `\text{Hom}(C,A\otimes B)` is nonzero. Changing the gauge changes the F-matrix though not in a very essential way. By varying the gauge it is possible to make the F-matrices unitary, or it is possible - to make them cyclotomic. We choose the latter. + to make them cyclotomic. Due to the large number of equations we may fail to find a Groebner basis if there are too many variables. + EXAMPLES:: sage: I=FusionRing("E8",2,conjugate=True) sage: I.fusion_labels(["i0","p","s"],inject_variables=True) sage: f = FMatrix(I,inject_variables=True); f creating variables fx1..fx14 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 F-Matrix factory for The Fusion Ring of Type E8 and level 2 with Integer Ring coefficients We've exported two sets of variables to the global namespace. @@ -164,16 +166,17 @@ class FMatrix(): definition of a monoidal category. The hexagon relations reflect the axioms of a *braided monoidal category*, which are constraints on both the F-matrix and on - the R-matrix. + the R-matrix. Finally, orthogonality constraints + may be imposed to obtain a unitary F-matrix. EXAMPLES:: - sage: f.pentagon()[1:3] - equations: 41 - [-fx0*fx1 + fx1, -fx1*fx2^2 + fx1] - sage: f.hexagon()[1:3] - equations: 14 - [fx1*fx5 + fx2, fx2 + 1] + sage: f.get_pentagons()[1:3] + [fx1*fx5 - fx7^2, fx5*fx8*fx13 - fx2*fx12] + sage: f.get_hexagons()[1:3] + [fx10*fx12 + (-zeta128^32)*fx12*fx13 + (-zeta128^16)*fx12, fx0 - 1] + sage: f.get_orthogonality_constraints()[1:3] + [fx1^2 - 1, fx2^2 - 1] You may solve these 41+14=55 equations to compute the F-matrix. @@ -613,12 +616,15 @@ def reduce_multi_process(self,reducer,worker_pool): ######################## ### Equations set up ### ######################## - - #Get constraints making F-matrices orthogonal - #If output=True, equations are returned as polynomial objects. Otherwise, - #polynomial generators (stored in the internal tuple representation) are - #appended to self.ideal_basis + def get_orthogonality_constraints(self,output=True): + """ + Get equations imposed on the F-matrix by orthogonality. + + If output=True, equations are returned as polynomial objects. Otherwise, + polynomial generators (stored in the internal tuple representation) are + appended to self.ideal_basis + """ eqns = list() for tup in product(self.FR.basis(), repeat=4): mat = self.fmatrix(*tup) @@ -627,14 +633,22 @@ def get_orthogonality_constraints(self,output=True): return eqns self.ideal_basis.extend([poly_to_tup(eq) for eq in eqns]) - #Get the equations defining the ideal generated by the hexagon or - #pentagon relations. Use option='hexagons' or option='pentagons' - #to specify desired equations. - #If output=True, equations are returned as polynomial objects. Otherwise, - #polynomial generators (stored in the internal tuple representation) are - #appended to self.ideal_basis - #If a worker_pool is passed, then we use multiprocessing def get_defining_equations(self,option,worker_pool=None,output=True): + """ + Get the equations defining the ideal generated by the hexagon or + pentagon relations. + + Use option='hexagons' to get equations imposed on the F-matrix by the hexagon + relations in the definition of a braided category. + + Use option='pentagons' to get equations imposed on the F-matrix by the pentagon + relations in the definition of a monoidal category. + + If output=True, equations are returned as polynomial objects. Otherwise, + polynomial generators (stored in the internal tuple representation) are + appended to self.ideal_basis + If a worker_pool is passed, then we use multiprocessing + """ n_proc = worker_pool._processes if worker_pool is not None else 1 params = [(child_id, n_proc) for child_id in range(n_proc)] eqns = self.map_triv_reduce('get_reduced_'+option,params,worker_pool=worker_pool,chunksize=1,mp_thresh=0) @@ -885,7 +899,7 @@ def get_numeric_solution(self,eqns=None,verbose=True): #Solver #If provided, optional param save_dir (for saving computed _fvars) should have a trailing forward slash #Supports "warm" start. Use load_fvars to re-start computation from checkpoint - def find_real_unitary_solution(self,use_mp=True,save_dir="",verbose=True): + def find_real_orthogonal_solution(self,use_mp=True,save_dir="",verbose=True): #Set multiprocessing parameters. Context can only be set once, so we try to set it try: set_start_method('fork') diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index ad8ef9d5fee..7add91bdcaf 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -1,6 +1,13 @@ -############################ -### Fast FMatrix methods ### -############################ +""" +Fast FMatrix methods +""" +# **************************************************************************** +# Copyright (C) 2021 Guillermo Aboumrad +# +# Distributed under the terms of the GNU General Public License (GPL) +# https://www.gnu.org/licenses/ +# **************************************************************************** + cimport cython import ctypes from itertools import product @@ -59,8 +66,10 @@ cdef _fmat(factory, a, b, c, d, x, y): return 0 return factory._fvars[a,b,c,d,x,y] -#Given an FMatrix factory and a sextuple, return a pentagon equation as a polynomial object cdef req_cy(factory, tuple sextuple, side="left"): + """ + Given an FMatrix factory and a sextuple, return a hexagon equation as a polynomial object + """ a, b, c, d, e, g = sextuple #To add typing we need to ensure all fmats.fmat are of the same type? #Return fmats._poly_ring.zero() and fmats._poly_ring.one() instead of 0 and 1? @@ -70,50 +79,59 @@ cdef req_cy(factory, tuple sextuple, side="left"): rhs += _fmat(factory,c,a,b,d,e,f)*factory.FR.r_matrix(f,c,d)*_fmat(factory,a,b,c,d,f,g) return lhs-rhs -#Set up and reduce the hexagon equations corresponding to this worker @cython.wraparound(False) @cython.nonecheck(False) @cython.cdivision(True) cpdef get_reduced_hexagons(factory, tuple mp_params): - cdef int child_id, n_proc, i - child_id, n_proc = mp_params - cdef tuple sextuple - global worker_results - for i, sextuple in enumerate(product(factory.FR.basis(),repeat=6)): - if i % n_proc == child_id: - he = req_cy(factory,sextuple) - if he: - worker_results.append(reduce_poly_dict(he.dict(),factory._nnz,factory._ks)) - -#Given an FMatrix factory and a nonuple, return a pentagon equation as a polynomial object + """ + Set up and reduce the hexagon equations corresponding to this worker + """ + global worker_results + cdef int i, child_id, n_proc + child_id, n_proc = mp_params + cdef tuple sextuple + for i, sextuple in enumerate(product(factory.FR.basis(),repeat=6)): + if i % n_proc == child_id: + he = req_cy(factory,sextuple) + if he: + worker_results.append(reduce_poly_dict(he.dict(),factory._nnz,factory._ks)) + cdef MPolynomial_libsingular feq_cy(factory, tuple nonuple, bint prune=False): + """ + Given an FMatrix factory and a nonuple, return a pentagon equation as a polynomial object + """ a, b, c, d, e, f, g, k, l = nonuple cdef lhs = _fmat(factory,f,c,d,e,g,l)*_fmat(factory,a,b,l,e,f,k) if lhs == 0 and prune: # it is believed that if lhs=0, the equation carries no new information - return factory._poly_ring.zero() + return factory._poly_ring.zero() cdef rhs = factory._poly_ring.zero() for h in factory.FR.basis(): rhs += _fmat(factory,a,b,c,g,f,h)*_fmat(factory,a,h,d,e,g,k)*_fmat(factory,b,c,d,k,h,l) return lhs - rhs -#Set up and reduce the pentagon equations corresponding to this worker @cython.wraparound(False) @cython.nonecheck(False) @cython.cdivision(True) cpdef get_reduced_pentagons(factory, tuple mp_params, bint prune=True): - cdef int child_id, n_proc, i - child_id, n_proc = mp_params - cdef tuple nonuple - cdef MPolynomial_libsingular pe - global worker_results - for i, nonuple in enumerate(product(factory.FR.basis(),repeat=9)): - if i % n_proc == child_id: - pe = feq_cy(factory,nonuple,prune=prune) - if pe: - worker_results.append(reduce_poly_dict(pe.dict(),factory._nnz,factory._ks)) - -#Substitute known values, known squares, and reduce! + """ + Set up and reduce the pentagon equations corresponding to this worker + """ + global worker_results + cdef int i, child_id, n_proc + child_id, n_proc = mp_params + cdef tuple nonuple + cdef MPolynomial_libsingular pe + for i, nonuple in enumerate(product(factory.FR.basis(),repeat=9)): + if i % n_proc == child_id: + pe = feq_cy(factory,nonuple,prune=prune) + if pe: + worker_results.append(reduce_poly_dict(pe.dict(),factory._nnz,factory._ks)) + + cpdef update_reduce(factory, tuple eq_tup): + """ + Substitute known values, known squares, and reduce! + """ global worker_results cdef dict eq_dict = subs(eq_tup,factory._kp) cdef reduced @@ -123,10 +141,12 @@ cpdef update_reduce(factory, tuple eq_tup): reduced = reduce_poly_dict(eq_dict,factory._nnz,factory._ks) worker_results.append(reduced) -#Compute Groebner basis for given equations iterable cpdef compute_gb(factory, tuple args): - eqns, term_order = args + """ + Compute Groebner basis for given equations iterable + """ global worker_results + eqns, term_order = args #Define smaller poly ring in component vars sorted_vars = list() for eq_tup in eqns: @@ -165,9 +185,11 @@ cpdef update_child_fmats(factory, tuple data_tup): ### Reducers ### ################ -#Helper function for returning processed results back to parent process -#Trivial reducer: simply collects objects with the same key in the worker def collect_eqns(proc): + """ + Helper function for returning processed results back to parent process. + Trivial reducer: simply collects objects with the same key in the worker + """ #Discard the zero polynomial global worker_results reduced = set(worker_results)-set([tuple()]) @@ -178,8 +200,10 @@ def collect_eqns(proc): ### Verification ### #################### -#Check the pentagon equation corresponding to the given nonuple cdef feq_verif(factory, tuple nonuple, float tol=5e-8): + """ + Check the pentagon equation corresponding to the given nonuple + """ global worker_results a, b, c, d, e, f, g, k, l = nonuple cdef float diff, lhs, rhs @@ -191,11 +215,14 @@ cdef feq_verif(factory, tuple nonuple, float tol=5e-8): if diff > tol or diff < -tol: worker_results.append(diff) -#Generate all the pentagon equations assigned to this process, and reduce them + @cython.wraparound(False) @cython.nonecheck(False) @cython.cdivision(True) cpdef pent_verify(factory, tuple mp_params): + """ + Generate all the pentagon equations assigned to this process, and reduce them + """ child_id, n_proc = mp_params cdef float t0 cdef tuple nonuple diff --git a/src/sage/combinat/root_system/poly_tup_engine.pyx b/src/sage/combinat/root_system/poly_tup_engine.pyx index beacc925e4d..f6edc15a235 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pyx +++ b/src/sage/combinat/root_system/poly_tup_engine.pyx @@ -1,26 +1,36 @@ +################################################### +### Arithmetic Engine for polynomials as tuples ### +################################################### +# **************************************************************************** +# Copyright (C) 2021 Guillermo Aboumrad +# +# Distributed under the terms of the GNU General Public License (GPL) +# https://www.gnu.org/licenses/ +# **************************************************************************** + from functools import cmp_to_key from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular, MPolynomialRing_libsingular from sage.rings.polynomial.polydict cimport ETuple from sage.rings.polynomial.term_order import TermOrder from sage.rings.rational_field import RationalField as QQ -################################################### -### Arithmetic Engine for polynomials as tuples ### -################################################### - ########### ### API ### ########### -#Convert a polynomial object into the internal representation as tuple of -#(ETuple exp, NumberFieldElement coeff) pairs cpdef tuple poly_to_tup(MPolynomial_libsingular poly): + """ + Convert a polynomial object into the internal representation as tuple of + (ETuple exp, NumberFieldElement coeff) pairs + """ return tuple(poly.dict().items()) -#Return a polynomial object from its tuple representation. Inverse of poly_to_tup. -#poly_to_tup(tup_to_poly(eq_tup, ring)) == eq_tup && tup_to_poly(poly_to_tup(eq), eq.parent()) == eq -#Assumes parent.ngens() == len(exp_tup) for exp_tup, c in eq_tup cpdef MPolynomial_libsingular tup_to_poly(tuple eq_tup, MPolynomialRing_libsingular parent): + """ + Return a polynomial object from its tuple representation. Inverse of poly_to_tup. + poly_to_tup(tup_to_poly(eq_tup, ring)) == eq_tup && tup_to_poly(poly_to_tup(eq), eq.parent()) == eq + Assumes parent.ngens() == len(exp_tup) for exp_tup, c in eq_tup + """ # Maybe the following is faster but we need to ensure all coefficients are # already in fmats._poly_ring.base_ring() so that implicit casting is avoided # (this is pretty slow) @@ -31,17 +41,21 @@ cpdef MPolynomial_libsingular tup_to_poly(tuple eq_tup, MPolynomialRing_libsingu ### "Change rings" ### ###################### -#Given a tuple of pairs representing a univariate polynomial and a univariate -#polynomial ring generator, return a univariate polynomial object def tup_to_univ_poly(tuple eq_tup, gen): + """ + Given a tuple of pairs representing a univariate polynomial and a univariate + polynomial ring generator, return a univariate polynomial object + """ univ_tup = tuple((exp.nonzero_values()[0] if exp.nonzero_values() else 0, c) for exp, c in eq_tup) return sum(c * gen ** p for p, c in univ_tup) -#Return a tuple representing a polynomial in a ring with len(sorted_vars) generators -#This method is used for creating polynomial objects with the "right number" of -#variables for computing Groebner bases of the partitioned equations graph -#and for adding constraints ensuring certain F-symbols are nonzero cpdef tuple resize(tuple eq_tup, dict idx_map, int nvars): + """ + Return a tuple representing a polynomial in a ring with len(sorted_vars) generators + This method is used for creating polynomial objects with the "right number" of + variables for computing Groebner bases of the partitioned equations graph + and for adding constraints ensuring certain F-symbols are nonzero + """ cdef ETuple new_e cdef list resized = list() for exp, c in eq_tup: @@ -53,8 +67,11 @@ cpdef tuple resize(tuple eq_tup, dict idx_map, int nvars): ### Convenience methods ### ########################### -#Return the maximal degree of each variable in the polynomial + cdef ETuple degrees(tuple poly_tup): + """ + Return the maximal degree of each variable in the polynomial + """ #Deal with the empty tuple, representing the zero polynomial if not poly_tup: return ETuple() cdef ETuple max_degs, exp @@ -63,8 +80,10 @@ cdef ETuple degrees(tuple poly_tup): max_degs = max_degs.emax(exp) return max_degs -#Find maximum degrees for each variable in equations cpdef ETuple get_variables_degrees(list eqns): + """ + Find maximum degrees for each variable in equations + """ cdef tuple eq_tup cdef ETuple max_deg max_deg = degrees(eqns[0]) @@ -72,36 +91,47 @@ cpdef ETuple get_variables_degrees(list eqns): max_deg = max_deg.emax(degrees(eq_tup)) return max_deg -#Return indices of all variables appearing in eq_tup cpdef list variables(tuple eq_tup): + """ + Return indices of all variables appearing in eq_tup + """ return degrees(eq_tup).nonzero_positions() -#Return the constant coefficient of the polynomial represented by given tuple cpdef constant_coeff(tuple eq_tup): + """ + Return the constant coefficient of the polynomial represented by given tuple + """ for exp, coeff in eq_tup: if exp.is_constant(): return coeff return 0 cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map): + """ + Apply `coeff_map` to coefficients + """ cdef list new_tup = list() for exp, coeff in eq_tup: new_tup.append((exp, coeff_map(coeff))) return tuple(new_tup) -#Determine if given equation fixes the square of a variable. An equation -#fixes the sq of a variable if it is of the form a*x^2 + c for NONZERO constants a, c cpdef bint tup_fixes_sq(tuple eq_tup): + """ + Determine if given equation fixes the square of a variable. An equation + fixes the sq of a variable if it is of the form `a*x^2 + c` for *nonzero* constants `a`, `c` + """ return len(eq_tup) == 2 and len(variables(eq_tup)) == 1 and eq_tup[0][0].nonzero_values() == [2] ###################### ### Simplification ### ###################### -#Substitutes for known squares in a given polynomial. -#The parameter known_sq is a dictionary of (int i, NumberFieldElement a) pairs such that x_i^2 - a == 0 -#Returns a dictionary of (ETuple, coeff) pairs representing polynomial cpdef dict subs_squares(dict eq_dict, dict known_sq): + """ + Substitutes for known squares in a given polynomial. + The parameter known_sq is a dictionary of (int i, NumberFieldElement a) pairs such that x_i^2 - a == 0 + Returns a dictionary of (ETuple, coeff) pairs representing polynomial + """ cdef dict subbed, new_e cdef ETuple exp, lm cdef int idx, power @@ -124,10 +154,12 @@ cpdef dict subs_squares(dict eq_dict, dict known_sq): subbed[exp] = coeff return { exp : a for exp, a in subbed.items() } -#Returns a dictionary of (ETuple, coeff) pairs describing the polynomial eq / GCF(eq) -#The input nonz is an ETuple indicating the positions of variables known to be nonzero. -#The entries of nonz are assumed to be some relatively large number, like 100 cdef dict remove_gcf(dict eq_dict, ETuple nonz): + """ + Returns a dictionary of (ETuple, coeff) pairs describing the polynomial eq / GCF(eq) + The input nonz is an ETuple indicating the positions of variables known to be nonzero. + The entries of nonz are assumed to be some relatively large number, like 100 + """ #Find common variables, filtered according to known nonzeros cdef ETuple common_powers, exp common_powers = nonz @@ -135,10 +167,12 @@ cdef dict remove_gcf(dict eq_dict, ETuple nonz): common_powers = common_powers.emin(exp) return { exp.esub(common_powers) : c for exp, c in eq_dict.items() } -#Return tuple of pairs (ETuple, coeff) describing the monic polynomial associated to eq_dict -#Here, the leading coefficient is chosen according to the degree reverse lexicographic ordering -#(default for multivariate polynomial rings) cdef tuple to_monic(dict eq_dict): + """ + Return tuple of pairs (ETuple, coeff) describing the monic polynomial associated to eq_dict + Here, the leading coefficient is chosen according to the degree reverse lexicographic ordering + (default for multivariate polynomial rings) + """ if not eq_dict: return tuple() it = reversed(sorted(eq_dict, key=TermOrder().sortkey_degrevlex)) lm = next(it) @@ -151,9 +185,11 @@ cdef tuple to_monic(dict eq_dict): ret.append((exp, inv_lc * eq_dict[exp])) return tuple(ret) -# Return a dictionary describing a monic polynomial with no known nonzero gcd and -# no known squares cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq): + """ + Return a dictionary describing a monic polynomial with no known nonzero gcd and + no known squares + """ if not eq_dict: return tuple() return to_monic(remove_gcf(subs_squares(eq_dict, known_sq), nonz)) @@ -163,6 +199,9 @@ cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq): #Pre-compute powers of known values for efficiency cpdef dict compute_known_powers(ETuple max_deg, dict val_dict): + """ + Pre-compute powers of known values for efficiency + """ assert max(max_deg.nonzero_values(sort=False)) <= 100, "NotImplementedError: Cannot substitute for degree larger than 100" max_deg = max_deg.emin(ETuple({ idx : 100 for idx in val_dict }, len(max_deg))) cdef dict known_powers @@ -175,8 +214,10 @@ cpdef dict compute_known_powers(ETuple max_deg, dict val_dict): known_powers[var_idx][power+1] = tup_mul(known_powers[var_idx][power],val_dict[var_idx]) return known_powers -#Substitute given variables into a polynomial tuple cpdef dict subs(tuple poly_tup, dict known_powers): + """ + Substitute given variables into a polynomial tuple + """ cdef dict subbed = {} cdef ETuple exp, m cdef int var_idx, power @@ -195,8 +236,10 @@ cpdef dict subs(tuple poly_tup, dict known_powers): subbed[exp.eadd(m)] = coeff*c return subbed -#Multiplication of two tuples... may have to make this faster cdef tuple tup_mul(tuple p1, tuple p2): + """ + Multiplication of two tuples... may have to make this faster + """ cdef dict prod = dict() cdef ETuple xi, yj for xi, ai in p1: @@ -213,8 +256,10 @@ cdef tuple tup_mul(tuple p1, tuple p2): #Implement richcmp comparator object that can be passed in as key to sorted method -#Determine which polynomial is larger with respect to the degrevlex ordering cpdef int poly_tup_cmp(tuple tleft, tuple tright): + """ + Determine which polynomial is larger with respect to the degrevlex ordering + """ cdef int i, ret, sf, sg, val cdef ETuple f, g ret = 0 From 1bf83afc8301bad32c0d97930c90816bf5407392 Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Fri, 26 Feb 2021 09:16:25 -0800 Subject: [PATCH 008/632] older cyclotomic method included --- src/sage/combinat/root_system/f_matrix.py | 152 +++++++++++++++++++++- 1 file changed, 150 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index b521b88c3d7..5bb41b12aea 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -171,9 +171,9 @@ class FMatrix(): EXAMPLES:: - sage: f.get_pentagons()[1:3] + sage: f.get_defininig_equations("pentagons")[1:3] [fx1*fx5 - fx7^2, fx5*fx8*fx13 - fx2*fx12] - sage: f.get_hexagons()[1:3] + sage: f.get_defining_equations("hexagons")[1:3] [fx10*fx12 + (-zeta128^32)*fx12*fx13 + (-zeta128^16)*fx12, fx0 - 1] sage: f.get_orthogonality_constraints()[1:3] [fx1^2 - 1, fx2^2 - 1] @@ -952,6 +952,154 @@ def find_real_orthogonal_solution(self,use_mp=True,save_dir="",verbose=True): self.save_fvars(filename) self.symbols_known = True + ######################### + ### Cyclotomic method ### + ######################### + + def fix_gauge(self, algorithm=''): + """ + Fix the gauge by forcing F-symbols not already fixed to equal 1. + This method should be used AFTER adding hex and pentagon eqns to ideal_basis + """ + while len(self.solved) < len(self._poly_ring.gens()): + #Get a variable that has not been fixed + #In ascending index order, for consistent results + for var in self._poly_ring.gens(): + if var not in self.solved: + break + + #Fix var = 1, substitute, and solve equations + self.ideal_basis.add(var-1) + print("adding equation...", var-1) + self.ideal_basis = set(Ideal(list(self.ideal_basis)).groebner_basis(algorithm=algorithm)) + self.substitute_degree_one() + self.update_equations() + + def substitute_degree_one(self, eqns=None): + if eqns is None: + eqns = self.ideal_basis + + new_knowns = set() + useless = set() + for eq in eqns: + #Substitute known value from univariate degree 1 polynomial or, + #Following Bonderson, p. 37, solve linear equation with two terms + #for one of the variables + if eq.degree() == 1 and sum(eq.degrees()) <= 2 and eq.lm() not in self.solved: + self._fvars[self._var_to_sextuple[eq.lm()]] = -sum(c * m for c, m in zip(eq.coefficients()[1:], eq.monomials()[1:])) / eq.lc() + #Add variable to set of known values and remove this equation + new_knowns.add(eq.lm()) + useless.add(eq) + + #Solve equation of the form x_i x_j + k == 0 for x_i + # print("equation: ", eq, "variables ", eq.variables()) + # if eq.degree() == 2 and max(eq.degrees()) == 1 and len(eq.variables()) == 2 and eq.variable(0) not in self.solved: + # self._fvars[self._var_to_sextuple[str(eq.variable(0))]] = - eq.constant_coefficient() / eq.variable(1) + # print("Subbed {} for {}".format(- eq.constant_coefficient() / eq.variable(1), eq.variable(0))) + # #Add variable to set of known values and remove this equation + # new_knowns.add(eq.variable(0)) + # useless.add(eq) + + #Update fvars depending on other variables + self.solved.update(new_knowns) + for sextuple, rhs in self._fvars.items(): + d = { var : self._fvars[self._var_to_sextuple[var]] for var in rhs.variables() if var in self.solved } + if len(d) == 2: print("THREE TERM LINEAR EQUATION ENCOUNTERED!") + if d: + self._fvars[sextuple] = rhs.subs(d) + + # if rhs.variables() and rhs.variable() in self.solved: + # assert rhs.is_univariate(), "RHS expression is not univariate" + # d = { rhs.variable() : } + # # print("Performing substitution of {} with dictionary {}".format(rhs, d)) + # self._fvars[sextuple] = rhs.subs(d) + + return new_knowns, useless + + def update_equations(self): + """ + Update ideal_basis equations by plugging in known values + """ + special_values = { known : self._fvars[self._var_to_sextuple[known]] for known in self.solved } + self.ideal_basis = set(eq.subs(special_values) for eq in self.ideal_basis) + self.ideal_basis.discard(0) + + def find_cyclotomic_solution(self, equations=None, factor=True, verbose=True, prune=True, algorithm='', output=False): + """ + Solve the the hexagon and pentagon relations to evaluate the F-matrix. + This method (omitting the orthogonality constraints) produces + output in the cyclotomic field, but it is very limited in the size + of examples it can handle: for example, `A_2` at level 2 is + too large for this method. You may use :meth:`find_real_orthogonal_solution` + to solve much larger examples. + + INPUT: + + - ``equations`` -- (optional) a set of equations to be + solved. Defaults to the hexagon and pentagon equations. + - ``factor`` -- (default: ``True``). Set true to use + the sreduce method to simplify the hexagon and pentagon + equations before solving them. + - ``algorithm`` -- (optional). Algorithm to compute Groebner Basis. + - ``output`` -- (optional, default False). Output a dictionary of + F-matrix values. This may be useful to see but may be omitted + since this information will be available afterwards via the + :meth:`fmatrix` and :meth:`fmat` methods. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("A2",1),fusion_label="a") + sage: f.get_solution(verbose=False,output=True) + equations: 8 + equations: 16 + adding equation... fx4 - 1 + {(a2, a2, a2, a0, a1, a1): 1, + (a2, a2, a1, a2, a1, a0): 1, + (a2, a1, a2, a2, a0, a0): 1, + (a2, a1, a1, a1, a0, a2): 1, + (a1, a2, a2, a2, a0, a1): 1, + (a1, a2, a1, a1, a0, a0): 1, + (a1, a1, a2, a1, a2, a0): 1, + (a1, a1, a1, a0, a2, a2): 1} + + After you successfully run ``get_solution`` you may check + the correctness of the F-matrix by running :meth:`hexagon` + and :meth:`pentagon`. These should return empty lists + of equations. In this example, we turn off the factor + and prune optimizations to test all instances. + + EXAMPLES:: + + sage: f.hexagon(factor=False) + equations: 0 + [] + sage: f.hexagon(factor=False,side="right") + equations: 0 + [] + sage: f.pentagon(factor=False,prune=False) + equations: 0 + [] + + """ + if equations is None: + if verbose: + print("Setting up hexagons and pentagons...") + equations = self.get_defining_equations("hexagons")+self.get_defining_equations("pentagons") + #equations = self.hexagon(verbose=False, factor=factor)+self.pentagon(verbose=False, factor=factor, prune=prune) + if verbose: + print("Finding a Groebner basis...") + self.ideal_basis = set(Ideal(equations).groebner_basis(algorithm=algorithm)) + if verbose: + print("Solving...") + self.substitute_degree_one() + if verbose: + print("Fixing the gauge...") + self.fix_gauge(algorithm=algorithm) + if verbose: + print("Done!") + if output: + return self._fvars + ##################### ### Verifications ### ##################### From 33000e164f4a19053153baf96b13a7e4ea38e7b4 Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Fri, 26 Feb 2021 12:53:02 -0800 Subject: [PATCH 009/632] added support for multiprocessing braid group representations. basic docstring coverage at 100p --- src/sage/combinat/root_system/f_matrix.py | 177 +++++++++----- .../fast_parallel_fmats_methods.pxd | 2 + .../fast_parallel_fmats_methods.pyx | 20 +- src/sage/combinat/root_system/fusion_ring.py | 218 +++++++----------- .../combinat/root_system/poly_tup_engine.pyx | 2 - 5 files changed, 221 insertions(+), 198 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 5bb41b12aea..d83494c3c77 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -171,9 +171,9 @@ class FMatrix(): EXAMPLES:: - sage: f.get_defininig_equations("pentagons")[1:3] + sage: f.get_pentagons()[1:3] [fx1*fx5 - fx7^2, fx5*fx8*fx13 - fx2*fx12] - sage: f.get_defining_equations("hexagons")[1:3] + sage: f.get_hexagons()[1:3] [fx10*fx12 + (-zeta128^32)*fx12*fx13 + (-zeta128^16)*fx12, fx0 - 1] sage: f.get_orthogonality_constraints()[1:3] [fx1^2 - 1, fx2^2 - 1] @@ -483,25 +483,37 @@ def f_to(self,a,b,c,d): #################### def get_fmats_in_alg_field(self): + """ + Return F-symbols as elements of the AlgebraicField. This method uses + self._qqbar_embedding to coerce F-symbols into QQbar. + """ return { sextuple : self._qqbar_embedding(fvar) for sextuple, fvar in self._fvars.items() } - #Return radical expression for fmats for easy visualization def get_radical_expression(self): + """ + Return radical expression of F-symbols for easy visualization + """ return { sextuple : val.radical_expression() for sextuple, val in get_fmats_in_alg_field().items() } - #Construct a dictionary of idx, known_val pairs for equation substitution def get_known_vals(self): + """ + Construct a dictionary of idx, known_val pairs for equation substitution + """ return { var_idx : self._fvars[self._var_to_sextuple[self._poly_ring.gen(var_idx)]] for var_idx in self.solved } - #Construct a dictionary of known squares. Keys are variable indices and corresponding values are the squares def get_known_sq(self,eqns=None): + """ + Construct a dictionary of known squares. Keys are variable indices and corresponding values are the squares + """ if eqns is None: eqns = self.ideal_basis return { variables(eq_tup)[0] : -eq_tup[-1][1] for eq_tup in eqns if tup_fixes_sq(eq_tup) } - #Construct an ETuple indicating positions of known nonzero variables. - #MUST be called after self._ks = get_known_sq() def get_known_nonz(self): + """ + Construct an ETuple indicating positions of known nonzero variables. + MUST be called after self._ks = get_known_sq() + """ nonz = { self._var_to_idx[var] : 100 for var in self._singles } for idx in self._ks: nonz[idx] = 100 @@ -511,25 +523,33 @@ def get_known_nonz(self): ### Useful predicates ### ######################### - #Determine if monomial exponent is univariate in a still unknown F-symbol def is_univariate_in_unknown(self,monom_exp): + """ + Determine if monomial exponent is univariate in a still unknown F-symbol + """ return len(monom_exp.nonzero_values()) == 1 and monom_exp.nonzero_positions()[0] not in self.solved - #Determine if monomial exponent is univariate and linear in a vstill unknown F-symbol def is_uni_linear_in_unkwown(self,monom_exp): + """ + Determine if monomial exponent is univariate and linear in a vstill unknown F-symbol + """ return monom_exp.nonzero_values() == [1] and monom_exp.nonzero_positions()[0] not in self.solved ############################## ### Variables partitioning ### ############################## - #Get the size of the largest F-matrix F^{abc}_d def largest_fmat_size(self): + """ + Get the size of the largest F-matrix F^{abc}_d + """ return max(self.fmatrix(*tup).nrows() for tup in product(self.FR.basis(),repeat=4)) - #Partition the F-symbols according to the size of the F-matrix F^{abc}_d - #they belong to def get_fmats_by_size(self,n): + """ + Partition the F-symbols according to the size of the F-matrix F^{abc}_d + they belong to + """ fvars_copy = deepcopy(self._fvars) solved_copy = deepcopy(self.solved) self.clear_vars() @@ -547,16 +567,23 @@ def get_fmats_by_size(self,n): ### Checkpoint utilities ### ############################ - #Auto-generate filename string def get_fr_str(self): + """ + Auto-generate filename string for saving results + """ return self.FR.cartan_type()[0] + str(self.FR.cartan_type()[1]) + str(self.FR.fusion_level()) def save_fvars(self,filename): + """ + Save current variables state + """ with open(filename, 'wb') as f: pickle.dump([self._fvars, self.solved], f) - #If provided, optional param save_dir should have a trailing forward slash def load_fvars(self,save_dir=""): + """ + If provided, optional param save_dir should have a trailing forward slash + """ with open(save_dir + "saved_fvars_" + self.get_fr_str() + ".pickle", 'rb') as f: self._fvars, self.solved = pickle.load(f) @@ -564,19 +591,23 @@ def load_fvars(self,save_dir=""): ### MapReduce ### ################# - #Apply the given mapper to each element of the given input iterable and - #return the results (with no duplicates) in a list. This method applies the - #mapper in parallel if a worker_pool is provided. - ## INPUT: - #mapper is a string specifying the name of a function defined in the - #fast_parallel_fmats_methods module. - ###NOTES: - #If worker_pool is not provided, function maps and reduces on a single process. - #If worker_pool is provided, the function attempts to determine whether it should - #use multiprocessing based on the length of the input iterable. If it can't determine - #the length of the input iterable then it uses multiprocessing with the default chunksize of 1 - #if chunksize is not explicitly provided. def map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_thresh=None): + """ + Apply the given mapper to each element of the given input iterable and + return the results (with no duplicates) in a list. This method applies the + mapper in parallel if a worker_pool is provided. + + # INPUT: + mapper is a string specifying the name of a function defined in the + fast_parallel_fmats_methods module. + + ##NOTES: + If worker_pool is not provided, function maps and reduces on a single process. + If worker_pool is provided, the function attempts to determine whether it should + use multiprocessing based on the length of the input iterable. If it can't determine + the length of the input iterable then it uses multiprocessing with the default chunksize of 1 + if chunksize is not explicitly provided. + """ if mp_thresh is None: mp_thresh = self.mp_thresh #Compute multiprocessing parameters @@ -599,24 +630,16 @@ def map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_th if no_mp: results = collect_eqns(0) else: - results = self.reduce_multi_process(collect_eqns,worker_pool) + results = set() + for child_eqns in worker_pool.imap_unordered(collect_eqns,range(worker_pool._processes)): + results.update(child_eqns) + results = list(results) return results - ################ - ### Reducers ### - ################ - - #All-to-one communication step - def reduce_multi_process(self,reducer,worker_pool): - collected_eqns = set() - for child_eqns in worker_pool.imap_unordered(reducer,range(worker_pool._processes)): - collected_eqns.update(child_eqns) - return list(collected_eqns) - ######################## ### Equations set up ### ######################## - + def get_orthogonality_constraints(self,output=True): """ Get equations imposed on the F-matrix by orthogonality. @@ -660,12 +683,16 @@ def get_defining_equations(self,option,worker_pool=None,output=True): ### Equations processing ### ############################ - #Assemble a polynomial object from its tuple representation def tup_to_fpoly(self,eq_tup): + """ + Assemble a polynomial object from its tuple representation + """ return tup_to_poly(eq_tup,parent=self._poly_ring) - #Solve for a linear term occurring in a two-term equation. def solve_for_linear_terms(self,eqns=None): + """ + Solve for a linear term occurring in a two-term equation. + """ if eqns is None: eqns = self.ideal_basis @@ -692,8 +719,10 @@ def solve_for_linear_terms(self,eqns=None): linear_terms_exist = True return linear_terms_exist - #Backward substitution step. Traverse variables in reverse lexicographical order. def backward_subs(self): + """ + Backward substitution step. Traverse variables in reverse lexicographical order. + """ for var in reversed(self._poly_ring.gens()): sextuple = self._var_to_sextuple[var] rhs = self._fvars[sextuple] @@ -702,8 +731,10 @@ def backward_subs(self): kp = compute_known_powers(get_variables_degrees([rhs]), d) self._fvars[sextuple] = tuple(subs_squares(subs(rhs,kp),self._ks).items()) - #Update reduction parameters in all processes def update_reduction_params(self,eqns=None,worker_pool=None,children_need_update=False): + """ + Update reduction parameters in all processes + """ if eqns is None: eqns = self.ideal_basis self._ks, self._var_degs = self.get_known_sq(eqns), get_variables_degrees(eqns) @@ -715,10 +746,11 @@ def update_reduction_params(self,eqns=None,worker_pool=None,children_need_update new_data = [(self._fvars,self.solved,self._ks,self._var_degs)]*n_proc self.map_triv_reduce('update_child_fmats',new_data,worker_pool=worker_pool,chunksize=1,mp_thresh=0) - #Perform triangular elimination of linear terms in two-term equations until no such terms exist - #For optimal usage of TRIANGULAR elimination, pass in a SORTED list of equations - #Returns a list of equations def triangular_elim(self,required_vars=None,eqns=None,worker_pool=None,verbose=True): + """ + Perform triangular elimination of linear terms in two-term equations until no such terms exist + For optimal usage of TRIANGULAR elimination, pass in a SORTED list of equations + """ ret = True if eqns is None: eqns = self.ideal_basis @@ -756,6 +788,15 @@ def triangular_elim(self,required_vars=None,eqns=None,worker_pool=None,verbose=T ##################### def equations_graph(self,eqns=None): + """ + Construct a graph corresponding to equations. The nodes in the graph + are indices corresponding to variables in the equations and two + nodes are connected if the corresponding variables appear together in + a given equation. + + If no list of equations is passed, the graph is built from equations in + self.ideal_basis + """ if eqns is None: eqns = self.ideal_basis @@ -769,8 +810,10 @@ def equations_graph(self,eqns=None): G.add_edge(x,y) return G - #Partition equations corresponding to edges in a disconnected graph def partition_eqns(self,graph,eqns=None,verbose=True): + """ + Partition equations corresponding to edges in a disconnected graph + """ if eqns is None: eqns = self.ideal_basis partition = { tuple(c) : [] for c in graph.connected_components() } @@ -781,8 +824,10 @@ def partition_eqns(self,graph,eqns=None,verbose=True): print(graph.connected_components_sizes()) return partition - #Compute a Groebner basis for a set of equations partitioned according to their corresponding graph def par_graph_gb(self,worker_pool=None,eqns=None,term_order="degrevlex",verbose=True): + """ + Compute a Groebner basis for a set of equations partitioned according to their corresponding graph + """ if eqns is None: eqns = self.ideal_basis graph = self.equations_graph(eqns) small_comps = list() @@ -811,9 +856,11 @@ def par_graph_gb(self,worker_pool=None,eqns=None,term_order="degrevlex",verbose= ret = small_comp_gb + temp_eqns return ret - #Translate equations in each connected component to smaller polynomial rings - #so we can call built-in variety method. def get_component_variety(self,var,eqns): + """ + Translate equations in each connected component to smaller polynomial rings + so we can call built-in variety method. + """ #Define smaller poly ring in component vars R = PolynomialRing(self.FR.field(),len(var),'a',order='lex') @@ -831,10 +878,12 @@ def get_component_variety(self,var,eqns): ### Solution method ### ####################### - #Get a numeric solution for given equations by using the partitioning the equations - #graph and selecting an arbitrary point on the discrete degrees-of-freedom variety, - #which is computed as a Cartesian product def get_numeric_solution(self,eqns=None,verbose=True): + """ + Get a numeric solution for given equations by using the partitioning the equations + graph and selecting an arbitrary point on the discrete degrees-of-freedom variety, + which is computed as a Cartesian product + """ if eqns is None: eqns = self.ideal_basis eqns_partition = self.partition_eqns(self.equations_graph(eqns),verbose=verbose) @@ -896,10 +945,12 @@ def get_numeric_solution(self,eqns=None,verbose=True): self._fvars = { sextuple : constant_coeff(rhs) for sextuple, rhs in self._fvars.items() } self.clear_equations() - #Solver - #If provided, optional param save_dir (for saving computed _fvars) should have a trailing forward slash - #Supports "warm" start. Use load_fvars to re-start computation from checkpoint def find_real_orthogonal_solution(self,use_mp=True,save_dir="",verbose=True): + """ + Solver + If provided, optional param save_dir (for saving computed _fvars) should have a trailing forward slash + Supports "warm" start. Use load_fvars to re-start computation from checkpoint + """ #Set multiprocessing parameters. Context can only be set once, so we try to set it try: set_start_method('fork') @@ -912,7 +963,6 @@ def find_real_orthogonal_solution(self,use_mp=True,save_dir="",verbose=True): poly_sortkey = cmp_to_key(poly_tup_cmp) self.get_orthogonality_constraints(output=False) self.get_defining_equations('hexagons',worker_pool=pool,output=False) - self.ideal_basis = sorted(self.ideal_basis, key=poly_sortkey) if verbose: print("Set up {} hex and orthogonality constraints...".format(len(self.ideal_basis))) @@ -1100,12 +1150,16 @@ def find_cyclotomic_solution(self, equations=None, factor=True, verbose=True, pr if output: return self._fvars + ##################### ### Verifications ### ##################### - #Ensure the hexagon equations are satisfied + def verify_hexagons(self): + """ + Ensure the hexagon equations are satisfied + """ hex = [] for a,b,c,d,e,g in product(self.FR.basis(),repeat=6): lhs = self.field(self.FR.r_matrix(a,c,e))*self.fmat(a,c,b,d,e,g)*self.field(self.FR.r_matrix(b,c,g)) @@ -1119,6 +1173,9 @@ def verify_hexagons(self): return hex def verify_pentagons(self,use_mp=True,prune=False): + """ + Ensure the pentagon equations are satisfied + """ print("Testing F-symbols for {}...".format(self.FR)) fvars_copy = deepcopy(self._fvars) self._fvars = { sextuple : float(RDF(rhs)) for sextuple, rhs in self.get_fmats_in_alg_field().items() } @@ -1138,8 +1195,10 @@ def verify_pentagons(self,use_mp=True,prune=False): self._fvars = fvars_copy return pe - #Verify that all F-matrices are real and unitary (orthogonal) def fmats_are_orthogonal(self): + """ + Verify that all F-matrices are real and unitary (orthogonal) + """ is_orthog = [] for a,b,c,d in product(self.FR.basis(),repeat=4): mat = self.fmatrix(a,b,c,d) diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd index 63dde1f57cf..487b7b60e83 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd @@ -4,3 +4,5 @@ cpdef update_reduce(factory, tuple eq_tup) cpdef compute_gb(factory, tuple args) cpdef update_child_fmats(factory, tuple data_tup) cpdef pent_verify(factory, tuple mp_params) + +cdef _fmat(factory, a, b, c, d, x, y) diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index 7add91bdcaf..0f99a504836 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -25,14 +25,16 @@ worker_results = list() ### Parallel code executor ### ############################## -#Execute a function defined in this module (sage.combinat.root_system.fast_parallel_fmats_methods) -#in a worker process, and supply the factory parameter by constructing a reference -#to the FMatrix object in the worker's memory adress space from its id -### NOTE: When the parent process is forked, each worker gets a copy of -#every global variable. The virtual memory address of object X in the parent -#process equals the VIRTUAL memory address of the copy of object X in each -#worker, so we may construct references to forked copies of X def executor(params): + """ + Execute a function defined in this module (sage.combinat.root_system.fast_parallel_fmats_methods) + in a worker process, and supply the factory parameter by constructing a reference + to the FMatrix object in the worker's memory adress space from its id + ## NOTE: When the parent process is forked, each worker gets a copy of + every global variable. The virtual memory address of object X in the parent + process equals the VIRTUAL memory address of the copy of object X in each + worker, so we may construct references to forked copies of X + """ (fn_name, fmats_id), args = params #Construct a reference to global FMatrix object in this worker's memory fmats_obj = ctypes.cast(fmats_id, ctypes.py_object).value @@ -44,8 +46,10 @@ def executor(params): ### Mappers ### ############### -#Cython version of fmat class method. Using cdef for fastest dispatch cdef _fmat(factory, a, b, c, d, x, y): + """ + Cython version of fmat class method. Using cdef for fastest dispatch + """ if factory.FR.Nk_ij(a,b,x) == 0 or factory.FR.Nk_ij(x,c,d) == 0 or factory.FR.Nk_ij(b,c,y) == 0 or factory.FR.Nk_ij(a,y,d) == 0: return 0 #Some known zero F-symbols diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 811935103b0..23d19dc4756 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -11,19 +11,19 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.combinat.root_system.weyl_characters import WeylCharacterRing +from itertools import product, zip_longest +from multiprocessing import Pool, set_start_method from sage.combinat.q_analogues import q_int -from sage.matrix.special import diagonal_matrix +import sage.combinat.root_system.f_matrix as FMatrix +from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import collect_eqns, executor +from sage.combinat.root_system.weyl_characters import WeylCharacterRing from sage.matrix.constructor import matrix +from sage.matrix.special import diagonal_matrix +from sage.misc.cachefunc import cached_method +from sage.misc.lazy_attribute import lazy_attribute from sage.misc.misc import inject_variable from sage.rings.integer_ring import ZZ from sage.rings.number_field.number_field import CyclotomicField -from sage.misc.cachefunc import cached_method - -from itertools import product -import sage.combinat.root_system.f_matrix as FMatrix -from sage.misc.lazy_attribute import lazy_attribute -from sage.matrix.special import diagonal_matrix class FusionRing(WeylCharacterRing): r""" @@ -875,11 +875,13 @@ def D_minus(self): ### Braid group representations ### ################################### - #Recursively enumerate all the admissible trees with given top row and root. - #Returns a list of tuples (l1,...,lk) such that - #root -> lk # m[-1], lk -> l_{k-1} # m[-2], ..., l1 -> m[0] # m[1], - #with top_row = m def get_trees(self,top_row,root): + """ + Recursively enumerate all the admissible trees with given top row and root. + Returns a list of tuples (l1,...,lk) such that + root -> lk # m[-1], lk -> l_{k-1} # m[-2], ..., l1 -> m[0] # m[1], + with top_row = m + """ if len(top_row) == 2: m1, m2 = top_row return [[]] if self.Nk_ij(m1,m2,root) else [] @@ -887,10 +889,12 @@ def get_trees(self,top_row,root): m1, m2 = top_row[:2] return [tuple([l,*b]) for l in self.basis() for b in self.get_trees([l]+top_row[2:],root) if self.Nk_ij(m1,m2,l)] - #Get the so-called computational basis for Hom(b, a^n). The basis is a list of - #(n-2)-tuples (m_1,...,m_{n//2},l_1,...,l_{(n-3)//2}) such that - #each m_i is a monomial in a^2 and l_{j+1} \in l_j # a, and l[-1] \in a # b def get_comp_basis(self,a,b,n_strands): + """ + Get the so-called computational basis for Hom(b, a^n). The basis is a list of + (n-2)-tuples (m_1,...,m_{n//2},l_1,...,l_{(n-3)//2}) such that + each m_i is a monomial in a^2 and l_{j+1} \in l_j # a, and l[-1] \in a # b + """ comp_basis = list() for top in product((a*a).monomials(),repeat=n_strands//2): #If the n_strands is odd, we must extend the top row by a fusing anyon @@ -898,143 +902,99 @@ def get_comp_basis(self,a,b,n_strands): comp_basis.extend(tuple([*top,*levels]) for levels in self.get_trees(top_row,b)) return comp_basis - #Compute the (xi,yi), (xj,yj) entry of generator braiding the middle two strands - #in the tree b -> xi # yi -> (a # a) # (a # a), which results in a sum over j - #of trees b -> xj # yj -> (a # a) # (a # a) - @cached_method - def mid_sig_ij(self,row,col,a,b): - xi, yi = row - xj, yj = col - entry = 0 - for c in self.basis(): - for d in self.basis(): - ##Warning: We assume F-matrices are orthogonal!!! (using transpose for inverse) - f1 = self.fmats.fmat(a,a,yi,b,xi,c) - f2 = self.fmats.fmat(a,a,a,c,d,yi) - f3 = self.fmats.fmat(a,a,a,c,d,yj) - f4 = self.fmats.fmat(a,a,yj,b,xj,c) - r = self.r_matrix(a,a,d) - entry += f1 * f2 * r * f3 * f4 - return entry - - #Compute the xi, xj entry of the braid generator on the right-most strands, - #corresponding to the tree b -> (xi # a) -> (a # a) # a, which results in a - #sum over j of trees b -> xj -> (a # a) # (a # a) - @cached_method - def odd_one_out_ij(self,xi,xj,a,b): - entry = 0 - for c in self.basis(): - ##Warning: We assume F-matrices are orthogonal!!! (using transpose for inverse) - f1 = self.fmats.fmat(a,a,a,b,xi,c) - f2 = self.fmats.fmat(a,a,a,b,xj,c) - r = self.r_matrix(a,a,c) - entry += f1 * r * f2 - return entry - @lazy_attribute def fmats(self): + """ + Construct an FMatrix factory to solve the pentagon relations and + organize the resulting F-symbols. We only need this attribute to compute + braid group representations. + """ return FMatrix.FMatrix(self) - #Compute generators of the Artin braid group on n_strands strands. If - #fusing_anyon = a and total_topological_charge = b, the generators are - #endomorphisms of Hom(a^n_strands, b). - ###NOTE: For now, we assume existence of fmats with relevant F-symbols ready. - #In the future this method will call an appropriate F-matrix solver... - #For useful group calculations, the F-symbols should lie in a NumberField. - def get_braid_generators(self,fusing_anyon,total_topological_charge,n_strands): - assert n_strands > 2, "The number of strands must be an integer greater than 2" - a, b = fusing_anyon, total_topological_charge + def emap(self,mapper,input_args,worker_pool=None): + """ + Apply the given mapper to each element of the given input iterable and + return the results (with no duplicates) in a list. This method applies the + mapper in parallel if a worker_pool is provided. + + # INPUT: + mapper is a string specifying the name of a function defined in the + fast_parallel_fmats_methods module. + + input_args should be a tuple holding arguments to be passed to mapper + + ##NOTES: + If worker_pool is not provided, function maps and reduces on a single process. + If worker_pool is provided, the function attempts to determine whether it should + use multiprocessing based on the length of the input iterable. If it can't determine + the length of the input iterable then it uses multiprocessing with the default chunksize of 1 + if chunksize is not explicitly provided. + """ + n_proc = worker_pool._processes if worker_pool is not None else 1 + input_iter = [(child_id, n_proc, input_args) for child_id in range(n_proc)] + no_mp = worker_pool is None + #Map phase. Casting Async Object blocks execution... Each process holds results + #in its copy of fmats.temp_eqns + input_iter = zip_longest([],input_iter,fillvalue=(mapper,id(self))) + if no_mp: + list(map(executor,input_iter)) + else: + list(worker_pool.imap_unordered(executor,input_iter,chunksize=1)) + #Reduce phase + if no_mp: + results = collect_eqns(0) + else: + results = set() + for worker_results in worker_pool.imap_unordered(collect_eqns,range(worker_pool._processes),chunksize=1): + results.update(worker_results) + results = list(results) + return results + def get_braid_generators(self,fusing_anyon,total_topological_charge,n_strands,use_mp=True): + """ + Compute generators of the Artin braid group on n_strands strands. If + fusing_anyon = a and total_topological_charge = b, the generators are + endomorphisms of Hom(a^n_strands, b) + """ + assert n_strands > 2, "The number of strands must be an integer greater than 2" #Construct associated FMatrix object and solve for F-symbols if not self.fmats.symbols_known: self.fmats.find_real_orthogonal_solution() - #Set up the computational basis + #Set multiprocessing parameters. Context can only be set once, so we try to set it + try: + set_start_method('fork') + except RuntimeError: + pass + pool = Pool() if use_mp else None + + #Set up computational basis and compute generators one at a time + a, b = fusing_anyon, total_topological_charge comp_basis = self.get_comp_basis(a,b,n_strands) - basis_dict = { elt : i for i, elt in enumerate(comp_basis) } - dim = len(comp_basis) - print("Computing an {}-dimensional representation of the Artin braid group on {} strands...".format(dim,n_strands)) + d = len(comp_basis) + print("Computing an {}-dimensional representation of the Artin braid group on {} strands...".format(d,n_strands)) #Compute diagonal odd-indexed generators using the 3j-symbols gens = { 2*i+1 : diagonal_matrix(self.r_matrix(a,a,c[i]) for c in comp_basis) for i in range(n_strands//2) } #Compute even-indexed generators using F-matrices for k in range(1,n_strands//2): - entries = dict() - for i in range(dim): - for f,e,q in product(self.basis(),repeat=3): - #Compute appropriate possible nonzero row index - nnz_pos = list(comp_basis[i]) - nnz_pos[k-1:k+1] = f,e - #Handle the special case k = 1 - if k > 1: - nnz_pos[n_strands//2+k-2] = q - nnz_pos = tuple(nnz_pos) - - #Skip repeated entries when k = 1 - if nnz_pos in comp_basis and (basis_dict[nnz_pos],i) not in entries: - m, l = comp_basis[i][:n_strands//2], comp_basis[i][n_strands//2:] - #A few special cases - top_left = m[0] - if k >= 3: - top_left = l[k-3] - root = b - if k - 1 < len(l): - root = l[k-1] - - #Handle the special case k = 1 - if k == 1: - entries[basis_dict[nnz_pos],i] = self.mid_sig_ij(m[:2],(f,e),a,root) - continue - - entry = 0 - for p in self.basis(): - f1 = self.fmats.fmat(top_left,m[k-1],m[k],root,l[k-2],p) - f2 = self.fmats.fmat(top_left,f,e,root,q,p) - entry += f1 * self.mid_sig_ij((m[k-1],m[k]),(f,e),a,p) * f2 - entries[basis_dict[nnz_pos],i] = entry - gens[2*k] = matrix(entries) + entries = self.emap('sig_2k',(k,a,b,n_strands),pool) + gens[2*k] = matrix(dict(entries)) #If n_strands is odd, we compute the final generator if n_strands % 2: - entries = dict() - for i in range(dim): - for f, q in product(self.basis(),repeat=2): - #Compute appropriate possible nonzero row index - nnz_pos = list(comp_basis[i]) - nnz_pos[n_strands//2-1] = f - #Handle small special case - if n_strands > 3: - nnz_pos[-1] = q - nnz_pos = tuple(nnz_pos) - - if nnz_pos in comp_basis: - m, l = comp_basis[i][:n_strands//2], comp_basis[i][n_strands//2:] - - #Handle a couple of small special cases - if n_strands == 3: - entries[basis_dict[nnz_pos],i] = self.odd_one_out_ij(m[-1],f,a,b) - continue - top_left = m[0] - if n_strands > 5: - top_left = l[-2] - root = b - - #Compute relevant entry - entry = 0 - for p in self.basis(): - f1 = self.fmats.fmat(top_left,m[-1],a,root,l[-1],p) - f2 = self.fmats.fmat(top_left,f,a,root,q,p) - entry += f1 * self.odd_one_out_ij(m[-1],f,a,p) * f2 - entries[basis_dict[nnz_pos],i] = entry - gens[n_strands-1] = matrix(entries) + entries = self.emap('odd_one_out',(a,b,n_strands),pool) + gens[n_strands-1] = matrix(dict(entries)) return comp_basis, [gens[k] for k in sorted(gens)] - #A useful sanity check - #Determine if given iterable of n matrices defines a representation of - #the Artin braid group on (n+1) strands def gens_satisfy_braid_gp_rels(self,sig): + """ + Determine if given iterable of n matrices defines a representation of + the Artin braid group on (n+1) strands. Tests correctness of + get_braid_generators method. + """ n = len(sig) braid_rels = all(sig[i] * sig[i+1] * sig[i] == sig[i+1] * sig[i] * sig[i+1] for i in range(n-1)) far_comm = all(sig[i] * sig[j] == sig[j] * sig[i] for i, j in product(range(n),repeat=2) if abs(i-j) > 1 and i > j) diff --git a/src/sage/combinat/root_system/poly_tup_engine.pyx b/src/sage/combinat/root_system/poly_tup_engine.pyx index f6edc15a235..f74d674619b 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pyx +++ b/src/sage/combinat/root_system/poly_tup_engine.pyx @@ -67,7 +67,6 @@ cpdef tuple resize(tuple eq_tup, dict idx_map, int nvars): ### Convenience methods ### ########################### - cdef ETuple degrees(tuple poly_tup): """ Return the maximal degree of each variable in the polynomial @@ -197,7 +196,6 @@ cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq): ### Substitution ### #################### -#Pre-compute powers of known values for efficiency cpdef dict compute_known_powers(ETuple max_deg, dict val_dict): """ Pre-compute powers of known values for efficiency From 2e0d1e9986630f074fcf002a63c26e97333fb158 Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Fri, 26 Feb 2021 14:32:59 -0800 Subject: [PATCH 010/632] begin work on base coercion --- src/sage/combinat/root_system/f_matrix.py | 188 ++++++++++++++---- .../fast_parallel_fmats_methods.pyx | 16 +- src/sage/combinat/root_system/fusion_ring.py | 13 +- .../combinat/root_system/weyl_characters.py | 2 + 4 files changed, 172 insertions(+), 47 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index d83494c3c77..d424dfe8edd 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -27,12 +27,12 @@ import cPickle as pickle except: import pickle -from multiprocessing import cpu_count, Pool, set_start_method +from multiprocessing import cpu_count, Pool, set_start_method, TimeoutError import numpy as np from sage.combinat.root_system.fast_parallel_fmats_methods import * from sage.combinat.root_system.poly_tup_engine import * from sage.rings.polynomial.polydict import ETuple -from sage.rings.qqbar import AA, QQbar, number_field_elements_from_algebraics +from sage.rings.qqbar import AA, QQbar from sage.rings.real_double import RDF from itertools import zip_longest @@ -220,12 +220,12 @@ class FMatrix(): """ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variables=False): - self.FR = fusion_ring - if self.FR._fusion_labels is None: - self.FR.fusion_labels(fusion_label, inject_variables=True) + self._FR = fusion_ring + if self._FR._fusion_labels is None: + self._FR.fusion_labels(fusion_label, inject_variables=True) #Set up F-symbols entry by entry n_vars = self.findcases() - self._poly_ring = PolynomialRing(self.FR.field(),n_vars,var_prefix) + self._poly_ring = PolynomialRing(self._FR.field(),n_vars,var_prefix) if inject_variables: print ("creating variables %s%s..%s%s"%(var_prefix,1,var_prefix,n_vars)) self._poly_ring.inject_variables() @@ -241,9 +241,9 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab self.solved = set() #New attributes of the FMatrix class - self.field = self.FR.field() - r = self.field.defining_polynomial().roots(ring=QQbar,multiplicities=False)[0] - self._qqbar_embedding = self.field.hom([r],QQbar) + self._field = self._FR.field() + r = self._field.defining_polynomial().roots(ring=QQbar,multiplicities=False)[0] + self._qqbar_embedding = self._field.hom([r],QQbar) self._var_to_idx = { var : idx for idx, var in enumerate(self._poly_ring.gens()) } self._ks = self.get_known_sq() self._nnz = self.get_known_nonz() @@ -265,7 +265,7 @@ def __repr__(self): sage: FMatrix(FusionRing("B2",1)) F-Matrix factory for The Fusion Ring of Type B2 and level 1 with Integer Ring coefficients """ - return "F-Matrix factory for %s"%self.FR + return "F-Matrix factory for %s"%self._FR def remaining_vars(self): """ @@ -316,21 +316,21 @@ def fmat(self, a, b, c, d, x, y, data=True): (zeta60^14 - zeta60^6 - zeta60^4 + 1)] """ - if self.FR.Nk_ij(a,b,x) == 0 or self.FR.Nk_ij(x,c,d) == 0 or self.FR.Nk_ij(b,c,y) == 0 or self.FR.Nk_ij(a,y,d) == 0: + if self._FR.Nk_ij(a,b,x) == 0 or self._FR.Nk_ij(x,c,d) == 0 or self._FR.Nk_ij(b,c,y) == 0 or self._FR.Nk_ij(a,y,d) == 0: return 0 #Some known zero F-symbols - if a == self.FR.one(): + if a == self._FR.one(): if x == b and y == d: return 1 else: return 0 - if b == self.FR.one(): + if b == self._FR.one(): if x == a and y == c: return 1 else: return 0 - if c == self.FR.one(): + if c == self._FR.one(): if x == d and y == b: return 1 else: @@ -374,6 +374,12 @@ def fmatrix(self,a,b,c,d): Y = self.f_to(a,b,c,d) return matrix([[self.fmat(a,b,c,d,x,y) for y in Y] for x in X]) + def field(self): + return self._field + + def FR(self): + return self._FR + def findcases(self,output=False): """ Return unknown F-matrix entries. If run with output=True, @@ -402,7 +408,7 @@ def findcases(self,output=False): if output: idx_map = dict() ret = dict() - for (a,b,c,d) in list(product(self.FR.basis(), repeat=4)): + for (a,b,c,d) in list(product(self._FR.basis(), repeat=4)): for x in self.f_from(a, b, c, d): for y in self.f_to(a, b, c, d): fm = self.fmat(a, b, c, d, x, y, data=False) @@ -422,7 +428,7 @@ def singletons(self): Find x_i that are automatically nonzero, because their F-matrix is 1x1 """ ret = [] - for (a, b, c, d) in list(product(self.FR.basis(), repeat=4)): + for (a, b, c, d) in list(product(self._FR.basis(), repeat=4)): (ff,ft) = (self.f_from(a,b,c,d),self.f_to(a,b,c,d)) if len(ff) == 1 and len(ft) == 1: v = self._fvars.get((a,b,c,d,ff[0],ft[0]), None) @@ -451,7 +457,7 @@ def f_from(self,a,b,c,d): [a1, a3] """ - return [x for x in self.FR.basis() if self.FR.Nk_ij(a,b,x) != 0 and self.FR.Nk_ij(x,c,d) != 0] + return [x for x in self._FR.basis() if self._FR.Nk_ij(a,b,x) != 0 and self._FR.Nk_ij(x,c,d) != 0] def f_to(self,a,b,c,d): r""" @@ -476,7 +482,7 @@ def f_to(self,a,b,c,d): """ - return [y for y in self.FR.basis() if self.FR.Nk_ij(b,c,y) != 0 and self.FR.Nk_ij(a,y,d) != 0] + return [y for y in self._FR.basis() if self._FR.Nk_ij(b,c,y) != 0 and self._FR.Nk_ij(a,y,d) != 0] #################### ### Data getters ### @@ -519,6 +525,28 @@ def get_known_nonz(self): nonz[idx] = 100 return ETuple(nonz, self._poly_ring.ngens()) + def get_qqbar_embedding(self): + """ + Return an embedding from the base field containing F-symbols (the + FusionRing's CyclotomicField, a NumberField, or QQbar) into QQbar + """ + return self._qqbar_embedding + + def get_coerce_map_from_fr_cyclotomic_field(self): + """ + Return a coercion map from the FusionRing's cyclotomic field into the + base field containing all F-symbols (this could be the FusionRing's + CyclotomicField, a NumberField, or QQbar). + """ + #If base field is different from associated FusionRing's CyclotomicField, + #return coercion map + try: + return self._coerce_map_from_cyc_field + #Otherwise, return identity map CyclotomicField <-> CyclotomicField + except AttributeError: + F = self._FR.field() + return F.hom([F.gen()], F) + ######################### ### Useful predicates ### ######################### @@ -543,7 +571,7 @@ def largest_fmat_size(self): """ Get the size of the largest F-matrix F^{abc}_d """ - return max(self.fmatrix(*tup).nrows() for tup in product(self.FR.basis(),repeat=4)) + return max(self.fmatrix(*tup).nrows() for tup in product(self._FR.basis(),repeat=4)) def get_fmats_by_size(self,n): """ @@ -554,7 +582,7 @@ def get_fmats_by_size(self,n): solved_copy = deepcopy(self.solved) self.clear_vars() var_set = set() - for quadruple in product(self.FR.basis(),repeat=4): + for quadruple in product(self._FR.basis(),repeat=4): F = self.fmatrix(*quadruple) #Discard trivial 1x1 F-matrix, if applicable if F.nrows() == n and F.coefficients() != [1]: @@ -571,7 +599,7 @@ def get_fr_str(self): """ Auto-generate filename string for saving results """ - return self.FR.cartan_type()[0] + str(self.FR.cartan_type()[1]) + str(self.FR.fusion_level()) + return self._FR.cartan_type()[0] + str(self._FR.cartan_type()[1]) + str(self._FR.fusion_level()) def save_fvars(self,filename): """ @@ -649,7 +677,7 @@ def get_orthogonality_constraints(self,output=True): appended to self.ideal_basis """ eqns = list() - for tup in product(self.FR.basis(), repeat=4): + for tup in product(self._FR.basis(), repeat=4): mat = self.fmatrix(*tup) eqns.extend((mat.T * mat - matrix.identity(mat.nrows())).coefficients()) if output: @@ -768,7 +796,7 @@ def triangular_elim(self,required_vars=None,eqns=None,worker_pool=None,verbose=T self.backward_subs() #Support early termination in case only some F-symbols are needed - req_vars_known = all(self._fvars[self._var_to_sextuple[var]] in self.FR.field() for var in required_vars) + req_vars_known = all(self._fvars[self._var_to_sextuple[var]] in self._FR.field() for var in required_vars) if req_vars_known: return 1 #Compute new reduction params, send to child processes if any, and update eqns @@ -862,7 +890,7 @@ def get_component_variety(self,var,eqns): so we can call built-in variety method. """ #Define smaller poly ring in component vars - R = PolynomialRing(self.FR.field(),len(var),'a',order='lex') + R = PolynomialRing(self._FR.field(),len(var),'a',order='lex') #Zip tuples into R and compute Groebner basis idx_map = { old : new for new, old in enumerate(sorted(var)) } @@ -878,7 +906,7 @@ def get_component_variety(self,var,eqns): ### Solution method ### ####################### - def get_numeric_solution(self,eqns=None,verbose=True): + def og_get_numeric_solution(self,eqns=None,verbose=True): """ Get a numeric solution for given equations by using the partitioning the equations graph and selecting an arbitrary point on the discrete degrees-of-freedom variety, @@ -888,11 +916,11 @@ def get_numeric_solution(self,eqns=None,verbose=True): eqns = self.ideal_basis eqns_partition = self.partition_eqns(self.equations_graph(eqns),verbose=verbose) - x = self.FR.field()['x'].gen() + x = self._FR.field()['x'].gen() numeric_fvars = dict() non_cyclotomic_roots = list() must_change_base_field = False - phi = self.FR.field().hom([self.field.gen()],self.FR.field()) + phi = self._FR.field().hom([self._field.gen()],self._FR.field()) for comp, part in eqns_partition.items(): #If component have only one equation in a single variable, get a root if len(comp) == 1 and len(part) == 1: @@ -916,17 +944,96 @@ def get_numeric_solution(self,eqns=None,verbose=True): if must_change_base_field: #If needed, find a NumberField containing all the roots - roots = [self.FR.field().gen()]+[r[1] for r in non_cyclotomic_roots] - self.field, nf_elts, self._qqbar_embedding = number_field_elements_from_algebraics(roots,minimal=True) + roots = [self._FR.field().gen()]+[r[1] for r in non_cyclotomic_roots] + self._field, nf_elts, self._qqbar_embedding = number_field_elements_from_algebraics(roots,minimal=True) #Embed cyclotomic field into newly constructed NumberField cyc_gen_as_nf_elt = nf_elts.pop(0) - phi = self.FR.field().hom([cyc_gen_as_nf_elt], self.field) + phi = self._FR.field().hom([cyc_gen_as_nf_elt], self._field) numeric_fvars = { k : phi(v) for k, v in numeric_fvars.items() } for i, elt in enumerate(nf_elts): numeric_fvars[non_cyclotomic_roots[i][0]] = elt #Do some appropriate conversions - new_poly_ring = self._poly_ring.change_ring(self.field) + new_poly_ring = self._poly_ring.change_ring(self._field) + nvars = self._poly_ring.ngens() + self._var_to_idx = { new_poly_ring.gen(i) : i for i in range(nvars) } + self._var_to_sextuple = { new_poly_ring.gen(i) : self._var_to_sextuple[self._poly_ring.gen(i)] for i in range(nvars) } + self._poly_ring = new_poly_ring + + #Ensure all F-symbols are known + self.solved.update(numeric_fvars) + nvars = self._poly_ring.ngens() + assert len(self.solved) == nvars, "Some F-symbols are still missing...{}".format([self._poly_ring.gen(fx) for fx in set(range(nvars)).difference(self.solved)]) + + #Backward substitution step. Traverse variables in reverse lexicographical order. (System is in triangular form) + self._fvars = { sextuple : apply_coeff_map(poly_to_tup(rhs),phi) for sextuple, rhs in self._fvars.items() } + for fx, rhs in numeric_fvars.items(): + self._fvars[self._var_to_sextuple[self._poly_ring.gen(fx)]] = ((ETuple({},nvars),rhs),) + self.backward_subs() + self._fvars = { sextuple : constant_coeff(rhs) for sextuple, rhs in self._fvars.items() } + self.clear_equations() + + def get_numeric_solution(self,eqns=None,verbose=True): + """ + Get a numeric solution for given equations by using the partitioning the equations + graph and selecting an arbitrary point on the discrete degrees-of-freedom variety, + which is computed as a Cartesian product + """ + if eqns is None: + eqns = self.ideal_basis + eqns_partition = self.partition_eqns(self.equations_graph(eqns),verbose=verbose) + + F = self._field + x = F['x'].gen() + numeric_fvars = dict() + non_cyclotomic_roots = list() + must_change_base_field = False + phi = F.hom([F.gen()],F) + for comp, part in eqns_partition.items(): + #If component have only one equation in a single variable, get a root + if len(comp) == 1 and len(part) == 1: + #Attempt to find cyclotomic root + univ_poly = tup_to_univ_poly(part[0],x) + real_roots = univ_poly.roots(ring=AA,multiplicities=False) + assert real_roots, "No real solution exists... {} has no real roots".format(univ_poly) + roots = univ_poly.roots(multiplicities=False) + if roots: + numeric_fvars[comp[0]] = roots[0] + else: + non_cyclotomic_roots.append((comp[0],real_roots[0])) + must_change_base_field = True + #Otherwise, compute the component variety and select a point to obtain a numerical solution + else: + sols = self.get_component_variety(comp,part) + assert len(sols) > 1, "No real solution exists... component with variables {} has no real points".format(comp) + for fx, rhs in sols[0].items(): + non_cyclotomic_roots.append((fx,rhs)) + must_change_base_field = True + + if must_change_base_field: + #Attempt to compute smallest number field containing all the F-symbols + #If calculation takes too long, we use QQbar as the base field + proc = Pool(1) + input_args = ((('get_appropriate_number_field',id(self)),non_cyclotomic_roots),) + p = proc.apply_async(executor,input_args) + try: + self._field, bf_elts, self._qqbar_embedding = p.get(timeout=100) + except TimeoutError: + self._field = QQbar + bf_elts = [self._qqbar_embedding(F.gen())] + bf_elts += [rhs for fx,rhs in non_cyclotomic_roots] + self._qqbar_embedding = lambda x : x + + #Embed cyclotomic field into newly constructed base field + cyc_gen_as_bf_elt = bf_elts.pop(0) + phi = self._FR.field().hom([cyc_gen_as_bf_elt], self._field) + self._coerce_map_from_cyc_field = phi + numeric_fvars = { k : phi(v) for k, v in numeric_fvars.items() } + for i, elt in enumerate(bf_elts): + numeric_fvars[non_cyclotomic_roots[i][0]] = elt + + #Do some appropriate conversions + new_poly_ring = self._poly_ring.change_ring(self._field) nvars = self._poly_ring.ngens() self._var_to_idx = { new_poly_ring.gen(i) : i for i in range(nvars) } self._var_to_sextuple = { new_poly_ring.gen(i) : self._var_to_sextuple[self._poly_ring.gen(i)] for i in range(nvars) } @@ -957,7 +1064,7 @@ def find_real_orthogonal_solution(self,use_mp=True,save_dir="",verbose=True): except RuntimeError: pass pool = Pool(processes=max(cpu_count()-1,1)) if use_mp else None - print("Computing F-symbols for {} with {} variables...".format(self.FR, len(self._fvars))) + print("Computing F-symbols for {} with {} variables...".format(self._FR, len(self._fvars))) #Set up hexagon equations and orthogonality constraints poly_sortkey = cmp_to_key(poly_tup_cmp) @@ -1001,6 +1108,7 @@ def find_real_orthogonal_solution(self,use_mp=True,save_dir="",verbose=True): self.get_numeric_solution(verbose=verbose) self.save_fvars(filename) self.symbols_known = True + self._FR._coer = self.get_coerce_map_from_fr_cyclotomic_field() ######################### ### Cyclotomic method ### @@ -1161,12 +1269,12 @@ def verify_hexagons(self): Ensure the hexagon equations are satisfied """ hex = [] - for a,b,c,d,e,g in product(self.FR.basis(),repeat=6): - lhs = self.field(self.FR.r_matrix(a,c,e))*self.fmat(a,c,b,d,e,g)*self.field(self.FR.r_matrix(b,c,g)) - rhs = sum(self.fmat(c,a,b,d,e,f)*self.field(self.FR.r_matrix(f,c,d))*self.fmat(a,b,c,d,f,g) for f in self.FR.basis()) + for a,b,c,d,e,g in product(self._FR.basis(),repeat=6): + lhs = self._field(self._FR.r_matrix(a,c,e))*self.fmat(a,c,b,d,e,g)*self._field(self._FR.r_matrix(b,c,g)) + rhs = sum(self.fmat(c,a,b,d,e,f)*self._field(self._FR.r_matrix(f,c,d))*self.fmat(a,b,c,d,f,g) for f in self._FR.basis()) hex.append(lhs-rhs) - if all(h == self.field.zero() for h in hex): - print("Success!!! Found valid F-symbols for {}".format(self.FR)) + if all(h == self._field.zero() for h in hex): + print("Success!!! Found valid F-symbols for {}".format(self._FR)) else: print("Ooops... something went wrong... These pentagons remain:") print(hex) @@ -1176,7 +1284,7 @@ def verify_pentagons(self,use_mp=True,prune=False): """ Ensure the pentagon equations are satisfied """ - print("Testing F-symbols for {}...".format(self.FR)) + print("Testing F-symbols for {}...".format(self._FR)) fvars_copy = deepcopy(self._fvars) self._fvars = { sextuple : float(RDF(rhs)) for sextuple, rhs in self.get_fmats_in_alg_field().items() } if use_mp: @@ -1187,7 +1295,7 @@ def verify_pentagons(self,use_mp=True,prune=False): params = [(child_id,n_proc) for child_id in range(n_proc)] pe = self.map_triv_reduce('pent_verify',params,worker_pool=pool,chunksize=1,mp_thresh=0) if np.all(np.isclose(np.array(pe),0,atol=1e-7)): - print("Success!!! Found valid F-symbols for {}".format(self.FR)) + print("Success!!! Found valid F-symbols for {}".format(self._FR)) pe = None else: print("Ooops... something went wrong... These pentagons remain:") @@ -1200,7 +1308,7 @@ def fmats_are_orthogonal(self): Verify that all F-matrices are real and unitary (orthogonal) """ is_orthog = [] - for a,b,c,d in product(self.FR.basis(),repeat=4): + for a,b,c,d in product(self._FR.basis(),repeat=4): mat = self.fmatrix(a,b,c,d) is_orthog.append(mat.T * mat == matrix.identity(mat.nrows())) return all(is_orthog) diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index 0f99a504836..ed75a7b7739 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -17,6 +17,7 @@ from sage.combinat.root_system.poly_tup_engine cimport * from sage.rings.ideal import Ideal from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.qqbar import number_field_elements_from_algebraics #Define a global temporary worker results repository worker_results = list() @@ -40,7 +41,7 @@ def executor(params): fmats_obj = ctypes.cast(fmats_id, ctypes.py_object).value mod = sage.combinat.root_system.fast_parallel_fmats_methods #Bind module method to FMatrix object in worker process, and call the method - getattr(mod,fn_name)(fmats_obj,args) + return getattr(mod,fn_name)(fmats_obj,args) ############### ### Mappers ### @@ -91,7 +92,8 @@ cpdef get_reduced_hexagons(factory, tuple mp_params): Set up and reduce the hexagon equations corresponding to this worker """ global worker_results - cdef int i, child_id, n_proc + cdef child_id, n_proc + cdef unsigned long i child_id, n_proc = mp_params cdef tuple sextuple for i, sextuple in enumerate(product(factory.FR.basis(),repeat=6)): @@ -121,8 +123,9 @@ cpdef get_reduced_pentagons(factory, tuple mp_params, bint prune=True): Set up and reduce the pentagon equations corresponding to this worker """ global worker_results - cdef int i, child_id, n_proc + cdef int child_id, n_proc child_id, n_proc = mp_params + cdef unsigned long i cdef tuple nonuple cdef MPolynomial_libsingular pe for i, nonuple in enumerate(product(factory.FR.basis(),repeat=9)): @@ -185,6 +188,11 @@ cpdef update_child_fmats(factory, tuple data_tup): factory._nnz = factory.get_known_nonz() factory._kp = compute_known_powers(factory._var_degs,factory.get_known_vals()) +def get_appropriate_number_field(factory,non_cyclotomic_roots): + #If needed, find a NumberField containing all the roots + roots = [factory.FR.field().gen()]+[r[1] for r in non_cyclotomic_roots] + return number_field_elements_from_algebraics(roots,minimal=True) + ################ ### Reducers ### ################ @@ -230,7 +238,7 @@ cpdef pent_verify(factory, tuple mp_params): child_id, n_proc = mp_params cdef float t0 cdef tuple nonuple - cdef long i + cdef unsigned long i for i, nonuple in enumerate(product(factory.FR.basis(),repeat=9)): if i % n_proc == child_id: feq_verif(factory,nonuple) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 23d19dc4756..82512bca9b2 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -790,13 +790,19 @@ def r_matrix(self, i, j, k): if self.Nk_ij(i, j, k) == 0: return 0 if i != j: - return self.root_of_unity((k.twist(reduced=False) - i.twist(reduced=False) - j.twist(reduced=False)) / 2) + ret = self.root_of_unity((k.twist(reduced=False) - i.twist(reduced=False) - j.twist(reduced=False)) / 2) + if self._basecoer is None: + return ret + return self._basecoer(ret) i0 = self.one() B = self.basis() - return sum(y.ribbon()**2 / (i.ribbon() * x.ribbon()**2) + ret = sum(y.ribbon()**2 / (i.ribbon() * x.ribbon()**2) * self.s_ij(i0,y) * self.s_ij(i,z) * self.s_ij(x,z).conjugate() * self.s_ij(k,x).conjugate() * self.s_ij(y,z).conjugate() / self.s_ij(i0,z) for x in B for y in B for z in B) / (self.total_q_order()**4) + if self._basecoer is None: + return ret + return self._basecoer(ret) def global_q_dimension(self): r""" @@ -975,7 +981,8 @@ def get_braid_generators(self,fusing_anyon,total_topological_charge,n_strands,us print("Computing an {}-dimensional representation of the Artin braid group on {} strands...".format(d,n_strands)) #Compute diagonal odd-indexed generators using the 3j-symbols - gens = { 2*i+1 : diagonal_matrix(self.r_matrix(a,a,c[i]) for c in comp_basis) for i in range(n_strands//2) } + phi = self.fmats.get_coerce_map_from_fr_cyclotomic_field() + gens = { 2*i+1 : diagonal_matrix(phi(self.r_matrix(a,a,c[i])) for c in comp_basis) for i in range(n_strands//2) } #Compute even-indexed generators using F-matrices for k in range(1,n_strands//2): diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 977d191646d..1111689341e 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -121,6 +121,7 @@ def __init__(self, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, conju self._base_ring = base_ring self._space = RootSystem(self._cartan_type).ambient_space() self._origin = self._space.zero() + self._coer = None if prefix is None: if ct.is_atomic(): prefix = ct[0]+str(ct[1]) @@ -129,6 +130,7 @@ def __init__(self, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, conju self._prefix = prefix self._style = style self._fusion_labels = None + self._basecoer = None self._k = k if ct.is_irreducible(): self._opposition = ct.opposition_automorphism() From e9b9e3779144aaf8370321ee9dfc7b64caaa9b81 Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Fri, 26 Feb 2021 15:04:43 -0800 Subject: [PATCH 011/632] bugfix: remember .FM -> ._FM --- src/sage/combinat/root_system/f_matrix.py | 2 +- .../fast_parallel_fmats_methods.pyx | 26 +++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index d424dfe8edd..f307c36cf9d 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -221,7 +221,7 @@ class FMatrix(): """ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variables=False): self._FR = fusion_ring - if self._FR._fusion_labels is None: + if inject_variables: self._FR.fusion_labels(fusion_label, inject_variables=True) #Set up F-symbols entry by entry n_vars = self.findcases() diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index ed75a7b7739..97b072efd27 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -51,20 +51,20 @@ cdef _fmat(factory, a, b, c, d, x, y): """ Cython version of fmat class method. Using cdef for fastest dispatch """ - if factory.FR.Nk_ij(a,b,x) == 0 or factory.FR.Nk_ij(x,c,d) == 0 or factory.FR.Nk_ij(b,c,y) == 0 or factory.FR.Nk_ij(a,y,d) == 0: + if factory._FR.Nk_ij(a,b,x) == 0 or factory._FR.Nk_ij(x,c,d) == 0 or factory._FR.Nk_ij(b,c,y) == 0 or factory._FR.Nk_ij(a,y,d) == 0: return 0 #Some known zero F-symbols - if a == factory.FR.one(): + if a == factory._FR.one(): if x == b and y == d: return 1 else: return 0 - if b == factory.FR.one(): + if b == factory._FR.one(): if x == a and y == c: return 1 else: return 0 - if c == factory.FR.one(): + if c == factory._FR.one(): if x == d and y == b: return 1 else: @@ -78,10 +78,10 @@ cdef req_cy(factory, tuple sextuple, side="left"): a, b, c, d, e, g = sextuple #To add typing we need to ensure all fmats.fmat are of the same type? #Return fmats._poly_ring.zero() and fmats._poly_ring.one() instead of 0 and 1? - lhs = factory.FR.r_matrix(a,c,e)*_fmat(factory,a,c,b,d,e,g)*factory.FR.r_matrix(b,c,g) + lhs = factory._FR.r_matrix(a,c,e)*_fmat(factory,a,c,b,d,e,g)*factory._FR.r_matrix(b,c,g) rhs = 0 - for f in factory.FR.basis(): - rhs += _fmat(factory,c,a,b,d,e,f)*factory.FR.r_matrix(f,c,d)*_fmat(factory,a,b,c,d,f,g) + for f in factory._FR.basis(): + rhs += _fmat(factory,c,a,b,d,e,f)*factory._FR.r_matrix(f,c,d)*_fmat(factory,a,b,c,d,f,g) return lhs-rhs @cython.wraparound(False) @@ -96,7 +96,7 @@ cpdef get_reduced_hexagons(factory, tuple mp_params): cdef unsigned long i child_id, n_proc = mp_params cdef tuple sextuple - for i, sextuple in enumerate(product(factory.FR.basis(),repeat=6)): + for i, sextuple in enumerate(product(factory._FR.basis(),repeat=6)): if i % n_proc == child_id: he = req_cy(factory,sextuple) if he: @@ -111,7 +111,7 @@ cdef MPolynomial_libsingular feq_cy(factory, tuple nonuple, bint prune=False): if lhs == 0 and prune: # it is believed that if lhs=0, the equation carries no new information return factory._poly_ring.zero() cdef rhs = factory._poly_ring.zero() - for h in factory.FR.basis(): + for h in factory._FR.basis(): rhs += _fmat(factory,a,b,c,g,f,h)*_fmat(factory,a,h,d,e,g,k)*_fmat(factory,b,c,d,k,h,l) return lhs - rhs @@ -128,7 +128,7 @@ cpdef get_reduced_pentagons(factory, tuple mp_params, bint prune=True): cdef unsigned long i cdef tuple nonuple cdef MPolynomial_libsingular pe - for i, nonuple in enumerate(product(factory.FR.basis(),repeat=9)): + for i, nonuple in enumerate(product(factory._FR.basis(),repeat=9)): if i % n_proc == child_id: pe = feq_cy(factory,nonuple,prune=prune) if pe: @@ -160,7 +160,7 @@ cpdef compute_gb(factory, tuple args): for fx in variables(eq_tup): sorted_vars.append(fx) sorted_vars = sorted(set(sorted_vars)) - R = PolynomialRing(factory.FR.field(),len(sorted_vars),'a',order=term_order) + R = PolynomialRing(factory._FR.field(),len(sorted_vars),'a',order=term_order) #Zip tuples into R and compute Groebner basis cdef idx_map = dict() @@ -190,7 +190,7 @@ cpdef update_child_fmats(factory, tuple data_tup): def get_appropriate_number_field(factory,non_cyclotomic_roots): #If needed, find a NumberField containing all the roots - roots = [factory.FR.field().gen()]+[r[1] for r in non_cyclotomic_roots] + roots = [factory._FR.field().gen()]+[r[1] for r in non_cyclotomic_roots] return number_field_elements_from_algebraics(roots,minimal=True) ################ @@ -221,7 +221,7 @@ cdef feq_verif(factory, tuple nonuple, float tol=5e-8): cdef float diff, lhs, rhs lhs = _fmat(factory,f,c,d,e,g,l)*_fmat(factory,a,b,l,e,f,k) rhs = 0.0 - for h in factory.FR.basis(): + for h in factory._FR.basis(): rhs += _fmat(factory,a,b,c,g,f,h)*_fmat(factory,a,h,d,e,g,k)*_fmat(factory,b,c,d,k,h,l) diff = lhs - rhs if diff > tol or diff < -tol: From d134a72a31963abfc9f377606b6351820c613c00 Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Fri, 26 Feb 2021 16:44:27 -0800 Subject: [PATCH 012/632] work on base coercions --- src/sage/combinat/root_system/f_matrix.py | 84 +++----------------- src/sage/combinat/root_system/fusion_ring.py | 45 +++++++---- 2 files changed, 41 insertions(+), 88 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index f307c36cf9d..822cc240fa1 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -906,78 +906,12 @@ def get_component_variety(self,var,eqns): ### Solution method ### ####################### - def og_get_numeric_solution(self,eqns=None,verbose=True): + def get_explicit_solution(self,eqns=None,verbose=True): """ - Get a numeric solution for given equations by using the partitioning the equations - graph and selecting an arbitrary point on the discrete degrees-of-freedom variety, - which is computed as a Cartesian product - """ - if eqns is None: - eqns = self.ideal_basis - eqns_partition = self.partition_eqns(self.equations_graph(eqns),verbose=verbose) - - x = self._FR.field()['x'].gen() - numeric_fvars = dict() - non_cyclotomic_roots = list() - must_change_base_field = False - phi = self._FR.field().hom([self._field.gen()],self._FR.field()) - for comp, part in eqns_partition.items(): - #If component have only one equation in a single variable, get a root - if len(comp) == 1 and len(part) == 1: - #Attempt to find cyclotomic root - univ_poly = tup_to_univ_poly(part[0],x) - real_roots = univ_poly.roots(ring=AA,multiplicities=False) - assert real_roots, "No real solution exists... {} has no real roots".format(univ_poly) - roots = univ_poly.roots(multiplicities=False) - if roots: - numeric_fvars[comp[0]] = roots[0] - else: - non_cyclotomic_roots.append((comp[0],real_roots[0])) - must_change_base_field = True - #Otherwise, compute the component variety and select a point to obtain a numerical solution - else: - sols = self.get_component_variety(comp,part) - assert len(sols) > 1, "No real solution exists... component with variables {} has no real points".format(comp) - for fx, rhs in sols[0].items(): - non_cyclotomic_roots.append((fx,rhs)) - must_change_base_field = True - - if must_change_base_field: - #If needed, find a NumberField containing all the roots - roots = [self._FR.field().gen()]+[r[1] for r in non_cyclotomic_roots] - self._field, nf_elts, self._qqbar_embedding = number_field_elements_from_algebraics(roots,minimal=True) - #Embed cyclotomic field into newly constructed NumberField - cyc_gen_as_nf_elt = nf_elts.pop(0) - phi = self._FR.field().hom([cyc_gen_as_nf_elt], self._field) - numeric_fvars = { k : phi(v) for k, v in numeric_fvars.items() } - for i, elt in enumerate(nf_elts): - numeric_fvars[non_cyclotomic_roots[i][0]] = elt - - #Do some appropriate conversions - new_poly_ring = self._poly_ring.change_ring(self._field) - nvars = self._poly_ring.ngens() - self._var_to_idx = { new_poly_ring.gen(i) : i for i in range(nvars) } - self._var_to_sextuple = { new_poly_ring.gen(i) : self._var_to_sextuple[self._poly_ring.gen(i)] for i in range(nvars) } - self._poly_ring = new_poly_ring - - #Ensure all F-symbols are known - self.solved.update(numeric_fvars) - nvars = self._poly_ring.ngens() - assert len(self.solved) == nvars, "Some F-symbols are still missing...{}".format([self._poly_ring.gen(fx) for fx in set(range(nvars)).difference(self.solved)]) - - #Backward substitution step. Traverse variables in reverse lexicographical order. (System is in triangular form) - self._fvars = { sextuple : apply_coeff_map(poly_to_tup(rhs),phi) for sextuple, rhs in self._fvars.items() } - for fx, rhs in numeric_fvars.items(): - self._fvars[self._var_to_sextuple[self._poly_ring.gen(fx)]] = ((ETuple({},nvars),rhs),) - self.backward_subs() - self._fvars = { sextuple : constant_coeff(rhs) for sextuple, rhs in self._fvars.items() } - self.clear_equations() - - def get_numeric_solution(self,eqns=None,verbose=True): - """ - Get a numeric solution for given equations by using the partitioning the equations - graph and selecting an arbitrary point on the discrete degrees-of-freedom variety, - which is computed as a Cartesian product + When this method is called, the solution is already found in + terms of Groeber basis. A few degrees of freedom remain. + By specializing the free variables and back substituting, a solution in + the base field is now obtained. """ if eqns is None: eqns = self.ideal_basis @@ -1059,6 +993,7 @@ def find_real_orthogonal_solution(self,use_mp=True,save_dir="",verbose=True): Supports "warm" start. Use load_fvars to re-start computation from checkpoint """ #Set multiprocessing parameters. Context can only be set once, so we try to set it + self.clear_vars() try: set_start_method('fork') except RuntimeError: @@ -1105,10 +1040,12 @@ def find_real_orthogonal_solution(self,use_mp=True,save_dir="",verbose=True): #Set up new equations graph and compute variety for each component self.ideal_basis = sorted(self.par_graph_gb(term_order="lex"), key=poly_sortkey) self.triangular_elim(verbose=verbose) - self.get_numeric_solution(verbose=verbose) + self.get_explicit_solution(verbose=verbose) self.save_fvars(filename) self.symbols_known = True - self._FR._coer = self.get_coerce_map_from_fr_cyclotomic_field() + self._FR._basecoer = self.get_coerce_map_from_fr_cyclotomic_field() + for x in self._FR.basis(): + x.q_dimension.clear_cache() ######################### ### Cyclotomic method ### @@ -1239,6 +1176,7 @@ def find_cyclotomic_solution(self, equations=None, factor=True, verbose=True, pr [] """ + self.clear_vars() if equations is None: if verbose: print("Setting up hexagons and pentagons...") diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 82512bca9b2..7f87ca1a9c1 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -467,7 +467,10 @@ def root_of_unity(self, r): """ n = 2 * r * self._cyclotomic_order if n in ZZ: - return self.field().gen() ** n + ret = self.field().gen() ** n + if self._basecoer is None: + return ret + return self._basecoer(ret) else: return None @@ -672,7 +675,7 @@ def Nk_ij(self, elt_i, elt_j, elt_k): return (elt_i * elt_j).monomial_coefficients(copy=False).get(elt_k.weight(), 0) @cached_method - def s_ij(self, elt_i, elt_j): + def s_ij(self, elt_i, elt_j, base_coercion=True): r""" Return the element of the S-matrix of this fusion ring corresponding to the given elements. @@ -699,9 +702,20 @@ def s_ij(self, elt_i, elt_j): [1, -zeta60^14 + zeta60^6 + zeta60^4, -zeta60^14 + zeta60^6 + zeta60^4, -1] """ ijtwist = elt_i.twist() + elt_j.twist() - return sum(k.q_dimension() * self.Nk_ij(elt_i, k, elt_j) + ret = sum(k.q_dimension() * self.Nk_ij(elt_i, k, elt_j) * self.root_of_unity(k.twist() - ijtwist) for k in self.basis()) + if (not base_coercion) or (self._basecoer is None): + return ret + return self._basecoer(ret) + + def s_ijconj(self, elt_i, elt_j, base_coercion=True): + """ + """ + ret = self.s_ij(elt_i, elt_j, base_coercion=False).conjugate() + if (not base_coercion) or (self._basecoer is None): + return ret + return self._basecoer(ret) def s_matrix(self, unitary=False): r""" @@ -790,19 +804,13 @@ def r_matrix(self, i, j, k): if self.Nk_ij(i, j, k) == 0: return 0 if i != j: - ret = self.root_of_unity((k.twist(reduced=False) - i.twist(reduced=False) - j.twist(reduced=False)) / 2) - if self._basecoer is None: - return ret - return self._basecoer(ret) + return self.root_of_unity((k.twist(reduced=False) - i.twist(reduced=False) - j.twist(reduced=False)) / 2) i0 = self.one() B = self.basis() - ret = sum(y.ribbon()**2 / (i.ribbon() * x.ribbon()**2) - * self.s_ij(i0,y) * self.s_ij(i,z) * self.s_ij(x,z).conjugate() - * self.s_ij(k,x).conjugate() * self.s_ij(y,z).conjugate() / self.s_ij(i0,z) + return sum(y.ribbon()**2 / (i.ribbon() * x.ribbon()**2) + * self.s_ij(i0,y) * self.s_ij(i,z) * self.s_ijconj(x,z) + * self.s_ijconj(k,x) * self.s_ijconj(y,z) / self.s_ij(i0,z) for x in B for y in B for z in B) / (self.total_q_order()**4) - if self._basecoer is None: - return ret - return self._basecoer(ret) def global_q_dimension(self): r""" @@ -832,7 +840,10 @@ def total_q_order(self): True """ c = self.virasoro_central_charge() - return self.D_plus() * self.root_of_unity(-c/4) + ret = self.D_plus() * self.root_of_unity(-c/4) + if self._basecoer is None: + return ret + return self._basecoer(ret) def D_plus(self): r""" @@ -1169,4 +1180,8 @@ def q_dimension(self): expr = R(expr) expr = expr.substitute(q=q**4) / (q**(2*expr.degree())) zet = P.field().gen() ** (P._cyclotomic_order/P._l) - return expr.substitute(q=zet) + ret = expr.substitute(q=zet) + if self.parent()._basecoer is None: + return ret + return self.parent()._basecoer(ret) + From b92197c60da31f434487377e521b832a4cedba6f Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Sat, 27 Feb 2021 12:32:05 -0800 Subject: [PATCH 013/632] clear equations in find_real_orthogonal_solution --- src/sage/combinat/root_system/f_matrix.py | 2 ++ src/sage/combinat/root_system/fusion_ring.py | 4 +++- src/sage/combinat/root_system/weyl_characters.py | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 822cc240fa1..f285c8807f1 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -993,6 +993,7 @@ def find_real_orthogonal_solution(self,use_mp=True,save_dir="",verbose=True): Supports "warm" start. Use load_fvars to re-start computation from checkpoint """ #Set multiprocessing parameters. Context can only be set once, so we try to set it + self.clear_equations() self.clear_vars() try: set_start_method('fork') @@ -1043,6 +1044,7 @@ def find_real_orthogonal_solution(self,use_mp=True,save_dir="",verbose=True): self.get_explicit_solution(verbose=verbose) self.save_fvars(filename) self.symbols_known = True + self._FR._field = self.field() self._FR._basecoer = self.get_coerce_map_from_fr_cyclotomic_field() for x in self._FR.basis(): x.q_dimension.clear_cache() diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 7f87ca1a9c1..c7f90c2aea5 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -447,7 +447,9 @@ def field(self): sage: FusionRing("B2",2).field() Cyclotomic Field of order 40 and degree 16 """ - return CyclotomicField(4 * self._cyclotomic_order) + if self._field is None: + self._field = CyclotomicField(4 * self._cyclotomic_order) + return self._field def root_of_unity(self, r): r""" diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 1111689341e..0a1e8c18fd8 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -121,7 +121,6 @@ def __init__(self, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, conju self._base_ring = base_ring self._space = RootSystem(self._cartan_type).ambient_space() self._origin = self._space.zero() - self._coer = None if prefix is None: if ct.is_atomic(): prefix = ct[0]+str(ct[1]) @@ -130,6 +129,7 @@ def __init__(self, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, conju self._prefix = prefix self._style = style self._fusion_labels = None + self._field = None self._basecoer = None self._k = k if ct.is_irreducible(): From 54bb6b6a2aa17f71b6a1def43e610f592442dad4 Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Sun, 28 Feb 2021 10:33:03 -0800 Subject: [PATCH 014/632] base coercion debugging --- src/sage/combinat/root_system/f_matrix.py | 2 + .../fast_parallel_fmats_methods.pyx | 4 +- src/sage/combinat/root_system/fusion_ring.py | 82 +++++++++++-------- 3 files changed, 54 insertions(+), 34 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index f285c8807f1..6926e950721 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -283,6 +283,7 @@ def clear_vars(self): """ Clear the set of variables. """ + self._FR._basecoer = None self._fvars = { self._var_to_sextuple[key] : key for key in self._var_to_sextuple } self.solved = set() @@ -1179,6 +1180,7 @@ def find_cyclotomic_solution(self, equations=None, factor=True, verbose=True, pr """ self.clear_vars() + self.clear_equations() if equations is None: if verbose: print("Setting up hexagons and pentagons...") diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index 97b072efd27..5f2bc0c3f18 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -78,10 +78,10 @@ cdef req_cy(factory, tuple sextuple, side="left"): a, b, c, d, e, g = sextuple #To add typing we need to ensure all fmats.fmat are of the same type? #Return fmats._poly_ring.zero() and fmats._poly_ring.one() instead of 0 and 1? - lhs = factory._FR.r_matrix(a,c,e)*_fmat(factory,a,c,b,d,e,g)*factory._FR.r_matrix(b,c,g) + lhs = factory._FR.r_matrix(a,c,e,base_coercion=False)*_fmat(factory,a,c,b,d,e,g)*factory._FR.r_matrix(b,c,g,base_coercion=False) rhs = 0 for f in factory._FR.basis(): - rhs += _fmat(factory,c,a,b,d,e,f)*factory._FR.r_matrix(f,c,d)*_fmat(factory,a,b,c,d,f,g) + rhs += _fmat(factory,c,a,b,d,e,f)*factory._FR.r_matrix(f,c,d,base_coercion=False)*_fmat(factory,a,b,c,d,f,g) return lhs-rhs @cython.wraparound(False) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index c7f90c2aea5..56e8be8eadb 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -360,7 +360,7 @@ def _test_total_q_order(self, **options): sage: G22._test_total_q_order() """ tester = self._tester(**options) - tqo = self.total_q_order() + tqo = self.total_q_order(base_coercion=False) tester.assertTrue(tqo.is_real_positive()) tester.assertEqual(tqo**2, self.global_q_dimension()) @@ -451,7 +451,7 @@ def field(self): self._field = CyclotomicField(4 * self._cyclotomic_order) return self._field - def root_of_unity(self, r): + def root_of_unity(self, r, base_coercion=True): r""" Return `e^{i\pi r}` as an element of ``self.field()`` if possible. @@ -470,7 +470,7 @@ def root_of_unity(self, r): n = 2 * r * self._cyclotomic_order if n in ZZ: ret = self.field().gen() ** n - if self._basecoer is None: + if (not base_coercion) or (self._basecoer is None): return ret return self._basecoer(ret) else: @@ -704,8 +704,8 @@ def s_ij(self, elt_i, elt_j, base_coercion=True): [1, -zeta60^14 + zeta60^6 + zeta60^4, -zeta60^14 + zeta60^6 + zeta60^4, -1] """ ijtwist = elt_i.twist() + elt_j.twist() - ret = sum(k.q_dimension() * self.Nk_ij(elt_i, k, elt_j) - * self.root_of_unity(k.twist() - ijtwist) + ret = sum(k.q_dimension(base_coercion=False) * self.Nk_ij(elt_i, k, elt_j) + * self.root_of_unity(k.twist() - ijtwist, base_coercion=False) for k in self.basis()) if (not base_coercion) or (self._basecoer is None): return ret @@ -719,7 +719,7 @@ def s_ijconj(self, elt_i, elt_j, base_coercion=True): return ret return self._basecoer(ret) - def s_matrix(self, unitary=False): + def s_matrix(self, unitary=False, base_coercion=True): r""" Return the S-matrix of this fusion ring. @@ -751,14 +751,14 @@ def s_matrix(self, unitary=False): [0 0 0 1] """ b = self.basis() - S = matrix([[self.s_ij(b[x], b[y]) for x in self.get_order()] for y in self.get_order()]) + S = matrix([[self.s_ij(b[x], b[y], base_coercion=base_coercion) for x in self.get_order()] for y in self.get_order()]) if unitary: - return S / self.total_q_order() + return S / self.total_q_order(base_coercion=base_coercion) else: return S @cached_method - def r_matrix(self, i, j, k): + def r_matrix(self, i, j, k, base_coercion=True): r""" Return the R-matrix entry corresponding to the subobject ``k`` in the tensor product of ``i`` with ``j``. @@ -804,19 +804,23 @@ def r_matrix(self, i, j, k): True """ if self.Nk_ij(i, j, k) == 0: - return 0 + ret = 0 if i != j: - return self.root_of_unity((k.twist(reduced=False) - i.twist(reduced=False) - j.twist(reduced=False)) / 2) - i0 = self.one() - B = self.basis() - return sum(y.ribbon()**2 / (i.ribbon() * x.ribbon()**2) - * self.s_ij(i0,y) * self.s_ij(i,z) * self.s_ijconj(x,z) - * self.s_ijconj(k,x) * self.s_ijconj(y,z) / self.s_ij(i0,z) - for x in B for y in B for z in B) / (self.total_q_order()**4) + ret = self.root_of_unity((k.twist(reduced=False) - i.twist(reduced=False) - j.twist(reduced=False)) / 2, base_coercion=False) + else: + i0 = self.one() + B = self.basis() + return sum(y.ribbon(base_coercion=False)**2 / (i.ribbon(base_coercion=False) * x.ribbon(base_coercion=False)**2) + * self.s_ij(i0,y,base_coercion=False) * self.s_ij(i,z,base_coercion=False) * self.s_ijconj(x,z,base_coercion=False) + * self.s_ijconj(k,x,base_coercion=False) * self.s_ijconj(y,z,base_coercion=False) / self.s_ij(i0,z,base_coercion=False) + for x in B for y in B for z in B) / (self.total_q_order(base_coercion=False)**4) + if (not base_coercion) or (self._basecoer is None): + return ret + return self._basecoer(ret) - def global_q_dimension(self): + def global_q_dimension(self, base_coercion=True): r""" - Return `\sum d_i^2`, where the sum is over all simple objects + Return `\sum d_i^2`, where the sum is over all simple objects and `d_i` is the quantum dimension. It is a positive real number. EXAMPLES:: @@ -824,9 +828,12 @@ def global_q_dimension(self): sage: FusionRing("E6",1).global_q_dimension() 3 """ - return sum(x.q_dimension()**2 for x in self.basis()) + ret = sum(x.q_dimension(base_coercion=False)**2 for x in self.basis()) + if (not base_coercion) or (self._basecoer is None): + return ret + return self._basecoer(ret) - def total_q_order(self): + def total_q_order(self, base_coercion=True): r""" Return the positive square root of ``self.global_q_dimension()`` as an element of ``self.field()``. @@ -842,12 +849,12 @@ def total_q_order(self): True """ c = self.virasoro_central_charge() - ret = self.D_plus() * self.root_of_unity(-c/4) - if self._basecoer is None: + ret = self.D_plus(base_coercion=False) * self.root_of_unity(-c/4,base_coercion=False) + if (not base_coercion) or (self._basecoer is None): return ret return self._basecoer(ret) - def D_plus(self): + def D_plus(self, base_coercion=True): r""" Return `\sum d_i^2\theta_i` where `i` runs through the simple objects, `d_i` is the quantum dimension and `\theta_i` is the twist. @@ -868,9 +875,13 @@ def D_plus(self): sage: Dp/Dm == B31.root_of_unity(c/2) True """ - return sum((x.q_dimension())**2 * x.ribbon() for x in self.basis()) + ret = sum((x.q_dimension(base_coercion=False))**2 * x.ribbon(base_coercion=False) for x in self.basis()) + if (not base_coercion) or (self._basecoer is None): + return ret + return self._basecoer(ret) + - def D_minus(self): + def D_minus(self, base_coercion=True): r""" Return `\sum d_i^2\theta_i^{-1}` where `i` runs through the simple objects, `d_i` is the quantum dimension and `\theta_i` is the twist. @@ -888,8 +899,11 @@ def D_minus(self): sage: Dp*Dm == E83.global_q_dimension() True """ - return sum((x.q_dimension())**2 / x.ribbon() for x in self.basis()) - + ret = sum((x.q_dimension(base_coercion=False))**2 / x.ribbon(base_coercion=False) for x in self.basis()) + if (not base_coercion) or (self._basecoer is None): + return ret + return self._basecoer(ret) + ################################### ### Braid group representations ### ################################### @@ -1114,7 +1128,7 @@ def twist(self, reduced=True): else: return twist - def ribbon(self): + def ribbon(self,base_coercion=True): r""" Return the twist or ribbon element of ``self``. @@ -1135,10 +1149,13 @@ def ribbon(self): sage: [F.root_of_unity(x) for x in [0, 3/10, 4/5, 3/2]] [1, zeta40^6, zeta40^12 - zeta40^8 + zeta40^4 - 1, -zeta40^10] """ - return self.parent().root_of_unity(self.twist()) + ret = self.parent().root_of_unity(self.twist(),base_coercion=False) + if (not base_coercion) or (self.parent()._basecoer is None): + return ret + return self.parent()._basecoer(ret) @cached_method - def q_dimension(self): + def q_dimension(self, base_coercion=True): r""" Return the quantum dimension as an element of the cyclotomic field of the `2\ell`-th roots of unity, where `l = m (k+h^\vee)` @@ -1183,7 +1200,8 @@ def q_dimension(self): expr = expr.substitute(q=q**4) / (q**(2*expr.degree())) zet = P.field().gen() ** (P._cyclotomic_order/P._l) ret = expr.substitute(q=zet) - if self.parent()._basecoer is None: + + if (not base_coercion) or (self.parent()._basecoer is None): return ret return self.parent()._basecoer(ret) From d94a214cdcb75d6322b5656073113bed232e23e4 Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Sun, 28 Feb 2021 14:57:43 -0800 Subject: [PATCH 015/632] two missing files --- .../fast_parallel_fusion_ring_braid_repn.pxd | 5 + .../fast_parallel_fusion_ring_braid_repn.pyx | 213 ++++++++++++++++++ 2 files changed, 218 insertions(+) create mode 100644 src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pxd create mode 100644 src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx diff --git a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pxd b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pxd new file mode 100644 index 00000000000..7f4c49b63cd --- /dev/null +++ b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pxd @@ -0,0 +1,5 @@ +cpdef mid_sig_ij(fusion_ring,row,col,a,b) +cpdef odd_one_out_ij(fusion_ring,xi,xj,a,b) + +cpdef sig_2k(fusion_ring, tuple args) +cpdef odd_one_out(fusion_ring, tuple args) diff --git a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx new file mode 100644 index 00000000000..9ecc49bc084 --- /dev/null +++ b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx @@ -0,0 +1,213 @@ +""" +Fast FusionRing methods for computing braid group representations +""" +# **************************************************************************** +# Copyright (C) 2021 Guillermo Aboumrad +# +# Distributed under the terms of the GNU General Public License (GPL) +# https://www.gnu.org/licenses/ +# **************************************************************************** + +cimport cython +import ctypes +from itertools import product +import sage +from sage.combinat.root_system.fast_parallel_fmats_methods cimport _fmat +from sage.misc.cachefunc import cached_function + +#Define a global temporary worker results repository +worker_results = list() + +############################## +### Parallel code executor ### +############################## + +def executor(params): + """ + Execute a function defined in this module (sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn) + in a worker process, and supply the factory parameter by constructing a reference + to the FMatrix object in the worker's memory adress space from its id + ## NOTE: When the parent process is forked, each worker gets a copy of + every global variable. The virtual memory address of object X in the parent + process equals the VIRTUAL memory address of the copy of object X in each + worker, so we may construct references to forked copies of X + """ + (fn_name, fr_id), args = params + #Construct a reference to global FMatrix object in this worker's memory + fusion_ring_obj = ctypes.cast(fr_id, ctypes.py_object).value + mod = sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn + #Bind module method to FMatrix object in worker process, and call the method + getattr(mod,fn_name)(fusion_ring_obj,args) + +############### +### Mappers ### +############### + +cpdef mid_sig_ij(fusion_ring,row,col,a,b): + """ + Compute the (xi,yi), (xj,yj) entry of generator braiding the middle two strands + in the tree b -> xi # yi -> (a # a) # (a # a), which results in a sum over j + of trees b -> xj # yj -> (a # a) # (a # a) + """ + xi, yi = row + xj, yj = col + entry = 0 + phi = fusion_ring.fmats.get_coerce_map_from_fr_cyclotomic_field() + for c in fusion_ring.basis(): + for d in fusion_ring.basis(): + ##Warning: We assume F-matrices are orthogonal!!! (using transpose for inverse) + f1 = _fmat(fusion_ring.fmats,a,a,yi,b,xi,c) + f2 = _fmat(fusion_ring.fmats,a,a,a,c,d,yi) + f3 = _fmat(fusion_ring.fmats,a,a,a,c,d,yj) + f4 = _fmat(fusion_ring.fmats,a,a,yj,b,xj,c) + r = fusion_ring.r_matrix(a,a,d) + if not phi.is_identity(): + r = phi(r) + entry += f1 * f2 * r * f3 * f4 + return entry + +cpdef odd_one_out_ij(fusion_ring,xi,xj,a,b): + """ + Compute the xi, xj entry of the braid generator on the right-most strands, + corresponding to the tree b -> (xi # a) -> (a # a) # a, which results in a + sum over j of trees b -> xj -> (a # a) # (a # a) + """ + entry = 0 + phi = fusion_ring.fmats.get_coerce_map_from_fr_cyclotomic_field() + for c in fusion_ring.basis(): + ##Warning: We assume F-matrices are orthogonal!!! (using transpose for inverse) + f1 = _fmat(fusion_ring.fmats,a,a,a,b,xi,c) + f2 = _fmat(fusion_ring.fmats,a,a,a,b,xj,c) + r = fusion_ring.r_matrix(a,a,c) + if not phi.is_identity(): + r = phi(r) + entry += f1 * r * f2 + return entry + +#Cache methods +mid_sig_ij = cached_function(mid_sig_ij, name='mid_sig_ij') +odd_one_out_ij = cached_function(odd_one_out_ij, name='odd_one_out_ij') + +@cython.wraparound(False) +@cython.nonecheck(False) +@cython.cdivision(True) +cpdef sig_2k(fusion_ring, tuple args): + """ + Compute entries of the 2k-th braid generator + """ + child_id, n_proc, fn_args = args + k, a, b, n_strands = fn_args + cdef int ctr = -1 + global worker_results + #Get computational basis + cdef list comp_basis = fusion_ring.get_comp_basis(a,b,n_strands) + cdef dict basis_dict = { elt : i for i, elt in enumerate(comp_basis) } + cdef int dim = len(comp_basis) + cdef set entries = set() + cdef int i + for i in range(dim): + for f,e,q in product(fusion_ring.basis(),repeat=3): + #Distribute work amongst processes + ctr += 1 + if ctr % n_proc != child_id: + continue + + #Compute appropriate possible nonzero row index + nnz_pos = list(comp_basis[i]) + nnz_pos[k-1:k+1] = f,e + #Handle the special case k = 1 + if k > 1: + nnz_pos[n_strands//2+k-2] = q + nnz_pos = tuple(nnz_pos) + + #Skip repeated entries when k = 1 + if nnz_pos in comp_basis and (basis_dict[nnz_pos],i) not in entries: + m, l = comp_basis[i][:n_strands//2], comp_basis[i][n_strands//2:] + #A few special cases + top_left = m[0] + if k >= 3: + top_left = l[k-3] + root = b + if k - 1 < len(l): + root = l[k-1] + + #Handle the special case k = 1 + if k == 1: + entry = mid_sig_ij(fusion_ring,m[:2],(f,e),a,root) + worker_results.append(((basis_dict[nnz_pos],i), entry)) + continue + + entry = 0 + for p in fusion_ring.basis(): + f1 = _fmat(fusion_ring.fmats,top_left,m[k-1],m[k],root,l[k-2],p) + f2 = _fmat(fusion_ring.fmats,top_left,f,e,root,q,p) + entry += f1 * mid_sig_ij(fusion_ring,(m[k-1],m[k]),(f,e),a,p) * f2 + worker_results.append(((basis_dict[nnz_pos],i), entry)) + entries.add((basis_dict[nnz_pos],i)) + +@cython.wraparound(False) +@cython.nonecheck(False) +@cython.cdivision(True) +cpdef odd_one_out(fusion_ring, tuple args): + """ + Compute entries of the rightmost braid generator, in case we have an odd number + of strands + """ + global worker_results + child_id, n_proc, fn_args = args + a, b, n_strands = fn_args + cdef ctr = -1 + #Get computational basis + comp_basis = fusion_ring.get_comp_basis(a,b,n_strands) + basis_dict = { elt : i for i, elt in enumerate(comp_basis) } + dim = len(comp_basis) + for i in range(dim): + for f, q in product(fusion_ring.basis(),repeat=2): + #Distribute work amongst processes + ctr += 1 + if ctr % n_proc != child_id: + continue + + #Compute appropriate possible nonzero row index + nnz_pos = list(comp_basis[i]) + nnz_pos[n_strands//2-1] = f + #Handle small special case + if n_strands > 3: + nnz_pos[-1] = q + nnz_pos = tuple(nnz_pos) + + if nnz_pos in comp_basis: + m, l = comp_basis[i][:n_strands//2], comp_basis[i][n_strands//2:] + + #Handle a couple of small special cases + if n_strands == 3: + entry = odd_one_out_ij(fusion_ring,m[-1],f,a,b) + worker_results.append(((basis_dict[nnz_pos],i), entry)) + continue + top_left = m[0] + if n_strands > 5: + top_left = l[-2] + root = b + + #Compute relevant entry + entry = 0 + for p in fusion_ring.basis(): + f1 = _fmat(fusion_ring.fmats,top_left,m[-1],a,root,l[-1],p) + f2 = _fmat(fusion_ring.fmats,top_left,f,a,root,q,p) + entry += f1 * odd_one_out_ij(fusion_ring,m[-1],f,a,p) * f2 + worker_results.append(((basis_dict[nnz_pos],i), entry)) + +################ +### Reducers ### +################ + +def collect_eqns(proc): + """ + Helper function for returning processed results back to parent process. + Trivial reducer: simply collects objects with the same key in the worker + """ + #Discard the zero polynomial + global worker_results + reduced = set(worker_results)-set([tuple()]) + worker_results = list() + return list(reduced) From 6cb376ac3d1250968420f974b16f70e85beeb882 Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Sun, 28 Feb 2021 15:06:48 -0800 Subject: [PATCH 016/632] get_main_globals() to inject variables in FMatrix --- src/sage/combinat/root_system/f_matrix.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 6926e950721..8c5862240fb 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -20,6 +20,7 @@ from sage.misc.misc import inject_variable from sage.rings.polynomial.all import PolynomialRing from sage.rings.ideal import Ideal +from sage.misc.misc import get_main_globals from copy import deepcopy #Import pickle for checkpointing and loading certain variables @@ -228,9 +229,7 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab self._poly_ring = PolynomialRing(self._FR.field(),n_vars,var_prefix) if inject_variables: print ("creating variables %s%s..%s%s"%(var_prefix,1,var_prefix,n_vars)) - self._poly_ring.inject_variables() - # for i in range(self._poly_ring.ngens()): - # inject_variable("%s%s"%(var_prefix,i),self._poly_ring.gens()[i]) + self._poly_ring.inject_variables(get_main_globals()) self._var_to_sextuple, self._fvars = self.findcases(output=True) self._singles = self.singletons() From ad5519952db746b1f195a57bbb1c9b859c4212e0 Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Tue, 2 Mar 2021 08:05:20 -0800 Subject: [PATCH 017/632] refuse to create FMatrix in non-multiplicity-free case --- src/sage/combinat/root_system/f_matrix.py | 8 ++++--- src/sage/combinat/root_system/fusion_ring.py | 25 ++++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 8c5862240fb..a284d201f8d 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -222,8 +222,10 @@ class FMatrix(): """ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variables=False): self._FR = fusion_ring - if inject_variables: + if inject_variables and self._FR.fusion_labels is None: self._FR.fusion_labels(fusion_label, inject_variables=True) + if not self._FR.is_multiplicity_free(): + raise ValueError("FMatrix is only available for multiplicity free FusionRings") #Set up F-symbols entry by entry n_vars = self.findcases() self._poly_ring = PolynomialRing(self._FR.field(),n_vars,var_prefix) @@ -986,7 +988,7 @@ def get_explicit_solution(self,eqns=None,verbose=True): self._fvars = { sextuple : constant_coeff(rhs) for sextuple, rhs in self._fvars.items() } self.clear_equations() - def find_real_orthogonal_solution(self,use_mp=True,save_dir="",verbose=True): + def find_orthogonal_solution(self,use_mp=True,save_dir="",verbose=True): """ Solver If provided, optional param save_dir (for saving computed _fvars) should have a trailing forward slash @@ -1246,7 +1248,7 @@ def verify_pentagons(self,use_mp=True,prune=False): def fmats_are_orthogonal(self): """ - Verify that all F-matrices are real and unitary (orthogonal) + Verify that all F-matrices are orthogonal """ is_orthog = [] for a,b,c,d in product(self._FR.basis(),repeat=4): diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 56e8be8eadb..e5759fd38f2 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -904,6 +904,31 @@ def D_minus(self, base_coercion=True): return ret return self._basecoer(ret) + def is_multiplicity_free(self): + """ + Return ``True`` if the fusion multiplicities + :meth:`Nk_ij` are bounded by 1. The :class:`FMatrix` + is available only for multiplicity free :class:`FusionRing`s. + + EXAMPLES:: + + sage: [FusionRing(ct,k).is_multiplicity_free() for ct in ("A1","A2","B2","C3") for k in (1,2,3)] + [True, True, True, True, True, False, True, True, False, True, False, False] + + """ + ct = self.cartan_type() + k = self.fusion_level() + if ct.letter == 'A': + if ct.n == 1: + return True + return k <= 2 + if ct.letter in ['B','D','G','F','E']: + return k <= 2 + if ct.letter == 'C': + if ct.n == 2: + return k <= 2 + return k == 1 + ################################### ### Braid group representations ### ################################### From b664aeddc9d3ba40c86118456b042411e8f2a639 Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Fri, 5 Mar 2021 14:26:21 -0800 Subject: [PATCH 018/632] bugfix r-matrix method --- src/sage/combinat/root_system/fusion_ring.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index e5759fd38f2..4247c1e3df5 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -804,13 +804,13 @@ def r_matrix(self, i, j, k, base_coercion=True): True """ if self.Nk_ij(i, j, k) == 0: - ret = 0 + return 0 if i != j: ret = self.root_of_unity((k.twist(reduced=False) - i.twist(reduced=False) - j.twist(reduced=False)) / 2, base_coercion=False) else: i0 = self.one() B = self.basis() - return sum(y.ribbon(base_coercion=False)**2 / (i.ribbon(base_coercion=False) * x.ribbon(base_coercion=False)**2) + ret = sum(y.ribbon(base_coercion=False)**2 / (i.ribbon(base_coercion=False) * x.ribbon(base_coercion=False)**2) * self.s_ij(i0,y,base_coercion=False) * self.s_ij(i,z,base_coercion=False) * self.s_ijconj(x,z,base_coercion=False) * self.s_ijconj(k,x,base_coercion=False) * self.s_ijconj(y,z,base_coercion=False) / self.s_ij(i0,z,base_coercion=False) for x in B for y in B for z in B) / (self.total_q_order(base_coercion=False)**4) From 2e6e804463a16ef5614271e9495c297e2a0a4cb0 Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Sat, 6 Mar 2021 11:05:37 -0800 Subject: [PATCH 019/632] doctest revision in f_matrix.py, fusion_label flexibility for FusionRings --- src/sage/combinat/root_system/f_matrix.py | 156 +++++++++--------- src/sage/combinat/root_system/fusion_ring.py | 12 +- .../combinat/root_system/weyl_characters.py | 9 +- 3 files changed, 95 insertions(+), 82 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index a284d201f8d..53d2ebefa1a 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -21,13 +21,14 @@ from sage.rings.polynomial.all import PolynomialRing from sage.rings.ideal import Ideal from sage.misc.misc import get_main_globals - from copy import deepcopy + #Import pickle for checkpointing and loading certain variables try: import cPickle as pickle except: import pickle + from multiprocessing import cpu_count, Pool, set_start_method, TimeoutError import numpy as np from sage.combinat.root_system.fast_parallel_fmats_methods import * @@ -167,46 +168,41 @@ class FMatrix(): definition of a monoidal category. The hexagon relations reflect the axioms of a *braided monoidal category*, which are constraints on both the F-matrix and on - the R-matrix. Finally, orthogonality constraints - may be imposed to obtain a unitary F-matrix. + the R-matrix. Optionally, orthogonality constraints + may be imposed to obtain an orthogonal F-matrix. EXAMPLES:: - sage: f.get_pentagons()[1:3] + sage: f.get_defining_equations("pentagons")[1:3] [fx1*fx5 - fx7^2, fx5*fx8*fx13 - fx2*fx12] - sage: f.get_hexagons()[1:3] + sage: f.get_defining_equations("hexagons")[1:3] [fx10*fx12 + (-zeta128^32)*fx12*fx13 + (-zeta128^16)*fx12, fx0 - 1] sage: f.get_orthogonality_constraints()[1:3] [fx1^2 - 1, fx2^2 - 1] - You may solve these 41+14=55 equations to compute the F-matrix. + There are two methods available to compute an F-matrix. + The first, :meth:`find_cyclotomic_solution` uses only + the pentagon and hexagon relations. The second, + :meth:`find_orthogonal_solution` uses additionally + the orthogonality relations. There are some differences + that should be kept in mind. + + :meth:`find_cyclotomic_solution` currently works only with + smaller examples. For example the FusionRing for A2 at + level 2 is too large. When it is available, this method + produces an F-matrix whose entries are in the same + cyclotomic field as the underlying FusionRing. EXAMPLES:: - sage: f.get_solution(output=True) + sage: f.find_cyclotomic_solution() Setting up hexagons and pentagons... - equations: 14 - equations: 37 Finding a Groebner basis... Solving... Fixing the gauge... adding equation... fx1 - 1 adding equation... fx11 - 1 Done! - {(s, s, s, s, i0, i0): (-1/2*zeta128^48 + 1/2*zeta128^16), - (s, s, s, s, i0, p): 1, - (s, s, s, s, p, i0): 1/2, - (s, s, s, s, p, p): (1/2*zeta128^48 - 1/2*zeta128^16), - (s, s, p, i0, p, s): (-1/2*zeta128^48 + 1/2*zeta128^16), - (s, s, p, p, i0, s): (-zeta128^48 + zeta128^16), - (s, p, s, i0, s, s): 1, - (s, p, s, p, s, s): -1, - (s, p, p, s, s, i0): 1, - (p, s, s, i0, s, p): (-zeta128^48 + zeta128^16), - (p, s, s, p, s, i0): (-1/2*zeta128^48 + 1/2*zeta128^16), - (p, s, p, s, s, s): -1, - (p, p, s, s, i0, s): 1, - (p, p, p, p, i0, i0): 1} We now have access to the values of the F-mstrix using the methods :meth:`fmatrix` and :meth:`fmat`. @@ -219,10 +215,20 @@ class FMatrix(): sage: f.fmat(s,s,s,s,p,p) (1/2*zeta128^48 - 1/2*zeta128^16) + :meth:`find_orthogonal_solution` is much more powerful + and is capable of handling large cases, sometimes + quickly but sometimes (in larger cases) hours of + computation. Its F-matrices are not always in the + cyclotomic field that is the base ring of the underlying + FusionRing, but sometimes in an extension field adjoining + some square roots. When this happens, the FusionRing is + modified, adding an attribute :attr:`_basecoer that is + a coercion from the cyclotomic field to the F-matrix. + """ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variables=False): self._FR = fusion_ring - if inject_variables and self._FR.fusion_labels is None: + if inject_variables and (self._FR._fusion_labels is None): self._FR.fusion_labels(fusion_label, inject_variables=True) if not self._FR.is_multiplicity_free(): raise ValueError("FMatrix is only available for multiplicity free FusionRings") @@ -294,29 +300,26 @@ def fmat(self, a, b, c, d, x, y, data=True): EXAMPLES:: - sage: f=FMatrix(FusionRing("G2",1),fusion_label=["i0","t"]) - sage: [f.fmat(t,t,t,t,x,y) for x in f.FR.basis() for y in f.FR.basis()] + sage: f=FMatrix(FusionRing("G2",1,fusion_labels=("i0","t"),inject_variables=True)) + sage: [f.fmat(t,t,t,t,x,y) for x in f._FR.basis() for y in f._FR.basis()] [fx1, fx2, fx3, fx4] - sage: f.get_solution(output=True) + sage: f.find_cyclotomic_solution(output=True) Setting up hexagons and pentagons... - equations: 5 - equations: 10 Finding a Groebner basis... Solving... Fixing the gauge... adding equation... fx2 - 1 Done! {(t, t, t, i0, t, t): 1, - (t, t, t, t, i0, i0): (-zeta60^14 + zeta60^6 + zeta60^4 - 1), - (t, t, t, t, i0, t): 1, - (t, t, t, t, t, i0): (-zeta60^14 + zeta60^6 + zeta60^4 - 1), - (t, t, t, t, t, t): (zeta60^14 - zeta60^6 - zeta60^4 + 1)} - sage: [f.fmat(t,t,t,t,x,y) for x in f.FR.basis() for y in f.FR.basis()] + (t, t, t, t, i0, i0): (-zeta60^14 + zeta60^6 + zeta60^4 - 1), + (t, t, t, t, i0, t): 1, + (t, t, t, t, t, i0): (-zeta60^14 + zeta60^6 + zeta60^4 - 1), + (t, t, t, t, t, t): (zeta60^14 - zeta60^6 - zeta60^4 + 1)} + sage: [f.fmat(t,t,t,t,x,y) for x in f._FR.basis() for y in f._FR.basis()] [(-zeta60^14 + zeta60^6 + zeta60^4 - 1), - 1, - (-zeta60^14 + zeta60^6 + zeta60^4 - 1), - (zeta60^14 - zeta60^6 - zeta60^4 + 1)] - + 1, + (-zeta60^14 + zeta60^6 + zeta60^4 - 1), + (zeta60^14 - zeta60^6 - zeta60^4 + 1)] """ if self._FR.Nk_ij(a,b,x) == 0 or self._FR.Nk_ij(x,c,d) == 0 or self._FR.Nk_ij(b,c,y) == 0 or self._FR.Nk_ij(a,y,d) == 0: return 0 @@ -357,10 +360,11 @@ def fmatrix(self,a,b,c,d): EXAMPLES:: - sage: f=FMatrix(FusionRing("A1",2),fusion_label="c") - sage: f.get_solution(verbose=False); - equations: 14 - equations: 37 + sage: f=FMatrix(FusionRing("A1",2,fusion_labels="c",inject_variables=True)) + sage: f.fmatrix(c1,c1,c1,c1) + [fx0 fx1] + [fx2 fx3] + sage: f.find_cyclotomic_solution(verbose=False); adding equation... fx4 - 1 adding equation... fx10 - 1 sage: f.f_from(c1,c1,c1,c1) @@ -390,7 +394,7 @@ def findcases(self,output=False): EXAMPLES:: - sage: f=FMatrix(FusionRing("G2",1),fusion_label=["i0","t"]) + sage: f=FMatrix(FusionRing("G2",1,fusion_labels=("i0","t"))) sage: f.findcases() 5 sage: f.findcases(output=True) @@ -449,7 +453,8 @@ def f_from(self,a,b,c,d): EXAMPLES:: - sage: f=FMatrix(FusionRing("A1",3),fusion_label="a") + sage: fr = FusionRing("A1", 3, fusion_labels="a", inject_variables=True) + sage: f = FMatrix(fr) sage: f.fmatrix(a1,a1,a2,a2) [fx6 fx7] [fx8 fx9] @@ -472,15 +477,17 @@ def f_to(self,a,b,c,d): EXAMPLES:: - sage: B=FMatrix(FusionRing("B2",2),fusion_label="b") - sage: B.fmatrix(b2,b4,b4,b2) - [fx278 fx279 fx280] - [fx281 fx282 fx283] - [fx284 fx285 fx286] - sage: B.f_from(b2,b4,b4,b2) + sage: b22 = FusionRing("B2",2) + sage: b22.fusion_labels("b",inject_variables=True) + sage: B=FMatrix(b22) + sage: B.fmatrix(b2,b4,b2,b4) + [fx266 fx267 fx268] + [fx269 fx270 fx271] + [fx272 fx273 fx274] + sage: B.f_from(b2,b4,b2,b4) + [b1, b3, b5] + sage: B.f_to(b2,b4,b2,b4) [b1, b3, b5] - sage: B.f_to(b2,b4,b4,b2) - [b0, b1, b5] """ @@ -1123,7 +1130,7 @@ def update_equations(self): self.ideal_basis = set(eq.subs(special_values) for eq in self.ideal_basis) self.ideal_basis.discard(0) - def find_cyclotomic_solution(self, equations=None, factor=True, verbose=True, prune=True, algorithm='', output=False): + def find_cyclotomic_solution(self, equations=None, algorithm='', verbose=True, output=False): """ Solve the the hexagon and pentagon relations to evaluate the F-matrix. This method (omitting the orthogonality constraints) produces @@ -1136,9 +1143,6 @@ def find_cyclotomic_solution(self, equations=None, factor=True, verbose=True, pr - ``equations`` -- (optional) a set of equations to be solved. Defaults to the hexagon and pentagon equations. - - ``factor`` -- (default: ``True``). Set true to use - the sreduce method to simplify the hexagon and pentagon - equations before solving them. - ``algorithm`` -- (optional). Algorithm to compute Groebner Basis. - ``output`` -- (optional, default False). Output a dictionary of F-matrix values. This may be useful to see but may be omitted @@ -1147,36 +1151,35 @@ def find_cyclotomic_solution(self, equations=None, factor=True, verbose=True, pr EXAMPLES:: - sage: f = FMatrix(FusionRing("A2",1),fusion_label="a") - sage: f.get_solution(verbose=False,output=True) - equations: 8 - equations: 16 + sage: f=FMatrix(FusionRing("A2",1,fusion_labels="a",inject_variables=True),inject_variables=True) + creating variables fx1..fx8 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 + sage: f.find_cyclotomic_solution(output=True) + Setting up hexagons and pentagons... + Finding a Groebner basis... + Solving... + Fixing the gauge... adding equation... fx4 - 1 + Done! {(a2, a2, a2, a0, a1, a1): 1, - (a2, a2, a1, a2, a1, a0): 1, - (a2, a1, a2, a2, a0, a0): 1, - (a2, a1, a1, a1, a0, a2): 1, - (a1, a2, a2, a2, a0, a1): 1, - (a1, a2, a1, a1, a0, a0): 1, - (a1, a1, a2, a1, a2, a0): 1, - (a1, a1, a1, a0, a2, a2): 1} + (a2, a2, a1, a2, a1, a0): 1, + (a2, a1, a2, a2, a0, a0): 1, + (a2, a1, a1, a1, a0, a2): 1, + (a1, a2, a2, a2, a0, a1): 1, + (a1, a2, a1, a1, a0, a0): 1, + (a1, a1, a2, a1, a2, a0): 1, + (a1, a1, a1, a0, a2, a2): 1} After you successfully run ``get_solution`` you may check the correctness of the F-matrix by running :meth:`hexagon` and :meth:`pentagon`. These should return empty lists - of equations. In this example, we turn off the factor - and prune optimizations to test all instances. + of equations. EXAMPLES:: - sage: f.hexagon(factor=False) - equations: 0 - [] - sage: f.hexagon(factor=False,side="right") - equations: 0 + sage: f.get_defining_equations("hexagons") [] - sage: f.pentagon(factor=False,prune=False) - equations: 0 + sage: f.get_defining_equations("pentagons") [] """ @@ -1186,7 +1189,6 @@ def find_cyclotomic_solution(self, equations=None, factor=True, verbose=True, pr if verbose: print("Setting up hexagons and pentagons...") equations = self.get_defining_equations("hexagons")+self.get_defining_equations("pentagons") - #equations = self.hexagon(verbose=False, factor=factor)+self.pentagon(verbose=False, factor=factor, prune=prune) if verbose: print("Finding a Groebner basis...") self.ideal_basis = set(Ideal(equations).groebner_basis(algorithm=algorithm)) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 4247c1e3df5..5eaac549be6 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -36,6 +36,12 @@ class FusionRing(WeylCharacterRing): - ``conjugate`` -- (default ``False``) set ``True`` to obtain the complex conjugate ring - ``cyclotomic_order`` -- (default computed depending on ``ct`` and ``k``) + - ``fusion_labels`` -- (default None) either a tuple of strings to use as labels of the + basis of simple objects, or a string from which the labels will be + constructed + - ``inject_variables`` -- (default ``False``): use with ``fusion_labels``. + If ``inject_variables`` is ``True``, the fusion labels will be variables + that can be accessed from the command line The cyclotomic order is an integer `N` such that all computations will return elements of the cyclotomic field of `N`-th roots of unity. @@ -284,7 +290,7 @@ class FusionRing(WeylCharacterRing): True """ @staticmethod - def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjugate=False, cyclotomic_order=None): + def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjugate=False, cyclotomic_order=None, fusion_labels=None, inject_variables=False): """ Normalize input to ensure a unique representation. @@ -326,7 +332,9 @@ def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjug return super(FusionRing, cls).__classcall__(cls, ct, base_ring=base_ring, prefix=prefix, style=style, k=k, conjugate=conjugate, - cyclotomic_order=cyclotomic_order) + cyclotomic_order=cyclotomic_order, + fusion_labels=fusion_labels, + inject_variables=inject_variables) def _test_verlinde(self, **options): """ diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 0a1e8c18fd8..c254e79a1e8 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -90,7 +90,7 @@ class WeylCharacterRing(CombinatorialFreeModule): https://doc.sagemath.org/html/en/thematic_tutorials/lie.html """ @staticmethod - def __classcall__(cls, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, conjugate=False, cyclotomic_order=None): + def __classcall__(cls, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, conjugate=False, cyclotomic_order=None, fusion_labels=None, inject_variables=False): """ TESTS:: @@ -106,9 +106,9 @@ def __classcall__(cls, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, c prefix = ct[0]+str(ct[1]) else: prefix = repr(ct) - return super(WeylCharacterRing, cls).__classcall__(cls, ct, base_ring=base_ring, prefix=prefix, style=style, k=k, conjugate=conjugate, cyclotomic_order=cyclotomic_order) + return super(WeylCharacterRing, cls).__classcall__(cls, ct, base_ring=base_ring, prefix=prefix, style=style, k=k, conjugate=conjugate, cyclotomic_order=cyclotomic_order, fusion_labels=fusion_labels, inject_variables=inject_variables) - def __init__(self, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, conjugate=False, cyclotomic_order=None): + def __init__(self, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, conjugate=False, cyclotomic_order=None, fusion_labels=None, inject_variables=False): """ EXAMPLES:: @@ -196,6 +196,9 @@ def next_level(wt): self._cyclotomic_order = self._fg * self._l else: self._cyclotomic_order = cyclotomic_order + self._fusion_labels = fusion_labels + if fusion_labels: + self.fusion_labels(labels=fusion_labels, inject_variables=inject_variables) @cached_method def ambient(self): From beac8e74714ec954bf78cf01a4d07607417e5ffc Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Mon, 8 Mar 2021 14:44:42 -0800 Subject: [PATCH 020/632] add braid method in fusion_ring.py --- src/sage/combinat/root_system/fusion_ring.py | 137 ++++++++++++++++++- 1 file changed, 133 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 5eaac549be6..817390bec01 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -40,7 +40,7 @@ class FusionRing(WeylCharacterRing): basis of simple objects, or a string from which the labels will be constructed - ``inject_variables`` -- (default ``False``): use with ``fusion_labels``. - If ``inject_variables`` is ``True``, the fusion labels will be variables + If ``inject_variables`` is ``True``, the fusion labels will be variables that can be accessed from the command line The cyclotomic order is an integer `N` such that all computations @@ -828,7 +828,7 @@ def r_matrix(self, i, j, k, base_coercion=True): def global_q_dimension(self, base_coercion=True): r""" - Return `\sum d_i^2`, where the sum is over all simple objects + Return `\sum d_i^2`, where the sum is over all simple objects and `d_i` is the quantum dimension. It is a positive real number. EXAMPLES:: @@ -911,7 +911,7 @@ def D_minus(self, base_coercion=True): if (not base_coercion) or (self._basecoer is None): return ret return self._basecoer(ret) - + def is_multiplicity_free(self): """ Return ``True`` if the fusion multiplicities @@ -1068,6 +1068,136 @@ def gens_satisfy_braid_gp_rels(self,sig): singular = any(s.is_singular() for s in sig) return braid_rels and far_comm and not singular + ################################### + ### Braid group representations ### + ################################### + + def get_trees(self,top_row,root): + """ + Recursively enumerate all the admissible trees with given top row and root. + Returns a list of tuples (l1,...,lk) such that + root -> lk # m[-1], lk -> l_{k-1} # m[-2], ..., l1 -> m[0] # m[1], + with top_row = m + """ + if len(top_row) == 2: + m1, m2 = top_row + return [[]] if self.Nk_ij(m1,m2,root) else [] + else: + m1, m2 = top_row[:2] + return [tuple([l,*b]) for l in self.basis() for b in self.get_trees([l]+top_row[2:],root) if self.Nk_ij(m1,m2,l)] + + def get_comp_basis(self,a,b,n_strands): + """ + Get the so-called computational basis for Hom(b, a^n). The basis is a list of + (n-2)-tuples (m_1,...,m_{n//2},l_1,...,l_{(n-3)//2}) such that + each m_i is a monomial in a^2 and l_{j+1} \in l_j # a, and l[-1] \in a # b + """ + comp_basis = list() + for top in product((a*a).monomials(),repeat=n_strands//2): + #If the n_strands is odd, we must extend the top row by a fusing anyon + top_row = list(top)+[a]*(n_strands%2) + comp_basis.extend(tuple([*top,*levels]) for levels in self.get_trees(top_row,b)) + return comp_basis + + @lazy_attribute + def fmats(self): + """ + Construct an FMatrix factory to solve the pentagon relations and + organize the resulting F-symbols. We only need this attribute to compute + braid group representations. + """ + return FMatrix.FMatrix(self) + + def emap(self,mapper,input_args,worker_pool=None): + """ + Apply the given mapper to each element of the given input iterable and + return the results (with no duplicates) in a list. This method applies the + mapper in parallel if a worker_pool is provided. + + # INPUT: + mapper is a string specifying the name of a function defined in the + fast_parallel_fmats_methods module. + + input_args should be a tuple holding arguments to be passed to mapper + + ##NOTES: + If worker_pool is not provided, function maps and reduces on a single process. + If worker_pool is provided, the function attempts to determine whether it should + use multiprocessing based on the length of the input iterable. If it can't determine + the length of the input iterable then it uses multiprocessing with the default chunksize of 1 + if chunksize is not explicitly provided. + """ + n_proc = worker_pool._processes if worker_pool is not None else 1 + input_iter = [(child_id, n_proc, input_args) for child_id in range(n_proc)] + no_mp = worker_pool is None + #Map phase. Casting Async Object blocks execution... Each process holds results + #in its copy of fmats.temp_eqns + input_iter = zip_longest([],input_iter,fillvalue=(mapper,id(self))) + if no_mp: + list(map(executor,input_iter)) + else: + list(worker_pool.imap_unordered(executor,input_iter,chunksize=1)) + #Reduce phase + if no_mp: + results = collect_eqns(0) + else: + results = set() + for worker_results in worker_pool.imap_unordered(collect_eqns,range(worker_pool._processes),chunksize=1): + results.update(worker_results) + results = list(results) + return results + + def get_braid_generators(self,fusing_anyon,total_topological_charge,n_strands,use_mp=True): + """ + Compute generators of the Artin braid group on n_strands strands. If + fusing_anyon = a and total_topological_charge = b, the generators are + endomorphisms of Hom(a^n_strands, b) + """ + assert n_strands > 2, "The number of strands must be an integer greater than 2" + #Construct associated FMatrix object and solve for F-symbols + if not self.fmats.symbols_known: + self.fmats.find_real_orthogonal_solution() + + #Set multiprocessing parameters. Context can only be set once, so we try to set it + try: + set_start_method('fork') + except RuntimeError: + pass + pool = Pool() if use_mp else None + + #Set up computational basis and compute generators one at a time + a, b = fusing_anyon, total_topological_charge + comp_basis = self.get_comp_basis(a,b,n_strands) + d = len(comp_basis) + print("Computing an {}-dimensional representation of the Artin braid group on {} strands...".format(d,n_strands)) + + #Compute diagonal odd-indexed generators using the 3j-symbols + gens = { 2*i+1 : diagonal_matrix(self.r_matrix(a,a,c[i]) for c in comp_basis) for i in range(n_strands//2) } + + #Compute even-indexed generators using F-matrices + for k in range(1,n_strands//2): + entries = self.emap('sig_2k',(k,a,b,n_strands),pool) + gens[2*k] = matrix(dict(entries)) + + #If n_strands is odd, we compute the final generator + if n_strands % 2: + entries = self.emap('odd_one_out',(a,b,n_strands),pool) + gens[n_strands-1] = matrix(dict(entries)) + + return comp_basis, [gens[k] for k in sorted(gens)] + + def gens_satisfy_braid_gp_rels(self,sig): + """ + Determine if given iterable of n matrices defines a representation of + the Artin braid group on (n+1) strands. Tests correctness of + get_braid_generators method. + """ + n = len(sig) + braid_rels = all(sig[i] * sig[i+1] * sig[i] == sig[i+1] * sig[i] * sig[i+1] for i in range(n-1)) + far_comm = all(sig[i] * sig[j] == sig[j] * sig[i] for i, j in product(range(n),repeat=2) if abs(i-j) > 1 and i > j) + singular = any(s.is_singular() for s in sig) + return braid_rels and far_comm and not singular + class Element(WeylCharacterRing.Element): """ A class for FusionRing elements. @@ -1237,4 +1367,3 @@ def q_dimension(self, base_coercion=True): if (not base_coercion) or (self.parent()._basecoer is None): return ret return self.parent()._basecoer(ret) - From c635db99d9ef20ed1731225f7b663cc2a14a851a Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Mon, 8 Mar 2021 16:46:30 -0800 Subject: [PATCH 021/632] work on doctests --- src/sage/combinat/root_system/f_matrix.py | 26 +- .../fast_parallel_fmats_methods.pxd | 6 +- .../fast_parallel_fmats_methods.pyx | 224 +++++++++----- .../fast_parallel_fusion_ring_braid_repn.pyx | 86 +++++- src/sage/combinat/root_system/fusion_ring.py | 10 +- .../combinat/root_system/poly_tup_engine.pxd | 6 +- .../combinat/root_system/poly_tup_engine.pyx | 277 ++++++++++++++++-- 7 files changed, 500 insertions(+), 135 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 53d2ebefa1a..8fcbb999097 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -217,14 +217,18 @@ class FMatrix(): :meth:`find_orthogonal_solution` is much more powerful and is capable of handling large cases, sometimes - quickly but sometimes (in larger cases) hours of + quickly but sometimes (in larger cases) after hours of computation. Its F-matrices are not always in the cyclotomic field that is the base ring of the underlying FusionRing, but sometimes in an extension field adjoining some square roots. When this happens, the FusionRing is - modified, adding an attribute :attr:`_basecoer that is + modified, adding an attribute :attr:`_basecoer` that is a coercion from the cyclotomic field to the F-matrix. + EXAMPLES:: + + (B3 level 2, F4 level 1 conjugated) + """ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variables=False): self._FR = fusion_ring @@ -252,7 +256,7 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab r = self._field.defining_polynomial().roots(ring=QQbar,multiplicities=False)[0] self._qqbar_embedding = self._field.hom([r],QQbar) self._var_to_idx = { var : idx for idx, var in enumerate(self._poly_ring.gens()) } - self._ks = self.get_known_sq() + self._ks = dict() self._nnz = self.get_known_nonz() self._known_vals = dict() self.symbols_known = False @@ -522,7 +526,12 @@ def get_known_sq(self,eqns=None): """ if eqns is None: eqns = self.ideal_basis - return { variables(eq_tup)[0] : -eq_tup[-1][1] for eq_tup in eqns if tup_fixes_sq(eq_tup) } + ks = deepcopy(self._ks) + for eq_tup in eqns: + if tup_fixes_sq(eq_tup): + ks[variables(eq_tup)[0]] = -eq_tup[-1][1] + # return { variables(eq_tup)[0] : -eq_tup[-1][1] for eq_tup in eqns if tup_fixes_sq(eq_tup) } + return ks def get_known_nonz(self): """ @@ -861,6 +870,14 @@ def partition_eqns(self,graph,eqns=None,verbose=True): print(graph.connected_components_sizes()) return partition + def add_square_fixers(self): + """ + Add square fixing equations back to ideal basis + """ + for fx, rhs in self._ks.items(): + if fx not in self.solved: + self.ideal_basis.append(poly_to_tup(self._poly_ring.gen(fx)**2 - rhs)) + def par_graph_gb(self,worker_pool=None,eqns=None,term_order="degrevlex",verbose=True): """ Compute a Groebner basis for a set of equations partitioned according to their corresponding graph @@ -924,6 +941,7 @@ def get_explicit_solution(self,eqns=None,verbose=True): """ if eqns is None: eqns = self.ideal_basis + self.add_square_fixers() eqns_partition = self.partition_eqns(self.equations_graph(eqns),verbose=verbose) F = self._field diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd index 487b7b60e83..9eebf2e5c58 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd @@ -3,6 +3,8 @@ cpdef get_reduced_pentagons(factory, tuple mp_params, bint prune=*) cpdef update_reduce(factory, tuple eq_tup) cpdef compute_gb(factory, tuple args) cpdef update_child_fmats(factory, tuple data_tup) -cpdef pent_verify(factory, tuple mp_params) +# cpdef pent_verify(factory, tuple mp_params) -cdef _fmat(factory, a, b, c, d, x, y) +cdef _fmat(fvars, Nk_ij, one, a, b, c, d, x, y) + +# cpdef update_reduce_chunk(factory, args) diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index 5f2bc0c3f18..3d0084c50ab 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -20,7 +20,7 @@ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.qqbar import number_field_elements_from_algebraics #Define a global temporary worker results repository -worker_results = list() +cdef list worker_results = list() ############################## ### Parallel code executor ### @@ -34,7 +34,23 @@ def executor(params): ## NOTE: When the parent process is forked, each worker gets a copy of every global variable. The virtual memory address of object X in the parent process equals the VIRTUAL memory address of the copy of object X in each - worker, so we may construct references to forked copies of X + worker, so we may construct references to forked copies of X using an id + obtained in the parent process + + TESTS: + + sage: from sage.combinat.root_system.fast_parallel_fmats_methods import executor + sage: fmats = FMatrix(FusionRing("A1",3)) + sage: params = (('get_reduced_hexagons', id(fmats)), (0,1)) + sage: executor(params) + sage: from sage.combinat.root_system.fast_parallel_fmats_methods import collect_eqns + sage: len(collect_eqns(0)) == 63 + True + sage: fmats = FMatrix(FusionRing("E8",2)) + sage: params = (('get_reduced_hexagons', id(fmats)), (0,1)) + sage: executor(params) + sage: len(collect_eqns(0)) == 11 + True """ (fn_name, fmats_id), args = params #Construct a reference to global FMatrix object in this worker's memory @@ -43,45 +59,71 @@ def executor(params): #Bind module method to FMatrix object in worker process, and call the method return getattr(mod,fn_name)(fmats_obj,args) + +###################################### +### Fast fusion coefficients cache ### +###################################### + +# from sage.misc.cachefunc import cached_function +# cdef dict _Nk_ij = dict() + +# cpdef _Nk_ij(factory,proc): +# cdef int coeff +# for a,b,c in product(factory._FR.basis(),repeat=3): +# try: +# coeff = (a*b).monomial_coefficients(copy=False)[c.weight()] +# except: +# coeff = 0 +# _Nk_ij[a,b,c] = coeff + +# cpdef int _Nk_ij(a,b,c): +# try: +# return (a*b).monomial_coefficients(copy=False)[c.weight()] +# except KeyError: +# return 0 +# +# _Nk_ij = cached_function(_Nk_ij, name='_Nk_ij') + + ############### ### Mappers ### ############### -cdef _fmat(factory, a, b, c, d, x, y): +cdef _fmat(fvars, _Nk_ij, one, a, b, c, d, x, y): """ Cython version of fmat class method. Using cdef for fastest dispatch """ - if factory._FR.Nk_ij(a,b,x) == 0 or factory._FR.Nk_ij(x,c,d) == 0 or factory._FR.Nk_ij(b,c,y) == 0 or factory._FR.Nk_ij(a,y,d) == 0: + if _Nk_ij(a,b,x) == 0 or _Nk_ij(x,c,d) == 0 or _Nk_ij(b,c,y) == 0 or _Nk_ij(a,y,d) == 0: return 0 - #Some known zero F-symbols - if a == factory._FR.one(): + #Some known F-symbols + if a == one: if x == b and y == d: return 1 else: return 0 - if b == factory._FR.one(): + if b == one: if x == a and y == c: return 1 else: return 0 - if c == factory._FR.one(): + if c == one: if x == d and y == b: return 1 else: return 0 - return factory._fvars[a,b,c,d,x,y] + return fvars[a,b,c,d,x,y] -cdef req_cy(factory, tuple sextuple, side="left"): +cdef req_cy(tuple basis, r_matrix, dict fvars, Nk_ij, one, tuple sextuple): """ Given an FMatrix factory and a sextuple, return a hexagon equation as a polynomial object """ a, b, c, d, e, g = sextuple #To add typing we need to ensure all fmats.fmat are of the same type? #Return fmats._poly_ring.zero() and fmats._poly_ring.one() instead of 0 and 1? - lhs = factory._FR.r_matrix(a,c,e,base_coercion=False)*_fmat(factory,a,c,b,d,e,g)*factory._FR.r_matrix(b,c,g,base_coercion=False) + lhs = r_matrix(a,c,e)*_fmat(fvars,Nk_ij,one,a,c,b,d,e,g)*r_matrix(b,c,g) rhs = 0 - for f in factory._FR.basis(): - rhs += _fmat(factory,c,a,b,d,e,f)*factory._FR.r_matrix(f,c,d,base_coercion=False)*_fmat(factory,a,b,c,d,f,g) + for f in basis: + rhs += _fmat(fvars,Nk_ij,one,c,a,b,d,e,f)*r_matrix(f,c,d)*_fmat(fvars,Nk_ij,one,a,b,c,d,f,g) return lhs-rhs @cython.wraparound(False) @@ -91,28 +133,40 @@ cpdef get_reduced_hexagons(factory, tuple mp_params): """ Set up and reduce the hexagon equations corresponding to this worker """ + #Set up multiprocessing parameters global worker_results cdef child_id, n_proc cdef unsigned long i child_id, n_proc = mp_params cdef tuple sextuple - for i, sextuple in enumerate(product(factory._FR.basis(),repeat=6)): + + #Pre-compute common parameters for speed + cdef tuple basis = tuple(factory._FR.basis()) + cdef dict fvars = factory._fvars + r_matrix = factory._FR.r_matrix + _Nk_ij = factory._FR.Nk_ij + one = factory._FR.one() + cdef ETuple _nnz = factory._nnz + cdef dict _ks = factory._ks + + #Computation loop + for i, sextuple in enumerate(product(basis,repeat=6)): if i % n_proc == child_id: - he = req_cy(factory,sextuple) + he = req_cy(basis,r_matrix,fvars,_Nk_ij,one,sextuple) if he: - worker_results.append(reduce_poly_dict(he.dict(),factory._nnz,factory._ks)) + worker_results.append(reduce_poly_dict(he.dict(),_nnz,_ks)) -cdef MPolynomial_libsingular feq_cy(factory, tuple nonuple, bint prune=False): +cdef MPolynomial_libsingular feq_cy(tuple basis, fvars, Nk_ij, one, zero, tuple nonuple, bint prune=False): """ Given an FMatrix factory and a nonuple, return a pentagon equation as a polynomial object """ a, b, c, d, e, f, g, k, l = nonuple - cdef lhs = _fmat(factory,f,c,d,e,g,l)*_fmat(factory,a,b,l,e,f,k) + cdef lhs = _fmat(fvars,Nk_ij,one,f,c,d,e,g,l)*_fmat(fvars,Nk_ij,one,a,b,l,e,f,k) if lhs == 0 and prune: # it is believed that if lhs=0, the equation carries no new information - return factory._poly_ring.zero() - cdef rhs = factory._poly_ring.zero() - for h in factory._FR.basis(): - rhs += _fmat(factory,a,b,c,g,f,h)*_fmat(factory,a,h,d,e,g,k)*_fmat(factory,b,c,d,k,h,l) + return zero + cdef rhs = zero + for h in basis: + rhs += _fmat(fvars,Nk_ij,one,a,b,c,g,f,h)*_fmat(fvars,Nk_ij,one,a,h,d,e,g,k)*_fmat(fvars,Nk_ij,one,b,c,d,k,h,l) return lhs - rhs @cython.wraparound(False) @@ -122,18 +176,31 @@ cpdef get_reduced_pentagons(factory, tuple mp_params, bint prune=True): """ Set up and reduce the pentagon equations corresponding to this worker """ + #Set up multiprocessing parameters global worker_results cdef int child_id, n_proc child_id, n_proc = mp_params cdef unsigned long i - cdef tuple nonuple + cdef tuple nonuple, red cdef MPolynomial_libsingular pe - for i, nonuple in enumerate(product(factory._FR.basis(),repeat=9)): + + #Pre-compute common parameters for speed + cdef tuple basis = tuple(factory._FR.basis()) + cdef dict fvars = factory._fvars + r_matrix = factory._FR.r_matrix + _Nk_ij = factory._FR.Nk_ij + one = factory._FR.one() + cdef ETuple _nnz = factory._nnz + cdef dict _ks = factory._ks + cdef MPolynomial_libsingular zero = factory._poly_ring.zero() + + #Computation loop + for i, nonuple in enumerate(product(basis,repeat=9)): if i % n_proc == child_id: - pe = feq_cy(factory,nonuple,prune=prune) + pe = feq_cy(basis,fvars,_Nk_ij,one,zero,nonuple,prune=prune) if pe: - worker_results.append(reduce_poly_dict(pe.dict(),factory._nnz,factory._ks)) - + red = reduce_poly_dict(pe.dict(),_nnz,_ks) + worker_results.append(red) cpdef update_reduce(factory, tuple eq_tup): """ @@ -141,12 +208,8 @@ cpdef update_reduce(factory, tuple eq_tup): """ global worker_results cdef dict eq_dict = subs(eq_tup,factory._kp) - cdef reduced - if tup_fixes_sq(tuple(eq_dict.items())): - reduced = to_monic(eq_dict) - else: - reduced = reduce_poly_dict(eq_dict,factory._nnz,factory._ks) - worker_results.append(reduced) + cdef tuple red = reduce_poly_dict(eq_dict,factory._nnz,factory._ks) + worker_results.append(red) cpdef compute_gb(factory, tuple args): """ @@ -163,9 +226,7 @@ cpdef compute_gb(factory, tuple args): R = PolynomialRing(factory._FR.field(),len(sorted_vars),'a',order=term_order) #Zip tuples into R and compute Groebner basis - cdef idx_map = dict() - for new, old in enumerate(sorted_vars): - idx_map[old] = new + cdef idx_map = { old : new for new, old in enumerate(sorted_vars) } nvars = len(sorted_vars) cdef list polys = list() for eq_tup in eqns: @@ -173,23 +234,27 @@ cpdef compute_gb(factory, tuple args): gb = Ideal(sorted(polys)).groebner_basis(algorithm="libsingular:slimgb") #Change back to fmats poly ring and append to temp_eqns - cdef dict inv_idx_map = dict() - for k, v in idx_map.items(): - inv_idx_map[v] = k + cdef dict inv_idx_map = { v : k for k, v in idx_map.items() } + cdef tuple t nvars = factory._poly_ring.ngens() for p in gb: - worker_results.append(resize(poly_to_tup(p),inv_idx_map,nvars)) + t = resize(poly_to_tup(p),inv_idx_map,nvars) + worker_results.append(t) -#One-to-all communication used to update fvars after triangular elim step. cpdef update_child_fmats(factory, tuple data_tup): - #fmats is assumed to be global before forking used to create the Pool object, + """ + One-to-all communication used to update fvars after triangular elim step. + """ + #factory object is assumed global before forking used to create the Pool object, #so each child has a global fmats variable. So it's enough to update that object factory._fvars, factory.solved, factory._ks, factory._var_degs = data_tup factory._nnz = factory.get_known_nonz() factory._kp = compute_known_powers(factory._var_degs,factory.get_known_vals()) def get_appropriate_number_field(factory,non_cyclotomic_roots): - #If needed, find a NumberField containing all the roots + """ + If needed, find a NumberField containing the roots given roots + """ roots = [factory._FR.field().gen()]+[r[1] for r in non_cyclotomic_roots] return number_field_elements_from_algebraics(roots,minimal=True) @@ -208,40 +273,39 @@ def collect_eqns(proc): worker_results = list() return list(reduced) -#################### -### Verification ### -#################### - -cdef feq_verif(factory, tuple nonuple, float tol=5e-8): - """ - Check the pentagon equation corresponding to the given nonuple - """ - global worker_results - a, b, c, d, e, f, g, k, l = nonuple - cdef float diff, lhs, rhs - lhs = _fmat(factory,f,c,d,e,g,l)*_fmat(factory,a,b,l,e,f,k) - rhs = 0.0 - for h in factory._FR.basis(): - rhs += _fmat(factory,a,b,c,g,f,h)*_fmat(factory,a,h,d,e,g,k)*_fmat(factory,b,c,d,k,h,l) - diff = lhs - rhs - if diff > tol or diff < -tol: - worker_results.append(diff) - - -@cython.wraparound(False) -@cython.nonecheck(False) -@cython.cdivision(True) -cpdef pent_verify(factory, tuple mp_params): - """ - Generate all the pentagon equations assigned to this process, and reduce them - """ - child_id, n_proc = mp_params - cdef float t0 - cdef tuple nonuple - cdef unsigned long i - for i, nonuple in enumerate(product(factory.FR.basis(),repeat=9)): - if i % n_proc == child_id: - feq_verif(factory,nonuple) - if i % 50000000 == 0 and i: - print("{:5d}m equations checked... {} potential misses so far...".format(i // 1000000,len(worker_results))) - print("Ran through {} pentagons... Worker {} with pid {} reporting {} potential misses...".format(i,child_id,getpid(),len(worker_results))) +# #################### +# ### Verification ### +# #################### +# +# cdef feq_verif(factory, tuple nonuple, float tol=5e-8): +# """ +# Check the pentagon equation corresponding to the given nonuple +# """ +# global worker_results +# a, b, c, d, e, f, g, k, l = nonuple +# cdef float diff, lhs, rhs +# lhs = _fmat(factory,f,c,d,e,g,l)*_fmat(factory,a,b,l,e,f,k) +# rhs = 0.0 +# for h in factory._FR.basis(): +# rhs += _fmat(factory,a,b,c,g,f,h)*_fmat(factory,a,h,d,e,g,k)*_fmat(factory,b,c,d,k,h,l) +# diff = lhs - rhs +# if diff > tol or diff < -tol: +# worker_results.append(diff) +# +# @cython.wraparound(False) +# @cython.nonecheck(False) +# @cython.cdivision(True) +# cpdef pent_verify(factory, tuple mp_params): +# """ +# Generate all the pentagon equations assigned to this process, and reduce them +# """ +# child_id, n_proc = mp_params +# cdef float t0 +# cdef tuple nonuple +# cdef unsigned long i +# for i, nonuple in enumerate(product(factory._FR.basis(),repeat=9)): +# if i % n_proc == child_id: +# feq_verif(factory,nonuple) +# if i % 50000000 == 0 and i: +# print("{:5d}m equations checked... {} potential misses so far...".format(i // 1000000,len(worker_results))) +# print("Ran through {} pentagons... Worker {} with pid {} reporting {} potential misses...".format(i,child_id,getpid(),len(worker_results))) diff --git a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx index 9ecc49bc084..5fcba25b9b9 100644 --- a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx @@ -31,6 +31,24 @@ def executor(params): every global variable. The virtual memory address of object X in the parent process equals the VIRTUAL memory address of the copy of object X in each worker, so we may construct references to forked copies of X + + TESTS: + + sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import executor + sage: FR = FusionRing("A1",4) + sage: FR.fusion_labels(['idd','one','two','three','four'],inject_variables=True) + sage: params = (('sig_2k',id(FR)),(0,1,(1,one,one,5))) + sage: executor(params) + sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import collect_results + sage: len(collect_results(0)) == 13 + True + sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import executor, collect_results + sage: FR = FusionRing("B2",2) + sage: FR.fusion_labels(['I0','Y1','X','Z','Xp','Y2'],inject_variables=True) + sage: params = (('odd_one_out',id(FR)),(0,1,(X,Xp,5))) + sage: executor(params) + sage: len(collect_results(0)) == 54 + True """ (fn_name, fr_id), args = params #Construct a reference to global FMatrix object in this worker's memory @@ -48,7 +66,24 @@ cpdef mid_sig_ij(fusion_ring,row,col,a,b): Compute the (xi,yi), (xj,yj) entry of generator braiding the middle two strands in the tree b -> xi # yi -> (a # a) # (a # a), which results in a sum over j of trees b -> xj # yj -> (a # a) # (a # a) + + EXAMPLES:: + + sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import mid_sig_ij + sage: FR = FusionRing("A1",4) + sage: FR.fusion_labels(['idd','one','two','three','four'],inject_variables=True) + sage: one.weight() + (1/2, -1/2) + sage: FR.get_comp_basis(one,two,4) + [(two, two), (two, idd), (idd, two)] + sage: mid_sig_ij(FR, (two, two), (two, idd), one, two) + (zeta48^10 - zeta48^2)*fx0*fx1*fx8 + (zeta48^2)*fx2*fx3*fx8 """ + #Pre-compute common parameters for efficiency + _fvars = fusion_ring.fmats._fvars + _Nk_ij = fusion_ring.Nk_ij + one = fusion_ring.one() + xi, yi = row xj, yj = col entry = 0 @@ -56,10 +91,10 @@ cpdef mid_sig_ij(fusion_ring,row,col,a,b): for c in fusion_ring.basis(): for d in fusion_ring.basis(): ##Warning: We assume F-matrices are orthogonal!!! (using transpose for inverse) - f1 = _fmat(fusion_ring.fmats,a,a,yi,b,xi,c) - f2 = _fmat(fusion_ring.fmats,a,a,a,c,d,yi) - f3 = _fmat(fusion_ring.fmats,a,a,a,c,d,yj) - f4 = _fmat(fusion_ring.fmats,a,a,yj,b,xj,c) + f1 = _fmat(_fvars,_Nk_ij,one,a,a,yi,b,xi,c) + f2 = _fmat(_fvars,_Nk_ij,one,a,a,a,c,d,yi) + f3 = _fmat(_fvars,_Nk_ij,one,a,a,a,c,d,yj) + f4 = _fmat(_fvars,_Nk_ij,one,a,a,yj,b,xj,c) r = fusion_ring.r_matrix(a,a,d) if not phi.is_identity(): r = phi(r) @@ -71,13 +106,30 @@ cpdef odd_one_out_ij(fusion_ring,xi,xj,a,b): Compute the xi, xj entry of the braid generator on the right-most strands, corresponding to the tree b -> (xi # a) -> (a # a) # a, which results in a sum over j of trees b -> xj -> (a # a) # (a # a) + + EXAMPLES:: + + sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import odd_one_out_ij + sage: FR = FusionRing("B2",2) + sage: FR.fusion_labels(['I0','Y1','X','Z','Xp','Y2'],inject_variables=True) + sage: X.weight() + (1/2, 1/2) + sage: FR.get_comp_basis(X,X,3) + [(Y2,), (Y1,), (I0,)] + sage: odd_one_out_ij(FR,Y2,Y1,X,X) + (zeta40^10)*fx205*fx208 + (zeta40^14 - zeta40^10 + zeta40^6 - zeta40^2)*fx206*fx209 + (zeta40^2)*fx207*fx210 """ - entry = 0 + #Pre-compute common parameters for efficiency + _fvars = fusion_ring.fmats._fvars + _Nk_ij = fusion_ring.Nk_ij + one = fusion_ring.one() + phi = fusion_ring.fmats.get_coerce_map_from_fr_cyclotomic_field() + entry = 0 for c in fusion_ring.basis(): ##Warning: We assume F-matrices are orthogonal!!! (using transpose for inverse) - f1 = _fmat(fusion_ring.fmats,a,a,a,b,xi,c) - f2 = _fmat(fusion_ring.fmats,a,a,a,b,xj,c) + f1 = _fmat(_fvars,_Nk_ij,one,a,a,a,b,xi,c) + f2 = _fmat(_fvars,_Nk_ij,one,a,a,a,b,xj,c) r = fusion_ring.r_matrix(a,a,c) if not phi.is_identity(): r = phi(r) @@ -95,6 +147,11 @@ cpdef sig_2k(fusion_ring, tuple args): """ Compute entries of the 2k-th braid generator """ + #Pre-compute common parameters for efficiency + _fvars = fusion_ring.fmats._fvars + _Nk_ij = fusion_ring.Nk_ij + one = fusion_ring.one() + child_id, n_proc, fn_args = args k, a, b, n_strands = fn_args cdef int ctr = -1 @@ -139,8 +196,8 @@ cpdef sig_2k(fusion_ring, tuple args): entry = 0 for p in fusion_ring.basis(): - f1 = _fmat(fusion_ring.fmats,top_left,m[k-1],m[k],root,l[k-2],p) - f2 = _fmat(fusion_ring.fmats,top_left,f,e,root,q,p) + f1 = _fmat(_fvars,_Nk_ij,one,top_left,m[k-1],m[k],root,l[k-2],p) + f2 = _fmat(_fvars,_Nk_ij,one,top_left,f,e,root,q,p) entry += f1 * mid_sig_ij(fusion_ring,(m[k-1],m[k]),(f,e),a,p) * f2 worker_results.append(((basis_dict[nnz_pos],i), entry)) entries.add((basis_dict[nnz_pos],i)) @@ -153,6 +210,11 @@ cpdef odd_one_out(fusion_ring, tuple args): Compute entries of the rightmost braid generator, in case we have an odd number of strands """ + #Pre-compute common parameters for efficiency + _fvars = fusion_ring.fmats._fvars + _Nk_ij = fusion_ring.Nk_ij + one = fusion_ring.one() + global worker_results child_id, n_proc, fn_args = args a, b, n_strands = fn_args @@ -192,8 +254,8 @@ cpdef odd_one_out(fusion_ring, tuple args): #Compute relevant entry entry = 0 for p in fusion_ring.basis(): - f1 = _fmat(fusion_ring.fmats,top_left,m[-1],a,root,l[-1],p) - f2 = _fmat(fusion_ring.fmats,top_left,f,a,root,q,p) + f1 = _fmat(_fvars,_Nk_ij,one,top_left,m[-1],a,root,l[-1],p) + f2 = _fmat(_fvars,_Nk_ij,one,top_left,f,a,root,q,p) entry += f1 * odd_one_out_ij(fusion_ring,m[-1],f,a,p) * f2 worker_results.append(((basis_dict[nnz_pos],i), entry)) @@ -201,7 +263,7 @@ cpdef odd_one_out(fusion_ring, tuple args): ### Reducers ### ################ -def collect_eqns(proc): +def collect_results(proc): """ Helper function for returning processed results back to parent process. Trivial reducer: simply collects objects with the same key in the worker diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 817390bec01..1fa7dea7906 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -15,7 +15,7 @@ from multiprocessing import Pool, set_start_method from sage.combinat.q_analogues import q_int import sage.combinat.root_system.f_matrix as FMatrix -from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import collect_eqns, executor +from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import collect_results, executor from sage.combinat.root_system.weyl_characters import WeylCharacterRing from sage.matrix.constructor import matrix from sage.matrix.special import diagonal_matrix @@ -1008,10 +1008,10 @@ def emap(self,mapper,input_args,worker_pool=None): list(worker_pool.imap_unordered(executor,input_iter,chunksize=1)) #Reduce phase if no_mp: - results = collect_eqns(0) + results = collect_results(0) else: results = set() - for worker_results in worker_pool.imap_unordered(collect_eqns,range(worker_pool._processes),chunksize=1): + for worker_results in worker_pool.imap_unordered(collect_results,range(worker_pool._processes),chunksize=1): results.update(worker_results) results = list(results) return results @@ -1139,10 +1139,10 @@ def emap(self,mapper,input_args,worker_pool=None): list(worker_pool.imap_unordered(executor,input_iter,chunksize=1)) #Reduce phase if no_mp: - results = collect_eqns(0) + results = collect_results(0) else: results = set() - for worker_results in worker_pool.imap_unordered(collect_eqns,range(worker_pool._processes),chunksize=1): + for worker_results in worker_pool.imap_unordered(collect_results,range(worker_pool._processes),chunksize=1): results.update(worker_results) results = list(results) return results diff --git a/src/sage/combinat/root_system/poly_tup_engine.pxd b/src/sage/combinat/root_system/poly_tup_engine.pxd index 78d4dffa4e5..5d5e49263d3 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pxd +++ b/src/sage/combinat/root_system/poly_tup_engine.pxd @@ -5,17 +5,17 @@ from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libs cpdef tuple poly_to_tup(MPolynomial_libsingular poly) cpdef MPolynomial_libsingular tup_to_poly(tuple eq_tup, MPolynomialRing_libsingular parent) cpdef tuple resize(tuple eq_tup, dict idx_map, int nvars) -cdef ETuple degrees(tuple poly_tup) cpdef ETuple get_variables_degrees(list eqns) cpdef list variables(tuple eq_tup) cpdef constant_coeff(tuple eq_tup) cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map) cpdef bint tup_fixes_sq(tuple eq_tup) cpdef dict subs_squares(dict eq_dict, dict known_sq) -cdef dict remove_gcf(dict eq_dict, ETuple nonz) cdef tuple to_monic(dict eq_dict) cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq) cpdef dict compute_known_powers(ETuple max_deg, dict val_dict) cpdef dict subs(tuple poly_tup, dict known_powers) -cdef tuple tup_mul(tuple p1, tuple p2) cpdef int poly_tup_cmp(tuple tleft, tuple tright) + +cdef tuple tup_mul(tuple p1, tuple p2) +cdef dict remove_gcf(dict eq_dict, ETuple nonz) diff --git a/src/sage/combinat/root_system/poly_tup_engine.pyx b/src/sage/combinat/root_system/poly_tup_engine.pyx index f74d674619b..9a3b0b7d83d 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pyx +++ b/src/sage/combinat/root_system/poly_tup_engine.pyx @@ -1,6 +1,6 @@ -################################################### -### Arithmetic Engine for polynomials as tuples ### -################################################### +""" +Arithmetic Engine for polynomials as tuples +""" # **************************************************************************** # Copyright (C) 2021 Guillermo Aboumrad # @@ -12,7 +12,11 @@ from functools import cmp_to_key from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular, MPolynomialRing_libsingular from sage.rings.polynomial.polydict cimport ETuple from sage.rings.polynomial.term_order import TermOrder -from sage.rings.rational_field import RationalField as QQ +from sage.rings.rational_field import QQ + +#Pre-compute common values for speed +one = QQ.one() +degrevlex_sortkey = TermOrder().sortkey_degrevlex ########### ### API ### @@ -22,6 +26,15 @@ cpdef tuple poly_to_tup(MPolynomial_libsingular poly): """ Convert a polynomial object into the internal representation as tuple of (ETuple exp, NumberFieldElement coeff) pairs + + EXAMPLES:: + + sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: R. = PolynomialRing(QQ) + sage: poly_to_tup(x**2 + 1) + (((2, 0), 1), ((0, 0), 1)) + sage: poly_to_tup(x**2*y**4 - 4/5*x*y**2 + 1/3 * y) + (((2, 4), 1), ((1, 2), -4/5), ((0, 1), 1/3)) """ return tuple(poly.dict().items()) @@ -29,7 +42,33 @@ cpdef MPolynomial_libsingular tup_to_poly(tuple eq_tup, MPolynomialRing_libsingu """ Return a polynomial object from its tuple representation. Inverse of poly_to_tup. poly_to_tup(tup_to_poly(eq_tup, ring)) == eq_tup && tup_to_poly(poly_to_tup(eq), eq.parent()) == eq - Assumes parent.ngens() == len(exp_tup) for exp_tup, c in eq_tup + + NOTE: + Assumes parent.ngens() == len(exp_tup) for exp_tup, c in eq_tup + + EXAMPLES:: + + sage: from sage.combinat.root_system.poly_tup_engine import tup_to_poly + sage: R. = PolynomialRing(CyclotomicField(20)) + sage: poly_tup = (((2,0),1), ((0,0),1)) + sage: tup_to_poly(poly_tup, parent=R) + x^2 + 1 + sage: poly = x**2*y**4 - 4/5*x*y**2 + 1/3 * y + sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: tup_to_poly(poly_to_tup(poly), parent=R) == poly + True + + TESTS: + + sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup, tup_to_poly + sage: R. = PolynomialRing(CyclotomicField(20)) + sage: r = R.random_element() + sage: tup_to_poly(poly_to_tup(r), parent=R) == r + True + sage: R = PolynomialRing(QQ, 'fx', 100) + sage: r = R.random_element() + sage: tup_to_poly(poly_to_tup(r), parent=R) == r + True """ # Maybe the following is faster but we need to ensure all coefficients are # already in fmats._poly_ring.base_ring() so that implicit casting is avoided @@ -44,7 +83,22 @@ cpdef MPolynomial_libsingular tup_to_poly(tuple eq_tup, MPolynomialRing_libsingu def tup_to_univ_poly(tuple eq_tup, gen): """ Given a tuple of pairs representing a univariate polynomial and a univariate - polynomial ring generator, return a univariate polynomial object + polynomial ring generator, return a univariate polynomial object. + + Each pair in the tuple is assumed to be of the form (ETuple, coeff), where + coeff is an element of gen.parent().base_ring(). + + EXAMPLES:: + + sage: from sage.combinat.root_system.poly_tup_engine import tup_to_univ_poly + sage: from sage.rings.polynomial.polydict import ETuple + sage: poly_tup = ((ETuple([0,3,0]),2), (ETuple([0,1,0]),-1), (ETuple([0,0,0]),-2/3)) + sage: b = QQ['b'].gen() + sage: tup_to_univ_poly(poly_tup, b) + 2*b^3 - b - 2/3 + sage: poly_tup = ((ETuple([0, 0, 0]), -1/5),) + sage: tup_to_univ_poly(poly_tup, b) + -1/5 """ univ_tup = tuple((exp.nonzero_values()[0] if exp.nonzero_values() else 0, c) for exp, c in eq_tup) return sum(c * gen ** p for p, c in univ_tup) @@ -55,6 +109,24 @@ cpdef tuple resize(tuple eq_tup, dict idx_map, int nvars): This method is used for creating polynomial objects with the "right number" of variables for computing Groebner bases of the partitioned equations graph and for adding constraints ensuring certain F-symbols are nonzero + + EXAMPLES:: + + sage: from sage.combinat.root_system.poly_tup_engine import resize + sage: from sage.rings.polynomial.polydict import ETuple + sage: poly_tup = ((ETuple([0,3,0,2]),2), (ETuple([0,1,0,1]),-1), (ETuple([0,0,0,0]),-2/3)) + sage: idx_map = { 1 : 0, 3 : 1 } + sage: resize(poly_tup,idx_map,2) + (((3, 2), 2), ((1, 1), -1), ((0, 0), -2/3)) + + sage: R = PolynomialRing(ZZ, 'fx', 20) + sage: R.inject_variables() + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19 + sage: sparse_poly = fx0**2 * fx17 + fx3 + sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup, tup_to_poly + sage: S. = PolynomialRing(ZZ) + sage: tup_to_poly(resize(poly_to_tup(sparse_poly),{0:0,3:1,17:2},3), parent=S) + x^2*z + y """ cdef ETuple new_e cdef list resized = list() @@ -82,7 +154,17 @@ cdef ETuple degrees(tuple poly_tup): cpdef ETuple get_variables_degrees(list eqns): """ Find maximum degrees for each variable in equations - """ + + EXAMPLES:: + + sage: from sage.combinat.root_system.poly_tup_engine import get_variables_degrees + sage: R. = PolynomialRing(QQ) + sage: polys = [x**2 + 1, x*y*z**2 - 4*x*y, x*z**3 - 4/3*y + 1] + sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: get_variables_degrees([poly_to_tup(p) for p in polys]) + (2, 1, 3) + """ + if not eqns: return ETuple([]) cdef tuple eq_tup cdef ETuple max_deg max_deg = degrees(eqns[0]) @@ -93,12 +175,38 @@ cpdef ETuple get_variables_degrees(list eqns): cpdef list variables(tuple eq_tup): """ Return indices of all variables appearing in eq_tup + + EXAMPLES:: + + sage: from sage.combinat.root_system.poly_tup_engine import variables + sage: from sage.rings.polynomial.polydict import ETuple + sage: poly_tup = ((ETuple([0,3,0]),2), (ETuple([0,1,0]),-1), (ETuple([0,0,0]),-2/3)) + sage: variables(poly_tup) + [1] + sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: R. = PolynomialRing(QQ) + sage: variables(poly_to_tup(x*2*y + y**3 - 4/3*x)) + [0, 1] + sage: variables(poly_to_tup(R(1/4))) + [] """ return degrees(eq_tup).nonzero_positions() cpdef constant_coeff(tuple eq_tup): """ Return the constant coefficient of the polynomial represented by given tuple + + EXAMPLES:: + + sage: from sage.combinat.root_system.poly_tup_engine import constant_coeff + sage: from sage.rings.polynomial.polydict import ETuple + sage: poly_tup = ((ETuple([0,3,0]),2), (ETuple([0,1,0]),-1), (ETuple([0,0,0]),-2/3)) + sage: constant_coeff(poly_tup) + -2/3 + sage: R. = PolynomialRing(QQ) + sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: constant_coeff(poly_to_tup(x**5 + x*y*z - 9)) + -9 """ for exp, coeff in eq_tup: if exp.is_constant(): @@ -108,6 +216,15 @@ cpdef constant_coeff(tuple eq_tup): cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map): """ Apply `coeff_map` to coefficients + + EXAMPLES:: + + sage: from sage.combinat.root_system.poly_tup_engine import apply_coeff_map + sage: sq = lambda x : x**2 + sage: R. = PolynomialRing(QQ) + sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup, tup_to_poly + sage: tup_to_poly(apply_coeff_map(poly_to_tup(x + 2*y + 3*z), sq), parent=R) + x + 4*y + 9*z """ cdef list new_tup = list() for exp, coeff in eq_tup: @@ -118,7 +235,23 @@ cpdef bint tup_fixes_sq(tuple eq_tup): """ Determine if given equation fixes the square of a variable. An equation fixes the sq of a variable if it is of the form `a*x^2 + c` for *nonzero* constants `a`, `c` - """ + + EXAMPLES:: + + sage: from sage.combinat.root_system.poly_tup_engine import tup_fixes_sq + sage: R. = PolynomialRing(QQ) + sage: sq_fixer = 3*z**2 - 1/8 + sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: tup_fixes_sq(poly_to_tup(sq_fixer)) + True + sage: tup_fixes_sq(poly_to_tup(y**3 + 2)) + False + sage: tup_fixes_sq(poly_to_tup(x*y + 2)) + False + sage: tup_fixes_sq(poly_to_tup(x**2 + y**2 + 2)) + False + """ + #Make this faster by combining two conditions into one... don't create temp variables return len(eq_tup) == 2 and len(variables(eq_tup)) == 1 and eq_tup[0][0].nonzero_values() == [2] ###################### @@ -130,6 +263,16 @@ cpdef dict subs_squares(dict eq_dict, dict known_sq): Substitutes for known squares in a given polynomial. The parameter known_sq is a dictionary of (int i, NumberFieldElement a) pairs such that x_i^2 - a == 0 Returns a dictionary of (ETuple, coeff) pairs representing polynomial + + EXAMPLES:: + + sage: from sage.combinat.root_system.poly_tup_engine import subs_squares + sage: R. = PolynomialRing(QQ) + sage: poly = x**2 + y**3 + x*z**3 + sage: known_sq = { 0 : 2, 1 : -1, 2 : -1/2 } + sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: subs_squares(dict(poly_to_tup(poly)), known_sq) + {(0, 0, 0): 2, (0, 1, 0): -1, (1, 0, 1): -1/2} """ cdef dict subbed, new_e cdef ETuple exp, lm @@ -151,7 +294,7 @@ cpdef dict subs_squares(dict eq_dict, dict known_sq): subbed[exp] += coeff else: subbed[exp] = coeff - return { exp : a for exp, a in subbed.items() } + return subbed cdef dict remove_gcf(dict eq_dict, ETuple nonz): """ @@ -164,7 +307,10 @@ cdef dict remove_gcf(dict eq_dict, ETuple nonz): common_powers = nonz for exp, c in eq_dict.items(): common_powers = common_powers.emin(exp) - return { exp.esub(common_powers) : c for exp, c in eq_dict.items() } + cdef dict ret = dict() + for exp, c in eq_dict.items(): + ret[exp.esub(common_powers)] = c + return ret cdef tuple to_monic(dict eq_dict): """ @@ -173,14 +319,14 @@ cdef tuple to_monic(dict eq_dict): (default for multivariate polynomial rings) """ if not eq_dict: return tuple() - it = reversed(sorted(eq_dict, key=TermOrder().sortkey_degrevlex)) - lm = next(it) + cdef list ord_monoms = sorted(eq_dict, key=degrevlex_sortkey) + cdef ETuple lm = ord_monoms[-1] lc = eq_dict[lm] if not lc: return tuple() - cdef ETuple exp - cdef list ret = [(lm, QQ().one())] + cdef list ret = [(lm, one)] inv_lc = lc.inverse_of_unit() - for exp in it: + cdef ETuple exp + for exp in reversed(ord_monoms[:-1]): ret.append((exp, inv_lc * eq_dict[exp])) return tuple(ret) @@ -198,15 +344,42 @@ cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq): cpdef dict compute_known_powers(ETuple max_deg, dict val_dict): """ - Pre-compute powers of known values for efficiency - """ + Pre-compute powers of known values for efficiency when preparing to substitute + into a list of polynomials. + + INPUTS: + + max_deg is an ETuple indicating the maximal degree of each variable + val_dict is a dictionary of (var_idx, poly_tup) key-value pairs. poly_tup + is a tuple of (ETuple, coeff) pairs reperesenting a multivariate polynomial + + EXAMPLES:: + + sage: from sage.combinat.root_system.poly_tup_engine import compute_known_powers + sage: R. = PolynomialRing(QQ) + sage: polys = [x**3 + 1, x**2*y + z**3, y**2 - 3*y] + sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: known_val = { 0 : poly_to_tup(R(-1)), 2 : poly_to_tup(y**2) } + sage: from sage.combinat.root_system.poly_tup_engine import get_variables_degrees + sage: max_deg = get_variables_degrees([poly_to_tup(p) for p in polys]) + sage: compute_known_powers(max_deg, known_val) + {0: [(((0, 0, 0), 1),), + (((0, 0, 0), -1),), + (((0, 0, 0), 1),), + (((0, 0, 0), -1),)], + 2: [(((0, 0, 0), 1),), + (((0, 2, 0), 1),), + (((0, 4, 0), 1),), + (((0, 6, 0), 1),)]} + """ + if not max_deg: return dict() assert max(max_deg.nonzero_values(sort=False)) <= 100, "NotImplementedError: Cannot substitute for degree larger than 100" max_deg = max_deg.emin(ETuple({ idx : 100 for idx in val_dict }, len(max_deg))) cdef dict known_powers #Get polynomial unit as tuple to initialize list elements - cdef tuple one = tuple(((ETuple({},len(max_deg)),QQ().one()),)) - cdef int d, var_idx - known_powers = { var_idx : [one]*(d+1) for var_idx, d in max_deg.sparse_iter() } + cdef tuple one_tup = ((max_deg._new(), one),) + cdef int d, power, var_idx + known_powers = { var_idx : [one_tup]*(d+1) for var_idx, d in max_deg.sparse_iter() } for var_idx, d in max_deg.sparse_iter(): for power in range(d): known_powers[var_idx][power+1] = tup_mul(known_powers[var_idx][power],val_dict[var_idx]) @@ -215,23 +388,39 @@ cpdef dict compute_known_powers(ETuple max_deg, dict val_dict): cpdef dict subs(tuple poly_tup, dict known_powers): """ Substitute given variables into a polynomial tuple + + EXAMPLES:: + + sage: from sage.combinat.root_system.poly_tup_engine import subs + sage: R. = PolynomialRing(QQ) + sage: polys = [x**3 + 1, x**2*y + z**3, y**2 - 3*y] + sage: from sage.combinat.root_system.poly_tup_engine import compute_known_powers, get_variables_degrees, poly_to_tup + sage: known_val = { 0 : poly_to_tup(R(-1)), 2 : poly_to_tup(y**2) } + sage: max_deg = get_variables_degrees([poly_to_tup(p) for p in polys]) + sage: poly_tup = poly_to_tup(polys[0]) + sage: subs(poly_tup, compute_known_powers(max_deg, known_val)) + {(0, 0, 0): 0} + sage: poly_tup = poly_to_tup(polys[1]) + sage: subs(poly_tup, compute_known_powers(max_deg, known_val)) + {(0, 1, 0): 1, (0, 6, 0): 1} """ cdef dict subbed = {} - cdef ETuple exp, m + cdef ETuple exp, m, shifted_exp cdef int var_idx, power cdef tuple temp for exp, coeff in poly_tup: #Get polynomial unit as tuple - temp = tuple(((ETuple({},len(exp)),QQ().one()),)) + temp = ((exp._new(), one),) for var_idx, power in exp.sparse_iter(): if var_idx in known_powers: exp = exp.eadd_p(-power,var_idx) temp = tup_mul(temp,known_powers[var_idx][power]) for m, c in temp: - if exp.eadd(m) in subbed: - subbed[exp.eadd(m)] += coeff*c + shifted_exp = exp.eadd(m) + if shifted_exp in subbed: + subbed[shifted_exp] += coeff*c else: - subbed[exp.eadd(m)] = coeff*c + subbed[shifted_exp] = coeff*c return subbed cdef tuple tup_mul(tuple p1, tuple p2): @@ -239,13 +428,14 @@ cdef tuple tup_mul(tuple p1, tuple p2): Multiplication of two tuples... may have to make this faster """ cdef dict prod = dict() - cdef ETuple xi, yj + cdef ETuple xi, yj, shifted_exp for xi, ai in p1: for yj, bj in p2: - if xi.eadd(yj) in prod: - prod[xi.eadd(yj)] += ai*bj + shifted_exp = xi.eadd(yj) + if shifted_exp in prod: + prod[shifted_exp] += ai*bj else: - prod[xi.eadd(yj)] = ai*bj + prod[shifted_exp] = ai*bj return tuple(prod.items()) ############### @@ -257,6 +447,35 @@ cdef tuple tup_mul(tuple p1, tuple p2): cpdef int poly_tup_cmp(tuple tleft, tuple tright): """ Determine which polynomial is larger with respect to the degrevlex ordering + + EXAMPLES:: + + sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_cmp + sage: R. = PolynomialRing(QQ) + sage: p1 = x*y*z - x**2 + 3/2 + sage: p2 = x*y*z - x * y +1/2 + sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: (p1 < p2) == (poly_tup_cmp(poly_to_tup(p1), poly_to_tup(p2)) < 0) + True + sage: R. = PolynomialRing(CyclotomicField(20)) + sage: zeta20 = R.base_ring().gen() + sage: p1 = zeta20**2 * x*z**2 - 2*zeta20 + sage: p2 = y**3 + 1/4 + sage: (p1 < p2) == (poly_tup_cmp(poly_to_tup(p1), poly_to_tup(p2)) < 0) + True + + TESTS: + + sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_cmp, poly_to_tup + sage: R. = PolynomialRing(CyclotomicField(20)) + sage: p1 = R.random_element() + sage: p2 = R.random_element() + sage: (p1 < p2) == (poly_tup_cmp(poly_to_tup(p1), poly_to_tup(p2)) < 0) + True + sage: (p1 > p2) == (poly_tup_cmp(poly_to_tup(p1), poly_to_tup(p2)) > 0) + True + sage: poly_tup_cmp(poly_to_tup(p1), poly_to_tup(p1)) == 0 + True """ cdef int i, ret, sf, sg, val cdef ETuple f, g From 7d96da941752178061812e2da0b8d5c45a509b35 Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Fri, 12 Mar 2021 16:56:14 -0800 Subject: [PATCH 022/632] doctests added, and braid group representations --- .../reference/combinat/media/fusiontree.png | Bin 0 -> 37425 bytes .../reference/combinat/media/fusiontree.tex | 29 +++ src/doc/en/reference/references/index.rst | 2 + src/sage/combinat/root_system/f_matrix.py | 15 +- .../fast_parallel_fusion_ring_braid_repn.pyx | 12 +- src/sage/combinat/root_system/fusion_ring.py | 213 +++++++----------- .../combinat/root_system/poly_tup_engine.pyx | 35 ++- 7 files changed, 160 insertions(+), 146 deletions(-) create mode 100644 src/doc/en/reference/combinat/media/fusiontree.png create mode 100644 src/doc/en/reference/combinat/media/fusiontree.tex diff --git a/src/doc/en/reference/combinat/media/fusiontree.png b/src/doc/en/reference/combinat/media/fusiontree.png new file mode 100644 index 0000000000000000000000000000000000000000..1fde2a94c81f02f7bd8d9752d5b46c1469436d48 GIT binary patch literal 37425 zcmeGFc|6r!_dkxmfd$L?4vpR?$wVUbDUq? z&9Yn8dD*m{EsUqP_adKzz1m~@EWf{X)4Oor-@3j(n3D^gj=UJB;GVYrOjOs=M_2US zzDj*&m*CxljmAEVi#pxMKj+odPX4#|RU5T?2OS6;w{bL&*DG&K=9N9;ce|Oc53V)y z+<8cNsNvYwcc)c)nR~6QZrJbr)OClO#Um@f6BDKzX-u{)cU(GVT=AOX#B=4*ql8nJ zPTc7Ea`?EHX+wjY3@=|c@;?5hOQh}nUPE@<*v#nH_x0|l=c1ekS~qvqYfq|-l_o@- zSG72xwD{z-Ev+Aee%@Sho>SRvl{xbouF@aLU* zGgc;S5CzY0yfCM*((K3gs&7?AV=uM^G65=H=aoMB6hSP?&X%)hGXC`6yA!n|@ulmg zIdtV;Vko_L*xvtMg|?Rn19VuJV;934BT(ojIzpCQ8yXXv%6-td)qK^ z=+09HyrG|YgKQ2=U!8bK{(JPvw~v z8EdI32mb+j7$(D3!>=pbVzS+GJFzXB9r`M4L!cC6B>P=oTZyc(*zx~$`hP6=|4j>$ z<`x7cX*&3ie!u1I`tlFke^}>D{cXSe=9cwu0)D@Leq#Ig!w1aU8m?@ucK%@|n%IN; z=U{h3|6%LzqWHk3=kaISo73AZ*S)$lpvmpp+YLACsx}5q4yZrxV7*Uct3Ubk?1-OD}Nin~mwM&+>xWYOidmdUK`r$>g&?0)A{S?-A56 z!|&(a&d)an)*kiwa@g%mtV7_$fYwTf1!s?W&GV~$JpSy7l9&}6<~ILG4|?NY`}p~^ z&I6)#w|^TQ-;MFs<^BcUnD*)AWe4*cAOBKOKDzNvBQL>Z&M-BNk;_dcd_Cr^eEIgT zH~I0~iuVjvzWF_^62EPziA|qEE3ou=`HHQCHf%~`1j*m_W z8*-LjUUhu_#Lv-n%|-2j70K(b6&k$XTC%@9L$SxPS~oDM{K}6z&1Yg)`3-yT^W3_8 zsCnbuv`?WrrcGlz>uz;S`+8#gg@Fo>wlVU5js5mM4w?Gn%xTjST~ z2WRUZ)(vW@{wL=ey85)%zdEbfeS%}@*NNq)qn`bGyD|QRU&TLl**Y5^HMAMFkNHhK`{3t!)0KF|L-dty@M?;aCNd&X;V zTnGCD6N}sC$GC(~-C}WceS7Qg1-?ZAzRRz;KYg9&`)Z}HT0qK&-*+iSdG&KL$J-(;a(dSJk$#?q+gN5-vN z?f+whL*s*)c>!OK`*sg_w{hmb6n(ih(L8ogjM1`{o0l{_bZ8q>QVreAbZK{;^!&Zo z!;xck-@3-Ge0J&P_loBo8m<^u`=#ZA#6bcz^7)?nwR`J_^0V<~PrcI{zZ@+mrdQu6 z3jJ4|9&PguG%Fls7ufu^=uF=cOI2GxJWH?~>i6Bey)m!+(6Uw44lkK6Q@DREuQ9ZL zdvlRqQ-fLax2P>|)|}mQV*Bf1i{`b}nEY1=fp(+pPHZjSbMoT#=tYx$y>=NI*ygmc z7|Z0~mUo`B<2%-gZpN|kU%y7zM}AJLymYjFd(@phl`k(^`aK@=rlM0 zRY{rM&8BgTm?E0E@k!o@xB3syEjhos^~33n#X1{r9c-&RYyNm^3%b_+pP`39pRx9% zyHEHi+H;~gJgCJ52CphC=lHQs9YmAJ&w0ce<$<^PIltemy9^sxEN5`w;7%R7lyOXN z&_;t7;b=U1ZdMzJ2Kc7Fj%A_*yU0`!ndGw$2=PodBy|O?{#zf*E z103-`B0sPE;rA*($M?gwuX|4XhOifqIaoc#tZ3M?)UPLw1~xu)ICJ)d--Ds59ooB% z@&0fu&o@`{*z~Pxfd8fSFAcxnC^K7hV+&ez5-fdM-YrPLdbH;l7dy@e^`P0mt zWGo|ok#hRqv5fhh-rlo!*^)JTi7CG%2@NC1)D$~lm_nR`+h9yKFm{8m$-dLuHq*x?Y zvxMmCNY`r@3u^Ps9U3R*P5gSS?$Ox1M_XI+XY@E;`}I*u|bMQ@>eZmp>+Hr^h0(LQy|NIQw} zWz~THnR4sVS{3iW!Rf!QUVgmw8(fu6^R`R3Y93tiD3n~;^y%KouU3{ZMu+R&My0pZ zfDW=F%WrHtxj^%ORDW2)qgbQPdj6XxHI#gcr8G|w|rZTx96ITsz$%~pEl z>X(O&eG*sgrZH;OlsY#Dzw(5CEZ(g-d;H6vN}MBP%|!IKWi89>J!kp-*j_Zbz8>c2 z+2z3c^9KWe-BTH572o{s>$9}YmCs=JW1mbY39CyO6|ko4=(tBc0)GaRxO_?v&OQ?! zSa-^7$^R^ibQc@Zy34n>Bo(ecsp3$mX zTPs2wB*NT}l844RVC;zCyN>*=^)HrnCiVVU@aKX79?4l6LCHq2Bu_{IlcJ zzi0i~-q=!`_kS%E}3?$CIvhq0p;fONN}ujcKC+gg7Fon3$Zy=hfSo>}?M z-t*_o)(i0Q`+c)$>ND@DKQk29G}e4BKeHQic%!o{IseE;LfkR8N=JO@XR({T6BsExrr%xT2G<<4}bnq%wCNAsE)pgIi4m}nT z2+?mFExK?4_9g%u;rV$}YQr=YdEy+speX~eA1QZeZ;I7C7#wiMeNwZ&}{-BdEPnNkKI5PVMm@r=eOVBUfBAy4_T9epOGN zO7LAC{djTt)#3F2YRZbEx(Rqh>)H4~zvWlgE}7K)E#)?EK#H(D|KX_gV|SY0|2Px< zp=p1$>%*9B3q?lu-jBzXKNxY@YFL5~c3%mg?bWJ2Ox%}3w|Hyww(9Ud=WT4wZn%D@ ze&ncb5h0ewsltkT-?6Y4U)i7fC z_y1B-d6p2n>d3V>YtKhNo4if8kS`wb{A)ga#~yC=-A+C4zdf9EUDHTK)7T0&IYh}a z$H;EdHDPX~PkBP@{D6@dbxo#~?Yv>fH{I)csgiN*e)F5evguQgpb7O?J+$%@)W$ju zyIsHUSbS5#*;{;Onnzq$%V7!LPbLl>;8%8XRosEx5ZCFZs%1y4w3x+%b(BjTZ4JJ` ztsYCerYQEv9OLx6C@AO+_La9M4Dim4>m`#o9oXsTe40 z@JtUUjj@#oElgG2qd-xCrK}qSWOP|8TU{$ln?VouZ8y>e2UcnQ{p6e zF1*Kx`^g*5QC&yu)3-bWIt)ONAVuB)K}$72P;RKgW+G?{2(p$DbYThz8mgmQKm@%3 zLDXOgg7=(MVU6v&`}Zzf~KIc1RdzRS*%gWK{dr}{ocyVwg_3H=TR$L)fn0L zf%Kg^puv9kateu5Q~WZRzN29w|K8u&uA9D&@}t?9Sj`YwgC=HcB>O&#z8l~>8tnJd zd)>eYtDzXj6?j7r$|R->dZN@65!_Q0jRBp{!CBC$5v6bW3DZ#{M%&37?00)x@IeH& zMb9-=oii7sPdc1*!(Q?SxJyhpEX-tNJabMFc7+-?5$_*>lvV@Tlsvqg?n*5MvAMP=m^$sYxdy)Pie>UFJZe zDjz1+GUk82H_H2umwlH||8(>(lD)f&s-h#l)TOwCSG zhjmy=qKexmYk>EB(;=EKy(LZ&BooIzXbhE!Htq=s0=_^(ybuVe0mLp^m{*iq@GU72 zmw+ME0IRe4UQVZ@SFg^D8Rc*y#&~5#$PNvnZ3#ei<)EgUS!CAkDFE3pnQWcGx2;hjAb*^3bEIk0YtTg$N-2Y%MmR$w$lRZju0Xf zAfg5qs8&gm(8&;E#{fzG5LtsoGQ&7*A_i)uflM*bSouKq#&&U09$Ezpz-#wlS%Y|e z4g-M@n^8oOwX8w5FmbwZ*gAom>Ps0MifbR$QndZg=6kLTVbZ-6lj}ZzPy2z zy0CL9e>eteWCwztJ(aRjvRG*^vDxFH^@ttlpsc|TB+Q+z4?@O+BA%P9!A>p8UaL0- zI!sgRg*k4L>Gi_#u|`&y+G*a*~RVl(DJYXrG~&n4MK2)r)e=# zm?@yo;O#A$(c>Q}jtp5Y4c$TVhlz-Qvd=dNGq97`s1)*>=2P#(9$Eo%zF9}AoLVYz zsym48J;8)luy^0Zqae-I&WnqwMfC`UfR*v>t~Lra~A4H%+h@rN9(zNN-; zP*F~DGK*3>#gU_*%3lt0_TW1s%ruOZQR4$@E@DbVB_nZ)CMN+Qd4ZVo=(QjG&6q4} z&`K#47>CW5hA!*`{)*r}3`1nYl-|=Qiilvw=CU0~YovSF?}L5mdrFiV+kjdg z?48P=1ZoZtHD8A6SX`3LYVdetyE0g}iKI<&@v;VSp}(CZe^?>ki+n48lB_`jpBSk! zSVaQ5z%@sIG^WcZ@HsNXNE&)jkqE8MWg9F(hxJwLKqzgZm=}=oIGHVx9=6xAJE(R_ zg}z6DZ6nYiLMO)>31^u?3ShIl7^0^ZI$S|ticil4$T6Gy0PCOV!1v?KhanDpYL*&mz9 zS%b!(iS>UFKcX8W2t-3Z&dg81n;SZs9lr&5(IC9L!65oheof>rYp~yE8i)OjAH!&z zHeqHQbd?>yBr@c`-?gxyNTml!geTo4PVBDArh6fuwo$~N-KF=sOfwCOCQY0-L?`>L zToYlkck*u^Ija7bZNAen*PvrRAP$;dplz{sx4TvH=68vcebvsgYWvx6#wO$w9Zh??Rk zwSAT{CNyOOYi?-P5E?z+!`4bhaotIz+o=(e+%mLGp`GIdxW;`G82t@R2xuma$#+1N_FSNVO59#>{hc zpC+5>)?NB4w}cMP-)aK&=FQ>XA9=0b4X&e}QM@D1te$45WFtdhOli@FG zkU$>6Azsc6{mjy{P}U%N;^rEwMtay9(>Pj~tC@V9PxcZ)q}oGLZ>9td5JqarpKPfC zxlSaOiH8>^$#+Z2DN!oHYSPfnGibVVb3-@FcCY-nqm~-4MIN@}SvsR-4Q$(j8|@?+ z%EC-`7bRFJYp`@KFjftXP+PZ)5O0oBTL)pXh+#1JJ3VY^bHpSbl{ILfrx*z519tx= z9wuu*kyR~*YASnUAkuHf8Ur1W4}|jvzjI;IYO*}}P%dgfHI++JggYimLtoHVLUc{m zAP#B8+iQghOfTq?3AtU&`M z&No)ghTf1jVpc|}oks^&1YwpMTEQN+;|PHd5NOLxhND-E5ns75iQPR#sK^>bX%v{< zO_*sxUPQ0~4f%GWAX8suF;B-7@C@TMm~D`gC<@rjSQe;a|wc>N6k^u%iMpUOpsp-h6PK?)Au@abi7!a~~19eR0 zdT(Q%%1;Ec6@<)izsGC&b~bqR4dD)LX{cKJ*BCj9H5KtIzyhoz_XGMW1p-srWQn|T zL$SbSVor|_sswA-kJG`F89{l|F*P|!M?lirf>$1|?c9#jsaEi`=}5=#kWVnK!o~F4 zk#(2m;&$sr!7E~G^xD{!Tjt^(>_fq;04X6LMm`23mFx6%OpSW~LKOHcE^|JlBSoRl zKMM-^G9icu7~6>z0&+vu9`3YDVi+6A9bqP>MymJARw7VriXhYs?;M;1_gxw?E%@)% z^dqy|*o&q`a(0ik)Nr>c@Q5Q$KArWLqoNA>qCD#n{mA5Ns;4-~^{ct`o>*z2gnne| ztWOght%Q8FSfl*EZ~!32HyO<1hA#sTukfbJn0RL|OBShP+F(uPk;CO&EIQP|#WZkn z`Qo+m{v+59v;uwd=({ufo%yhcZ4mioC+xL+#L+VHJuY<66VVy;>{ukDGbvftz@B58 znUS4XQJ>`s<9MSN+kyR#dCf?bvBz=4L^qCYx1b>1KeLi#M5(d{O`yglx6^mdycn&c}5j)_dh@m#taGo*| z^BlaKA`V`XedixG?hwES*g%W42drQ+gt7*yO(O&r=5vshyU0)0U9=--jL`bE zW2r%eM&H#}5hh-gajxJz+aO8WbO)g}Ix<4tE!YOTUi*Wf)X1XZ2;m!MpIl|JH2GJ>w0XB#X*#+Dj}f&nsu z$}HFhOOU;nlTE}q`S+JNbIKQo2Nnl!2}>W7f06CL_VHFP(9SQ(YUgRK8(; zqWt^A3EVh5E6v+v_Bc*9dcy_hpo{V$>$LHI%sCV>)3mZ!NvY6O|VxU8iLz z_;AK8(-c0m=RPZ~`=yaeo#IZYZHFisrgXiZsYhlGpo$~92H!BUW1*13;F&W39~m)2 zvC~Nyc)TAeq{dIl?N*Fdw_-r51J?Rhh6qPFwPuPRfegNt8?8hON}m8>=*e4t8SmZ z${&cgF$g5G+en1cFy$yUMMfl#$0)a!L@N0S2r5nPuIi%nRq8~9%SXPiD;t%{FH<0T z$`IUOjhzD|IvW8#MjSv~f{fGcB!Y0n4H>~rnY~qnJ&|=;DeIyDm|ZN)Hu;BPfEkrb zFe!4F4+fgbQxUAgsRV~KS}a?!RVf=y$P?wv$Owf6%V7svB_HT{ZpgH7cA(qxfzEj6 zc4DbflYao<8(Pp%0U&y|NY>Z+sO(MS31(k9MPk!NTs+zL`{jDfpOvXTKrkW zu53Az`FgQN8Hipo3?I8eT4th!NRS&#$TQx_kz43%DnCN_f?>Sn&Qj};rG_Jd3;>gR z=!^+Qb|O2PL`Jq8ESta(YhoBvxsdtBz;=ts#+eiWSH8F###u$<)E$?}MzF-oN!F!o zvYeoE_7Y|=iCr8nAKAbuw^JA?i-^J%#OJtZ%xnztxk-MeaYQ&sluQKWcb4;PbCOUY zn>0!wKlm$b1{eR&7qD?L+lP+*Pz{C65~0FyLeG7V7I0l3J15p%e#fy;m$nc@#{^@@n=C?(>yf?@RK>f#=$uR^vPn;*G=@=a3tVpHN! zw0vUXHJI2NLT|$%GX=7%EP4ti5Yg*_Y%K!9jNrQ**&85p4vEW2xk85i$xL zkWa}s1+vkEtdv8BP^&1LkVPO|z|PDV$o8}1pg;j3TM1;^*Q>6*J@e4`rbop(8uAT^ z{B51p>+yH0)l@XZeKF2U(yDwVZX7!abCDEQyq+5;N=0!w!nus#t(@qzFCh9j5xri1 zrjg=on2#U_!yJ(-RfGzN-b_TB$gK-c6Ud&h$o`NcI|ibUv&c5ck)?v@Ni4GWa%5gG zCS}J6*=7#eK_Ht*$PDB`w1^Qv=1s_~=E+PMZyAtz5HcJ2>0a;zOM(5um!IZD@1QY- zxUOIv?K$*V;lkOZ!VBd`h3Je(CFub|Un^H?Lj-Xn;4cvdk_*VDTda}b8Z991=q?@- zBmWX7-$RQ{yqsix{`~?Hzp+A$#43YA3J?*IeSy~^$=eK1<@2`Fyf|teKoeDgTNEf) z7ej=HWMA4i`i|Rc{ogN^8sZ5w3?0&G;GU3|O@A7vLC)sCBUBXk665J^gUqp@56E*S z^6ry!Bp`&wY>WtKD+dH=?ZrhA&i!&A695&nKpZ9r%Vr7z(HWa|Ff(C*`YZsC#r|@x zFH99G+#@*{%FUJl@d|+!5&T&`sogkGb(ujxQ{+HT35ayZP<~z&=s}x4k;qGNt|NaY z9#wizw29E4pX=z2 zx#L_+f?z;k4`Qi_d|IOG(h!B)B&+->vW3JTrl3?dl5lZH+@IKRig9PiFctEVGqS-A zT-+e~Cl@iJ19h)sry&pfE5_wwps6%au3TLFWAf3Smzol^it)Odh&d=Ia8R9SbIxYsOX>|3{=TA4h_RWDvt}+Huq+FnoB5 z4~wy9=Jx>Abf;Jbn$E$^@Nucbh%d`2Oe9n@0VB}k_|Xrj5NoFxwoGIhLZAx6g(Xv_ zf`Q@Oz$>VSNTBgH0)0c);mm{+o z1!Q{(YBxs^PL`ss)Q7WBHs;(Hb-AA9s{)Gc^Y0gk45tQMc*sqrz}v`N8H02d=D^^j}cW3Sv!He+cTedIhe0T0Eii633%1i52kg2zN+FAjZoAX6h`UL3OR zK&DN|N;za(fXsxDNjYRyK$g!3GM=U!*%d<2c3n*u7pfSj!YU1WDg0MsT>)df0+S< z>*^YyyQ$@{dTf_H%ioQg7xtI>A@_AwVV?GjUJYNk z*WLc_uKOoitz9=Lduh0z)rtelC$4ujxjKAL`5w!6R~L4_m^kdvo0+PM6jByj7Z^;R zR&awE+Zw#d^@eX~YIAejhrF-p%|}yxC;WJ{Fz%~a(RFJgbTxEq4otP++H0YLJ1e<` zn9Vbo-H557M$CHJ>6j1`-r%zY0YNbI0GW;hK+hGdmeTNnDgt&(qzc8nLY2AI8c3SqIi0$Xojv$5i^k>NUq`HGyeEM?e;DgA)W zuseq>9@uCvBNz?UI88eoH7_S-B5{CX&n%lvh)7%kF>~nZig@yj=JqGBMIe;Ml6`uX zu`2H@xWu!TiWAqe9HW$Y zB%=sAhS5NV-JvBjVU`4@5C$x{+w#ATBK3W9ZVC{dSGzKAvvqL*@Hj5<`L8*+A-BNW!A$zE`gcCxX-Wb^yWC+iph8kJ~j zkL62(dFcjZ$mw9jo^oUvp{f{(WTeeew0MxFvdtzMNlQMGxEJ()CW)

!f-=LP@ln z^l!PGlcKv2Ku?mJ%?LRI_*#IgBW;^OdW}WNm}UbGw^NKGc~@d(h7I8Iak0jzlAk68 z)anMf#dH={WTkJV#ms=32xigX$YfwUCBn$EZb;zDk*N)?XyKNVxkX-?xU%>m&>`nV zA!9k$NyE#DQKggROZmcKt|sMn%7_5RZG>E>rBE+bT&^>-;2T)ND+X#R%Q`LQI_Uyk zBJL`gSunG*PDJGr%+9czoCWz^fNn94&7!|K+3kl~ijDwX#2BuVA<&Ue6D{VLX%VTS zC^nEdF{kOMKvOY^wANrMV8aFTL+1rG@){fH~}A-W~?eoqp>>(Pz9Clq?a`m z{9#AhH&BI9L#~ow6^Cv$p+gcEBjse5Z$)s11jpGtlcNL&g%YgdG;=BT(yV(Ip_z?MJ+%-9T^9gPk;Ffp5(fmddzVp(Z^}mtEdH?qB z4>~FuM!Qifa=-iJZ#_|hG3OXk5<}+;C?POHDbv0avwY1;Q;(L0YW3D>yK@IX{Xlchiq1>N5>|PMX^Mm zoc`Wc{#EN7_8mCT-8@2&$s!;sk*UEPnQP#25via02d30WkxDq1Fjb!oYC2neIM3g} zw8+0U&UF3q^mfhPb#?cnhy7X6ccAzD2?Lj2T)t$|P1Hgaqx@#C-aJU`9ymBd zgLwm6BwBs2>x#

5bMtn>S|_XYDJYTvL0LHY9(JSrKE__HxC>ETo9{UH9_Bp543E z2V2|^YX2RSgWT(-wXa$V&YJbD{qnfdcMFoR?>{;`w$-td8jhTT7@xF1m@AoS9I(l18Y1~Bb)#(bBGw=WWl<9)HmQhm!kxgC_X=(gVUcS_3G4Y#VZUwfoH9v=7s^#K})c#hcqJv2D> zq+i|B*$>Mq zZI$TZ9vvNLU1cMr!rc z42!z@`mA@U-X$n>*_X9XGw1x}JKB@KCC%`!JEgau3MsZgF9)W-{Byz7zUB>gdlVz9 zKMb+n9OIkcdTp<|x{R2ejN-+l#eX}=C?Z?#nX zYvC!2EF}7NyPj3^%yLu~svG;AUwU!xw%RXQRMqnF>#8k z^P_!gLge))BMfA*apT7HT4X`<&Mb)k-nmcWTO{}uixj&Xu1^l@H`d@wUi`*=N}_8z zUwSc%e<6W6%QwCGJ-f`10NvO8YhHUxUI|K)_KmoF%PYT6HccHR2;oTKPOAwD+oiUT z^316urQ!ZivxU;i_s^gArecC&eP6zRKVap`m92}M8Jj&;ubVfZQpXlq-~QiR^mKeO zda1X5c$f6#b+XHO-K3c_TYl~`H_}0)zvq4RK*5eQTCc8NPE21EX-vr8ucHZ(9-um# z>eRdd<4Z`>j+?rD%aqo-=her5_zy*KO?+8Z)&3ma?M7*7X%9YhO?y&T{`}PW^CM26 znsEZE$fz#l0I{y6v9<(74|_NIm*|Z4DwW2KTelR|U;{l-YNAnda~`xOmj@aNf?*q3a&>N?NTrz8 z{4G??d7?x!+q@;y{C?uXQxD88U%x*7$)w5)4X7eqHmUi1deiwErID8N!ngbR6`yx# zT?+*_^~5!1wwrl#q2$#zRN)mP1ApJffXWr>$y?65q6j1z2sCP%h)T7?Nxw(hCh>?p zM?EBSTPl{N?;U{T_l&&T4aNIP&Yizh;rkLxBfaJGC>M--scD_w){i?z2eq|GQuI)Y z5;$i^8j-#kmhojRsz2|eGEy7Wty}ynR}}3wWrIFIcc3eWtCiukWyd z%1kA4TYo&At+#*o?vilxz6NnvuCqagp1Ctg}e@!H=QycK6O>fYHO?Glg1&#O`EEO>VEyoRvZ7W zC)L>&dZWs%Z~}@`OWykyjck4mtE|!-fbEuJqVQf*^T*ESTC_6#QKzd{2kU!ObJU#% z=fXzZKQDRyDgZJZ{vrG`)%?xqR)9=E(Gy zcTt{iR;7S9_>GSxI8;Ow{N`fizUmlz?Kn#9;lxfUiZ7?bkzI`-}m3s z+uLrSinvwW$%cwI?!#!<`%PI}kVszWg({I?XT8Dwc+gRE%zwcsE%=}--h1B zFxA5*$3s7Sw!Hk+c5~i8N!F%?_a{HuafFuJ>`)`YNh-uuSND7{?Ckv?tsj28sMBt* zcW7U-#ni8^Ol#cQ0*iPm!7-sivZCG7E<6m~uV-#H=tEv8v^c=HJB~FwU%0)zV3+LQ zSR<+3+#Y^0OYqyJF@Kj?>0gRVu-E5>Bl3JPYhN6c7r$}dvnl1( z>az0k^4Knw-@jklTC$3HHn{iF^ba3CG{goq#!70jrp-dt&X3&0&u_JDnc5}sbNWIO z^}KX*7~H)g-M%Vx@IzS;;n33QF`BV#Ij39 zlZ#$YEmbaobv;EqbHlcNbKF?B`Gtkho&l4;-#E4JITCaj znrD?T_%r?9FB>WwQM-HS_~rKYr)xn{47P|jK4w@DR4;;}+O5Er61~{tZ=KiRPD139 zyK~$sSkb~l)Mz_AUze(s>Q(KF+ zo5P?}iPI9AlfPm6lnjG+bHYeOry{x^PWm$OOPtGtC#z7Zb#B!u@{=eV-Mex11Xv>H zhM%8|P56d~aW)fzf`rcxW0!r4c$Jb>L$3c$L)NHlY#vKGy}|`F&FILwdpozGatRAATX z74F1&=@{xqiC+6wt78+AiwbG;6V&!KD73DH-430MZGNT@jQAAK6)Ltw8^+~ocRQ+U z-U@AV#+w6SaK=xSE+KPW5SY93YBqKx%?Z}cO-;@ZqK75>)mJFQ*TSQc@90ll{&wa0 ze9!!8oeu>(IdgP_HuQP7imEE&r=n<(9P$gbZ8_fxQ)u@x{Py-OF01oStlV-9jLgCQ zVr|n{JeEKv1!sqb&;mrKc|pQ^%Skr-CwhOmPiKrt+fjL23||(GFVUeRH{Pya`snER zQFoeNT+Yrh`;~kH?>-d&#+jr9t7R`elrdua`vv~Ry}##lVks#gO0KMae5B#a`1F2I zoAv!sILPjFfIy?YTCaU{SuJ*!diyj-8Bf-m>gQt}bO80>!^yJ_wg}rjDyZ?{ozZHz z$Bxh&<8*O6?49k6O-??(zB#msRbQ9lM6Wh@V8fV;x|qVSJ~dn9dDNpGQNM>TnhP>b9Z8c3oC&eUbX| z{VJnw@dpiR`vvhx1x=fXC}3r)oSDVBzN0sOpDG;$&RaE`GQ71~zQx=O2BCV=kS=8sO~uqoOaY zM?l$(I}_D>n##}Sz*epD^T&ySc4-y&7~y?Ph27(<+2>l2xG>rAZl@JS{qRUaP8J;A zvmYKLdV0hIrz(4%I9}f}=DtGb{x)x%`;A>~^(fxFNJE9mMToA?lVcOThj+IfG>DQ@ z7Wc63g%l-(Y$mD9)}3hjkWih3ly7QENR_v zNmsr4d3>9X)FBAR7SEd8p*h$xI9C+>BQdl_h=u}=jo##f@m|7%CwUV}R5d#r(xLbu zf?XlCzCpizJ?|=MMjzJ;Z1^-92WvVgHXXmb>h>fvlvZZLDWM26C#Zi>d)sdp+CoFe z>1Z_bLV(*#l1^RO)8N3WWex%34!{m=#bKfZD{&t^>hhtrPzWbeIH>o_Np$wY24nJx zZC?(*#!{nQ-RRYi;*PAU#F=Wh+lg&wiVF7(J)zZO%1@DsS{>2&6c#w7w&MKd?Ac)L zR;bb7LC!cK?S@azDfs747wAK)+3&~Wi^AwZnd*ZUY7MC-Pq5*1{`NXKVexBvEdUDi zEw-1UcpRN&uty)cKH0~|=RSOaN3zLXY{_)6F4y9Ob;IXcmd1+vnLW(=)B83{E|1&( z>)QlW-8Z+-kenJ!tGOh5zjyMENcj4)ii+VFXv-Gj>;{}4Pvfp7Bn3VttNxXP!y8_| z(atl!-Ku?3O`gomjimbAdVCh9Rf1z{m}g0L79GH2l#SHYgKCBb^`du5%B$%_K?Ukl zP#28Tj^YumJOi(y@$>ZOR$X1zOv?dF_w?1;~I7NzHjj2`>z)T-t@qMvKk?T z`3Y!vT#oIG5Ag{GbY7m%ss7 z9W@f7PR13oJ1?Z(`nU9-Xgq(5qUJyBenO)tA>rmMkszp*s9 zu(|Em#&**)^N%mDc5%r$Q1#n4et&A^rJK&h_xH3u!R9=+A$LJwufekq_$2S>1$Vah zL4=awEjp8;N?*ggwR}NIY;4urXSW1F^@WBje5&=g=?dRfC9C zB#~r16mlQ?oA#I|FkU5eB%@7^wjunPSQ?u8hu~>IO;r4a?Kg$c(9ur!cE<6Y{!%%z z5aFkiobd47zf+u`%<=ma^AYI1c(u(&7(nk7h;)n(QQU3B_KR?m&qPYHm5O-2*amD` z4mQWa{bLeI%)ITtmYIZ3juAxQ-ocZkTIBH z;GU%K-@h-Kt}4o|sI1%vuXowRvAnEoH(pP9DoV)BJ}^K>UH$FG^Z@_1+W7E){ydxg ze1ZAYQU!YVNe^D2+;bHHnOHFAmj;U^LZtw6Qkg^fehXSi15@VffPtrWJ znEgY0ztnX*=4ml=ChfQ=#Bl?a?Jh9nI?uduU?-uH2im>+H3o2Lsw*EI(Bz}3LGxw2 z-~bRi7kI<#zuMWgH=PIeJUul#hjjeZ>~P(>TB=HKKpnU)-UwPz?m_+qd)mR>%^Dmv z`>=zoqlh#m_w7q|$%$FyM81|Z*I}Nj=nVy;l5=!dym2NL*rlv4bY623yO?Z9azJ}y ze0#s68`9R0PsM8vy_>79JNRt5q^6B9nojjQ@4PI$jnRjE;w>a8s;iIoZ}`b7yG~Jq zg~!zwON|X|&R`dYP|Ca&M#qmIuf{p)t+RqS0vlt)3y2HwvcvTp{H6r9G|-O0UQ0A- zB_e@O-@jk!_YRRCIMGc`%fG>69Y}9^n2vq!Y6=Lz_MN|YZSV0dZ-(Iom;GBCKgTu) zwBZCw8W5J=urK}R8AV5%SN7i4_C8N%CFIa*B{yNL%7kF=CqP|@f=P^(?XO!cH4m%l z-7W;{dXuBXqXAh6*J)6GCcSVn8u7b^cVEpkcj@}M({mukTI?3G440Im>9}oj?=O#D z3sUFDGNM5`6TO6_feAU5vBvwMUU?x3iFXl2u+fG)v^qQ{OQggbNa5x+FgBi_LNv9L z7@Mt#obaI772MrM;-d&929aR)1WWg1q8%`g0$d|>-1qSLxROaiCBLWw2Nb$FW0bMA zW-4(Q)|w=zDkB?U8GaZ#l=6uv7N6t^Wa3VMyNlxh3m1wjAZ9pxuomTy(|wO)St&}& zsnd>*PEQO}7*(RlTt2v9Gvw&l6Gq|(cPX?Or5TJe0{uVDxK8yR5+5+y|_s{j?0zAztG79M6)btf)^C@6=sSC$**&n0z!8Vd%!+> zwO8{55qDxX0Cf>y_UkDvs1uA)hx@*i$&lNeA* z#k+Gl#;zu&bjR4-eGjuA*LRFIP7G2?Z=~B6fj3d9h>^B+phJ=gQ&dI`B;;xIuuk?0_SD%Yn}wRkE`AZ6|yxz*i-|p2hVl!67GCe4K-; zB7Dn%&z-6cS-xy1d@8_qu>;PW;06NjPzN00dqemrUx0-(C44%-_qgEID^0qQd30jx z_9}-aWKu_hbfysZfXsR*fyr}joxx)1Br!v9ZN*43p|)3Wc*>{RS5}WaMnl%7$P3Ws z?wKN{df{>v?oKe6Q+G)Y;Dks$rqX9QB{TzNP(tNAbivOEV{zljcIv@(+5#sfQpFlP z$E{_lDXw*-Juyvfj)HGUgphSYoPbq#sTo#t#5BC&kj<^;n!8w)W57c#?19-;$ulp- zvjV|b0SMd3mTnDAPZ;N+l{Ao};0`e?6pTigR7Peg5`|@*I#y*H>3e@H3Xb!QFpSJZ zaFHCQj3nd`F;a-tutOGsG`ao<)q;z;RZ>bfbfu_%?t?6N8IkQoB$BPgwjE=WRJ|~h zXPgO`6HH)8A}9O9nx%;Dgbm8TW=@a|;te3Hh}>%4RE}>~Xle&?L#Hk0hAu}Yp{Re9 zTJRN)BhN_ndSN1s9mt}9YzUO9rURKVkj;T`ODX4tMYfWV4a8#t<{ijL+|z;Hp#vF- zZKpJJ+MNz$G_zi~7cb+e}wb{f*;eAG#qO zf+T1ORf$tan<-w%lR#dRRD+XjH3_X#gj(<~PO^PyYJD(~D_r)5_y(|vQTHsS>~q!t zX1RjMZn(@|&RxRWcp%nb!@~mu+$B6iPy#?*<)Bzxk!rzTI9xr+j)q{ak2%eDBtv5e z1LbZkCy}XS!7#II4lKzG3^Bw!+>N+SI=K54A@x5vo4fE&8VL2rG)zQtd5Gr0x4>kw zPoR*1L_2U!WAFe0mz2UN;>gKFLmKrQ$*SbX456MK9`UsnC=_Gr=dht-wc61I{)llE zW;&L1fQ5jGHVF1Ab3$}XECt3fcoSd(NAnC~q88*D%}vZk4HI(|fQgi-$kMnLZj4a` z)7rSP7eB=MOhU>F<=en!Ghz*}9+9}vg+6j^VAdkBmGQt%ufs!%mqHsw15pc2>3ggY z3okmJ3fe;?tU@{1y-hE1)Qq8BXymj6u#3P1e}67U@8oBEV^-2n$Wto8DGe+ z;kFxs6p|aNGzb6a=!8rqM(iZaln&<5JtlM}Nb%an$w^#DvkN__Hh5zP9FmS0@h8A3 za&Q+N2@YA_cAV(MOISGItK{IW>?OE0fa{pvMHU}&O6@xEy&$+Vz_+dg?g+t^1FjQ? zuZqQo6!t34N{UyqaFD|L4mfsxP%-z;9dLy23g&mQ1MWS+#h}dmMF$)^zt4c9q-g84n>;CLfgIN&Sf;DjMW=X$^?cfcWH!d$`p_IJRoBe-k8_p}3UKfzT2j`Hed z_*gnIzj|(EMl69D61;`Ja(PI**2U`Nfoh#c;3Ry*IFC}+sK;>kvOlzNrpFyg~J;P`kV^aAlHeJ8WrLx82Pg1 zZKUZvid1u%(a|Xziy>m0#Azm>gFZ6S!Gx}sEo5i?cf>N9+?U)?&JFV}6Ry;OI!n2< zKBk7Y;!?H3qz<1K;Y&~(q@jH&d!1eQw^Cq>N)LG0a%p{RD4&U#EO9F5R#`+hEFhk* zFtfu8^7_)cdl#u@N%`ySK+&{ZQ}uOXbUB9n0}sb|S(+MgUXWQt9!@&7Fv*1D@1qvoD&|llsU_uO$-N-%pk?8jup-t=hEEJtN-FhV20@fvbnTA#&8V4 z#C&09htCr{CF2}2Q0Jvz2a$B7v6&!oy2#0&At;eB^9biDnRP7GV4au#9F#3oO866v(QTB0a z#MJefRpfLlJo-SIq=k}wSB`G2Nno5v0goAQ+Ai3P1<5F*mYwqi%nNeZN$L`(bsdxQgHA9y z1E~s@^^T%9q!J!G*=kTa!7*z|BOG_{Wov%fMJuQxibE53v!$V|*(yLOA$5wQdRtki z09we4|1tbeC!En^S$*VpQ7DG44jh?alM_*_lvUr6`+(oA@X4?l^IqAmg189AESw0wxlbCUaK( zXCz(Xq;jqcTdK^Wdrau^dE40XZy8OgES&8swyIS|0d2EH$`5D!3wM!BX};sO|KJ

PXp=@Fd9n%Vz>V{s3v`VZV~7S8tkf8fknxOKDsg)8LIT%9le3&+!8 z;pV3N3nxXgF_QAvSo{YLskKbL!Q=nHtzhBoPW=atm5J%Png4+sLU611{|64qGk9-k z=EZ*nE=0O9Bd{ol{8ufn(Lxo@9QmJ4%))6K{;P5=NGV~YiSr7R+~NaFaqHv()tiYin9_ZudKOC_gkB8Ni|K;Tw6DWw zc8s@toon>;#d2B!HFWS5WIv7@J$TD&r(J<=0|pH6d^qwtLY_nZMl{c0!3lfqBnsRk z`X|f)p`Nse`(1Z3HFB=fc&I+R|EFSR2(JGbUOZ9l<(%p98ZkWGC{ak+qFyyb$T4)OIE!h=`)R`iLO<^&0X}l#4HFq4he4W!^Ew04T|w z5&4kYEu~;9IX;H6>lrq^z6Q>U;f;jpX7lEGdc>k<$n0h!yd}8NfdxiVJ{d_$y_e#A z&4^6MJW@_Qo72vmM#eM4rq35*OT$ENJc?E@otLux~5@@y(Q^Nf|f76_$7 z9%HCXz3-#<6?i*FFdFA8W)2Zbu8!%mi!|(S%$och!{*Mj$@BT-70g-?0140qc1fe!~F$%a4;_!f5Vwgk36y6|kL74(V83IzS-9$JA z>KLssbZ68Nl|&99TEXOp1bE~J&K;vAjV7*8QamY6Mm~8U%6`XDlwoF&`=LrI5fxi8 zj)^pMB0?F4I7`5HkUmltynrgN7#lTm1C+~d*p2-)vd@_eF@-Di2=@bUXmrLC@gO(avx*`j5 zUt(kGPKuVV%63QEJRQE6w)opO!|NrN+9$M`%?@?@ST+C2&68zo?=AMQUb9Cu=FTv@U5v?VYAieGe2ZblqN{L~8k(|C5p)dTrn!vFXF)%A@ zRva+cVr^2wM5XbhIoJ+}7qLJ09Sra#7;Z5;+*NWY0z5sbaER1UFfx^tbP06)GwHP8 zVm|^O4)Fd2F71>`>PMP}Lna=y%fQ-Y)*?JVhgGGeTGSm$_`DDz&W_xiz?yW0h3>@? zgIs&ZB=%=*B4!-sqRPI7WXBR?4dlVJw2`(<#Ngpc1w~>F?>wJkFa$Gqv7=9-(GTMmo=##+L=Tda5Q^H99o*~&J9uvBG*42@2u%yjWDF8d z_98sXh^El_hZlv=R2(~4Zz5f4g0LhRDW)bHOH6PE&~)@-B4)FLBf8NC<-K@pf|XJ# z!h!WD-NI87V~8{r#F66J(Z&)Ry%6QEz!41HzmQNhBRT#vsOwred?8ltiVE7~W8#wPFw39mJKgZ&%f`v|pm` zM7vEm+a2K>sciIv)V)9nKO3{)he(0bFc+$ff;~Vu5=&GXI*n?l7@Is+fiL0bE$nbV zNJ<5;7gEFp7-p}4%t;#NR>*3YI?MN!h*rH}_1%!|4q_A4*zPmg?uhqYWyN$*);$W* zHMYAu+a1IvvU1ovhwY9`tQTx|byj5tf|$=NF^fs6-N5MKEMo_;wD*Km$B-Nh$B& z54nJFQ7>Vp;c)7%HHEqx;)Ooe)rjtT!`v}j1g2qh--U8WGItr>@g`*-ni$`QbTLU$ z;?##-;;P4Kbbc3!Q^8moZVo&8G{j`6ric+tqS3Yfz_W;lh)t*2(L*u1AK4cslkGl` zx)aAUe$qyjuH+VuV&~RC%H=4<-x#>aW~U=net&)Cs~lu7X+J-^fNsjWH?Bl2#%Q-E z$0i^T&ly>zsS_0u3PjZr&Wwnn!4=-dm1uRaUJEY}iPAYJ2x$svFO6Dops4k`@6&hh zrj+^8UxGQHq@rF}zN~<_$e|#6hiewa-&n2|FCtpR*TpZE^ZXsbcxTz7Z zM(b%qABgA-=bP)o!}U zos;fMDf*NkU~O&PdgnTFQz++}vdu@kK8UtWcAjNo`lUAVDhOc}_%mLCf+$xan?t{I z3?*s&Dhg^>$T^6l#MzdXmLBhJc;5VqvB+S-iB$}D*CcpKu&pMzr92av|lN{KFb z`BDRig1y<<*~BB(^H`r(L}|?#q+om5z81NR>t7LmBf(D$>)m-!zTN8?;~E zq{M-^DcihSkes#`$#X`?PI9>Z6|w9XQh$Lfxm&|EpC24b#$Ui`z%%z+R0dG9y$uiP zW+UaQ1b@I_&pzWcL)3{aeno&wS-{WZCc$FNYs8-e&_hN+|CbpOi=E;=WN0g`^rZxI+etWBHW}-eRW@2|zOjou@4k@A~_)}ToDNiPipufJLg{*{= zPq5b6osFo1T&nm2Wgt&LrfD&C#owkeN1pDY8$0yz05IzjL=~RwhHK5bb^GwT@L=R| z6yr~v?2E@=8G^uV%Els^e1xonIE`g=7XEaT3sNbNz%c+xgDdTkozh)jn3;(99tfoC zY$mjdDro4>0-zYg`P01+<(O~`!yY}wO*C7c9kS}|k)ZEcvVVLw{=AenRV0<*Pa16D z>%$(+Wlez&`IAV>=0xg`3kr>>q$Qh5C6Hv|%U5g=nqEjvod_d@fO8US0jp?d&lesw zUB8#6Ls`j`i;uFFpwUU+?hj3ILa7m624>A^!2vR-s(92-rPmw{Sx+pF5042x4-6jDVFb?@tO}{9ug&j+fq<*B{ZUFmI z#UL(ys006ga)N*qu&H@4v-{EEW{F1lh^xZpBRCz~i#-1-eu99@J9iy#aDgJ8IE!7B z4cTPiZ!vlK;|O{gsS~OY64TnPy>D{L1R1jeLiHgf?1P|-g|13W*Km&_23CJpX*sD% zhz~O0k@#0bqSC;?K&j^(+W#B~eeqe^F*IVZB%~w+qJ3MfgHgOat$30;6TY7HISwoL zKP~FQp+(|ofu6$VJJZfw$a5+osp)`%J~%#Y6K}}WjZsW;E2+tQLi_?+E7+T3?R01i@y71b_VEiHW`L2A0@&|c)x zct0&#A}}9n=t9;K^~U-JJ%$?4R_DX8$9P^Udd|61%$sYXW@lfdS(u{}p5|wJYva)f zZw;Xh0rOIS9AfO#%rbQW0^#$2Ml#TcmIbiPSCSi-OERbH{eCCJa{}NCMg##vo)(hq zBoR|%&eqmoK<&H0BvP&~(jZN#XJ7-`X($f1ApdbSHE7yi;ZV3$mZy%Kt$?m6 zR5Dg)7rHm;qW3!qv(*gg8|G|mXjnm@En-(XMSx_Dm6hNcu*RJLfp-UKp$$^!p)y>e z4BH9mg)7zuY*6jrps zf1GggIk(O%U{KjOd@Q_>6LCagr`Dfy&05@ zhRwUsA);0?3DmplW&WFM`C-cd8?E*9^!&a{A!&L$#b-IKCStBX4P)yo;aO76FCEwv ziqzqFYTbgns;FFiolt7f07jE0O_nWN=8Gh_N<4=8JE%(i$cn%;%52cl)XtkgK-8bf z7k;e@Y^(6$;f2}~;C_zziOd7-q~CdZ@_~6U0%A@>p<2tp*timjy%5md-LVkRrlAFP(rvWfqZe}tssCN@@L~ns3Q%t zD+$RvYRv5DhyibNtGfU9w$#=jE#J|}X{@YF61!9qJ*Hg=FDy-DynQk8v;eBFmGDrH$qeZZygbVB(*$LXb^%FS;JCS9rH#7&+2i za%AKcjQk6u++aq2h>;8KMLUq!keijr$dM3{%qCmm4o0Wkkju=BS-ku@7GyEkSnwO$ zVF{Z%azJuR*=P;KRg?hooXKgr7_AN?dc($MLVH${cS9dNC#OowvKSS){b^5b$FPYY zMpj3w!fDDz{=&$cvJkk)M7Ao2ac`cddG;}%qK;`@?R@|PWPm~dyt4c{xDR?q~)UZy z?(ff+P$&R;^;x?5lJsn4lSyx8bB|uuBmX>;Hve|Po;5OZ`&c5^_uf$}8!cHvld&_} zAs;=NjsBa?X8G6a*klUtZIw+X%YaR$@bW?VXGK~XyMSx5YQW3*}$WqqEDYeSJ^1 zk9JFmsX2#jz4FhSXsZNZo%75^@cu_mC3(YueEu0?OVb-6p*hulH~M zA1;E+-xlDhyk5hT7gt+bOTCp##%zK3qtaYd94IrhVl6b#Gj#=YIs|x|b&)`rz)>F| z_Yaluxe6C>!voDPb-qlQ1qZORIuEC1iA+-sBnT*#7wAfx`gL|$>wTzjzxSUZJ+b~= zaCf#Z-20WlTh9P|bAy3V??|G~wo6+MdgA|IFx}Vi3EZuTW&;~j;Z-G6xX-W<9(4e0 zV^QF!noUlo()%GiK^3CzqEyZkX$ayM`UQBnGfcr>m4Q5)$5(bWNw3?J`QX4b!jGOd zHEp&qCZ%`>`I5L*8SwIme~ONdhWos*oc?l;&cFM*?Bi74Ceb#mdz9<4HzEXh&VVWhC_U4)WY9sj7QBE5-l7DMo}w`r;I7=V7mJY;O`$u z8&M8yHzE!PSL4Awn9Y#V{qp6pp`oF0Q6}lVxny~AhSv@}-;`E~(K`Olbn_*$4-`A) zva+|PRSvwgjWAI*>$8f1M(j>W0Z`!4UHHVf*~cZ@F_0Ov0Su`GMHj%L!_A#nF>nyG z$-FMw>*`UIm+L71Gn`7?qfOZ=Ld%iXsCIy^dxyuV;DN*CuPWeCnw?+v&g&Jh-URPn zjbKnae}Ulp)3mXM-Z(2~-SGXK{S=`=7-_KjD1iJ+sqzD#PgST0o=t@`P!MgAV(63O z8#LySQ$5x9?i^((>sfP|-IapKj<~J!Dx8fU`oe3w-pR=cK*QA4D*-E2BOEpb!dO+c z8~)##p@daGP?F?Oq&CbH+<=?+gaSmFZY{I4bf_%W2#Ur$l8#bkfY+~IuLO>}q3GLf zJRCvhl)rQAqp^us#rvBiD9IO}Hr}x)Ohn$#pQk3r+<=2uo`d-<)(lm_`dDX++}2R> z1IqOxTzzSf-%j`lsoYaCY!>agbLZ~vZN5(E;MXl7@DH21kio|+r<^M{Z<7;K?-gCL59+wfem6SGB3~ z+(1Y|LP9^nHx^|B$@xm}!f-MffEj4tXT#p`da^*7xBp>H%w8$tDecY`d)-%`qXYcI zB*l|_KGklRAHyqr_S39BU=*(uAxfv<#raLjy)Fs0-afmHvJ#-B-N zwR6a{IoaP{S&5K@Y3^_tAjk{T0s{g}{IN>fvQ<)g8>ENoXc|K|r`@=Rkd@*zY@Yq! z*t}B^{Gf_G!=dn9mIq6*nvw+Lmc^sO7cV^ilWuEv0l!V`z5^B7?x~a|@~P;Bx)~jC$uITcr_m_5@3q zD9}9*fyj8Wl((ST%6JEXz{w-ev-tOq=rW0wX7FuYLCa0JU=N|L*5+{O+lRJ+ePF!` z<`&BF+_$f1nBk=ntexH?RIwOojTD={E_%qOvr_hRh#ozZHWC;*A)$QC?H+dAZeWxu zl#mGikKqsvc{S_}Fn4)yCp_WpVAdt0LPCz)2zW1B|9wc*N^nU)GItM;pPt$$73}(9 zvYZoGetjh{YT4HNT15u_)`};i*2Pu7v~!AzI1+W(>4Q4Gt)JIPKK*0oiyxHRKIMOz zu>45Ww%C?rt-zgU#|3d>>5~QF`wt0=MHj@PKJUu2E5w&<^Zli#Rya$>$4mEqqno@t zAmFhCQ`onV&gqlhw{Hy`^S%;|=N#XIfN9Aa$Gng0z3Lf-L$|+)`aV!y*>>Rpe^X}N zgxinyv0q(Ip46#M)U@2~;-ZyO;~^3U1P12wz6af89Fs!(zFvL`^%wK^1|%o8Jn0z} zE1q=B(Dyylm?9r?MHC!V9j~6pqWzftrBVW1wSJrBncdNg8U31LM&);BejruL26YmT)%!A+Ni$ceSmf39B z(B9F}almNRs+ohq=8B4ndMK;*vuX?R@&|)2A71)|$BXClzq+iJ?|e(p-i~@G-`#vB zsHmtoH1w)*kIVJ2v4G!2qROG6;@h}~niqdZ+j`X0%q)KPif`^rPKH}sD=91A{q@&% z@dCkJVaTwgQ=|7Ga!FD6H<(FgrA3>C&bB&$EGb;IzI#wyE`mk!Nv`0fwU(Y5uZ=V!y=KyB k!0SB;TZaFVb!nSzuk*`chs>{!(X9Wnev9>$buLH#32e}aJOBUy literal 0 HcmV?d00001 diff --git a/src/doc/en/reference/combinat/media/fusiontree.tex b/src/doc/en/reference/combinat/media/fusiontree.tex new file mode 100644 index 00000000000..34f2447ab47 --- /dev/null +++ b/src/doc/en/reference/combinat/media/fusiontree.tex @@ -0,0 +1,29 @@ +\documentclass[crop,tikz]{standalone}% 'crop' is the default for v1.0, before +% it was 'preview' +%\usetikzlibrary{...}% tikz package already loaded by 'tikz' option +\begin{document} +\begin{tikzpicture}[xscale=0.8,yscale=1.2] + \draw (0,4) -- (4,0); + \draw (2,4) -- (1,3); + \draw (4,4) -- (5,3); + \draw (6,4) -- (3,1); + \draw (4,0) -- (8,4); + \draw (4,0) -- (4,-1); + \path[fill=white] (0,4) circle (.3); + \node at (0,4) {$a$}; + \path[fill=white] (2,4) circle (.3); + \node at (2,4) {$a$}; + \path[fill=white] (4,4) circle (.3); + \node at (4,4) {$a$}; + \path[fill=white] (6,4) circle (.3); + \node at (6,4) {$a$}; + \path[fill=white] (8,4) circle (.3); + \node at (8,4) {$a$}; + \path[fill=white] (1.5,2) circle (.3); + \node at (1.5,2) {$x$}; + \node at (4.5,2) {$y$}; + \node at (3.2,.4) {$z$}; + \path[fill=white] (4,-1) circle (.3); + \node at (4,-1) {$b$}; +\end{tikzpicture} +\end{document} diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index ad49f9c38cb..7753a4e9716 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1725,6 +1725,8 @@ REFERENCES: .. [CW2005] \J. E. Cremona and M. Watkins. Computing isogenies of elliptic curves. preprint, 2005. +.. [CHW2015] Shawn X.; Hong, Seung-Moon; Wang, Zhenghan Universal quantum computation + with weakly integral anyons. Quantum Inf. Process. 14 (2015), no. 8, 2687–2727. .. _ref-D: **D** diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 8fcbb999097..fff53f6f00b 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -227,7 +227,7 @@ class FMatrix(): EXAMPLES:: - (B3 level 2, F4 level 1 conjugated) + """ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variables=False): @@ -264,7 +264,6 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab #Multiprocessing attributes self.mp_thresh = 10000 - ####################### ### Class utilities ### ####################### @@ -1027,7 +1026,8 @@ def find_orthogonal_solution(self,use_mp=True,save_dir="",verbose=True): except RuntimeError: pass pool = Pool(processes=max(cpu_count()-1,1)) if use_mp else None - print("Computing F-symbols for {} with {} variables...".format(self._FR, len(self._fvars))) + if verbose: + print("Computing F-symbols for {} with {} variables...".format(self._FR, len(self._fvars))) #Set up hexagon equations and orthogonality constraints poly_sortkey = cmp_to_key(poly_tup_cmp) @@ -1037,8 +1037,7 @@ def find_orthogonal_solution(self,use_mp=True,save_dir="",verbose=True): print("Set up {} hex and orthogonality constraints...".format(len(self.ideal_basis))) #Set up equations graph. Find GB for each component in parallel. Eliminate variables - self.ideal_basis = sorted(self.par_graph_gb(worker_pool=pool), key=poly_sortkey) - print("GB is of length",len(self.ideal_basis)) + self.ideal_basis = sorted(self.par_graph_gb(worker_pool=pool,verbose=verbose), key=poly_sortkey) self.triangular_elim(worker_pool=pool,verbose=verbose) #Report progress and checkpoint! @@ -1066,7 +1065,7 @@ def find_orthogonal_solution(self,use_mp=True,save_dir="",verbose=True): if pool is not None: pool.close() #Set up new equations graph and compute variety for each component - self.ideal_basis = sorted(self.par_graph_gb(term_order="lex"), key=poly_sortkey) + self.ideal_basis = sorted(self.par_graph_gb(term_order="lex",verbose=verbose), key=poly_sortkey) self.triangular_elim(verbose=verbose) self.get_explicit_solution(verbose=verbose) self.save_fvars(filename) @@ -1191,8 +1190,8 @@ def find_cyclotomic_solution(self, equations=None, algorithm='', verbose=True, o After you successfully run ``get_solution`` you may check the correctness of the F-matrix by running :meth:`hexagon` and :meth:`pentagon`. These should return empty lists - of equations. - + of equations. + EXAMPLES:: sage: f.get_defining_equations("hexagons") diff --git a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx index 5fcba25b9b9..01016ca3e0d 100644 --- a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx @@ -67,6 +67,9 @@ cpdef mid_sig_ij(fusion_ring,row,col,a,b): in the tree b -> xi # yi -> (a # a) # (a # a), which results in a sum over j of trees b -> xj # yj -> (a # a) # (a # a) + ..warning: + This method assumes F-matrices are orthogonal + EXAMPLES:: sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import mid_sig_ij @@ -87,7 +90,6 @@ cpdef mid_sig_ij(fusion_ring,row,col,a,b): xi, yi = row xj, yj = col entry = 0 - phi = fusion_ring.fmats.get_coerce_map_from_fr_cyclotomic_field() for c in fusion_ring.basis(): for d in fusion_ring.basis(): ##Warning: We assume F-matrices are orthogonal!!! (using transpose for inverse) @@ -96,8 +98,6 @@ cpdef mid_sig_ij(fusion_ring,row,col,a,b): f3 = _fmat(_fvars,_Nk_ij,one,a,a,a,c,d,yj) f4 = _fmat(_fvars,_Nk_ij,one,a,a,yj,b,xj,c) r = fusion_ring.r_matrix(a,a,d) - if not phi.is_identity(): - r = phi(r) entry += f1 * f2 * r * f3 * f4 return entry @@ -107,6 +107,9 @@ cpdef odd_one_out_ij(fusion_ring,xi,xj,a,b): corresponding to the tree b -> (xi # a) -> (a # a) # a, which results in a sum over j of trees b -> xj -> (a # a) # (a # a) + ..warning: + This method assumes F-matrices are orthogonal + EXAMPLES:: sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import odd_one_out_ij @@ -124,15 +127,12 @@ cpdef odd_one_out_ij(fusion_ring,xi,xj,a,b): _Nk_ij = fusion_ring.Nk_ij one = fusion_ring.one() - phi = fusion_ring.fmats.get_coerce_map_from_fr_cyclotomic_field() entry = 0 for c in fusion_ring.basis(): ##Warning: We assume F-matrices are orthogonal!!! (using transpose for inverse) f1 = _fmat(_fvars,_Nk_ij,one,a,a,a,b,xi,c) f2 = _fmat(_fvars,_Nk_ij,one,a,a,a,b,xj,c) r = fusion_ring.r_matrix(a,a,c) - if not phi.is_identity(): - r = phi(r) entry += f1 * r * f2 return entry diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 1fa7dea7906..40ac7a376e2 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -370,7 +370,46 @@ def _test_total_q_order(self, **options): tester = self._tester(**options) tqo = self.total_q_order(base_coercion=False) tester.assertTrue(tqo.is_real_positive()) - tester.assertEqual(tqo**2, self.global_q_dimension()) + tester.assertEqual(tqo**2, self.global_q_dimension(base_coercion=False)) + + def test_braid_representation(self, **options): + """ + Check that we can compute valid braid group representations. + + This test indirectly partially verifies the correctness of the orthogonal + F-matrix solver. + + EXAMPLES:: + + sage: F41 = FusionRing("F4",1) + sage: F41.test_braid_representation() + + sage: B22 = FusionRing("B2",2) # long time + sage: B22.test_braid_representation() # long time (~45s) + """ + if not self.is_multiplicity_free(): # Braid group representation is not available if self is not multiplicity free + return True + print ("test_braid_representation:%s"%self) + tester = self._tester(**options) + b = self.basis() + #Test with different numbers of strands + for n_strands in range(3,7): + #Randomly select a fusing anyon. Skip the identity element, since + #its braiding matrices are trivial + while True: + a = b.random_element() + if a != self.one(): + break + pow = a ** n_strands + d = pow.monomials()[0] + #Try to find 'interesting' braid group reps i.e. skip 1-d reps + for k, v in pow.monomial_coefficients().items(): + if v > 1: + d = self(k) + break + comp_basis, sig = self.get_braid_generators(a,d,n_strands,verbose=False) + tester.assertTrue(len(comp_basis) > 0) + tester.assertTrue(self.gens_satisfy_braid_gp_rels(sig)) def fusion_labels(self, labels=None, inject_variables=False): r""" @@ -1016,147 +1055,58 @@ def emap(self,mapper,input_args,worker_pool=None): results = list(results) return results - def get_braid_generators(self,fusing_anyon,total_topological_charge,n_strands,use_mp=True): + def get_braid_generators(self,fusing_anyon,total_charge_anyon,n_strands,use_mp=True,verbose=True): """ + INPUT: + Compute generators of the Artin braid group on n_strands strands. If - fusing_anyon = a and total_topological_charge = b, the generators are + fusing_anyon = a and total_charge_anyon = b, the generators are endomorphisms of Hom(a^n_strands, b) - """ - assert n_strands > 2, "The number of strands must be an integer greater than 2" - #Construct associated FMatrix object and solve for F-symbols - if not self.fmats.symbols_known: - self.fmats.find_real_orthogonal_solution() - - #Set multiprocessing parameters. Context can only be set once, so we try to set it - try: - set_start_method('fork') - except RuntimeError: - pass - pool = Pool() if use_mp else None - - #Set up computational basis and compute generators one at a time - a, b = fusing_anyon, total_topological_charge - comp_basis = self.get_comp_basis(a,b,n_strands) - d = len(comp_basis) - print("Computing an {}-dimensional representation of the Artin braid group on {} strands...".format(d,n_strands)) - - #Compute diagonal odd-indexed generators using the 3j-symbols - phi = self.fmats.get_coerce_map_from_fr_cyclotomic_field() - gens = { 2*i+1 : diagonal_matrix(phi(self.r_matrix(a,a,c[i])) for c in comp_basis) for i in range(n_strands//2) } - - #Compute even-indexed generators using F-matrices - for k in range(1,n_strands//2): - entries = self.emap('sig_2k',(k,a,b,n_strands),pool) - gens[2*k] = matrix(dict(entries)) - #If n_strands is odd, we compute the final generator - if n_strands % 2: - entries = self.emap('odd_one_out',(a,b,n_strands),pool) - gens[n_strands-1] = matrix(dict(entries)) - - return comp_basis, [gens[k] for k in sorted(gens)] - - def gens_satisfy_braid_gp_rels(self,sig): - """ - Determine if given iterable of n matrices defines a representation of - the Artin braid group on (n+1) strands. Tests correctness of - get_braid_generators method. - """ - n = len(sig) - braid_rels = all(sig[i] * sig[i+1] * sig[i] == sig[i+1] * sig[i] * sig[i+1] for i in range(n-1)) - far_comm = all(sig[i] * sig[j] == sig[j] * sig[i] for i, j in product(range(n),repeat=2) if abs(i-j) > 1 and i > j) - singular = any(s.is_singular() for s in sig) - return braid_rels and far_comm and not singular - - ################################### - ### Braid group representations ### - ################################### - - def get_trees(self,top_row,root): - """ - Recursively enumerate all the admissible trees with given top row and root. - Returns a list of tuples (l1,...,lk) such that - root -> lk # m[-1], lk -> l_{k-1} # m[-2], ..., l1 -> m[0] # m[1], - with top_row = m - """ - if len(top_row) == 2: - m1, m2 = top_row - return [[]] if self.Nk_ij(m1,m2,root) else [] - else: - m1, m2 = top_row[:2] - return [tuple([l,*b]) for l in self.basis() for b in self.get_trees([l]+top_row[2:],root) if self.Nk_ij(m1,m2,l)] + - ``fusing_anyon`` -- a basis element of self + - ``total_charge_anyon`` -- a basis element of self + - ``n_strands`` -- a positive integer greater than 2 - def get_comp_basis(self,a,b,n_strands): - """ - Get the so-called computational basis for Hom(b, a^n). The basis is a list of - (n-2)-tuples (m_1,...,m_{n//2},l_1,...,l_{(n-3)//2}) such that - each m_i is a monomial in a^2 and l_{j+1} \in l_j # a, and l[-1] \in a # b - """ - comp_basis = list() - for top in product((a*a).monomials(),repeat=n_strands//2): - #If the n_strands is odd, we must extend the top row by a fusing anyon - top_row = list(top)+[a]*(n_strands%2) - comp_basis.extend(tuple([*top,*levels]) for levels in self.get_trees(top_row,b)) - return comp_basis + Given a simple object in the fusion category, here called + ``fusing_anyon`` allowing the universal R-matrix to act on adjacent + pairs in the fusion of ``n_strands`` copies of ``fusing_anyon`` + produces an action of the braid group. This representation can + be decomposed over another anyon, here called ``total_charge_anyon``. + See [CHW2015]_. - @lazy_attribute - def fmats(self): - """ - Construct an FMatrix factory to solve the pentagon relations and - organize the resulting F-symbols. We only need this attribute to compute - braid group representations. - """ - return FMatrix.FMatrix(self) + The method outputs a pair of data ``(comp_basis,sig)`` where + ``comp_basis`` is a list of basis elements of the braid group + module, parametrized by a list of fusion ring elements describing + a fusion tree. For example with 5 strands the fusion tree + is as follows:: - def emap(self,mapper,input_args,worker_pool=None): - """ - Apply the given mapper to each element of the given input iterable and - return the results (with no duplicates) in a list. This method applies the - mapper in parallel if a worker_pool is provided. + .. image:: ../../../media/fusiontree.png - # INPUT: - mapper is a string specifying the name of a function defined in the - fast_parallel_fmats_methods module. + ``sig`` is a list of braid group generators as matrices. In + some cases these will be represented as sparse matrices. - input_args should be a tuple holding arguments to be passed to mapper + In the following example we compute a 5-dimensional braid group + representation on 5 strands associated to the spin representation in the + modular tensor category `SU(2)_4 \cong SO(3)_2`. - ##NOTES: - If worker_pool is not provided, function maps and reduces on a single process. - If worker_pool is provided, the function attempts to determine whether it should - use multiprocessing based on the length of the input iterable. If it can't determine - the length of the input iterable then it uses multiprocessing with the default chunksize of 1 - if chunksize is not explicitly provided. - """ - n_proc = worker_pool._processes if worker_pool is not None else 1 - input_iter = [(child_id, n_proc, input_args) for child_id in range(n_proc)] - no_mp = worker_pool is None - #Map phase. Casting Async Object blocks execution... Each process holds results - #in its copy of fmats.temp_eqns - input_iter = zip_longest([],input_iter,fillvalue=(mapper,id(self))) - if no_mp: - list(map(executor,input_iter)) - else: - list(worker_pool.imap_unordered(executor,input_iter,chunksize=1)) - #Reduce phase - if no_mp: - results = collect_results(0) - else: - results = set() - for worker_results in worker_pool.imap_unordered(collect_results,range(worker_pool._processes),chunksize=1): - results.update(worker_results) - results = list(results) - return results + EXAMPLES:: - def get_braid_generators(self,fusing_anyon,total_topological_charge,n_strands,use_mp=True): - """ - Compute generators of the Artin braid group on n_strands strands. If - fusing_anyon = a and total_topological_charge = b, the generators are - endomorphisms of Hom(a^n_strands, b) + sage: A14 = FusionRing("A1",4) + sage: A14.fusion_labels(["one","two","three","four","five"],inject_variables=True) + sage: A14.basis() + Finite family {(0, 0): one, (1/2, -1/2): two, (1, -1): three, (3/2, -3/2): four, (2, -2): five} + sage: two ** 5 + 5*two + 4*four + sage: comp_basis, sig = A14.get_braid_generators(two,two,5,verbose=False) + sage: A14.gens_satisfy_braid_gp_rels(sig) + True + sage: len(comp_basis) == 5 + True """ assert n_strands > 2, "The number of strands must be an integer greater than 2" #Construct associated FMatrix object and solve for F-symbols if not self.fmats.symbols_known: - self.fmats.find_real_orthogonal_solution() + self.fmats.find_orthogonal_solution(verbose=verbose) #Set multiprocessing parameters. Context can only be set once, so we try to set it try: @@ -1166,10 +1116,11 @@ def get_braid_generators(self,fusing_anyon,total_topological_charge,n_strands,us pool = Pool() if use_mp else None #Set up computational basis and compute generators one at a time - a, b = fusing_anyon, total_topological_charge + a, b = fusing_anyon, total_charge_anyon comp_basis = self.get_comp_basis(a,b,n_strands) d = len(comp_basis) - print("Computing an {}-dimensional representation of the Artin braid group on {} strands...".format(d,n_strands)) + if verbose: + print("Computing an {}-dimensional representation of the Artin braid group on {} strands...".format(d,n_strands)) #Compute diagonal odd-indexed generators using the 3j-symbols gens = { 2*i+1 : diagonal_matrix(self.r_matrix(a,a,c[i]) for c in comp_basis) for i in range(n_strands//2) } @@ -1191,6 +1142,10 @@ def gens_satisfy_braid_gp_rels(self,sig): Determine if given iterable of n matrices defines a representation of the Artin braid group on (n+1) strands. Tests correctness of get_braid_generators method. + + EXAMPLES:: + + """ n = len(sig) braid_rels = all(sig[i] * sig[i+1] * sig[i] == sig[i+1] * sig[i] * sig[i+1] for i in range(n-1)) diff --git a/src/sage/combinat/root_system/poly_tup_engine.pyx b/src/sage/combinat/root_system/poly_tup_engine.pyx index 9a3b0b7d83d..8499745abeb 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pyx +++ b/src/sage/combinat/root_system/poly_tup_engine.pyx @@ -14,6 +14,8 @@ from sage.rings.polynomial.polydict cimport ETuple from sage.rings.polynomial.term_order import TermOrder from sage.rings.rational_field import QQ +from sage.arith.functions cimport LCM_list + #Pre-compute common values for speed one = QQ.one() degrevlex_sortkey = TermOrder().sortkey_degrevlex @@ -179,7 +181,7 @@ cpdef list variables(tuple eq_tup): EXAMPLES:: sage: from sage.combinat.root_system.poly_tup_engine import variables - sage: from sage.rings.polynomial.polydict import ETuple + sage: from sage.rings.polynomial.polydict import ETuple sage: poly_tup = ((ETuple([0,3,0]),2), (ETuple([0,1,0]),-1), (ETuple([0,0,0]),-2/3)) sage: variables(poly_tup) [1] @@ -323,7 +325,8 @@ cdef tuple to_monic(dict eq_dict): cdef ETuple lm = ord_monoms[-1] lc = eq_dict[lm] if not lc: return tuple() - cdef list ret = [(lm, one)] + # cdef list ret = [(lm, one)] + cdef list ret = [(lm, lc.parent().one())] inv_lc = lc.inverse_of_unit() cdef ETuple exp for exp in reversed(ord_monoms[:-1]): @@ -338,6 +341,31 @@ cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq): if not eq_dict: return tuple() return to_monic(remove_gcf(subs_squares(eq_dict, known_sq), nonz)) +# cdef int common_denom(tuple eq_tup): +# #Compute the common denominator +# cdef list denoms = list() +# cdef int common_denom +# cdef ETuple exp +# for exp, c in eq_tup: +# denoms.append(c.denominator()) +# return LCM_list(denoms) +# +# cdef tuple integralify(tuple eq_tup): +# if not eq_tup: return tuple() +# cdef list ret = list() +# cdef int cd = common_denom(eq_tup) +# for exp, c in eq_tup: +# ret.append((exp, c * cd)) +# return tuple(ret) +# +# cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq): +# """ +# Return a dictionary describing a monic polynomial with no known nonzero gcd and +# no known squares +# """ +# if not eq_dict: return tuple() +# return integralify(to_monic(remove_gcf(subs_squares(eq_dict, known_sq), nonz))) + #################### ### Substitution ### #################### @@ -410,7 +438,8 @@ cpdef dict subs(tuple poly_tup, dict known_powers): cdef tuple temp for exp, coeff in poly_tup: #Get polynomial unit as tuple - temp = ((exp._new(), one),) + # temp = ((exp._new(), one),) + temp = ((exp._new(), coeff.parent().one()),) for var_idx, power in exp.sparse_iter(): if var_idx in known_powers: exp = exp.eadd_p(-power,var_idx) From 614a6394d40e336cf287c57ca47f2176298b0833 Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Sat, 13 Mar 2021 07:21:12 -0800 Subject: [PATCH 023/632] fixed get_order issue in fusion_ring doctests --- src/sage/combinat/root_system/fusion_ring.py | 23 ++++++++++---------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 40ac7a376e2..ac3bc7cfa06 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -383,13 +383,11 @@ def test_braid_representation(self, **options): sage: F41 = FusionRing("F4",1) sage: F41.test_braid_representation() - sage: B22 = FusionRing("B2",2) # long time sage: B22.test_braid_representation() # long time (~45s) """ if not self.is_multiplicity_free(): # Braid group representation is not available if self is not multiplicity free return True - print ("test_braid_representation:%s"%self) tester = self._tester(**options) b = self.basis() #Test with different numbers of strands @@ -531,12 +529,12 @@ def get_order(self): EXAMPLES:: - sage: A14 = FusionRing("A1",4) - sage: w = A14.get_order(); w - [(0, 0), (1/2, -1/2), (1, -1), (3/2, -3/2), (2, -2)] - sage: A14.set_order([w[k] for k in [0,4,1,3,2]]) - sage: [A14(x) for x in A14.get_order()] - [A14(0), A14(4), A14(1), A14(3), A14(2)] + sage: A15 = FusionRing("A1",5) + sage: w = A15.get_order(); w + [(0, 0), (1/2, -1/2), (1, -1), (3/2, -3/2), (2, -2), (5/2, -5/2)] + sage: A15.set_order([w[k] for k in [0,4,1,3,5,2]]) + sage: [A15(x) for x in A15.get_order()] + [A15(0), A15(4), A15(1), A15(3), A15(5), A15(2)] .. WARNING:: @@ -1069,7 +1067,7 @@ def get_braid_generators(self,fusing_anyon,total_charge_anyon,n_strands,use_mp=T Given a simple object in the fusion category, here called ``fusing_anyon`` allowing the universal R-matrix to act on adjacent - pairs in the fusion of ``n_strands`` copies of ``fusing_anyon`` + pairs in the fusion of ``n_strands`` copies of ``fusing_anyon`` produces an action of the braid group. This representation can be decomposed over another anyon, here called ``total_charge_anyon``. See [CHW2015]_. @@ -1092,9 +1090,11 @@ def get_braid_generators(self,fusing_anyon,total_charge_anyon,n_strands,use_mp=T EXAMPLES:: sage: A14 = FusionRing("A1",4) + sage: A14.get_order() + [(0, 0), (1/2, -1/2), (1, -1), (3/2, -3/2), (2, -2)] sage: A14.fusion_labels(["one","two","three","four","five"],inject_variables=True) - sage: A14.basis() - Finite family {(0, 0): one, (1/2, -1/2): two, (1, -1): three, (3/2, -3/2): four, (2, -2): five} + sage: [A14(x) for x in A14.get_order()] + [one, two, three, four, five] sage: two ** 5 5*two + 4*four sage: comp_basis, sig = A14.get_braid_generators(two,two,5,verbose=False) @@ -1102,6 +1102,7 @@ def get_braid_generators(self,fusing_anyon,total_charge_anyon,n_strands,use_mp=T True sage: len(comp_basis) == 5 True + """ assert n_strands > 2, "The number of strands must be an integer greater than 2" #Construct associated FMatrix object and solve for F-symbols From 4b7684f671b07e2d0093086a01afc1bcf533b61f Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Sun, 14 Mar 2021 16:18:02 -0700 Subject: [PATCH 024/632] docstring revisions --- src/doc/en/reference/references/index.rst | 5 +++++ src/sage/combinat/root_system/fusion_ring.py | 18 +++++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 7753a4e9716..808d2fa07c1 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -917,6 +917,11 @@ REFERENCES: .. [Bond2007] P. Bonderson, Nonabelian anyons and interferometry, Dissertation (2007). https://thesis.library.caltech.edu/2447/ +.. [BDGRTW2019] Bonderson, Delaney, Galindo, Rowell, Tran, and Wang, Zhenghan, + On invariants of modular categories beyond modular data. + J. Pure Appl. Algebra 223 (2019), no. 9, 4065–4088. + :arXiv:`1805.05736`. + .. [BM2004] John M. Boyer and Wendy J. Myrvold, *On the Cutting Edge: *Simplified `O(n)` Planarity by Edge Addition*. Journal of Graph Algorithms and Applications, Vol. 8, No. 3, pp. 241-273, diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index ac3bc7cfa06..96c83378d19 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -951,9 +951,10 @@ def D_minus(self, base_coercion=True): def is_multiplicity_free(self): """ - Return ``True`` if the fusion multiplicities + Return True if the fusion multiplicities :meth:`Nk_ij` are bounded by 1. The :class:`FMatrix` - is available only for multiplicity free :class:`FusionRing`s. + is available only for multiplicity free instances of + :class:`FusionRing`. EXAMPLES:: @@ -1140,13 +1141,20 @@ def get_braid_generators(self,fusing_anyon,total_charge_anyon,n_strands,use_mp=T def gens_satisfy_braid_gp_rels(self,sig): """ - Determine if given iterable of n matrices defines a representation of - the Artin braid group on (n+1) strands. Tests correctness of + Return True if the matrices in the list ``sig`` satisfy + the braid relations. This if `n` is the cardinality of ``sig``, this + confirms that these matrices define a representation of + the Artin braid group on `n+1` strands.Tests correctness of get_braid_generators method. EXAMPLES:: - + sage: F41 = FusionRing("F4",1,fusion_labels="f",inject_variables=True) + sage: f1*f1 + f0 + f1 + sage: comp, sig = F41.get_braid_generators(f1,f0,4,verbose=False) + sage: F41.gens_satisfy_braid_gp_rels(sig) + True """ n = len(sig) braid_rels = all(sig[i] * sig[i+1] * sig[i] == sig[i+1] * sig[i] * sig[i+1] for i in range(n-1)) From 14d951b62c45213a4fdc2388ee3e54ffbcd6cfac Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Mon, 15 Mar 2021 15:32:01 -0700 Subject: [PATCH 025/632] revision of get_comp_basis docstring --- src/sage/combinat/root_system/fusion_ring.py | 50 +++++++++++++++++--- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 96c83378d19..5de086252e7 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -982,9 +982,27 @@ def is_multiplicity_free(self): def get_trees(self,top_row,root): """ Recursively enumerate all the admissible trees with given top row and root. - Returns a list of tuples (l1,...,lk) such that - root -> lk # m[-1], lk -> l_{k-1} # m[-2], ..., l1 -> m[0] # m[1], - with top_row = m + + INPUT: + + - ``top_row`` -- a list of basis elements of self + - ``root`` -- a simple element of self + + Let `k` denote the length ``top_row``. This method returns + Returns a list of tuples `(l_1,...,l_{k-2})` such that + + .. MATH:: + + \begin{array}{l} + root \righthookarrow l_{k-2} \otimes m_{k},\\ + l_{k-2} \righthookarrow l_{k-3} \otimes m_{k-1},\\ + \vdots\\ + l_2\righthookarrow l_1\otimes m_3\\ + l_1\righthookarrow m_1\otimes m_2 + \end{array} + + where `a\righthookarrow b\otimes c` means `N_{bc}^a\neq 0`. + """ if len(top_row) == 2: m1, m2 = top_row @@ -995,9 +1013,29 @@ def get_trees(self,top_row,root): def get_comp_basis(self,a,b,n_strands): """ - Get the so-called computational basis for Hom(b, a^n). The basis is a list of - (n-2)-tuples (m_1,...,m_{n//2},l_1,...,l_{(n-3)//2}) such that - each m_i is a monomial in a^2 and l_{j+1} \in l_j # a, and l[-1] \in a # b + Return the so-called computational basis for `\text{Hom}(b, a^n)`. + + INPUT: + + - ``a`` -- a basis element + - ``b`` -- another basis elements + - ``n_strands`` -- the number of strands for a braid group + + Let `n=` ``n_strands`` and let `k` be the greatest integer `\leqslant n/2`. + The braid group acts on ``\text{Hom}(b,a^n)``. This action + is computed in :meth:`get_braid_generators`. This method + returns the computational basis in the form of a set of + fusion trees. Each tree is represented by a `(n-2)`-tuple + + .. MATH:: + + (m_1,\cdots,m_k,l_1,\cdots,l_{k-2}) + + These are computed by :meth:`get_trees`. As a computational + device when ``n_strands`` is odd, we pad the vector `(m_1,\cdots,m_k)` + with an additional `m_{k+1}` equal to `a` before passing it to + :meth:`get_trees`. This `m_{k+1}` does not appear in the output + of this method. """ comp_basis = list() for top in product((a*a).monomials(),repeat=n_strands//2): From 4a4fde802316260971a435dec7290825dcdedbd6 Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Tue, 16 Mar 2021 11:46:06 -0700 Subject: [PATCH 026/632] work on get_computational_basis and get_trees --- src/sage/combinat/root_system/fusion_ring.py | 22 ++++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 5de086252e7..5ba9f6eea0e 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -993,16 +993,15 @@ def get_trees(self,top_row,root): .. MATH:: - \begin{array}{l} - root \righthookarrow l_{k-2} \otimes m_{k},\\ - l_{k-2} \righthookarrow l_{k-3} \otimes m_{k-1},\\ - \vdots\\ - l_2\righthookarrow l_1\otimes m_3\\ - l_1\righthookarrow m_1\otimes m_2 + \\begin{array}{l} + root \\to l_{k-2} \otimes m_{k},\\ + l_{k-2} \\to l_{k-3} \otimes m_{k-1},\\ + \\cdots\\ + l_2\\to l_1\otimes m_3\\ + l_1\\to m_1\otimes m_2 \end{array} - where `a\righthookarrow b\otimes c` means `N_{bc}^a\neq 0`. - + where `a \\to b\otimes c` means `N_{bc}^a\\neq 0`. """ if len(top_row) == 2: m1, m2 = top_row @@ -1011,7 +1010,7 @@ def get_trees(self,top_row,root): m1, m2 = top_row[:2] return [tuple([l,*b]) for l in self.basis() for b in self.get_trees([l]+top_row[2:],root) if self.Nk_ij(m1,m2,l)] - def get_comp_basis(self,a,b,n_strands): + def get_computational_basis(self,a,b,n_strands): """ Return the so-called computational basis for `\text{Hom}(b, a^n)`. @@ -1115,7 +1114,8 @@ def get_braid_generators(self,fusing_anyon,total_charge_anyon,n_strands,use_mp=T ``comp_basis`` is a list of basis elements of the braid group module, parametrized by a list of fusion ring elements describing a fusion tree. For example with 5 strands the fusion tree - is as follows:: + is as follows. See :meth:`get_computational_basis` and :meth:`get_trees` + for more information. .. image:: ../../../media/fusiontree.png @@ -1157,7 +1157,7 @@ def get_braid_generators(self,fusing_anyon,total_charge_anyon,n_strands,use_mp=T #Set up computational basis and compute generators one at a time a, b = fusing_anyon, total_charge_anyon - comp_basis = self.get_comp_basis(a,b,n_strands) + comp_basis = self.get_computational_basis(a,b,n_strands) d = len(comp_basis) if verbose: print("Computing an {}-dimensional representation of the Artin braid group on {} strands...".format(d,n_strands)) From 74a2d76a1a5d617f7f24778bb6b243eb248c697d Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Tue, 16 Mar 2021 18:08:48 -0700 Subject: [PATCH 027/632] FusionRing bugfix in weyl_characters: cast level as Integer --- src/sage/combinat/root_system/weyl_characters.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index c254e79a1e8..f77a86efb34 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -18,8 +18,7 @@ from sage.misc.lazy_attribute import lazy_attribute from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet from sage.misc.functional import is_even -from sage.rings.all import ZZ - +from sage.rings.all import ZZ, Integer class WeylCharacterRing(CombinatorialFreeModule): r""" @@ -132,6 +131,8 @@ def __init__(self, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, conju self._field = None self._basecoer = None self._k = k + if k is not None: + self._k = Integer(k) if ct.is_irreducible(): self._opposition = ct.opposition_automorphism() self._highest = self._space.highest_root() From 7f738aa67d02d2908c18a7be51c85bbd7f94a5bb Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Wed, 17 Mar 2021 16:43:20 -0700 Subject: [PATCH 028/632] no default picking, case-by-case determination of field --- src/sage/combinat/root_system/f_matrix.py | 507 +++++++++++++----- .../fast_parallel_fmats_methods.pyx | 64 ++- .../fast_parallel_fusion_ring_braid_repn.pyx | 8 +- src/sage/combinat/root_system/fusion_ring.py | 26 +- .../combinat/root_system/poly_tup_engine.pxd | 15 +- .../combinat/root_system/poly_tup_engine.pyx | 195 ++++--- 6 files changed, 555 insertions(+), 260 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index fff53f6f00b..08563fade2e 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -11,8 +11,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from functools import cmp_to_key, partial -from itertools import product +from itertools import product, zip_longest import sage.combinat.root_system.fusion_ring as FusionRing import sage.graphs from sage.graphs.generators.basic import EmptyGraph @@ -29,15 +28,17 @@ except: import pickle -from multiprocessing import cpu_count, Pool, set_start_method, TimeoutError +from multiprocessing import cpu_count, Pool, set_start_method import numpy as np +import os from sage.combinat.root_system.fast_parallel_fmats_methods import * from sage.combinat.root_system.poly_tup_engine import * +#Import faster unsafe method (not for client use) +from sage.combinat.root_system.poly_tup_engine import _tup_to_poly from sage.rings.polynomial.polydict import ETuple -from sage.rings.qqbar import AA, QQbar +from sage.rings.qqbar import AA, QQbar, number_field_elements_from_algebraics from sage.rings.real_double import RDF -from itertools import zip_longest class FMatrix(): r"""Return an F-Matrix factory for a FusionRing. @@ -248,16 +249,18 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab #Initialize list of defining equations self.ideal_basis = list() - #Initialize empty set of solved F-symbols - self.solved = set() - - #New attributes of the FMatrix class + #Base field attributes self._field = self._FR.field() r = self._field.defining_polynomial().roots(ring=QQbar,multiplicities=False)[0] self._qqbar_embedding = self._field.hom([r],QQbar) + self._non_cyc_roots = list() + + #Solver state attributes + #Initialize empty set of solved F-symbols + self.solved = set() self._var_to_idx = { var : idx for idx, var in enumerate(self._poly_ring.gens()) } self._ks = dict() - self._nnz = self.get_known_nonz() + self._nnz = self._get_known_nonz() self._known_vals = dict() self.symbols_known = False @@ -291,7 +294,7 @@ def clear_equations(self): def clear_vars(self): """ - Clear the set of variables. + Clear the set of variables. Also reset the set of solved F-symbols. """ self._FR._basecoer = None self._fvars = { self._var_to_sextuple[key] : key for key in self._var_to_sextuple } @@ -384,9 +387,32 @@ def fmatrix(self,a,b,c,d): return matrix([[self.fmat(a,b,c,d,x,y) for y in Y] for x in X]) def field(self): + r""" + Return the base field containing the F-symbols. When ``self`` is initialized, + the field is set to be the cyclotomic field of the FusionRing associated + to ``self``. The field may change after running :meth:`find_orthogonal_solution`. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("F4",1,conjugate=True)) + sage: f.field() + Cyclotomic Field of order 80 and degree 32 + sage: f.find_orthogonal_solution(verbose=False) + sage: f.field() + Number Field in a with defining polynomial y^64 - 16*y^62 + 104*y^60 - 320*y^58 + 258*y^56 + 1048*y^54 - 2864*y^52 - 3400*y^50 + 47907*y^48 - 157616*y^46 + 301620*y^44 - 322648*y^42 + 2666560*y^40 + 498040*y^38 + 54355076*y^36 - 91585712*y^34 + 592062753*y^32 - 1153363592*y^30 + 3018582788*y^28 - 4848467552*y^26 + 7401027796*y^24 - 8333924904*y^22 + 8436104244*y^20 - 7023494736*y^18 + 4920630467*y^16 - 2712058560*y^14 + 1352566244*y^12 - 483424648*y^10 + 101995598*y^8 - 12532920*y^6 + 1061168*y^4 - 57864*y^2 + 1681 + """ return self._field def FR(self): + r""" + Return the FusionRing associated to ``self``. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("D3",1)) + sage: f.FR() + The Fusion Ring of Type D3 and level 1 with Integer Ring coefficients + """ return self._FR def findcases(self,output=False): @@ -493,17 +519,90 @@ def f_to(self,a,b,c,d): [b1, b3, b5] """ - return [y for y in self._FR.basis() if self._FR.Nk_ij(b,c,y) != 0 and self._FR.Nk_ij(a,y,d) != 0] #################### ### Data getters ### #################### - def get_fmats_in_alg_field(self): + def get_fvars(self): + r""" + Return a dictionary of F-symbols. + + The keys are sextuples `(a,b,c,d,x,y)` basis elements of ``self`` and + the values are the corresponding F-symbols `(F^{a,b,c}_d)_{xy}`. + + These values reflect the current state of a solver's computation. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("A2",1), inject_variables=True) + creating variables fx1..fx8 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 + sage: f.get_fvars()[(f1, f1, f1, f0, f2, f2)] + fx0 + sage: f.find_orthogonal_solution(verbose=False) + sage: f.get_fvars()[(f1, f1, f1, f0, f2, f2)] + 1 + """ + return self._fvars + + def get_non_cyclotomic_roots(self): + r""" + Return a list of roots that define the extension of the associated + ``FusionRing``'s base ``CyclotomicField`` containing all the F-symbols. + + OUTPUT: + + The list of non-cyclotomic roots is given as a list of elements of + ``self.field()``. + + If ``self.field() == self.FR().field()`` this method returns an empty list. + + When ``self.field()`` is a ``NumberField``, one may use + :meth:``get_qqbar_embedding`` to embed the resulting values into ``QQbar``. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("E6",1)) + sage: f.find_orthogonal_solution(verbose=False) + sage: f.get_non_cyclotomic_roots() + [] + sage: f = FMatrix(FusionRing("E7",2)) # long time + sage: f.find_orthogonal_solution(verbose=False) # long time + sage: f.get_non_cyclotomic_roots() # long time + [-0.7861513777574233?, -0.5558929702514212?] + """ + return sorted(set(self._non_cyc_roots)) + + def get_qqbar_embedding(self): + r""" + Return an embedding from the base field containing F-symbols (the + ``FusionRing``'s ``CyclotomicField``, a ``NumberField``, or ``QQbar``) + into ``QQbar``. + """ + return self._qqbar_embedding + + def get_coerce_map_from_fr_cyclotomic_field(self): + r""" + Return a coercion map from the associated ``FusionRing``'s cyclotomic + field into the base field containing all F-symbols (this could be the + ``FusionRing``'s ``CyclotomicField``, a ``NumberField``, or ``QQbar``). """ - Return F-symbols as elements of the AlgebraicField. This method uses - self._qqbar_embedding to coerce F-symbols into QQbar. + #If base field is different from associated FusionRing's CyclotomicField, + #return coercion map + try: + return self._coerce_map_from_cyc_field + #Otherwise, return identity map CyclotomicField <-> CyclotomicField + except AttributeError: + F = self._FR.field() + return F.hom([F.gen()], F) + + def get_fmats_in_alg_field(self): + r""" + Return F-symbols as elements of the ``AlgebraicField``. This method uses + the embedding defined by :meth:``self.get_qqbar_embedding`` to coerce + F-symbols into QQbar. """ return { sextuple : self._qqbar_embedding(fvar) for sextuple, fvar in self._fvars.items() } @@ -513,15 +612,21 @@ def get_radical_expression(self): """ return { sextuple : val.radical_expression() for sextuple, val in get_fmats_in_alg_field().items() } - def get_known_vals(self): - """ - Construct a dictionary of idx, known_val pairs for equation substitution + ####################### + ### Private helpers ### + ####################### + + def _get_known_vals(self): + r""" + Construct a dictionary of ``idx``, ``known_val`` pairs used for substituting + into remaining equations. """ return { var_idx : self._fvars[self._var_to_sextuple[self._poly_ring.gen(var_idx)]] for var_idx in self.solved } - def get_known_sq(self,eqns=None): - """ - Construct a dictionary of known squares. Keys are variable indices and corresponding values are the squares + def _get_known_sq(self,eqns=None): + r""" + Update ```self``'s dictionary of known squares. Keys are variable + indices and corresponding values are the squares. """ if eqns is None: eqns = self.ideal_basis @@ -529,54 +634,34 @@ def get_known_sq(self,eqns=None): for eq_tup in eqns: if tup_fixes_sq(eq_tup): ks[variables(eq_tup)[0]] = -eq_tup[-1][1] - # return { variables(eq_tup)[0] : -eq_tup[-1][1] for eq_tup in eqns if tup_fixes_sq(eq_tup) } return ks - def get_known_nonz(self): - """ + def _get_known_nonz(self): + r""" Construct an ETuple indicating positions of known nonzero variables. - MUST be called after self._ks = get_known_sq() + + NOTES: + + MUST be called after ``self._ks = _get_known_sq()``. """ nonz = { self._var_to_idx[var] : 100 for var in self._singles } for idx in self._ks: nonz[idx] = 100 return ETuple(nonz, self._poly_ring.ngens()) - def get_qqbar_embedding(self): - """ - Return an embedding from the base field containing F-symbols (the - FusionRing's CyclotomicField, a NumberField, or QQbar) into QQbar - """ - return self._qqbar_embedding + ################################# + ### Useful private predicates ### + ################################# - def get_coerce_map_from_fr_cyclotomic_field(self): + def _is_univariate_in_unknown(self,monom_exp): """ - Return a coercion map from the FusionRing's cyclotomic field into the - base field containing all F-symbols (this could be the FusionRing's - CyclotomicField, a NumberField, or QQbar). - """ - #If base field is different from associated FusionRing's CyclotomicField, - #return coercion map - try: - return self._coerce_map_from_cyc_field - #Otherwise, return identity map CyclotomicField <-> CyclotomicField - except AttributeError: - F = self._FR.field() - return F.hom([F.gen()], F) - - ######################### - ### Useful predicates ### - ######################### - - def is_univariate_in_unknown(self,monom_exp): - """ - Determine if monomial exponent is univariate in a still unknown F-symbol + Determine if monomial exponent is univariate in an unknown F-symbol """ return len(monom_exp.nonzero_values()) == 1 and monom_exp.nonzero_positions()[0] not in self.solved - def is_uni_linear_in_unkwown(self,monom_exp): + def _is_uni_linear_in_unkwown(self,monom_exp): """ - Determine if monomial exponent is univariate and linear in a vstill unknown F-symbol + Determine if monomial exponent is univariate and linear in an unknown F-symbol """ return monom_exp.nonzero_values() == [1] and monom_exp.nonzero_positions()[0] not in self.solved @@ -585,15 +670,46 @@ def is_uni_linear_in_unkwown(self,monom_exp): ############################## def largest_fmat_size(self): - """ - Get the size of the largest F-matrix F^{abc}_d + r""" + Get the size of the largest F-matrix `F^{abc}_d`. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("B3",2)) + sage: f.largest_fmat_size() + 4 """ return max(self.fmatrix(*tup).nrows() for tup in product(self._FR.basis(),repeat=4)) - def get_fmats_by_size(self,n): - """ - Partition the F-symbols according to the size of the F-matrix F^{abc}_d - they belong to + def get_fvars_by_size(self,n,indices=False): + r""" + Return the set of F-symbols that are entries of an `n \times n` matrix + `F^{a,b,c}_d`. + + INPUT: + + -``n`` -- positive integer + -``indices`` -- If ``indices`` is ``False`` (default), this method + returns a set of sextuples `(a,b,c,d,x,y)` identifying the + corresponding F-symbol. Each sextuple is a key in the dictionary + returned by :meth:`get_fvars`. + + Otherwise the method returns a list of integer indices that internally + identify the F-symbols. The ``indices=True`` option is meant + for internal use mostly. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("E8",2), inject_variables=True) + creating variables fx1..fx14 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 + sage: f.largest_fmat_size() + 2 + sage: f.get_fvars_by_size(2) + {(f2, f2, f2, f2, f0, f0), + (f2, f2, f2, f2, f0, f1), + (f2, f2, f2, f2, f1, f0), + (f2, f2, f2, f2, f1, f1)} """ fvars_copy = deepcopy(self._fvars) solved_copy = deepcopy(self.solved) @@ -603,10 +719,12 @@ def get_fmats_by_size(self,n): F = self.fmatrix(*quadruple) #Discard trivial 1x1 F-matrix, if applicable if F.nrows() == n and F.coefficients() != [1]: - var_set.update(self._var_to_idx[fx] for fx in F.coefficients()) + var_set.update(F.coefficients()) self._fvars = fvars_copy self.solved = solved_copy - return var_set + if indices: + return { self._var_to_idx[fx] for fx in var_set } + return { self._var_to_sextuple[fx] for fx in var_set } ############################ ### Checkpoint utilities ### @@ -614,9 +732,16 @@ def get_fmats_by_size(self,n): def get_fr_str(self): """ - Auto-generate filename string for saving results + Auto-generate identifying key for saving results + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("B3",1)) + sage: f.get_fr_str() + 'B31' """ - return self._FR.cartan_type()[0] + str(self._FR.cartan_type()[1]) + str(self._FR.fusion_level()) + ct = self._FR.cartan_type() + return ct.letter + str(ct.n) + str(self._FR.fusion_level()) def save_fvars(self,filename): """ @@ -626,8 +751,12 @@ def save_fvars(self,filename): pickle.dump([self._fvars, self.solved], f) def load_fvars(self,save_dir=""): - """ - If provided, optional param save_dir should have a trailing forward slash + r""" + Load solver state from file. Use this method both for warm-starting + :meth:`find_orthogonal_solution` and to load pickled results. + + If provided, the optional parameter ``save_dir`` must have a trailing + forward slash e.g. "my_dir/" """ with open(save_dir + "saved_fvars_" + self.get_fr_str() + ".pickle", 'rb') as f: self._fvars, self.solved = pickle.load(f) @@ -642,11 +771,13 @@ def map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_th return the results (with no duplicates) in a list. This method applies the mapper in parallel if a worker_pool is provided. - # INPUT: - mapper is a string specifying the name of a function defined in the - fast_parallel_fmats_methods module. + INPUT: + + -``mapper`` -- string specifying the name of a function defined in the + ``fast_parallel_fmats_methods`` module. + + NOTES: - ##NOTES: If worker_pool is not provided, function maps and reduces on a single process. If worker_pool is provided, the function attempts to determine whether it should use multiprocessing based on the length of the input iterable. If it can't determine @@ -686,12 +817,19 @@ def map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_th ######################## def get_orthogonality_constraints(self,output=True): - """ + r""" Get equations imposed on the F-matrix by orthogonality. - If output=True, equations are returned as polynomial objects. Otherwise, - polynomial generators (stored in the internal tuple representation) are - appended to self.ideal_basis + INPUT: + + -``output``-- a boolean. + + If ``output==True``, orthogonality constraints are returned as + polynomial objects. + + Otherwise, the constraints are appended to ``self.ideal_basis``. + They are stored in the internal tuple representation. The ``output==False`` + option is meant mostly for internal use by the F-matrix solver. """ eqns = list() for tup in product(self._FR.basis(), repeat=4): @@ -702,26 +840,31 @@ def get_orthogonality_constraints(self,output=True): self.ideal_basis.extend([poly_to_tup(eq) for eq in eqns]) def get_defining_equations(self,option,worker_pool=None,output=True): - """ + r""" Get the equations defining the ideal generated by the hexagon or pentagon relations. - Use option='hexagons' to get equations imposed on the F-matrix by the hexagon + Use ``option='hexagons'`` to get equations imposed on the F-matrix by the hexagon relations in the definition of a braided category. - Use option='pentagons' to get equations imposed on the F-matrix by the pentagon + Use ``option='pentagons'`` to get equations imposed on the F-matrix by the pentagon relations in the definition of a monoidal category. - If output=True, equations are returned as polynomial objects. Otherwise, - polynomial generators (stored in the internal tuple representation) are - appended to self.ideal_basis - If a worker_pool is passed, then we use multiprocessing + If ``output=True``, equations are returned as polynomial objects. + + Otherwise, the constraints are appended to ``self.ideal_basis``. + They are stored in the internal tuple representation. The ``output==False`` + option is meant mostly for internal use by the F-matrix solver. + + If a ``worker_pool`` object is passed, then we use multiprocessing. + The ``worker_pool`` object is assumed to be a ``Pool`` object of the + Python ``multiprocessing`` module. """ n_proc = worker_pool._processes if worker_pool is not None else 1 params = [(child_id, n_proc) for child_id in range(n_proc)] eqns = self.map_triv_reduce('get_reduced_'+option,params,worker_pool=worker_pool,chunksize=1,mp_thresh=0) if output: - return [self.tup_to_fpoly(p) for p in eqns] + return [self._tup_to_fpoly(p) for p in eqns] self.ideal_basis.extend(eqns) ############################ @@ -734,7 +877,14 @@ def tup_to_fpoly(self,eq_tup): """ return tup_to_poly(eq_tup,parent=self._poly_ring) - def solve_for_linear_terms(self,eqns=None): + def _tup_to_fpoly(self,eq_tup): + r""" + Faster version of :meth:`tup_to_fpoly`. Unsafe for client use, since it + avoids implicit casting and it may lead to segmentation faults. + """ + return _tup_to_poly(eq_tup,parent=self._poly_ring) + + def _solve_for_linear_terms(self,eqns=None): """ Solve for a linear term occurring in a two-term equation. """ @@ -745,7 +895,7 @@ def solve_for_linear_terms(self,eqns=None): for eq_tup in eqns: if len(eq_tup) == 1: m = eq_tup[0][0] - if self.is_univariate_in_unknown(m): + if self._is_univariate_in_unknown(m): var = m.nonzero_positions()[0] self._fvars[self._var_to_sextuple[self._poly_ring.gen(var)]] = tuple() self.solved.add(var) @@ -755,7 +905,7 @@ def solve_for_linear_terms(self,eqns=None): max_var = monomials[0].emax(monomials[1]).nonzero_positions()[0] for this, m in enumerate(monomials): other = (this+1)%2 - if self.is_uni_linear_in_unkwown(m) and m.nonzero_positions()[0] == max_var and monomials[other][m.nonzero_positions()[0]] == 0: + if self._is_uni_linear_in_unkwown(m) and m.nonzero_positions()[0] == max_var and monomials[other][m.nonzero_positions()[0]] == 0: var = m.nonzero_positions()[0] rhs_key = monomials[other] rhs_coeff = -eq_tup[other][1] / eq_tup[this][1] @@ -764,17 +914,18 @@ def solve_for_linear_terms(self,eqns=None): linear_terms_exist = True return linear_terms_exist - def backward_subs(self): + def _backward_subs(self): """ Backward substitution step. Traverse variables in reverse lexicographical order. """ + one = self._field.one() for var in reversed(self._poly_ring.gens()): sextuple = self._var_to_sextuple[var] rhs = self._fvars[sextuple] d = { var_idx : self._fvars[self._var_to_sextuple[self._poly_ring.gen(var_idx)]] for var_idx in variables(rhs) if var_idx in self.solved } if d: - kp = compute_known_powers(get_variables_degrees([rhs]), d) - self._fvars[sextuple] = tuple(subs_squares(subs(rhs,kp),self._ks).items()) + kp = compute_known_powers(get_variables_degrees([rhs]), d, one) + self._fvars[sextuple] = tuple(subs_squares(subs(rhs,kp,one),self._ks).items()) def update_reduction_params(self,eqns=None,worker_pool=None,children_need_update=False): """ @@ -782,9 +933,9 @@ def update_reduction_params(self,eqns=None,worker_pool=None,children_need_update """ if eqns is None: eqns = self.ideal_basis - self._ks, self._var_degs = self.get_known_sq(eqns), get_variables_degrees(eqns) - self._nnz = self.get_known_nonz() - self._kp = compute_known_powers(self._var_degs,self.get_known_vals()) + self._ks, self._var_degs = self._get_known_sq(eqns), get_variables_degrees(eqns) + self._nnz = self._get_known_nonz() + self._kp = compute_known_powers(self._var_degs,self._get_known_vals(), self._field.one()) if worker_pool is not None and children_need_update: #self._nnz and self._kp are computed in child processes to reduce IPC overhead n_proc = worker_pool._processes @@ -802,15 +953,16 @@ def triangular_elim(self,required_vars=None,eqns=None,worker_pool=None,verbose=T ret = False if required_vars is None: required_vars = self._poly_ring.gens() - poly_sortkey = cmp_to_key(poly_tup_cmp) + # poly_sortkey = cmp_to_key(poly_tup_cmp) + poly_sortkey = poly_tup_sortkey_degrevlex - #Testing new _fvars representation... Unzip polynomials + #Unzip polynomials self._fvars = { sextuple : poly_to_tup(rhs) for sextuple, rhs in self._fvars.items() } while True: - linear_terms_exist = self.solve_for_linear_terms(eqns) + linear_terms_exist = self._solve_for_linear_terms(eqns) if not linear_terms_exist: break - self.backward_subs() + self._backward_subs() #Support early termination in case only some F-symbols are needed req_vars_known = all(self._fvars[self._var_to_sextuple[var]] in self._FR.field() for var in required_vars) @@ -818,12 +970,13 @@ def triangular_elim(self,required_vars=None,eqns=None,worker_pool=None,verbose=T #Compute new reduction params, send to child processes if any, and update eqns self.update_reduction_params(eqns=eqns,worker_pool=worker_pool,children_need_update=len(eqns)>self.mp_thresh) - eqns = sorted(self.map_triv_reduce('update_reduce',eqns,worker_pool=worker_pool), key=poly_sortkey) + eqns = self.map_triv_reduce('update_reduce',eqns,worker_pool=worker_pool) + eqns.sort(key=poly_sortkey) if verbose: print("Elimination epoch completed... {} eqns remain in ideal basis".format(len(eqns))) #Zip up _fvars before exiting - self._fvars = { sextuple : self.tup_to_fpoly(rhs_tup) for sextuple, rhs_tup in self._fvars.items() } + self._fvars = { sextuple : self._tup_to_fpoly(rhs_tup) for sextuple, rhs_tup in self._fvars.items() } if ret: return eqns self.ideal_basis = eqns @@ -890,7 +1043,7 @@ def par_graph_gb(self,worker_pool=None,eqns=None,term_order="degrevlex",verbose= # nmax = self.largest_fmat_size() # vars_by_size = list() # for i in range(nmax+1): - # vars_by_size.append(self.get_fmats_by_size(i)) + # vars_by_size.append(self.get_fvars_by_size(i)) for comp, comp_eqns in self.partition_eqns(graph,verbose=verbose).items(): #Check if component is too large to process @@ -931,6 +1084,42 @@ def get_component_variety(self,var,eqns): ### Solution method ### ####################### + def attempt_number_field_computation(self): + """ + Based on the ``CartanType`` of ``self``, determine whether to attempt + to find a ``NumberField`` containing all the F-symbols based on data + known on March 17, 2021. + + For certain ``FusionRing``s, the number field computation does not + seem to terminate. In these cases, we report F-symbols as elements + of the ``AlgebraicField``. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("E6",2)) + sage: f.attempt_number_field_computation() + False + sage: f = FMatrix(FusionRing("G2",1)) + sage: f.attempt_number_field_computation() + True + """ + ct = self._FR.cartan_type() + k = self._FR._k + if ct.letter == 'A': + if ct.n == 1 and k >= 9: + return False + if ct.letter == 'C': + if ct.n >= 9 and k == 1: + return False + if ct.letter == 'E': + if ct.n < 8 and k == 2: + return False + if ct.letter == 'F' and k == 2: + return False + if ct.letter == 'G' and k == 2: + return False + return True + def get_explicit_solution(self,eqns=None,verbose=True): """ When this method is called, the solution is already found in @@ -973,16 +1162,21 @@ def get_explicit_solution(self,eqns=None,verbose=True): if must_change_base_field: #Attempt to compute smallest number field containing all the F-symbols #If calculation takes too long, we use QQbar as the base field - proc = Pool(1) - input_args = ((('get_appropriate_number_field',id(self)),non_cyclotomic_roots),) - p = proc.apply_async(executor,input_args) - try: - self._field, bf_elts, self._qqbar_embedding = p.get(timeout=100) - except TimeoutError: + if self.attempt_number_field_computation(): + roots = [self._FR.field().gen()]+[r[1] for r in non_cyclotomic_roots] + self._field, bf_elts, self._qqbar_embedding = number_field_elements_from_algebraics(roots,minimal=True) + else: + # proc = Pool(1) + # input_args = ((('get_appropriate_number_field',id(self)),non_cyclotomic_roots),) + # p = proc.apply_async(executor,input_args) + # try: + # self._field, bf_elts, self._qqbar_embedding = p.get(timeout=100) + # except TimeoutError: self._field = QQbar bf_elts = [self._qqbar_embedding(F.gen())] bf_elts += [rhs for fx,rhs in non_cyclotomic_roots] self._qqbar_embedding = lambda x : x + self._non_cyc_roots = bf_elts[1:] #Embed cyclotomic field into newly constructed base field cyc_gen_as_bf_elt = bf_elts.pop(0) @@ -1008,15 +1202,58 @@ def get_explicit_solution(self,eqns=None,verbose=True): self._fvars = { sextuple : apply_coeff_map(poly_to_tup(rhs),phi) for sextuple, rhs in self._fvars.items() } for fx, rhs in numeric_fvars.items(): self._fvars[self._var_to_sextuple[self._poly_ring.gen(fx)]] = ((ETuple({},nvars),rhs),) - self.backward_subs() + self._backward_subs() self._fvars = { sextuple : constant_coeff(rhs) for sextuple, rhs in self._fvars.items() } self.clear_equations() - def find_orthogonal_solution(self,use_mp=True,save_dir="",verbose=True): - """ - Solver - If provided, optional param save_dir (for saving computed _fvars) should have a trailing forward slash - Supports "warm" start. Use load_fvars to re-start computation from checkpoint + #Update base field attributes + self._FR._field = self.field() + self._FR._basecoer = self.get_coerce_map_from_fr_cyclotomic_field() + for x in self._FR.basis(): + x.q_dimension.clear_cache() + + def find_orthogonal_solution(self,checkpoint=False,save_results=False,save_dir="",verbose=True,use_mp=True): + r""" + Find an orthogonal solution to the pentagon equations associated to the + monoidal category represented by ``self``. + + INPUT: + + -``checkpoint`` -- (optional) a boolean indicating whether the computation should + be checkpointed. Depending on the associated ``CartanType``, the computation + may take hours to complete. For large examples, checkpoints are + recommended. This method supports "warm" starting, so the calculation + may be resumed from a checkpoint. Checkpoints store necessary state in + the form of pickle files. + + -``save_results`` -- (optional) a boolean indicating whether the F-symbols + should be stored to file as a pickle for later use. + + -``save_dir`` -- (optional) a string specifying the directory in which + to save the pickled F-symbols. If used, the string must have a trailing + forward slash e.g. "my_dir/" + The file name is "saved_fvars_" + ``key`` + .pickle, where + key is computed by :meth:`get_fr_str`. The file name is fixed to allow + for automatic loading. The ``save_dir`` is also used to specify the + location of a checkpoint pickle, if one exists. + + -``use_mp`` -- (optional) a boolean indicating whether to use + multiprocessing to speed up calculation. The default value + ``True`` is recommended, since parallel processing yields results + much quicker. + + OUTPUT: + + This method returns ``None``. If the solver ran successfully, the + results may be accessed through various methods, such as + :meth:``get_fvars``, :meth:``fmatrix``, :meth:``fmat``, etc. + + In many cases the F-symbols obtained are in fact real. In any case, the + F-symbols are obtained as elements of the associated ``FusionRing``'s + ``CyclotomicField``, a computed ``NumberField``, or ``QQbar``. + Currently, the output field is determined based on the ``CartanType`` + associated to ``self``. See :meth:``attempt_number_field_computation`` + for details. """ #Set multiprocessing parameters. Context can only be set once, so we try to set it self.clear_equations() @@ -1029,22 +1266,29 @@ def find_orthogonal_solution(self,use_mp=True,save_dir="",verbose=True): if verbose: print("Computing F-symbols for {} with {} variables...".format(self._FR, len(self._fvars))) + #Attempt warm-start + try: + self.load_fvars(save_dir) + except FileNotFoundError: + pass + #Set up hexagon equations and orthogonality constraints - poly_sortkey = cmp_to_key(poly_tup_cmp) + poly_sortkey = poly_tup_sortkey_degrevlex self.get_orthogonality_constraints(output=False) self.get_defining_equations('hexagons',worker_pool=pool,output=False) if verbose: print("Set up {} hex and orthogonality constraints...".format(len(self.ideal_basis))) #Set up equations graph. Find GB for each component in parallel. Eliminate variables - self.ideal_basis = sorted(self.par_graph_gb(worker_pool=pool,verbose=verbose), key=poly_sortkey) + self.ideal_basis = self.par_graph_gb(worker_pool=pool,verbose=verbose) + self.ideal_basis.sort(key=poly_sortkey) self.triangular_elim(worker_pool=pool,verbose=verbose) #Report progress and checkpoint! if verbose: print("Hex elim step solved for {} / {} variables".format(len(self.solved), len(self._poly_ring.gens()))) filename = save_dir + "saved_fvars_" + self.get_fr_str() + ".pickle" - self.save_fvars(filename) + if checkpoint: self.save_fvars(filename) #Update reduction parameters, also in children if any self.update_reduction_params(worker_pool=pool,children_need_update=True) @@ -1053,27 +1297,30 @@ def find_orthogonal_solution(self,use_mp=True,save_dir="",verbose=True): self.get_defining_equations('pentagons',worker_pool=pool,output=False) if verbose: print("Set up {} reduced pentagons...".format(len(self.ideal_basis))) - self.ideal_basis = sorted(self.ideal_basis, key=poly_sortkey) + self.ideal_basis.sort(key=poly_sortkey) self.triangular_elim(worker_pool=pool,verbose=verbose) #Report progress and checkpoint! if verbose: print("Pent elim step solved for {} / {} variables".format(len(self.solved), len(self._poly_ring.gens()))) - self.save_fvars(filename) + if checkpoint: self.save_fvars(filename) #Close worker pool to free resources if pool is not None: pool.close() #Set up new equations graph and compute variety for each component - self.ideal_basis = sorted(self.par_graph_gb(term_order="lex",verbose=verbose), key=poly_sortkey) + self.ideal_basis = self.par_graph_gb(term_order="lex",verbose=verbose) + self.ideal_basis.sort(key=poly_sortkey) self.triangular_elim(verbose=verbose) + if checkpoint: self.save_fvars(filename) self.get_explicit_solution(verbose=verbose) - self.save_fvars(filename) + + #The calculation was successful, so we may delete checkpoints self.symbols_known = True - self._FR._field = self.field() - self._FR._basecoer = self.get_coerce_map_from_fr_cyclotomic_field() - for x in self._FR.basis(): - x.q_dimension.clear_cache() + if checkpoint: + os.remove(filename) + if save_results: + self.save_fvars(filename) ######################### ### Cyclotomic method ### @@ -1114,29 +1361,12 @@ def substitute_degree_one(self, eqns=None): new_knowns.add(eq.lm()) useless.add(eq) - #Solve equation of the form x_i x_j + k == 0 for x_i - # print("equation: ", eq, "variables ", eq.variables()) - # if eq.degree() == 2 and max(eq.degrees()) == 1 and len(eq.variables()) == 2 and eq.variable(0) not in self.solved: - # self._fvars[self._var_to_sextuple[str(eq.variable(0))]] = - eq.constant_coefficient() / eq.variable(1) - # print("Subbed {} for {}".format(- eq.constant_coefficient() / eq.variable(1), eq.variable(0))) - # #Add variable to set of known values and remove this equation - # new_knowns.add(eq.variable(0)) - # useless.add(eq) - #Update fvars depending on other variables self.solved.update(new_knowns) for sextuple, rhs in self._fvars.items(): d = { var : self._fvars[self._var_to_sextuple[var]] for var in rhs.variables() if var in self.solved } - if len(d) == 2: print("THREE TERM LINEAR EQUATION ENCOUNTERED!") if d: self._fvars[sextuple] = rhs.subs(d) - - # if rhs.variables() and rhs.variable() in self.solved: - # assert rhs.is_univariate(), "RHS expression is not univariate" - # d = { rhs.variable() : } - # # print("Performing substitution of {} with dictionary {}".format(rhs, d)) - # self._fvars[sextuple] = rhs.subs(d) - return new_knowns, useless def update_equations(self): @@ -1225,7 +1455,6 @@ def find_cyclotomic_solution(self, equations=None, algorithm='', verbose=True, o ### Verifications ### ##################### - def verify_hexagons(self): """ Ensure the hexagon equations are satisfied diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index 3d0084c50ab..7e9c5ba980a 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -17,7 +17,7 @@ from sage.combinat.root_system.poly_tup_engine cimport * from sage.rings.ideal import Ideal from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -from sage.rings.qqbar import number_field_elements_from_algebraics +# from sage.rings.qqbar import number_field_elements_from_algebraics #Define a global temporary worker results repository cdef list worker_results = list() @@ -89,41 +89,41 @@ def executor(params): ### Mappers ### ############### -cdef _fmat(fvars, _Nk_ij, one, a, b, c, d, x, y): +cdef _fmat(fvars, _Nk_ij, id_anyon, a, b, c, d, x, y): """ Cython version of fmat class method. Using cdef for fastest dispatch """ if _Nk_ij(a,b,x) == 0 or _Nk_ij(x,c,d) == 0 or _Nk_ij(b,c,y) == 0 or _Nk_ij(a,y,d) == 0: return 0 #Some known F-symbols - if a == one: + if a == id_anyon: if x == b and y == d: return 1 else: return 0 - if b == one: + if b == id_anyon: if x == a and y == c: return 1 else: return 0 - if c == one: + if c == id_anyon: if x == d and y == b: return 1 else: return 0 return fvars[a,b,c,d,x,y] -cdef req_cy(tuple basis, r_matrix, dict fvars, Nk_ij, one, tuple sextuple): +cdef req_cy(tuple basis, r_matrix, dict fvars, Nk_ij, id_anyon, tuple sextuple): """ Given an FMatrix factory and a sextuple, return a hexagon equation as a polynomial object """ a, b, c, d, e, g = sextuple #To add typing we need to ensure all fmats.fmat are of the same type? #Return fmats._poly_ring.zero() and fmats._poly_ring.one() instead of 0 and 1? - lhs = r_matrix(a,c,e)*_fmat(fvars,Nk_ij,one,a,c,b,d,e,g)*r_matrix(b,c,g) + lhs = r_matrix(a,c,e)*_fmat(fvars,Nk_ij,id_anyon,a,c,b,d,e,g)*r_matrix(b,c,g) rhs = 0 for f in basis: - rhs += _fmat(fvars,Nk_ij,one,c,a,b,d,e,f)*r_matrix(f,c,d)*_fmat(fvars,Nk_ij,one,a,b,c,d,f,g) + rhs += _fmat(fvars,Nk_ij,id_anyon,c,a,b,d,e,f)*r_matrix(f,c,d)*_fmat(fvars,Nk_ij,id_anyon,a,b,c,d,f,g) return lhs-rhs @cython.wraparound(False) @@ -138,35 +138,37 @@ cpdef get_reduced_hexagons(factory, tuple mp_params): cdef child_id, n_proc cdef unsigned long i child_id, n_proc = mp_params - cdef tuple sextuple + cdef tuple sextuple, red #Pre-compute common parameters for speed cdef tuple basis = tuple(factory._FR.basis()) cdef dict fvars = factory._fvars r_matrix = factory._FR.r_matrix _Nk_ij = factory._FR.Nk_ij - one = factory._FR.one() + id_anyon = factory._FR.one() + cdef NumberFieldElement_absolute one = factory._field.one() cdef ETuple _nnz = factory._nnz cdef dict _ks = factory._ks #Computation loop for i, sextuple in enumerate(product(basis,repeat=6)): if i % n_proc == child_id: - he = req_cy(basis,r_matrix,fvars,_Nk_ij,one,sextuple) + he = req_cy(basis,r_matrix,fvars,_Nk_ij,id_anyon,sextuple) if he: - worker_results.append(reduce_poly_dict(he.dict(),_nnz,_ks)) + red = reduce_poly_dict(he.dict(),_nnz,_ks,one) + worker_results.append(red) -cdef MPolynomial_libsingular feq_cy(tuple basis, fvars, Nk_ij, one, zero, tuple nonuple, bint prune=False): +cdef MPolynomial_libsingular feq_cy(tuple basis, fvars, Nk_ij, id_anyon, zero, tuple nonuple, bint prune=False): """ Given an FMatrix factory and a nonuple, return a pentagon equation as a polynomial object """ a, b, c, d, e, f, g, k, l = nonuple - cdef lhs = _fmat(fvars,Nk_ij,one,f,c,d,e,g,l)*_fmat(fvars,Nk_ij,one,a,b,l,e,f,k) + cdef lhs = _fmat(fvars,Nk_ij,id_anyon,f,c,d,e,g,l)*_fmat(fvars,Nk_ij,id_anyon,a,b,l,e,f,k) if lhs == 0 and prune: # it is believed that if lhs=0, the equation carries no new information return zero cdef rhs = zero for h in basis: - rhs += _fmat(fvars,Nk_ij,one,a,b,c,g,f,h)*_fmat(fvars,Nk_ij,one,a,h,d,e,g,k)*_fmat(fvars,Nk_ij,one,b,c,d,k,h,l) + rhs += _fmat(fvars,Nk_ij,id_anyon,a,b,c,g,f,h)*_fmat(fvars,Nk_ij,id_anyon,a,h,d,e,g,k)*_fmat(fvars,Nk_ij,id_anyon,b,c,d,k,h,l) return lhs - rhs @cython.wraparound(False) @@ -189,7 +191,8 @@ cpdef get_reduced_pentagons(factory, tuple mp_params, bint prune=True): cdef dict fvars = factory._fvars r_matrix = factory._FR.r_matrix _Nk_ij = factory._FR.Nk_ij - one = factory._FR.one() + id_anyon = factory._FR.one() + cdef NumberFieldElement_absolute one = factory._field.one() cdef ETuple _nnz = factory._nnz cdef dict _ks = factory._ks cdef MPolynomial_libsingular zero = factory._poly_ring.zero() @@ -197,9 +200,9 @@ cpdef get_reduced_pentagons(factory, tuple mp_params, bint prune=True): #Computation loop for i, nonuple in enumerate(product(basis,repeat=9)): if i % n_proc == child_id: - pe = feq_cy(basis,fvars,_Nk_ij,one,zero,nonuple,prune=prune) + pe = feq_cy(basis,fvars,_Nk_ij,id_anyon,zero,nonuple,prune=prune) if pe: - red = reduce_poly_dict(pe.dict(),_nnz,_ks) + red = reduce_poly_dict(pe.dict(),_nnz,_ks,one) worker_results.append(red) cpdef update_reduce(factory, tuple eq_tup): @@ -207,8 +210,9 @@ cpdef update_reduce(factory, tuple eq_tup): Substitute known values, known squares, and reduce! """ global worker_results - cdef dict eq_dict = subs(eq_tup,factory._kp) - cdef tuple red = reduce_poly_dict(eq_dict,factory._nnz,factory._ks) + cdef NumberFieldElement_absolute one =factory._field.one() + cdef dict eq_dict = subs(eq_tup,factory._kp,one) + cdef tuple red = reduce_poly_dict(eq_dict,factory._nnz,factory._ks,one) worker_results.append(red) cpdef compute_gb(factory, tuple args): @@ -243,20 +247,22 @@ cpdef compute_gb(factory, tuple args): cpdef update_child_fmats(factory, tuple data_tup): """ - One-to-all communication used to update fvars after triangular elim step. + One-to-all communication used to update FMatrix object after each triangular + elim step. We must update the algorithm's state values. These are: + _fvars, solved, _ks, _var_degs, _nnz, and _kp. """ #factory object is assumed global before forking used to create the Pool object, #so each child has a global fmats variable. So it's enough to update that object factory._fvars, factory.solved, factory._ks, factory._var_degs = data_tup - factory._nnz = factory.get_known_nonz() - factory._kp = compute_known_powers(factory._var_degs,factory.get_known_vals()) + factory._nnz = factory._get_known_nonz() + factory._kp = compute_known_powers(factory._var_degs,factory._get_known_vals(),factory._field.one()) -def get_appropriate_number_field(factory,non_cyclotomic_roots): - """ - If needed, find a NumberField containing the roots given roots - """ - roots = [factory._FR.field().gen()]+[r[1] for r in non_cyclotomic_roots] - return number_field_elements_from_algebraics(roots,minimal=True) +# def get_appropriate_number_field(factory,non_cyclotomic_roots): +# """ +# If needed, find a NumberField containing the roots given roots +# """ +# roots = [factory._FR.field().gen()]+[r[1] for r in non_cyclotomic_roots] +# return number_field_elements_from_algebraics(roots,minimal=True) ################ ### Reducers ### diff --git a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx index 01016ca3e0d..a55bc74b83a 100644 --- a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx @@ -77,7 +77,7 @@ cpdef mid_sig_ij(fusion_ring,row,col,a,b): sage: FR.fusion_labels(['idd','one','two','three','four'],inject_variables=True) sage: one.weight() (1/2, -1/2) - sage: FR.get_comp_basis(one,two,4) + sage: FR.get_computational_basis(one,two,4) [(two, two), (two, idd), (idd, two)] sage: mid_sig_ij(FR, (two, two), (two, idd), one, two) (zeta48^10 - zeta48^2)*fx0*fx1*fx8 + (zeta48^2)*fx2*fx3*fx8 @@ -117,7 +117,7 @@ cpdef odd_one_out_ij(fusion_ring,xi,xj,a,b): sage: FR.fusion_labels(['I0','Y1','X','Z','Xp','Y2'],inject_variables=True) sage: X.weight() (1/2, 1/2) - sage: FR.get_comp_basis(X,X,3) + sage: FR.get_computational_basis(X,X,3) [(Y2,), (Y1,), (I0,)] sage: odd_one_out_ij(FR,Y2,Y1,X,X) (zeta40^10)*fx205*fx208 + (zeta40^14 - zeta40^10 + zeta40^6 - zeta40^2)*fx206*fx209 + (zeta40^2)*fx207*fx210 @@ -157,7 +157,7 @@ cpdef sig_2k(fusion_ring, tuple args): cdef int ctr = -1 global worker_results #Get computational basis - cdef list comp_basis = fusion_ring.get_comp_basis(a,b,n_strands) + cdef list comp_basis = fusion_ring.get_computational_basis(a,b,n_strands) cdef dict basis_dict = { elt : i for i, elt in enumerate(comp_basis) } cdef int dim = len(comp_basis) cdef set entries = set() @@ -220,7 +220,7 @@ cpdef odd_one_out(fusion_ring, tuple args): a, b, n_strands = fn_args cdef ctr = -1 #Get computational basis - comp_basis = fusion_ring.get_comp_basis(a,b,n_strands) + comp_basis = fusion_ring.get_computational_basis(a,b,n_strands) basis_dict = { elt : i for i, elt in enumerate(comp_basis) } dim = len(comp_basis) for i in range(dim): diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 5ba9f6eea0e..4031c3e10ea 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -1012,7 +1012,7 @@ def get_trees(self,top_row,root): def get_computational_basis(self,a,b,n_strands): """ - Return the so-called computational basis for `\text{Hom}(b, a^n)`. + Return the so-called computational basis for `\\text{Hom}(b, a^n)`. INPUT: @@ -1021,7 +1021,7 @@ def get_computational_basis(self,a,b,n_strands): - ``n_strands`` -- the number of strands for a braid group Let `n=` ``n_strands`` and let `k` be the greatest integer `\leqslant n/2`. - The braid group acts on ``\text{Hom}(b,a^n)``. This action + The braid group acts on `\\text{Hom}(b,a^n)`. This action is computed in :meth:`get_braid_generators`. This method returns the computational basis in the form of a set of fusion trees. Each tree is represented by a `(n-2)`-tuple @@ -1031,7 +1031,7 @@ def get_computational_basis(self,a,b,n_strands): (m_1,\cdots,m_k,l_1,\cdots,l_{k-2}) These are computed by :meth:`get_trees`. As a computational - device when ``n_strands`` is odd, we pad the vector `(m_1,\cdots,m_k)` + device when ``n_strands`` is odd, we pad the vector `(m_1,\cdots,m_k)` with an additional `m_{k+1}` equal to `a` before passing it to :meth:`get_trees`. This `m_{k+1}` does not appear in the output of this method. @@ -1052,17 +1052,17 @@ def fmats(self): """ return FMatrix.FMatrix(self) - def emap(self,mapper,input_args,worker_pool=None): + def _emap(self,mapper,input_args,worker_pool=None): """ Apply the given mapper to each element of the given input iterable and return the results (with no duplicates) in a list. This method applies the mapper in parallel if a worker_pool is provided. - # INPUT: - mapper is a string specifying the name of a function defined in the - fast_parallel_fmats_methods module. + INPUT: - input_args should be a tuple holding arguments to be passed to mapper + - ``mapper`` -- a string specifying the name of a function defined in the + fast_parallel_fmats_methods module. + - ``input_args`` -- a tuple holding arguments to be passed to mapper ##NOTES: If worker_pool is not provided, function maps and reduces on a single process. @@ -1095,9 +1095,9 @@ def get_braid_generators(self,fusing_anyon,total_charge_anyon,n_strands,use_mp=T """ INPUT: - Compute generators of the Artin braid group on n_strands strands. If + Compute generators of the Artin braid group on `n=` n_strands strands. If fusing_anyon = a and total_charge_anyon = b, the generators are - endomorphisms of Hom(a^n_strands, b) + endomorphisms of `\\text{Hom}(b, a^n)` - ``fusing_anyon`` -- a basis element of self - ``total_charge_anyon`` -- a basis element of self @@ -1143,7 +1143,7 @@ def get_braid_generators(self,fusing_anyon,total_charge_anyon,n_strands,use_mp=T True """ - assert n_strands > 2, "The number of strands must be an integer greater than 2" + assert int(n_strands) > 2, "The number of strands must be an integer greater than 2" #Construct associated FMatrix object and solve for F-symbols if not self.fmats.symbols_known: self.fmats.find_orthogonal_solution(verbose=verbose) @@ -1167,12 +1167,12 @@ def get_braid_generators(self,fusing_anyon,total_charge_anyon,n_strands,use_mp=T #Compute even-indexed generators using F-matrices for k in range(1,n_strands//2): - entries = self.emap('sig_2k',(k,a,b,n_strands),pool) + entries = self._emap('sig_2k',(k,a,b,n_strands),pool) gens[2*k] = matrix(dict(entries)) #If n_strands is odd, we compute the final generator if n_strands % 2: - entries = self.emap('odd_one_out',(a,b,n_strands),pool) + entries = self._emap('odd_one_out',(a,b,n_strands),pool) gens[n_strands-1] = matrix(dict(entries)) return comp_basis, [gens[k] for k in sorted(gens)] diff --git a/src/sage/combinat/root_system/poly_tup_engine.pxd b/src/sage/combinat/root_system/poly_tup_engine.pxd index 5d5e49263d3..5f614ae69de 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pxd +++ b/src/sage/combinat/root_system/poly_tup_engine.pxd @@ -1,6 +1,6 @@ from sage.rings.polynomial.polydict cimport ETuple from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular, MPolynomialRing_libsingular - +from sage.rings.number_field.number_field_element cimport NumberFieldElement_absolute cpdef tuple poly_to_tup(MPolynomial_libsingular poly) cpdef MPolynomial_libsingular tup_to_poly(tuple eq_tup, MPolynomialRing_libsingular parent) @@ -11,11 +11,10 @@ cpdef constant_coeff(tuple eq_tup) cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map) cpdef bint tup_fixes_sq(tuple eq_tup) cpdef dict subs_squares(dict eq_dict, dict known_sq) -cdef tuple to_monic(dict eq_dict) -cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq) -cpdef dict compute_known_powers(ETuple max_deg, dict val_dict) -cpdef dict subs(tuple poly_tup, dict known_powers) -cpdef int poly_tup_cmp(tuple tleft, tuple tright) +cpdef dict compute_known_powers(ETuple max_deg, dict val_dict, one) +cpdef dict subs(tuple poly_tup, dict known_powers, one) +# cpdef int poly_tup_cmp(tuple tleft, tuple tright) +cpdef tuple poly_tup_sortkey_degrevlex(tuple eq_tup) -cdef tuple tup_mul(tuple p1, tuple p2) -cdef dict remove_gcf(dict eq_dict, ETuple nonz) +cpdef MPolynomial_libsingular _tup_to_poly(tuple eq_tup, MPolynomialRing_libsingular parent) +cpdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq, NumberFieldElement_absolute one) diff --git a/src/sage/combinat/root_system/poly_tup_engine.pyx b/src/sage/combinat/root_system/poly_tup_engine.pyx index 8499745abeb..45b4f34679c 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pyx +++ b/src/sage/combinat/root_system/poly_tup_engine.pyx @@ -9,15 +9,11 @@ Arithmetic Engine for polynomials as tuples # **************************************************************************** from functools import cmp_to_key -from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular, MPolynomialRing_libsingular -from sage.rings.polynomial.polydict cimport ETuple from sage.rings.polynomial.term_order import TermOrder -from sage.rings.rational_field import QQ -from sage.arith.functions cimport LCM_list +# from sage.arith.functions cimport LCM_list #Pre-compute common values for speed -one = QQ.one() degrevlex_sortkey = TermOrder().sortkey_degrevlex ########### @@ -75,9 +71,21 @@ cpdef MPolynomial_libsingular tup_to_poly(tuple eq_tup, MPolynomialRing_libsingu # Maybe the following is faster but we need to ensure all coefficients are # already in fmats._poly_ring.base_ring() so that implicit casting is avoided # (this is pretty slow) - # return parent._element_constructor_({ exp : c for exp, c in tup_of_pairs }, check=False) + # return parent._element_constructor_(dict(eq_tup), check=False) return parent(dict(eq_tup)) +cpdef MPolynomial_libsingular _tup_to_poly(tuple eq_tup, MPolynomialRing_libsingular parent): + r""" + Faster version of :meth:`tup_to_poly`. Unsafe for client use, since it + avoids implicit casting and it may lead to segmentation faults. + + Safe for internal use because our methods ensure that + polynomial coefficients always lie in the same base ring. + """ + return parent._element_constructor_(dict(eq_tup), check=False) + + + ###################### ### "Change rings" ### ###################### @@ -210,6 +218,7 @@ cpdef constant_coeff(tuple eq_tup): sage: constant_coeff(poly_to_tup(x**5 + x*y*z - 9)) -9 """ + cdef ETuple exp for exp, coeff in eq_tup: if exp.is_constant(): return coeff @@ -228,6 +237,7 @@ cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map): sage: tup_to_poly(apply_coeff_map(poly_to_tup(x + 2*y + 3*z), sq), parent=R) x + 4*y + 9*z """ + cdef ETuple exp cdef list new_tup = list() for exp, coeff in eq_tup: new_tup.append((exp, coeff_map(coeff))) @@ -314,7 +324,7 @@ cdef dict remove_gcf(dict eq_dict, ETuple nonz): ret[exp.esub(common_powers)] = c return ret -cdef tuple to_monic(dict eq_dict): +cdef tuple to_monic(dict eq_dict, one): """ Return tuple of pairs (ETuple, coeff) describing the monic polynomial associated to eq_dict Here, the leading coefficient is chosen according to the degree reverse lexicographic ordering @@ -325,21 +335,22 @@ cdef tuple to_monic(dict eq_dict): cdef ETuple lm = ord_monoms[-1] lc = eq_dict[lm] if not lc: return tuple() - # cdef list ret = [(lm, one)] - cdef list ret = [(lm, lc.parent().one())] + cdef list ret = [(lm, one)] inv_lc = lc.inverse_of_unit() cdef ETuple exp for exp in reversed(ord_monoms[:-1]): ret.append((exp, inv_lc * eq_dict[exp])) return tuple(ret) -cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq): +cpdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq, NumberFieldElement_absolute one): """ Return a dictionary describing a monic polynomial with no known nonzero gcd and no known squares """ if not eq_dict: return tuple() - return to_monic(remove_gcf(subs_squares(eq_dict, known_sq), nonz)) + cdef dict sq_rmvd = subs_squares(eq_dict, known_sq) + cdef dict gcf_rmvd = remove_gcf(sq_rmvd, nonz) + return to_monic(gcf_rmvd, one) # cdef int common_denom(tuple eq_tup): # #Compute the common denominator @@ -370,7 +381,7 @@ cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq): ### Substitution ### #################### -cpdef dict compute_known_powers(ETuple max_deg, dict val_dict): +cpdef dict compute_known_powers(ETuple max_deg, dict val_dict, one): """ Pre-compute powers of known values for efficiency when preparing to substitute into a list of polynomials. @@ -390,7 +401,7 @@ cpdef dict compute_known_powers(ETuple max_deg, dict val_dict): sage: known_val = { 0 : poly_to_tup(R(-1)), 2 : poly_to_tup(y**2) } sage: from sage.combinat.root_system.poly_tup_engine import get_variables_degrees sage: max_deg = get_variables_degrees([poly_to_tup(p) for p in polys]) - sage: compute_known_powers(max_deg, known_val) + sage: compute_known_powers(max_deg, known_val, R.base_ring().one()) {0: [(((0, 0, 0), 1),), (((0, 0, 0), -1),), (((0, 0, 0), 1),), @@ -413,7 +424,7 @@ cpdef dict compute_known_powers(ETuple max_deg, dict val_dict): known_powers[var_idx][power+1] = tup_mul(known_powers[var_idx][power],val_dict[var_idx]) return known_powers -cpdef dict subs(tuple poly_tup, dict known_powers): +cpdef dict subs(tuple poly_tup, dict known_powers, one): """ Substitute given variables into a polynomial tuple @@ -426,10 +437,11 @@ cpdef dict subs(tuple poly_tup, dict known_powers): sage: known_val = { 0 : poly_to_tup(R(-1)), 2 : poly_to_tup(y**2) } sage: max_deg = get_variables_degrees([poly_to_tup(p) for p in polys]) sage: poly_tup = poly_to_tup(polys[0]) - sage: subs(poly_tup, compute_known_powers(max_deg, known_val)) + sage: one = R.base_ring().one() + sage: subs(poly_tup, compute_known_powers(max_deg, known_val, one), one) {(0, 0, 0): 0} sage: poly_tup = poly_to_tup(polys[1]) - sage: subs(poly_tup, compute_known_powers(max_deg, known_val)) + sage: subs(poly_tup, compute_known_powers(max_deg, known_val, one), one) {(0, 1, 0): 1, (0, 6, 0): 1} """ cdef dict subbed = {} @@ -438,8 +450,7 @@ cpdef dict subs(tuple poly_tup, dict known_powers): cdef tuple temp for exp, coeff in poly_tup: #Get polynomial unit as tuple - # temp = ((exp._new(), one),) - temp = ((exp._new(), coeff.parent().one()),) + temp = ((exp._new(), one),) for var_idx, power in exp.sparse_iter(): if var_idx in known_powers: exp = exp.eadd_p(-power,var_idx) @@ -473,54 +484,104 @@ cdef tuple tup_mul(tuple p1, tuple p2): #Implement richcmp comparator object that can be passed in as key to sorted method -cpdef int poly_tup_cmp(tuple tleft, tuple tright): - """ - Determine which polynomial is larger with respect to the degrevlex ordering - - EXAMPLES:: - - sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_cmp - sage: R. = PolynomialRing(QQ) - sage: p1 = x*y*z - x**2 + 3/2 - sage: p2 = x*y*z - x * y +1/2 - sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup - sage: (p1 < p2) == (poly_tup_cmp(poly_to_tup(p1), poly_to_tup(p2)) < 0) - True - sage: R. = PolynomialRing(CyclotomicField(20)) - sage: zeta20 = R.base_ring().gen() - sage: p1 = zeta20**2 * x*z**2 - 2*zeta20 - sage: p2 = y**3 + 1/4 - sage: (p1 < p2) == (poly_tup_cmp(poly_to_tup(p1), poly_to_tup(p2)) < 0) - True - - TESTS: +# cpdef int poly_tup_cmp(tuple tleft, tuple tright): +# """ +# Determine which polynomial is larger with respect to the degrevlex ordering +# +# EXAMPLES:: +# +# sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_cmp +# sage: R. = PolynomialRing(QQ) +# sage: p1 = x*y*z - x**2 + 3/2 +# sage: p2 = x*y*z - x * y +1/2 +# sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup +# sage: (p1 < p2) == (poly_tup_cmp(poly_to_tup(p1), poly_to_tup(p2)) < 0) +# True +# sage: R. = PolynomialRing(CyclotomicField(20)) +# sage: zeta20 = R.base_ring().gen() +# sage: p1 = zeta20**2 * x*z**2 - 2*zeta20 +# sage: p2 = y**3 + 1/4 +# sage: (p1 < p2) == (poly_tup_cmp(poly_to_tup(p1), poly_to_tup(p2)) < 0) +# True +# +# TESTS: +# +# sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_cmp, poly_to_tup +# sage: R. = PolynomialRing(CyclotomicField(20)) +# sage: p1 = R.random_element() +# sage: p2 = R.random_element() +# sage: (p1 < p2) == (poly_tup_cmp(poly_to_tup(p1), poly_to_tup(p2)) < 0) +# True +# sage: (p1 > p2) == (poly_tup_cmp(poly_to_tup(p1), poly_to_tup(p2)) > 0) +# True +# sage: poly_tup_cmp(poly_to_tup(p1), poly_to_tup(p1)) == 0 +# True +# """ +# cdef int i, ret, sf, sg, val +# cdef ETuple f, g +# ret = 0 +# for i in range(min(len(tleft),len(tright))): +# f, g = tleft[i][0], tright[i][0] +# if f == g: +# if tleft[i][1] != tright[i][1]: +# ret = -1 + 2*(tleft[i][1] > tright[i][1]) +# else: +# sf, sg = 0, 0 +# for val in f.nonzero_values(sort=False): +# sf += val +# for val in g.nonzero_values(sort=False): +# sg += val +# ret = -1 + 2*(sf > sg or ( sf == sg and f.reversed() < g.reversed() )) +# if ret != 0: +# return ret +# return len(tleft) - len(tright) + +import numpy as np +cpdef tuple poly_tup_sortkey_degrevlex(tuple eq_tup): + """ + Return the sortkey of a polynomial represented as a tuple of (ETuple, coeff) + pairs with respect to the degree reversed lexicographical term order. + + Using this key to sort polynomial tuples results in comparing polynomials + term by term (we assume the tuple representation is sorted so that the + leading term with respect to the degree reverse lexicographical order comes + first). For each term, we first compare degrees, then the monomials themselves, + then the corresponding coefficient. + + This method relies on the built-in comparison of ETuple's. + + EXAMPLES :: - sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_cmp, poly_to_tup - sage: R. = PolynomialRing(CyclotomicField(20)) - sage: p1 = R.random_element() - sage: p2 = R.random_element() - sage: (p1 < p2) == (poly_tup_cmp(poly_to_tup(p1), poly_to_tup(p2)) < 0) - True - sage: (p1 > p2) == (poly_tup_cmp(poly_to_tup(p1), poly_to_tup(p2)) > 0) - True - sage: poly_tup_cmp(poly_to_tup(p1), poly_to_tup(p1)) == 0 - True """ - cdef int i, ret, sf, sg, val - cdef ETuple f, g - ret = 0 - for i in range(min(len(tleft),len(tright))): - f, g = tleft[i][0], tright[i][0] - if f == g: - if tleft[i][1] != tright[i][1]: - ret = -1 + 2*(tleft[i][1] > tright[i][1]) - else: - sf, sg = 0, 0 - for val in f.nonzero_values(sort=False): - sf += val - for val in g.nonzero_values(sort=False): - sg += val - ret = -1 + 2*(sf > sg or ( sf == sg and f.reversed() < g.reversed() )) - if ret != 0: - return ret - return len(tleft) - len(tright) + cdef ETuple exp + cdef int i, l, nnz + key = list() + # for exp, c in eq_tup: + # key.extend([sum(exp.nonzero_values(sort=False)),exp.reversed().emul(-1),c._coefficients()]) + # for exp, c in eq_tup: + # #Compare by term degree + # key.append(exp.unweighted_degree()) + # revlex_exp = exp.reversed().emul(-1) + # #Then by term w.r.t. revlex order + # revlex_exp_key = list() + # for i from 0 <= i < exp._nonzero: + # #Reverse tuple and negate values + # # key.append(-(exp._length - exp._data[2*(exp._nonzero-i-1)] - 1)) + # # key.append(-exp._data[2*(exp._nonzero-i-1)+1]) + # key.append(-exp._data[2*i]) + # key.append(exp._data[2*i+1]) + # #Finally by coefficient + # key.extend(c._coefficients()) + for exp, c in eq_tup: + #Compare by term degree + key.append(exp.unweighted_degree()) + #Next compare by term w.r.t. revlex order + l = exp._length + nnz = exp._nonzero + for i from 0 <= i < nnz: + # key.append(l-1-exp._data[2*(nnz-i-1)]) + # key.append(-exp._data[2*(nnz-i-1)+1]) + # Try sorting in lex order instead + key.append(-exp._data[2*i]) + key.append(exp._data[2*i+1]) + return tuple(key) From 6ceb6d9a3c97f760c21b3b44396508fbe7b3f53e Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Fri, 19 Mar 2021 08:03:06 -0700 Subject: [PATCH 029/632] f_matrix documentation added to reference manual --- src/doc/en/reference/combinat/module_list.rst | 1 + src/doc/en/reference/references/index.rst | 7 +- src/sage/combinat/root_system/__init__.py | 1 + src/sage/combinat/root_system/f_matrix.py | 423 +++++++++++------- .../fast_parallel_fmats_methods.pyx | 9 +- .../fast_parallel_fusion_ring_braid_repn.pyx | 21 +- src/sage/combinat/root_system/fusion_ring.py | 120 ++++- .../combinat/root_system/poly_tup_engine.pyx | 25 -- 8 files changed, 393 insertions(+), 214 deletions(-) diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index a95069590b4..3cc1f591827 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -290,6 +290,7 @@ Comprehensive Module list sage/combinat/root_system/weight_space sage/combinat/root_system/weyl_characters sage/combinat/root_system/fusion_ring + sage/combinat/root_system/f_matrix sage/combinat/root_system/weyl_group sage/combinat/rooted_tree sage/combinat/rsk diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 808d2fa07c1..76fc6693f18 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1731,7 +1731,9 @@ REFERENCES: curves. preprint, 2005. .. [CHW2015] Shawn X.; Hong, Seung-Moon; Wang, Zhenghan Universal quantum computation - with weakly integral anyons. Quantum Inf. Process. 14 (2015), no. 8, 2687–2727. + with weakly integral anyons. Quantum Inf. Process. 14 (2015), no. 8, 2687–2727. + + .. _ref-D: **D** @@ -5476,6 +5478,9 @@ REFERENCES: calculus*, Algebra and Number Theory 3 (2009), 121-148, https://projecteuclid.org/euclid.ant/1513797353 +.. [TTWL2009] Trebst, Troyer, Wang and Ludwig, A short introduction to + Fibonacci anyon models, :arxiv:`0902.3275`. + .. _ref-U: **U** diff --git a/src/sage/combinat/root_system/__init__.py b/src/sage/combinat/root_system/__init__.py index b04c503fad1..694205392d1 100644 --- a/src/sage/combinat/root_system/__init__.py +++ b/src/sage/combinat/root_system/__init__.py @@ -78,6 +78,7 @@ - :ref:`sage.combinat.root_system.weyl_characters` - :ref:`sage.combinat.root_system.fusion_ring` +- :ref:`sage.combinat.root_system.f_matrix` - :ref:`sage.combinat.root_system.integrable_representations` - :ref:`sage.combinat.root_system.branching_rules` - :ref:`sage.combinat.root_system.hecke_algebra_representation` diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 08563fade2e..9df09e88ab7 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -47,6 +47,18 @@ class FMatrix(): - ``FR`` -- a FusionRing. + The :class:`FusionRing` or Verlinde algebra is the + Grothendieck ring of a modular tensor category [Baki2001]_. + Such categories arise in conformal field theory or in the + representation theories of affine Lie algebras, or + quantum groups at roots of unity. They have applications + to low dimensional topology and knot theory, to conformal + field theory and to topological quantum computing. The + FusionRing captures much information about a fusion category, + but to complete the picture, the F-matrices or 6j-symbols + are needed. For example these are required in order to + construct braid group representations. + We only undertake to compute the F-matrix if the FusionRing is *multiplicity free* meaning that the Fusion coefficients `N^{ij}_k` are bounded @@ -104,7 +116,7 @@ class FMatrix(): definition and [Bond2007]_ Section 2.5 for a discussion of how to compute the F-matrix. In addition to [Bond2007]_ worked out F-matrices may be found in - [RoStWa2009]_ and [CuWa2015]_. + [RoStWa2009]_ and [CHW2015]_. The F-matrix is only determined up to a *gauge*. This is a family of embeddings `C\to A\otimes B` for @@ -228,7 +240,10 @@ class FMatrix(): EXAMPLES:: - + sage: f = FMatrix(FusionRing("B3",2)) + sage: f.find_orthogonal_solution(verbose=False,checkpoint=True) # long time + sage: all(v in CyclotomicField(56) for v in f.get_fvars().values()) # long time + True """ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variables=False): @@ -244,25 +259,20 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab print ("creating variables %s%s..%s%s"%(var_prefix,1,var_prefix,n_vars)) self._poly_ring.inject_variables(get_main_globals()) self._var_to_sextuple, self._fvars = self.findcases(output=True) + self._var_to_idx = { var : idx for idx, var in enumerate(self._poly_ring.gens()) } self._singles = self.singletons() - #Initialize list of defining equations - self.ideal_basis = list() - #Base field attributes self._field = self._FR.field() r = self._field.defining_polynomial().roots(ring=QQbar,multiplicities=False)[0] self._qqbar_embedding = self._field.hom([r],QQbar) self._non_cyc_roots = list() - #Solver state attributes - #Initialize empty set of solved F-symbols - self.solved = set() - self._var_to_idx = { var : idx for idx, var in enumerate(self._poly_ring.gens()) } + #Useful solver state attributes + self._solved = set() self._ks = dict() self._nnz = self._get_known_nonz() - self._known_vals = dict() - self.symbols_known = False + self._chkpt_status = -1 #Multiprocessing attributes self.mp_thresh = 10000 @@ -284,7 +294,7 @@ def remaining_vars(self): """ Return a list of unknown F-symbols (reflects current stage of computation) """ - return [var for var in self._poly_ring.gens() if var not in self.solved] + return [var for var in self._poly_ring.gens() if var not in self._solved] def clear_equations(self): """ @@ -296,9 +306,38 @@ def clear_vars(self): """ Clear the set of variables. Also reset the set of solved F-symbols. """ - self._FR._basecoer = None self._fvars = { self._var_to_sextuple[key] : key for key in self._var_to_sextuple } - self.solved = set() + self._solved = set() + + def _reset_solver_state(self): + """ + Reset solver state and clear relevant cache. + """ + self._FR._basecoer = None + self._field = self._FR.field() + self._update_poly_ring_base_field(field=self._field) + self._chkpt_status = -1 + self.clear_vars() + self.clear_equations() + self._ks = dict() + self._nnz = self._get_known_nonz() + + #Clear relevant caches + [x.q_dimension.clear_cache() for x in self._FR.basis()] + self._FR.r_matrix.clear_cache() + self._FR.s_ij.clear_cache() + + def _update_poly_ring_base_field(self,field): + """ + Change base field of PolynomialRing and the corresponding + index attributes + """ + new_poly_ring = self._poly_ring.change_ring(field) + nvars = self._poly_ring.ngens() + #Do some appropriate conversions + self._var_to_idx = { new_poly_ring.gen(i) : i for i in range(nvars) } + self._var_to_sextuple = { new_poly_ring.gen(i) : self._var_to_sextuple[self._poly_ring.gen(i)] for i in range(nvars) } + self._poly_ring = new_poly_ring def fmat(self, a, b, c, d, x, y, data=True): """ @@ -459,8 +498,9 @@ def findcases(self,output=False): return i def singletons(self): - """ - Find x_i that are automatically nonzero, because their F-matrix is 1x1 + r""" + Find `x_i` that are automatically nonzero, because their F-matrix is + `1 \\times 1`. """ ret = [] for (a, b, c, d) in list(product(self._FR.basis(), repeat=4)): @@ -600,9 +640,10 @@ def get_coerce_map_from_fr_cyclotomic_field(self): def get_fmats_in_alg_field(self): r""" - Return F-symbols as elements of the ``AlgebraicField``. This method uses - the embedding defined by :meth:``self.get_qqbar_embedding`` to coerce - F-symbols into QQbar. + Return F-symbols as elements of the :class:`AlgebraicField` ``QQbar``. + This method uses the embedding defined by + :meth:``self.get_qqbar_embedding`` to coerce + F-symbols into ``QQbar``. """ return { sextuple : self._qqbar_embedding(fvar) for sextuple, fvar in self._fvars.items() } @@ -621,7 +662,7 @@ def _get_known_vals(self): Construct a dictionary of ``idx``, ``known_val`` pairs used for substituting into remaining equations. """ - return { var_idx : self._fvars[self._var_to_sextuple[self._poly_ring.gen(var_idx)]] for var_idx in self.solved } + return { var_idx : self._fvars[self._var_to_sextuple[self._poly_ring.gen(var_idx)]] for var_idx in self._solved } def _get_known_sq(self,eqns=None): r""" @@ -657,13 +698,13 @@ def _is_univariate_in_unknown(self,monom_exp): """ Determine if monomial exponent is univariate in an unknown F-symbol """ - return len(monom_exp.nonzero_values()) == 1 and monom_exp.nonzero_positions()[0] not in self.solved + return len(monom_exp.nonzero_values()) == 1 and monom_exp.nonzero_positions()[0] not in self._solved def _is_uni_linear_in_unkwown(self,monom_exp): """ Determine if monomial exponent is univariate and linear in an unknown F-symbol """ - return monom_exp.nonzero_values() == [1] and monom_exp.nonzero_positions()[0] not in self.solved + return monom_exp.nonzero_values() == [1] and monom_exp.nonzero_positions()[0] not in self._solved ############################## ### Variables partitioning ### @@ -683,7 +724,7 @@ def largest_fmat_size(self): def get_fvars_by_size(self,n,indices=False): r""" - Return the set of F-symbols that are entries of an `n \times n` matrix + Return the set of F-symbols that are entries of an `n \\times n` matrix `F^{a,b,c}_d`. INPUT: @@ -712,7 +753,7 @@ def get_fvars_by_size(self,n,indices=False): (f2, f2, f2, f2, f1, f1)} """ fvars_copy = deepcopy(self._fvars) - solved_copy = deepcopy(self.solved) + solved_copy = deepcopy(self._solved) self.clear_vars() var_set = set() for quadruple in product(self._FR.basis(),repeat=4): @@ -721,7 +762,7 @@ def get_fvars_by_size(self,n,indices=False): if F.nrows() == n and F.coefficients() != [1]: var_set.update(F.coefficients()) self._fvars = fvars_copy - self.solved = solved_copy + self._solved = solved_copy if indices: return { self._var_to_idx[fx] for fx in var_set } return { self._var_to_sextuple[fx] for fx in var_set } @@ -730,6 +771,30 @@ def get_fvars_by_size(self,n,indices=False): ### Checkpoint utilities ### ############################ + def save_fvars(self, filename): + r""" + Save computed F-symbols for later use. This method should only be used + AFTER successfully one of the solvers, e.g. + :meth:`find_cyclotomic_solution` or :meth:`find_orthogonal_solution`. + + Specify the ``filename`` as a string for storing computed F-symbols in a + pickle file. + """ + with open(filename, 'wb') as f: + pickle.dump(self._fvars, f) + + def load_fvars(self, filename): + r""" + Load previously computed F-symbols from a pickle file generated by + :meth:`save_fvars`. This method does not work with intermediate + checkpoint pickles; it only works with pickles containing ALL F-symbols. + """ + with open(filename, 'rb') as f: + self._fvars = pickle.load(f) + #Update class state attributes + self.symbols_known = True + self._solved = set(range(self._poly_ring.ngens())) + def get_fr_str(self): """ Auto-generate identifying key for saving results @@ -743,46 +808,58 @@ def get_fr_str(self): ct = self._FR.cartan_type() return ct.letter + str(ct.n) + str(self._FR.fusion_level()) - def save_fvars(self,filename): + def _checkpoint(self,do_chkpt,status,verbose=True): """ - Save current variables state + Pickle current solver state """ + if not do_chkpt: + return + filename = "fmatrix_solver_checkpoint_" + self.get_fr_str() + ".pickle" + eqns = self.ideal_basis + self._add_square_fixers(output=True) with open(filename, 'wb') as f: - pickle.dump([self._fvars, self.solved], f) + pickle.dump([self._fvars, self._solved, eqns, status], f) + if verbose: + print(f"Checkpoint {status} reached!") - def load_fvars(self,save_dir=""): + def _restore_state(self,filename): r""" Load solver state from file. Use this method both for warm-starting :meth:`find_orthogonal_solution` and to load pickled results. - - If provided, the optional parameter ``save_dir`` must have a trailing - forward slash e.g. "my_dir/" """ - with open(save_dir + "saved_fvars_" + self.get_fr_str() + ".pickle", 'rb') as f: - self._fvars, self.solved = pickle.load(f) + with open(filename, 'rb') as f: + state = pickle.load(f) + #Loading saved results pickle + if type(state) == type(dict()): + self.load_fvars(filename) + self._chkpt_status = 7 + return + self._fvars, self._solved, self.ideal_basis, self._chkpt_status = state + self._update_reduction_params() ################# ### MapReduce ### ################# - def map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_thresh=None): - """ + def _map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_thresh=None): + r""" Apply the given mapper to each element of the given input iterable and - return the results (with no duplicates) in a list. This method applies the - mapper in parallel if a worker_pool is provided. + return the results (with no duplicates) in a list. This method applies + the mapper in parallel if a worker_pool is provided. INPUT: - -``mapper`` -- string specifying the name of a function defined in the - ``fast_parallel_fmats_methods`` module. + -``mapper`` -- string specifying the name of a function defined in + the ``fast_parallel_fmats_methods`` module. NOTES: - If worker_pool is not provided, function maps and reduces on a single process. - If worker_pool is provided, the function attempts to determine whether it should - use multiprocessing based on the length of the input iterable. If it can't determine - the length of the input iterable then it uses multiprocessing with the default chunksize of 1 - if chunksize is not explicitly provided. + If ``worker_pool`` is not provided, function maps and reduces on a + single process. + If ``worker_pool`` is provided, the function attempts to determine + whether it should use multiprocessing based on the length of the + input iterable. If it can't determine the length of the input + iterable then it uses multiprocessing with the default chunksize of + `1` if chunksize is not explicitly provided. """ if mp_thresh is None: mp_thresh = self.mp_thresh @@ -862,7 +939,7 @@ def get_defining_equations(self,option,worker_pool=None,output=True): """ n_proc = worker_pool._processes if worker_pool is not None else 1 params = [(child_id, n_proc) for child_id in range(n_proc)] - eqns = self.map_triv_reduce('get_reduced_'+option,params,worker_pool=worker_pool,chunksize=1,mp_thresh=0) + eqns = self._map_triv_reduce('get_reduced_'+option,params,worker_pool=worker_pool,chunksize=1,mp_thresh=0) if output: return [self._tup_to_fpoly(p) for p in eqns] self.ideal_basis.extend(eqns) @@ -887,6 +964,8 @@ def _tup_to_fpoly(self,eq_tup): def _solve_for_linear_terms(self,eqns=None): """ Solve for a linear term occurring in a two-term equation. + + Also solve for variables that are zero. """ if eqns is None: eqns = self.ideal_basis @@ -898,7 +977,7 @@ def _solve_for_linear_terms(self,eqns=None): if self._is_univariate_in_unknown(m): var = m.nonzero_positions()[0] self._fvars[self._var_to_sextuple[self._poly_ring.gen(var)]] = tuple() - self.solved.add(var) + self._solved.add(var) linear_terms_exist = True if len(eq_tup) == 2: monomials = [m for m, c in eq_tup] @@ -910,7 +989,7 @@ def _solve_for_linear_terms(self,eqns=None): rhs_key = monomials[other] rhs_coeff = -eq_tup[other][1] / eq_tup[this][1] self._fvars[self._var_to_sextuple[self._poly_ring.gen(var)]] = ((rhs_key,rhs_coeff),) - self.solved.add(var) + self._solved.add(var) linear_terms_exist = True return linear_terms_exist @@ -922,12 +1001,12 @@ def _backward_subs(self): for var in reversed(self._poly_ring.gens()): sextuple = self._var_to_sextuple[var] rhs = self._fvars[sextuple] - d = { var_idx : self._fvars[self._var_to_sextuple[self._poly_ring.gen(var_idx)]] for var_idx in variables(rhs) if var_idx in self.solved } + d = { var_idx : self._fvars[self._var_to_sextuple[self._poly_ring.gen(var_idx)]] for var_idx in variables(rhs) if var_idx in self._solved } if d: kp = compute_known_powers(get_variables_degrees([rhs]), d, one) self._fvars[sextuple] = tuple(subs_squares(subs(rhs,kp,one),self._ks).items()) - def update_reduction_params(self,eqns=None,worker_pool=None,children_need_update=False): + def _update_reduction_params(self,eqns=None,worker_pool=None,children_need_update=False): """ Update reduction parameters in all processes """ @@ -939,10 +1018,10 @@ def update_reduction_params(self,eqns=None,worker_pool=None,children_need_update if worker_pool is not None and children_need_update: #self._nnz and self._kp are computed in child processes to reduce IPC overhead n_proc = worker_pool._processes - new_data = [(self._fvars,self.solved,self._ks,self._var_degs)]*n_proc - self.map_triv_reduce('update_child_fmats',new_data,worker_pool=worker_pool,chunksize=1,mp_thresh=0) + new_data = [(self._fvars,self._solved,self._ks,self._var_degs)]*n_proc + self._map_triv_reduce('update_child_fmats',new_data,worker_pool=worker_pool,chunksize=1,mp_thresh=0) - def triangular_elim(self,required_vars=None,eqns=None,worker_pool=None,verbose=True): + def _triangular_elim(self,required_vars=None,eqns=None,worker_pool=None,verbose=True): """ Perform triangular elimination of linear terms in two-term equations until no such terms exist For optimal usage of TRIANGULAR elimination, pass in a SORTED list of equations @@ -953,7 +1032,6 @@ def triangular_elim(self,required_vars=None,eqns=None,worker_pool=None,verbose=T ret = False if required_vars is None: required_vars = self._poly_ring.gens() - # poly_sortkey = cmp_to_key(poly_tup_cmp) poly_sortkey = poly_tup_sortkey_degrevlex #Unzip polynomials @@ -969,8 +1047,8 @@ def triangular_elim(self,required_vars=None,eqns=None,worker_pool=None,verbose=T if req_vars_known: return 1 #Compute new reduction params, send to child processes if any, and update eqns - self.update_reduction_params(eqns=eqns,worker_pool=worker_pool,children_need_update=len(eqns)>self.mp_thresh) - eqns = self.map_triv_reduce('update_reduce',eqns,worker_pool=worker_pool) + self._update_reduction_params(eqns=eqns,worker_pool=worker_pool,children_need_update=len(eqns)>self.mp_thresh) + eqns = self._map_triv_reduce('update_reduce',eqns,worker_pool=worker_pool) eqns.sort(key=poly_sortkey) if verbose: print("Elimination epoch completed... {} eqns remain in ideal basis".format(len(eqns))) @@ -1022,13 +1100,20 @@ def partition_eqns(self,graph,eqns=None,verbose=True): print(graph.connected_components_sizes()) return partition - def add_square_fixers(self): + def _add_square_fixers(self,output=False): """ Add square fixing equations back to ideal basis """ + sq_fixers = list() + n = self._poly_ring.ngens() + one = self._field.one() for fx, rhs in self._ks.items(): - if fx not in self.solved: - self.ideal_basis.append(poly_to_tup(self._poly_ring.gen(fx)**2 - rhs)) + if fx not in self._solved: + lt = (ETuple({ fx : 2 },n), one) + sq_fixers.append((lt, (ETuple({},n), -rhs))) + if output: + return sq_fixers + self.ideal_basis.extend(sq_fixers) def par_graph_gb(self,worker_pool=None,eqns=None,term_order="degrevlex",verbose=True): """ @@ -1058,7 +1143,7 @@ def par_graph_gb(self,worker_pool=None,eqns=None,term_order="degrevlex",verbose= else: small_comps.append(comp_eqns) input_iter = zip_longest(small_comps,[],fillvalue=term_order) - small_comp_gb = self.map_triv_reduce('compute_gb',input_iter,worker_pool=worker_pool,chunksize=1,mp_thresh=50) + small_comp_gb = self._map_triv_reduce('compute_gb',input_iter,worker_pool=worker_pool,chunksize=1,mp_thresh=50) ret = small_comp_gb + temp_eqns return ret @@ -1120,7 +1205,7 @@ def attempt_number_field_computation(self): return False return True - def get_explicit_solution(self,eqns=None,verbose=True): + def _get_explicit_solution(self,eqns=None,verbose=True): """ When this method is called, the solution is already found in terms of Groeber basis. A few degrees of freedom remain. @@ -1129,7 +1214,9 @@ def get_explicit_solution(self,eqns=None,verbose=True): """ if eqns is None: eqns = self.ideal_basis - self.add_square_fixers() + #Don't add square fixers when warm starting from a late-stage checkpoint + if self._chkpt_status < 5: + self._add_square_fixers() eqns_partition = self.partition_eqns(self.equations_graph(eqns),verbose=verbose) F = self._field @@ -1163,15 +1250,11 @@ def get_explicit_solution(self,eqns=None,verbose=True): #Attempt to compute smallest number field containing all the F-symbols #If calculation takes too long, we use QQbar as the base field if self.attempt_number_field_computation(): + if verbose: + print("Computing appropriate NumberField...") roots = [self._FR.field().gen()]+[r[1] for r in non_cyclotomic_roots] self._field, bf_elts, self._qqbar_embedding = number_field_elements_from_algebraics(roots,minimal=True) else: - # proc = Pool(1) - # input_args = ((('get_appropriate_number_field',id(self)),non_cyclotomic_roots),) - # p = proc.apply_async(executor,input_args) - # try: - # self._field, bf_elts, self._qqbar_embedding = p.get(timeout=100) - # except TimeoutError: self._field = QQbar bf_elts = [self._qqbar_embedding(F.gen())] bf_elts += [rhs for fx,rhs in non_cyclotomic_roots] @@ -1186,17 +1269,13 @@ def get_explicit_solution(self,eqns=None,verbose=True): for i, elt in enumerate(bf_elts): numeric_fvars[non_cyclotomic_roots[i][0]] = elt - #Do some appropriate conversions - new_poly_ring = self._poly_ring.change_ring(self._field) - nvars = self._poly_ring.ngens() - self._var_to_idx = { new_poly_ring.gen(i) : i for i in range(nvars) } - self._var_to_sextuple = { new_poly_ring.gen(i) : self._var_to_sextuple[self._poly_ring.gen(i)] for i in range(nvars) } - self._poly_ring = new_poly_ring + #Update polynomial ring + self._update_poly_ring_base_field(self._field) #Ensure all F-symbols are known - self.solved.update(numeric_fvars) + self._solved.update(numeric_fvars) nvars = self._poly_ring.ngens() - assert len(self.solved) == nvars, "Some F-symbols are still missing...{}".format([self._poly_ring.gen(fx) for fx in set(range(nvars)).difference(self.solved)]) + assert len(self._solved) == nvars, "Some F-symbols are still missing...{}".format([self._poly_ring.gen(fx) for fx in set(range(nvars)).difference(self._solved)]) #Backward substitution step. Traverse variables in reverse lexicographical order. (System is in triangular form) self._fvars = { sextuple : apply_coeff_map(poly_to_tup(rhs),phi) for sextuple, rhs in self._fvars.items() } @@ -1204,60 +1283,75 @@ def get_explicit_solution(self,eqns=None,verbose=True): self._fvars[self._var_to_sextuple[self._poly_ring.gen(fx)]] = ((ETuple({},nvars),rhs),) self._backward_subs() self._fvars = { sextuple : constant_coeff(rhs) for sextuple, rhs in self._fvars.items() } - self.clear_equations() #Update base field attributes self._FR._field = self.field() self._FR._basecoer = self.get_coerce_map_from_fr_cyclotomic_field() - for x in self._FR.basis(): - x.q_dimension.clear_cache() - def find_orthogonal_solution(self,checkpoint=False,save_results=False,save_dir="",verbose=True,use_mp=True): + def find_orthogonal_solution(self,checkpoint=False,save_results="",warm_start="",verbose=True,use_mp=True): r""" Find an orthogonal solution to the pentagon equations associated to the monoidal category represented by ``self``. INPUT: - -``checkpoint`` -- (optional) a boolean indicating whether the computation should - be checkpointed. Depending on the associated ``CartanType``, the computation - may take hours to complete. For large examples, checkpoints are - recommended. This method supports "warm" starting, so the calculation - may be resumed from a checkpoint. Checkpoints store necessary state in - the form of pickle files. - - -``save_results`` -- (optional) a boolean indicating whether the F-symbols - should be stored to file as a pickle for later use. - - -``save_dir`` -- (optional) a string specifying the directory in which - to save the pickled F-symbols. If used, the string must have a trailing - forward slash e.g. "my_dir/" - The file name is "saved_fvars_" + ``key`` + .pickle, where - key is computed by :meth:`get_fr_str`. The file name is fixed to allow - for automatic loading. The ``save_dir`` is also used to specify the - location of a checkpoint pickle, if one exists. - - -``use_mp`` -- (optional) a boolean indicating whether to use + - ``checkpoint`` -- (default: ``False``) a boolean indicating whether + the computation should be checkpointed. Depending on the associated + ``CartanType``, the computation may take hours to complete. For + large examples, checkpoints are recommended. This method supports + "warm" starting, so the calculation may be resumed from a checkpoint, + using the ``warm_start`` option. + + Checkpoints store necessary state in the pickle file + ``"fmatrix_solver_checkpoint_" + key + ".pickle"``, where ``key`` + is the result of :meth:`get_fr_str`. + + Generated checkpoint pickles are automatically deleted when the + solver runs successfully. + + - ``save_results`` -- (optional) a string indicating the name of a + pickle file in which to store calculated F-symbols for later use. + + If ``save_results`` is not provided (default), results are not + stored to file. + + The F-symbols may be saved to file after running the solver using + :meth:`save_fvars`. + + - ``warm_start`` -- (optional) a string indicating the name of a pickle + file containing checkpointed solver state. This file must have been + produced by a previous call to the solver using the ``checkpoint`` + option. + + If no file name is provided, the calculation begins from scratch. + + - ``use_mp`` -- (default: ``True``) a boolean indicating whether to use multiprocessing to speed up calculation. The default value - ``True`` is recommended, since parallel processing yields results - much quicker. + ``True`` is highly recommended, since parallel processing yields + results much more quickly. OUTPUT: - This method returns ``None``. If the solver ran successfully, the + This method returns ``None``. If the solver runs successfully, the results may be accessed through various methods, such as - :meth:``get_fvars``, :meth:``fmatrix``, :meth:``fmat``, etc. + :meth:`get_fvars`, :meth:`fmatrix`, :meth:`fmat`, etc. In many cases the F-symbols obtained are in fact real. In any case, the F-symbols are obtained as elements of the associated ``FusionRing``'s ``CyclotomicField``, a computed ``NumberField``, or ``QQbar``. Currently, the output field is determined based on the ``CartanType`` - associated to ``self``. See :meth:``attempt_number_field_computation`` + associated to ``self``. See :meth:`attempt_number_field_computation` for details. """ + self._reset_solver_state() + + #Resume computation from checkpoint + if warm_start: + self._restore_state(warm_start) + #Loading from a pickle with solved F-symbols + if self._chkpt_status > 5: return + #Set multiprocessing parameters. Context can only be set once, so we try to set it - self.clear_equations() - self.clear_vars() try: set_start_method('fork') except RuntimeError: @@ -1266,61 +1360,78 @@ def find_orthogonal_solution(self,checkpoint=False,save_results=False,save_dir=" if verbose: print("Computing F-symbols for {} with {} variables...".format(self._FR, len(self._fvars))) - #Attempt warm-start - try: - self.load_fvars(save_dir) - except FileNotFoundError: - pass - - #Set up hexagon equations and orthogonality constraints poly_sortkey = poly_tup_sortkey_degrevlex - self.get_orthogonality_constraints(output=False) - self.get_defining_equations('hexagons',worker_pool=pool,output=False) - if verbose: - print("Set up {} hex and orthogonality constraints...".format(len(self.ideal_basis))) + if self._chkpt_status < 1: + #Set up hexagon equations and orthogonality constraints + self.get_orthogonality_constraints(output=False) + self.get_defining_equations('hexagons',worker_pool=pool,output=False) - #Set up equations graph. Find GB for each component in parallel. Eliminate variables - self.ideal_basis = self.par_graph_gb(worker_pool=pool,verbose=verbose) - self.ideal_basis.sort(key=poly_sortkey) - self.triangular_elim(worker_pool=pool,verbose=verbose) + #Report progress + if verbose: + print("Set up {} hex and orthogonality constraints...".format(len(self.ideal_basis))) - #Report progress and checkpoint! - if verbose: - print("Hex elim step solved for {} / {} variables".format(len(self.solved), len(self._poly_ring.gens()))) - filename = save_dir + "saved_fvars_" + self.get_fr_str() + ".pickle" - if checkpoint: self.save_fvars(filename) + self._checkpoint(checkpoint,1,verbose=verbose) - #Update reduction parameters, also in children if any - self.update_reduction_params(worker_pool=pool,children_need_update=True) + if self._chkpt_status < 2: + #Set up equations graph. Find GB for each component in parallel. Eliminate variables + self.ideal_basis = self.par_graph_gb(worker_pool=pool,verbose=verbose) + self.ideal_basis.sort(key=poly_sortkey) + self._triangular_elim(worker_pool=pool,verbose=verbose) - #Set up pentagon equations in parallel, simplify, and eliminate variables - self.get_defining_equations('pentagons',worker_pool=pool,output=False) - if verbose: - print("Set up {} reduced pentagons...".format(len(self.ideal_basis))) - self.ideal_basis.sort(key=poly_sortkey) - self.triangular_elim(worker_pool=pool,verbose=verbose) + #Update reduction parameters, also in children if any + self._update_reduction_params(worker_pool=pool,children_need_update=True) - #Report progress and checkpoint! - if verbose: - print("Pent elim step solved for {} / {} variables".format(len(self.solved), len(self._poly_ring.gens()))) - if checkpoint: self.save_fvars(filename) + #Report progress + if verbose: + print("Hex elim step solved for {} / {} variables".format(len(self._solved), len(self._poly_ring.gens()))) + + self._checkpoint(checkpoint,2,verbose=verbose) + + if self._chkpt_status < 3: + #Set up pentagon equations in parallel + self.get_defining_equations('pentagons',worker_pool=pool,output=False) + self.ideal_basis.sort(key=poly_sortkey) + + #Report progress + if verbose: + print("Set up {} reduced pentagons...".format(len(self.ideal_basis))) + + self._checkpoint(checkpoint,3,verbose=verbose) + + #Simplify and eliminate variables + if self._chkpt_status < 4: + self._triangular_elim(worker_pool=pool,verbose=verbose) + + #Report progress + if verbose: + print("Pent elim step solved for {} / {} variables".format(len(self._solved), len(self._poly_ring.gens()))) + + self._checkpoint(checkpoint,4,verbose=verbose) + + #Try adding degrevlex gb -> elim loop until len(ideal_basis) does not change #Close worker pool to free resources - if pool is not None: pool.close() + if pool is not None: + pool.close() #Set up new equations graph and compute variety for each component - self.ideal_basis = self.par_graph_gb(term_order="lex",verbose=verbose) - self.ideal_basis.sort(key=poly_sortkey) - self.triangular_elim(verbose=verbose) - if checkpoint: self.save_fvars(filename) - self.get_explicit_solution(verbose=verbose) + if self._chkpt_status < 5: + self.ideal_basis = self.par_graph_gb(term_order="lex",verbose=verbose) + self.ideal_basis.sort(key=poly_sortkey) + self._triangular_elim(verbose=verbose) + + self._checkpoint(checkpoint,5,verbose=verbose) + + #Find numeric values for each F-symbol + self._get_explicit_solution(verbose=verbose) #The calculation was successful, so we may delete checkpoints - self.symbols_known = True + self._chkpt_status = 7 + self.clear_equations() if checkpoint: - os.remove(filename) + os.remove("fmatrix_solver_checkpoint_"+self.get_fr_str()+".pickle") if save_results: - self.save_fvars(filename) + self.save_fvars(save_results) ######################### ### Cyclotomic method ### @@ -1331,11 +1442,11 @@ def fix_gauge(self, algorithm=''): Fix the gauge by forcing F-symbols not already fixed to equal 1. This method should be used AFTER adding hex and pentagon eqns to ideal_basis """ - while len(self.solved) < len(self._poly_ring.gens()): + while len(self._solved) < len(self._poly_ring.gens()): #Get a variable that has not been fixed #In ascending index order, for consistent results for var in self._poly_ring.gens(): - if var not in self.solved: + if var not in self._solved: break #Fix var = 1, substitute, and solve equations @@ -1355,16 +1466,16 @@ def substitute_degree_one(self, eqns=None): #Substitute known value from univariate degree 1 polynomial or, #Following Bonderson, p. 37, solve linear equation with two terms #for one of the variables - if eq.degree() == 1 and sum(eq.degrees()) <= 2 and eq.lm() not in self.solved: + if eq.degree() == 1 and sum(eq.degrees()) <= 2 and eq.lm() not in self._solved: self._fvars[self._var_to_sextuple[eq.lm()]] = -sum(c * m for c, m in zip(eq.coefficients()[1:], eq.monomials()[1:])) / eq.lc() #Add variable to set of known values and remove this equation new_knowns.add(eq.lm()) useless.add(eq) #Update fvars depending on other variables - self.solved.update(new_knowns) + self._solved.update(new_knowns) for sextuple, rhs in self._fvars.items(): - d = { var : self._fvars[self._var_to_sextuple[var]] for var in rhs.variables() if var in self.solved } + d = { var : self._fvars[self._var_to_sextuple[var]] for var in rhs.variables() if var in self._solved } if d: self._fvars[sextuple] = rhs.subs(d) return new_knowns, useless @@ -1373,7 +1484,7 @@ def update_equations(self): """ Update ideal_basis equations by plugging in known values """ - special_values = { known : self._fvars[self._var_to_sextuple[known]] for known in self.solved } + special_values = { known : self._fvars[self._var_to_sextuple[known]] for known in self._solved } self.ideal_basis = set(eq.subs(special_values) for eq in self.ideal_basis) self.ideal_basis.discard(0) @@ -1430,8 +1541,8 @@ def find_cyclotomic_solution(self, equations=None, algorithm='', verbose=True, o [] """ - self.clear_vars() - self.clear_equations() + self._reset_solver_state() + if equations is None: if verbose: print("Setting up hexagons and pentagons...") @@ -1484,7 +1595,7 @@ def verify_pentagons(self,use_mp=True,prune=False): pool = None n_proc = pool._processes if pool is not None else 1 params = [(child_id,n_proc) for child_id in range(n_proc)] - pe = self.map_triv_reduce('pent_verify',params,worker_pool=pool,chunksize=1,mp_thresh=0) + pe = self._map_triv_reduce('pent_verify',params,worker_pool=pool,chunksize=1,mp_thresh=0) if np.all(np.isclose(np.array(pe),0,atol=1e-7)): print("Success!!! Found valid F-symbols for {}".format(self._FR)) pe = None diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index 7e9c5ba980a..2e7c31fe9a2 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -120,10 +120,10 @@ cdef req_cy(tuple basis, r_matrix, dict fvars, Nk_ij, id_anyon, tuple sextuple): a, b, c, d, e, g = sextuple #To add typing we need to ensure all fmats.fmat are of the same type? #Return fmats._poly_ring.zero() and fmats._poly_ring.one() instead of 0 and 1? - lhs = r_matrix(a,c,e)*_fmat(fvars,Nk_ij,id_anyon,a,c,b,d,e,g)*r_matrix(b,c,g) + lhs = r_matrix(a,c,e,base_coercion=False)*_fmat(fvars,Nk_ij,id_anyon,a,c,b,d,e,g)*r_matrix(b,c,g,base_coercion=False) rhs = 0 for f in basis: - rhs += _fmat(fvars,Nk_ij,id_anyon,c,a,b,d,e,f)*r_matrix(f,c,d)*_fmat(fvars,Nk_ij,id_anyon,a,b,c,d,f,g) + rhs += _fmat(fvars,Nk_ij,id_anyon,c,a,b,d,e,f)*r_matrix(f,c,d,base_coercion=False)*_fmat(fvars,Nk_ij,id_anyon,a,b,c,d,f,g) return lhs-rhs @cython.wraparound(False) @@ -189,7 +189,6 @@ cpdef get_reduced_pentagons(factory, tuple mp_params, bint prune=True): #Pre-compute common parameters for speed cdef tuple basis = tuple(factory._FR.basis()) cdef dict fvars = factory._fvars - r_matrix = factory._FR.r_matrix _Nk_ij = factory._FR.Nk_ij id_anyon = factory._FR.one() cdef NumberFieldElement_absolute one = factory._field.one() @@ -249,11 +248,11 @@ cpdef update_child_fmats(factory, tuple data_tup): """ One-to-all communication used to update FMatrix object after each triangular elim step. We must update the algorithm's state values. These are: - _fvars, solved, _ks, _var_degs, _nnz, and _kp. + _fvars, _solved, _ks, _var_degs, _nnz, and _kp. """ #factory object is assumed global before forking used to create the Pool object, #so each child has a global fmats variable. So it's enough to update that object - factory._fvars, factory.solved, factory._ks, factory._var_degs = data_tup + factory._fvars, factory._solved, factory._ks, factory._var_degs = data_tup factory._nnz = factory._get_known_nonz() factory._kp = compute_known_powers(factory._var_degs,factory._get_known_vals(),factory._field.one()) diff --git a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx index a55bc74b83a..4e587f1955f 100644 --- a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx @@ -24,10 +24,15 @@ worker_results = list() def executor(params): """ - Execute a function defined in this module (sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn) - in a worker process, and supply the factory parameter by constructing a reference - to the FMatrix object in the worker's memory adress space from its id - ## NOTE: When the parent process is forked, each worker gets a copy of + Execute a function defined in this module + (sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn) + in a worker process, and supply the factory parameter by constructing a + reference to the FMatrix object in the worker's memory adress space + from its id. + + NOTES: + + When the parent process is forked, each worker gets a copy of every global variable. The virtual memory address of object X in the parent process equals the VIRTUAL memory address of the copy of object X in each worker, so we may construct references to forked copies of X @@ -102,10 +107,10 @@ cpdef mid_sig_ij(fusion_ring,row,col,a,b): return entry cpdef odd_one_out_ij(fusion_ring,xi,xj,a,b): - """ - Compute the xi, xj entry of the braid generator on the right-most strands, - corresponding to the tree b -> (xi # a) -> (a # a) # a, which results in a - sum over j of trees b -> xj -> (a # a) # (a # a) + r""" + Compute the `xi`, `xj` entry of the braid generator on the two right-most + strands, corresponding to the tree b -> (xi # a) -> (a # a) # a, which + results in a sum over j of trees b -> xj -> (a # a) # (a # a) ..warning: This method assumes F-matrices are orthogonal diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 4031c3e10ea..9acc5807bec 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -492,9 +492,59 @@ def field(self): sage: FusionRing("B2",2).field() Cyclotomic Field of order 40 and degree 16 """ - if self._field is None: - self._field = CyclotomicField(4 * self._cyclotomic_order) - return self._field + # if self._field is None: + # self._field = CyclotomicField(4 * self._cyclotomic_order) + # return self._field + return CyclotomicField(4 * self._cyclotomic_order) + + def fvars_field(self): + r""" + Return a field containing the ``CyclotomicField`` computed by + :meth:`field` as well as all the F-symbols of the associated + ``FMatrix`` factory object. + + This method is only available if ``self`` is multiplicity-free. + + OUTPUT: + + Depending on the ``CartanType`` associated to ``self`` and whether + a call to an F-matrix solver has been made, this method + will return the same field as :meth:`field`, a ``NumberField``, + or the ``AlgebraicField`` ``QQbar``. + See :meth:`FMatrix.attempt_number_field_computation` for more details. + + Before running an F-matrix solver, the output of this method matches + that of :meth:`field`. However, the output may change upon successfully + computing F-symbols. Requesting braid generators triggers a call to + :meth:`FMatrix.find_orthogonal_solution`, so the output of this method + may change after such a computation. + + By default, the output of methods like :meth:`r_matrix`, + :meth:`s_matrix`, :meth:`twists_matrix`, etc. will lie in the + ``fvars_field``, unless the ``base_coercion`` option is set to + ``False``. + + This method does not trigger a solver run. + + EXAMPLES:: + + sage: A13 = FusionRing("A1",3,fusion_labels="a",inject_variables=True) + sage: A13.fvars_field() + Cyclotomic Field of order 40 and degree 16 + sage: A13.field() + Cyclotomic Field of order 40 and degree 16 + sage: a2**4 + 2*a0 + 3*a2 + sage: comp_basis, sig = A13.get_braid_generators(a2,a2,4,verbose=False) + sage: A13.fvars_field() + Number Field in a with defining polynomial y^32 - 8*y^30 + 18*y^28 - 44*y^26 + 93*y^24 - 56*y^22 + 2132*y^20 - 1984*y^18 + 19738*y^16 - 28636*y^14 + 77038*y^12 - 109492*y^10 + 92136*y^8 - 32300*y^6 + 5640*y^4 - 500*y^2 + 25 + sage: a2.q_dimension().parent() + Number Field in a with defining polynomial y^32 - 8*y^30 + 18*y^28 - 44*y^26 + 93*y^24 - 56*y^22 + 2132*y^20 - 1984*y^18 + 19738*y^16 - 28636*y^14 + 77038*y^12 - 109492*y^10 + 92136*y^8 - 32300*y^6 + 5640*y^4 - 500*y^2 + 25 + sage: A13.field() + Cyclotomic Field of order 40 and degree 16 + """ + if self.is_multiplicity_free(): + return self.fmats.field() def root_of_unity(self, r, base_coercion=True): r""" @@ -1061,15 +1111,18 @@ def _emap(self,mapper,input_args,worker_pool=None): INPUT: - ``mapper`` -- a string specifying the name of a function defined in the - fast_parallel_fmats_methods module. - - ``input_args`` -- a tuple holding arguments to be passed to mapper - - ##NOTES: - If worker_pool is not provided, function maps and reduces on a single process. - If worker_pool is provided, the function attempts to determine whether it should - use multiprocessing based on the length of the input iterable. If it can't determine - the length of the input iterable then it uses multiprocessing with the default chunksize of 1 - if chunksize is not explicitly provided. + ``fast_parallel_fusion_ring_braid_repn`` module. + - ``input_args`` -- a tuple of arguments to be passed to mapper + + NOTES: + + If ``worker_pool`` is not provided, function maps and reduces on a + single process. + If ``worker_pool`` is provided, the function attempts to determine + whether it should use multiprocessing based on the length of the + input iterable. If it can't determine the length of the input + iterable then it uses multiprocessing with the default chunksize of + `1` if chunksize is not explicitly provided. """ n_proc = worker_pool._processes if worker_pool is not None else 1 input_iter = [(child_id, n_proc, input_args) for child_id in range(n_proc)] @@ -1091,17 +1144,40 @@ def _emap(self,mapper,input_args,worker_pool=None): results = list(results) return results - def get_braid_generators(self,fusing_anyon,total_charge_anyon,n_strands,use_mp=True,verbose=True): + def get_braid_generators(self, + fusing_anyon, + total_charge_anyon, + n_strands, + checkpoint=False, + save_results="", + warm_start="", + use_mp=True, + verbose=True): """ - INPUT: + Compute generators of the Artin braid group on `n=` ``n_strands`` + strands. If `a` = ``fusing_anyon`` and `b` = ``total_charge_anyon`` + the generators are endomorphisms of `\\text{Hom}(b, a^n)`. - Compute generators of the Artin braid group on `n=` n_strands strands. If - fusing_anyon = a and total_charge_anyon = b, the generators are - endomorphisms of `\\text{Hom}(b, a^n)` + INPUT: - ``fusing_anyon`` -- a basis element of self - ``total_charge_anyon`` -- a basis element of self - ``n_strands`` -- a positive integer greater than 2 + - ``checkpoint`` -- (optional) a boolean indicating whether the + F-matrix solver should pickle checkpoints. + - ``save_results`` -- (optional) a string indicating the name of + a file in which to pickle computed F-symbols for later use. + - ``warm_start`` -- (optional) a string indicating the name of a + pickled checkpoint file to "warm" start the F-matrix solver. + The pickle may be a checkpoint generated by the solver, or + a file containing solver results. If all F-symbols are known, + we don't run the solver again. + - ``use_mp`` -- (optional) a boolean indicating whether to use + multiprocessing to speed up the computation. This is highly + recommended. + + For more information on the optional parameters, see + :meth:`find_orthogonal_solution` of the :class:`FMatrix` module. Given a simple object in the fusion category, here called ``fusing_anyon`` allowing the universal R-matrix to act on adjacent @@ -1110,6 +1186,8 @@ def get_braid_generators(self,fusing_anyon,total_charge_anyon,n_strands,use_mp=T be decomposed over another anyon, here called ``total_charge_anyon``. See [CHW2015]_. + OUTPUT: + The method outputs a pair of data ``(comp_basis,sig)`` where ``comp_basis`` is a list of basis elements of the braid group module, parametrized by a list of fusion ring elements describing @@ -1145,8 +1223,12 @@ def get_braid_generators(self,fusing_anyon,total_charge_anyon,n_strands,use_mp=T """ assert int(n_strands) > 2, "The number of strands must be an integer greater than 2" #Construct associated FMatrix object and solve for F-symbols - if not self.fmats.symbols_known: - self.fmats.find_orthogonal_solution(verbose=verbose) + if self.fmats._chkpt_status < 7: + self.fmats.find_orthogonal_solution(checkpoint=checkpoint, + save_results=save_results, + warm_start=warm_start, + use_mp=use_mp, + verbose=verbose) #Set multiprocessing parameters. Context can only be set once, so we try to set it try: diff --git a/src/sage/combinat/root_system/poly_tup_engine.pyx b/src/sage/combinat/root_system/poly_tup_engine.pyx index 45b4f34679c..588278fe217 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pyx +++ b/src/sage/combinat/root_system/poly_tup_engine.pyx @@ -352,31 +352,6 @@ cpdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq, NumberFie cdef dict gcf_rmvd = remove_gcf(sq_rmvd, nonz) return to_monic(gcf_rmvd, one) -# cdef int common_denom(tuple eq_tup): -# #Compute the common denominator -# cdef list denoms = list() -# cdef int common_denom -# cdef ETuple exp -# for exp, c in eq_tup: -# denoms.append(c.denominator()) -# return LCM_list(denoms) -# -# cdef tuple integralify(tuple eq_tup): -# if not eq_tup: return tuple() -# cdef list ret = list() -# cdef int cd = common_denom(eq_tup) -# for exp, c in eq_tup: -# ret.append((exp, c * cd)) -# return tuple(ret) -# -# cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq): -# """ -# Return a dictionary describing a monic polynomial with no known nonzero gcd and -# no known squares -# """ -# if not eq_dict: return tuple() -# return integralify(to_monic(remove_gcf(subs_squares(eq_dict, known_sq), nonz))) - #################### ### Substitution ### #################### From 3202a1fd6d1de6687efe4de6d737626219d9839a Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Wed, 24 Mar 2021 16:07:06 -0700 Subject: [PATCH 030/632] correct BaKi2001 reference in f_matrix.py --- src/sage/combinat/root_system/f_matrix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 9df09e88ab7..a183231d3d5 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -48,7 +48,7 @@ class FMatrix(): - ``FR`` -- a FusionRing. The :class:`FusionRing` or Verlinde algebra is the - Grothendieck ring of a modular tensor category [Baki2001]_. + Grothendieck ring of a modular tensor category [BaKi2001]_. Such categories arise in conformal field theory or in the representation theories of affine Lie algebras, or quantum groups at roots of unity. They have applications From 5559e96a70e49f2aa32bfe8e654e26e419c7c044 Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Fri, 26 Mar 2021 08:33:28 -0700 Subject: [PATCH 031/632] changes to avoid segfault form #30537 --- src/sage/combinat/root_system/f_matrix.py | 22 +++-- .../fast_parallel_fmats_methods.pyx | 24 +++++- .../fast_parallel_fusion_ring_braid_repn.pxd | 4 +- .../fast_parallel_fusion_ring_braid_repn.pyx | 81 ++++++++++++++++--- src/sage/combinat/root_system/fusion_ring.py | 22 +++-- .../combinat/root_system/poly_tup_engine.pxd | 4 + .../combinat/root_system/poly_tup_engine.pyx | 24 ++++++ 7 files changed, 157 insertions(+), 24 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index a183231d3d5..d7c52a2db10 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -34,7 +34,7 @@ from sage.combinat.root_system.fast_parallel_fmats_methods import * from sage.combinat.root_system.poly_tup_engine import * #Import faster unsafe method (not for client use) -from sage.combinat.root_system.poly_tup_engine import _tup_to_poly +from sage.combinat.root_system.poly_tup_engine import _tup_to_poly, _unflatten_coeffs from sage.rings.polynomial.polydict import ETuple from sage.rings.qqbar import AA, QQbar, number_field_elements_from_algebraics from sage.rings.real_double import RDF @@ -48,7 +48,7 @@ class FMatrix(): - ``FR`` -- a FusionRing. The :class:`FusionRing` or Verlinde algebra is the - Grothendieck ring of a modular tensor category [BaKi2001]_. + Grothendieck ring of a modular tensor category [BaKi2001]_. Such categories arise in conformal field theory or in the representation theories of affine Lie algebras, or quantum groups at roots of unity. They have applications @@ -187,9 +187,10 @@ class FMatrix(): EXAMPLES:: sage: f.get_defining_equations("pentagons")[1:3] - [fx1*fx5 - fx7^2, fx5*fx8*fx13 - fx2*fx12] + [fx9*fx12 - fx2*fx13, fx3*fx8 - fx4*fx9] sage: f.get_defining_equations("hexagons")[1:3] - [fx10*fx12 + (-zeta128^32)*fx12*fx13 + (-zeta128^16)*fx12, fx0 - 1] + [fx11*fx12 + (-zeta128^32)*fx13^2 + (-zeta128^48)*fx13, + fx10*fx11 + (-zeta128^32)*fx11*fx13 + (-zeta128^16)*fx11] sage: f.get_orthogonality_constraints()[1:3] [fx1^2 - 1, fx2^2 - 1] @@ -672,9 +673,10 @@ def _get_known_sq(self,eqns=None): if eqns is None: eqns = self.ideal_basis ks = deepcopy(self._ks) + F = self._field for eq_tup in eqns: if tup_fixes_sq(eq_tup): - ks[variables(eq_tup)[0]] = -eq_tup[-1][1] + ks[variables(eq_tup)[0]] = -F(list(eq_tup[-1][1])) return ks def _get_known_nonz(self): @@ -941,6 +943,9 @@ def get_defining_equations(self,option,worker_pool=None,output=True): params = [(child_id, n_proc) for child_id in range(n_proc)] eqns = self._map_triv_reduce('get_reduced_'+option,params,worker_pool=worker_pool,chunksize=1,mp_thresh=0) if output: + F = self._field + for i, eq_tup in enumerate(eqns): + eqns[i] = _unflatten_coeffs(F, eq_tup) return [self._tup_to_fpoly(p) for p in eqns] self.ideal_basis.extend(eqns) @@ -970,8 +975,15 @@ def _solve_for_linear_terms(self,eqns=None): if eqns is None: eqns = self.ideal_basis + F = self._field linear_terms_exist = False for eq_tup in eqns: + + #Only unflatten relevant polynomials + if len(eq_tup) > 2: + continue + eq_tup = _unflatten_coeffs(F, eq_tup) + if len(eq_tup) == 1: m = eq_tup[0][0] if self._is_univariate_in_unknown(m): diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index 2e7c31fe9a2..9b8e013e93e 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -156,6 +156,10 @@ cpdef get_reduced_hexagons(factory, tuple mp_params): he = req_cy(basis,r_matrix,fvars,_Nk_ij,id_anyon,sextuple) if he: red = reduce_poly_dict(he.dict(),_nnz,_ks,one) + + #Avoid pickling cyclotomic coefficients + red = _flatten_coeffs(red) + worker_results.append(red) cdef MPolynomial_libsingular feq_cy(tuple basis, fvars, Nk_ij, id_anyon, zero, tuple nonuple, bint prune=False): @@ -202,6 +206,10 @@ cpdef get_reduced_pentagons(factory, tuple mp_params, bint prune=True): pe = feq_cy(basis,fvars,_Nk_ij,id_anyon,zero,nonuple,prune=prune) if pe: red = reduce_poly_dict(pe.dict(),_nnz,_ks,one) + + #Avoid pickling cyclotomic coefficients + red = _flatten_coeffs(red) + worker_results.append(red) cpdef update_reduce(factory, tuple eq_tup): @@ -209,9 +217,17 @@ cpdef update_reduce(factory, tuple eq_tup): Substitute known values, known squares, and reduce! """ global worker_results - cdef NumberFieldElement_absolute one =factory._field.one() + cdef NumberFieldElement_absolute one = factory._field.one() + + #Construct cyclotomic field elts from list repn + eq_tup = _unflatten_coeffs(factory._field, eq_tup) + cdef dict eq_dict = subs(eq_tup,factory._kp,one) cdef tuple red = reduce_poly_dict(eq_dict,factory._nnz,factory._ks,one) + + #Avoid pickling cyclotomic coefficients + red = _flatten_coeffs(red) + worker_results.append(red) cpdef compute_gb(factory, tuple args): @@ -231,8 +247,10 @@ cpdef compute_gb(factory, tuple args): #Zip tuples into R and compute Groebner basis cdef idx_map = { old : new for new, old in enumerate(sorted_vars) } nvars = len(sorted_vars) + F = factory.field() cdef list polys = list() for eq_tup in eqns: + eq_tup = _unflatten_coeffs(F, eq_tup) polys.append(tup_to_poly(resize(eq_tup,idx_map,nvars),parent=R)) gb = Ideal(sorted(polys)).groebner_basis(algorithm="libsingular:slimgb") @@ -242,6 +260,10 @@ cpdef compute_gb(factory, tuple args): nvars = factory._poly_ring.ngens() for p in gb: t = resize(poly_to_tup(p),inv_idx_map,nvars) + + #Avoid pickling cyclotomic coefficients + t = _flatten_coeffs(t) + worker_results.append(t) cpdef update_child_fmats(factory, tuple data_tup): diff --git a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pxd b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pxd index 7f4c49b63cd..134b6b57f2d 100644 --- a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pxd +++ b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pxd @@ -1,5 +1,3 @@ -cpdef mid_sig_ij(fusion_ring,row,col,a,b) -cpdef odd_one_out_ij(fusion_ring,xi,xj,a,b) - cpdef sig_2k(fusion_ring, tuple args) cpdef odd_one_out(fusion_ring, tuple args) +cpdef _unflatten_entries(factory, list entries) diff --git a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx index 4e587f1955f..0cb4bd2833a 100644 --- a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx @@ -12,8 +12,10 @@ cimport cython import ctypes from itertools import product import sage +from sage.combinat.root_system.poly_tup_engine cimport _flatten_coeffs, _unflatten_coeffs, poly_to_tup from sage.combinat.root_system.fast_parallel_fmats_methods cimport _fmat from sage.misc.cachefunc import cached_function +from sage.rings.qqbar import QQbar #Define a global temporary worker results repository worker_results = list() @@ -72,7 +74,8 @@ cpdef mid_sig_ij(fusion_ring,row,col,a,b): in the tree b -> xi # yi -> (a # a) # (a # a), which results in a sum over j of trees b -> xj # yj -> (a # a) # (a # a) - ..warning: + .. WARNING:: + This method assumes F-matrices are orthogonal EXAMPLES:: @@ -112,7 +115,8 @@ cpdef odd_one_out_ij(fusion_ring,xi,xj,a,b): strands, corresponding to the tree b -> (xi # a) -> (a # a) # a, which results in a sum over j of trees b -> xj -> (a # a) # (a # a) - ..warning: + .. WARNING:: + This method assumes F-matrices are orthogonal EXAMPLES:: @@ -145,7 +149,7 @@ cpdef odd_one_out_ij(fusion_ring,xi,xj,a,b): mid_sig_ij = cached_function(mid_sig_ij, name='mid_sig_ij') odd_one_out_ij = cached_function(odd_one_out_ij, name='odd_one_out_ij') -@cython.wraparound(False) +#@cython.wraparound(False) @cython.nonecheck(False) @cython.cdivision(True) cpdef sig_2k(fusion_ring, tuple args): @@ -165,8 +169,10 @@ cpdef sig_2k(fusion_ring, tuple args): cdef list comp_basis = fusion_ring.get_computational_basis(a,b,n_strands) cdef dict basis_dict = { elt : i for i, elt in enumerate(comp_basis) } cdef int dim = len(comp_basis) - cdef set entries = set() + cdef set coords = set() cdef int i + #Avoid pickling cyclotomic field element objects + must_flatten_coeff = fusion_ring.fvars_field() != QQbar for i in range(dim): for f,e,q in product(fusion_ring.basis(),repeat=3): #Distribute work amongst processes @@ -183,7 +189,7 @@ cpdef sig_2k(fusion_ring, tuple args): nnz_pos = tuple(nnz_pos) #Skip repeated entries when k = 1 - if nnz_pos in comp_basis and (basis_dict[nnz_pos],i) not in entries: + if nnz_pos in comp_basis and (basis_dict[nnz_pos],i) not in coords: m, l = comp_basis[i][:n_strands//2], comp_basis[i][n_strands//2:] #A few special cases top_left = m[0] @@ -196,7 +202,13 @@ cpdef sig_2k(fusion_ring, tuple args): #Handle the special case k = 1 if k == 1: entry = mid_sig_ij(fusion_ring,m[:2],(f,e),a,root) + + #Avoid pickling cyclotomic field element objects + if must_flatten_coeff: + entry = _flatten_entry(fusion_ring, entry) + worker_results.append(((basis_dict[nnz_pos],i), entry)) + coords.add((basis_dict[nnz_pos],i)) continue entry = 0 @@ -204,10 +216,18 @@ cpdef sig_2k(fusion_ring, tuple args): f1 = _fmat(_fvars,_Nk_ij,one,top_left,m[k-1],m[k],root,l[k-2],p) f2 = _fmat(_fvars,_Nk_ij,one,top_left,f,e,root,q,p) entry += f1 * mid_sig_ij(fusion_ring,(m[k-1],m[k]),(f,e),a,p) * f2 + + #Avoid pickling cyclotomic field element objects + if must_flatten_coeff: + #The entry is either a polynomial or a base field element + if entry.parent() == fusion_ring.fmats._poly_ring: + entry = _flatten_coeffs(poly_to_tup(entry)) + else: + entry = entry.list() + worker_results.append(((basis_dict[nnz_pos],i), entry)) - entries.add((basis_dict[nnz_pos],i)) -@cython.wraparound(False) +#@cython.wraparound(False) @cython.nonecheck(False) @cython.cdivision(True) cpdef odd_one_out(fusion_ring, tuple args): @@ -228,6 +248,9 @@ cpdef odd_one_out(fusion_ring, tuple args): comp_basis = fusion_ring.get_computational_basis(a,b,n_strands) basis_dict = { elt : i for i, elt in enumerate(comp_basis) } dim = len(comp_basis) + + #Avoid pickling cyclotomic field element objects + must_flatten_coeff = fusion_ring.fvars_field() != QQbar for i in range(dim): for f, q in product(fusion_ring.basis(),repeat=2): #Distribute work amongst processes @@ -249,6 +272,15 @@ cpdef odd_one_out(fusion_ring, tuple args): #Handle a couple of small special cases if n_strands == 3: entry = odd_one_out_ij(fusion_ring,m[-1],f,a,b) + + #Avoid pickling cyclotomic field element objects + if must_flatten_coeff: + #The entry is either a polynomial or a base field element + if entry.parent() == fusion_ring.fmats._poly_ring: + entry = _flatten_coeffs(poly_to_tup(entry)) + else: + entry = entry.list() + worker_results.append(((basis_dict[nnz_pos],i), entry)) continue top_left = m[0] @@ -262,6 +294,11 @@ cpdef odd_one_out(fusion_ring, tuple args): f1 = _fmat(_fvars,_Nk_ij,one,top_left,m[-1],a,root,l[-1],p) f2 = _fmat(_fvars,_Nk_ij,one,top_left,f,a,root,q,p) entry += f1 * odd_one_out_ij(fusion_ring,m[-1],f,a,p) * f2 + + #Avoid pickling cyclotomic field element objects + if must_flatten_coeff: + entry = _flatten_entry(fusion_ring, entry) + worker_results.append(((basis_dict[nnz_pos],i), entry)) ################ @@ -275,6 +312,32 @@ def collect_results(proc): """ #Discard the zero polynomial global worker_results - reduced = set(worker_results)-set([tuple()]) + reduced = worker_results #set(worker_results)-set([tuple()]) worker_results = list() - return list(reduced) + return reduced + +###################################### +### Pickling circumvention helpers ### +###################################### + +cdef _flatten_entry(fusion_ring, entry): + #The entry is either a polynomial or a base field element + if entry.parent() == fusion_ring.fmats._poly_ring: + entry = _flatten_coeffs(poly_to_tup(entry)) + else: + entry = entry.list() + return entry + +cpdef _unflatten_entries(factory, list entries): + F = factory.fvars_field() + fm = factory.fmats + must_unflatten = F != QQbar + if must_unflatten: + for i, (coord, entry) in enumerate(entries): + #In this case entry represents a polynomial + if type(entry) == type(tuple()): + entry = fm.tup_to_fpoly(_unflatten_coeffs(F,entry)) + #Otherwise entry belongs to base field + else: + entry = F(entry) + entries[i] = (coord, entry) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 7d6b7aff8ba..e0d62ac0d58 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -15,7 +15,7 @@ from multiprocessing import Pool, set_start_method from sage.combinat.q_analogues import q_int import sage.combinat.root_system.f_matrix as FMatrix -from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import collect_results, executor +from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import collect_results, executor, _unflatten_entries from sage.combinat.root_system.weyl_characters import WeylCharacterRing from sage.matrix.constructor import matrix from sage.matrix.special import diagonal_matrix @@ -25,6 +25,8 @@ from sage.rings.integer_ring import ZZ from sage.rings.number_field.number_field import CyclotomicField +from sage.rings.qqbar import QQbar + class FusionRing(WeylCharacterRing): r""" Return the Fusion Ring (Verlinde Algebra) of level ``k``. @@ -1138,10 +1140,10 @@ def _emap(self,mapper,input_args,worker_pool=None): if no_mp: results = collect_results(0) else: - results = set() + results = list() for worker_results in worker_pool.imap_unordered(collect_results,range(worker_pool._processes),chunksize=1): - results.update(worker_results) - results = list(results) + results.extend(worker_results) + # results = list(results) return results def get_braid_generators(self, @@ -1235,7 +1237,8 @@ def get_braid_generators(self, set_start_method('fork') except RuntimeError: pass - pool = Pool() if use_mp else None + #Turn off multiprocessing when field is QQbar due to pickling issues introduced by PARI upgrade in trac ticket #30537 + pool = Pool() if use_mp and self.fvars_field() != QQbar else None #Set up computational basis and compute generators one at a time a, b = fusing_anyon, total_charge_anyon @@ -1250,11 +1253,19 @@ def get_braid_generators(self, #Compute even-indexed generators using F-matrices for k in range(1,n_strands//2): entries = self._emap('sig_2k',(k,a,b,n_strands),pool) + + #Build cyclotomic field element objects from tuple of rationals repn + _unflatten_entries(self, entries) + gens[2*k] = matrix(dict(entries)) #If n_strands is odd, we compute the final generator if n_strands % 2: entries = self._emap('odd_one_out',(a,b,n_strands),pool) + + #Build cyclotomic field element objects from tuple of rationals repn + _unflatten_entries(self, entries) + gens[n_strands-1] = matrix(dict(entries)) return comp_basis, [gens[k] for k in sorted(gens)] @@ -1451,4 +1462,3 @@ def q_dimension(self, base_coercion=True): if (not base_coercion) or (self.parent()._basecoer is None): return ret return self.parent()._basecoer(ret) - diff --git a/src/sage/combinat/root_system/poly_tup_engine.pxd b/src/sage/combinat/root_system/poly_tup_engine.pxd index 5f614ae69de..5eb7e11ad18 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pxd +++ b/src/sage/combinat/root_system/poly_tup_engine.pxd @@ -18,3 +18,7 @@ cpdef tuple poly_tup_sortkey_degrevlex(tuple eq_tup) cpdef MPolynomial_libsingular _tup_to_poly(tuple eq_tup, MPolynomialRing_libsingular parent) cpdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq, NumberFieldElement_absolute one) + + +cdef tuple _flatten_coeffs(tuple eq_tup) +cpdef tuple _unflatten_coeffs(field, tuple eq_tup) diff --git a/src/sage/combinat/root_system/poly_tup_engine.pyx b/src/sage/combinat/root_system/poly_tup_engine.pyx index 588278fe217..2e6dec0db4a 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pyx +++ b/src/sage/combinat/root_system/poly_tup_engine.pyx @@ -84,7 +84,31 @@ cpdef MPolynomial_libsingular _tup_to_poly(tuple eq_tup, MPolynomialRing_libsing """ return parent._element_constructor_(dict(eq_tup), check=False) +cdef tuple _flatten_coeffs(tuple eq_tup): + """ + Flatten cyclotomic coefficients to a representation as a tuple of rational + coefficients. + + This is used to avoid pickling cyclotomic coefficient objects, which fails + with new PARI settings introduced in trac ticket #30537 + """ + cdef list flat = list() + for exp, cyc_coeff in eq_tup: + flat.append((exp, tuple(cyc_coeff._coefficients()))) + return tuple(flat) +cpdef tuple _unflatten_coeffs(field, tuple eq_tup): + """ + Restore cyclotomic coefficient object from its tuple of rational + coefficients representation. + + Used to circumvent pickling issue introduced by PARI settigs in trac + ticket #30537 + """ + cdef list unflat = list() + for exp, coeff_tup in eq_tup: + unflat.append((exp, field(list(coeff_tup)))) + return tuple(unflat) ###################### ### "Change rings" ### From 3429f2561ac0ae71ae626971d93ff3a47a9d3db3 Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Fri, 26 Mar 2021 16:02:12 -0700 Subject: [PATCH 032/632] docstring revisions --- src/sage/combinat/root_system/f_matrix.py | 169 +++++++++++------- .../fast_parallel_fusion_ring_braid_repn.pyx | 37 +++- src/sage/combinat/root_system/fusion_ring.py | 8 +- .../combinat/root_system/poly_tup_engine.pyx | 13 ++ 4 files changed, 156 insertions(+), 71 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index d7c52a2db10..2e78fc0e108 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -41,11 +41,11 @@ class FMatrix(): - r"""Return an F-Matrix factory for a FusionRing. + r"""Return an F-Matrix factory for a :class:`FusionRing`. INPUT: - - ``FR`` -- a FusionRing. + - ``FR`` -- a :class:`FusionRing`. The :class:`FusionRing` or Verlinde algebra is the Grothendieck ring of a modular tensor category [BaKi2001]_. @@ -54,13 +54,13 @@ class FMatrix(): quantum groups at roots of unity. They have applications to low dimensional topology and knot theory, to conformal field theory and to topological quantum computing. The - FusionRing captures much information about a fusion category, - but to complete the picture, the F-matrices or 6j-symbols - are needed. For example these are required in order to - construct braid group representations. + :class:`FusionRing` captures much information about a fusion + category, but to complete the picture, the F-matrices or + 6j-symbols are needed. For example these are required in + order to construct braid group representations. We only undertake to compute the F-matrix if the - FusionRing is *multiplicity free* meaning that + :class:`FusionRing` is *multiplicity free* meaning that the Fusion coefficients `N^{ij}_k` are bounded by 1. For Cartan Types `X_r` and level `k`, the multiplicity-free cases are given by the @@ -87,10 +87,11 @@ class FMatrix(): Beyond this limitation, computation of the F-matrix can involve very large systems of equations. A rule of thumb is that this code can compute the - F-matrix for systems with `\leq 4` primary fields, - with the exception of `G_2` at level `2`. + F-matrix for systems with `\leq 14` primary fields + (simple objects), on a machine with 16 GB of memory. + Larger examples can be quite time consuming. - The FusionRing and its methods capture much + The :class:`FusionRing` and its methods capture much of the structure of the underlying tensor category. But an important aspect that is not encoded in the fusion ring is the associator, which is a homomorphism @@ -202,10 +203,10 @@ class FMatrix(): that should be kept in mind. :meth:`find_cyclotomic_solution` currently works only with - smaller examples. For example the FusionRing for A2 at + smaller examples. For example the :class:`FusionRing` for A2 at level 2 is too large. When it is available, this method produces an F-matrix whose entries are in the same - cyclotomic field as the underlying FusionRing. + cyclotomic field as the underlying :class:`FusionRing`. EXAMPLES:: @@ -234,18 +235,24 @@ class FMatrix(): quickly but sometimes (in larger cases) after hours of computation. Its F-matrices are not always in the cyclotomic field that is the base ring of the underlying - FusionRing, but sometimes in an extension field adjoining - some square roots. When this happens, the FusionRing is + :class:`FusionRing`, but sometimes in an extension field adjoining + some square roots. When this happens, the :class:`FusionRing` is modified, adding an attribute :attr:`_basecoer` that is - a coercion from the cyclotomic field to the F-matrix. + a coercion from the cyclotomic field to the field + containing the F-matrix. The field containing the F-matrix + is available through :meth:`field`. EXAMPLES:: sage: f = FMatrix(FusionRing("B3",2)) - sage: f.find_orthogonal_solution(verbose=False,checkpoint=True) # long time + sage: f.find_orthogonal_solution(verbose=False,checkpoint=True) # long time (~100 s) sage: all(v in CyclotomicField(56) for v in f.get_fvars().values()) # long time True + sage: f = FMatrix(FusionRing("G2",2)) + sage: f.find_orthogonal_solution(verbose=False) # long time (~18 s) + sage: f.field() # long time + Algebraic Field """ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variables=False): self._FR = fusion_ring @@ -312,7 +319,8 @@ def clear_vars(self): def _reset_solver_state(self): """ - Reset solver state and clear relevant cache. + Reset solver state and clear relevant cache. Used to ensure + state variables are the same for each orthogonal solver run. """ self._FR._basecoer = None self._field = self._FR.field() @@ -402,7 +410,7 @@ def fmatrix(self,a,b,c,d): INPUT: - - ``a,b,c,d`` -- basis elements of the FusionRing + - ``a,b,c,d`` -- basis elements of the associated :class:`FusionRing` EXAMPLES:: @@ -429,7 +437,7 @@ def fmatrix(self,a,b,c,d): def field(self): r""" Return the base field containing the F-symbols. When ``self`` is initialized, - the field is set to be the cyclotomic field of the FusionRing associated + the field is set to be the cyclotomic field of the :class:`FusionRing` associated to ``self``. The field may change after running :meth:`find_orthogonal_solution`. EXAMPLES:: @@ -445,7 +453,7 @@ def field(self): def FR(self): r""" - Return the FusionRing associated to ``self``. + Return the :class:`FusionRing` associated to ``self``. EXAMPLES:: @@ -501,7 +509,14 @@ def findcases(self,output=False): def singletons(self): r""" Find `x_i` that are automatically nonzero, because their F-matrix is - `1 \\times 1`. + `1 \times 1`. + + EXAMPLES:: + + sage: fm = FMatrix(FusionRing("E7", 1)) + sage: singles = fm.singletons() + sage: all(fm.fmatrix(*fm._var_to_sextuple[v][:4]).nrows() == 1 for v in singles) + True """ ret = [] for (a, b, c, d) in list(product(self._FR.basis(), repeat=4)): @@ -519,7 +534,7 @@ def f_from(self,a,b,c,d): INPUT: - - ``a,b,c,d`` -- basis elements of the FusionRing. + - ``a,b,c,d`` -- basis elements of the associated :class:`FusionRing`. EXAMPLES:: @@ -543,7 +558,7 @@ def f_to(self,a,b,c,d): INPUT: - - ``a,b,c,d`` -- basis elements of the FusionRing. + - ``a,b,c,d`` -- basis elements of the associated :class:`FusionRing`. EXAMPLES:: @@ -591,7 +606,8 @@ def get_fvars(self): def get_non_cyclotomic_roots(self): r""" Return a list of roots that define the extension of the associated - ``FusionRing``'s base ``CyclotomicField`` containing all the F-symbols. + :class:`FusionRing`'s base :class:`CyclotomicField` containing all + the F-symbols. OUTPUT: @@ -601,7 +617,7 @@ def get_non_cyclotomic_roots(self): If ``self.field() == self.FR().field()`` this method returns an empty list. When ``self.field()`` is a ``NumberField``, one may use - :meth:``get_qqbar_embedding`` to embed the resulting values into ``QQbar``. + :meth:`get_qqbar_embedding` to embed the resulting values into ``QQbar``. EXAMPLES:: @@ -619,16 +635,32 @@ def get_non_cyclotomic_roots(self): def get_qqbar_embedding(self): r""" Return an embedding from the base field containing F-symbols (the - ``FusionRing``'s ``CyclotomicField``, a ``NumberField``, or ``QQbar``) + associated :class:`FusionRing`'s :class:`CyclotomicField`, a + :class:`NumberField`, or ``QQbar``) into ``QQbar``. + + This embedding is useful for getting a better sense for the + F-symbols, particularly when they are computed as elements of a + :class:`NumberField`. + + EXAMPLES:: + + sage: fm = FMatrix(FusionRing("C3",1), fusion_label="c", inject_variables=True) + creating variables fx1..fx71 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19, fx20, fx21, fx22, fx23, fx24, fx25, fx26, fx27, fx28, fx29, fx30, fx31, fx32, fx33, fx34, fx35, fx36, fx37, fx38, fx39, fx40, fx41, fx42, fx43, fx44, fx45, fx46, fx47, fx48, fx49, fx50, fx51, fx52, fx53, fx54, fx55, fx56, fx57, fx58, fx59, fx60, fx61, fx62, fx63, fx64, fx65, fx66, fx67, fx68, fx69, fx70 + sage: fm.find_orthogonal_solution(verbose=False) + sage: phi = fm.get_qqbar_embedding() + sage: phi(fm.get_fvars()[c1,c2,c2,c1,c1,c2]) + -0.61803399? + 0.?e-8*I """ return self._qqbar_embedding def get_coerce_map_from_fr_cyclotomic_field(self): r""" - Return a coercion map from the associated ``FusionRing``'s cyclotomic - field into the base field containing all F-symbols (this could be the - ``FusionRing``'s ``CyclotomicField``, a ``NumberField``, or ``QQbar``). + Return a coercion map from the associated :class:`FusionRing`'s + cyclotomic field into the base field containing all F-symbols + (this could be the :class:`FusionRing`'s :class:`CyclotomicField`, a + :class:`NumberField`, or ``QQbar``). """ #If base field is different from associated FusionRing's CyclotomicField, #return coercion map @@ -643,7 +675,7 @@ def get_fmats_in_alg_field(self): r""" Return F-symbols as elements of the :class:`AlgebraicField` ``QQbar``. This method uses the embedding defined by - :meth:``self.get_qqbar_embedding`` to coerce + :meth:`self.get_qqbar_embedding` to coerce F-symbols into ``QQbar``. """ return { sextuple : self._qqbar_embedding(fvar) for sextuple, fvar in self._fvars.items() } @@ -651,8 +683,18 @@ def get_fmats_in_alg_field(self): def get_radical_expression(self): """ Return radical expression of F-symbols for easy visualization + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("A1",3), fusion_label="a", inject_variables=True) + creating variables fx1..fx71 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19, fx20, fx21, fx22, fx23, fx24, fx25, fx26, fx27, fx28, fx29, fx30, fx31, fx32, fx33, fx34, fx35, fx36, fx37, fx38, fx39, fx40, fx41, fx42, fx43, fx44, fx45, fx46, fx47, fx48, fx49, fx50, fx51, fx52, fx53, fx54, fx55, fx56, fx57, fx58, fx59, fx60, fx61, fx62, fx63, fx64, fx65, fx66, fx67, fx68, fx69, fx70 + sage: f.find_orthogonal_solution(verbose=False) + sage: fvars = f.get_radical_expression() + sage: fvars[a1, a1, a1, a1, a2, a0] + -sqrt(1/2*sqrt(5) - 1/2) """ - return { sextuple : val.radical_expression() for sextuple, val in get_fmats_in_alg_field().items() } + return { sextuple : val.radical_expression() for sextuple, val in self.get_fmats_in_alg_field().items() } ####################### ### Private helpers ### @@ -726,7 +768,7 @@ def largest_fmat_size(self): def get_fvars_by_size(self,n,indices=False): r""" - Return the set of F-symbols that are entries of an `n \\times n` matrix + Return the set of F-symbols that are entries of an `n \times n` matrix `F^{a,b,c}_d`. INPUT: @@ -907,7 +949,7 @@ def get_orthogonality_constraints(self,output=True): polynomial objects. Otherwise, the constraints are appended to ``self.ideal_basis``. - They are stored in the internal tuple representation. The ``output==False`` + They are stored in the internal tuple representation. The ``output=False`` option is meant mostly for internal use by the F-matrix solver. """ eqns = list() @@ -1083,7 +1125,7 @@ def equations_graph(self,eqns=None): a given equation. If no list of equations is passed, the graph is built from equations in - self.ideal_basis + ``self.ideal_basis``. """ if eqns is None: eqns = self.ideal_basis @@ -1127,7 +1169,7 @@ def _add_square_fixers(self,output=False): return sq_fixers self.ideal_basis.extend(sq_fixers) - def par_graph_gb(self,worker_pool=None,eqns=None,term_order="degrevlex",verbose=True): + def _par_graph_gb(self,worker_pool=None,eqns=None,term_order="degrevlex",verbose=True): """ Compute a Groebner basis for a set of equations partitioned according to their corresponding graph """ @@ -1159,7 +1201,7 @@ def par_graph_gb(self,worker_pool=None,eqns=None,term_order="degrevlex",verbose= ret = small_comp_gb + temp_eqns return ret - def get_component_variety(self,var,eqns): + def _get_component_variety(self,var,eqns): """ Translate equations in each connected component to smaller polynomial rings so we can call built-in variety method. @@ -1184,12 +1226,13 @@ def get_component_variety(self,var,eqns): def attempt_number_field_computation(self): """ Based on the ``CartanType`` of ``self``, determine whether to attempt - to find a ``NumberField`` containing all the F-symbols based on data + to find a :class:`NumberField` containing all the F-symbols based on data known on March 17, 2021. - For certain ``FusionRing``s, the number field computation does not - seem to terminate. In these cases, we report F-symbols as elements - of the ``AlgebraicField``. + For certain :class:`FusionRing` 's, the number field computation does + not terminate in a reasonable amount of time. + In these cases, we report F-symbols as elements + of the ``AlgebraicField`` :class:`QQbar`. EXAMPLES:: @@ -1202,11 +1245,12 @@ def attempt_number_field_computation(self): """ ct = self._FR.cartan_type() k = self._FR._k + #Don't try when k is large and odd for SU(2)_k if ct.letter == 'A': - if ct.n == 1 and k >= 9: + if ct.n == 1 and k >= 9 and k % 2: return False if ct.letter == 'C': - if ct.n >= 9 and k == 1: + if ct.n >= 9 and ct.n % 2 and k == 1: return False if ct.letter == 'E': if ct.n < 8 and k == 2: @@ -1252,7 +1296,7 @@ def _get_explicit_solution(self,eqns=None,verbose=True): must_change_base_field = True #Otherwise, compute the component variety and select a point to obtain a numerical solution else: - sols = self.get_component_variety(comp,part) + sols = self._get_component_variety(comp,part) assert len(sols) > 1, "No real solution exists... component with variables {} has no real points".format(comp) for fx, rhs in sols[0].items(): non_cyclotomic_roots.append((fx,rhs)) @@ -1318,14 +1362,14 @@ def find_orthogonal_solution(self,checkpoint=False,save_results="",warm_start="" ``"fmatrix_solver_checkpoint_" + key + ".pickle"``, where ``key`` is the result of :meth:`get_fr_str`. - Generated checkpoint pickles are automatically deleted when the - solver runs successfully. + Checkpoint pickles are automatically deleted when the solver exits + a successful run. - ``save_results`` -- (optional) a string indicating the name of a pickle file in which to store calculated F-symbols for later use. - If ``save_results`` is not provided (default), results are not - stored to file. + If ``save_results`` is not provided (default), F-matrix results + are not stored to file. The F-symbols may be saved to file after running the solver using :meth:`save_fvars`. @@ -1349,11 +1393,11 @@ def find_orthogonal_solution(self,checkpoint=False,save_results="",warm_start="" :meth:`get_fvars`, :meth:`fmatrix`, :meth:`fmat`, etc. In many cases the F-symbols obtained are in fact real. In any case, the - F-symbols are obtained as elements of the associated ``FusionRing``'s - ``CyclotomicField``, a computed ``NumberField``, or ``QQbar``. - Currently, the output field is determined based on the ``CartanType`` - associated to ``self``. See :meth:`attempt_number_field_computation` - for details. + F-symbols are obtained as elements of the associated + :class:`FusionRing`'s :class:`CyclotomicField`, a computed + :class:`NumberField`, or ``QQbar``. Currently, the output field is + determined based on the ``CartanType`` associated to ``self``. + See :meth:`attempt_number_field_computation` for details. """ self._reset_solver_state() @@ -1386,7 +1430,7 @@ def find_orthogonal_solution(self,checkpoint=False,save_results="",warm_start="" if self._chkpt_status < 2: #Set up equations graph. Find GB for each component in parallel. Eliminate variables - self.ideal_basis = self.par_graph_gb(worker_pool=pool,verbose=verbose) + self.ideal_basis = self._par_graph_gb(worker_pool=pool,verbose=verbose) self.ideal_basis.sort(key=poly_sortkey) self._triangular_elim(worker_pool=pool,verbose=verbose) @@ -1428,7 +1472,7 @@ def find_orthogonal_solution(self,checkpoint=False,save_results="",warm_start="" #Set up new equations graph and compute variety for each component if self._chkpt_status < 5: - self.ideal_basis = self.par_graph_gb(term_order="lex",verbose=verbose) + self.ideal_basis = self._par_graph_gb(term_order="lex",verbose=verbose) self.ideal_basis.sort(key=poly_sortkey) self._triangular_elim(verbose=verbose) @@ -1452,7 +1496,8 @@ def find_orthogonal_solution(self,checkpoint=False,save_results="",warm_start="" def fix_gauge(self, algorithm=''): """ Fix the gauge by forcing F-symbols not already fixed to equal 1. - This method should be used AFTER adding hex and pentagon eqns to ideal_basis + This method should be used AFTER adding hex and pentagon eqns to + ``self.ideal_basis`` """ while len(self._solved) < len(self._poly_ring.gens()): #Get a variable that has not been fixed @@ -1465,10 +1510,10 @@ def fix_gauge(self, algorithm=''): self.ideal_basis.add(var-1) print("adding equation...", var-1) self.ideal_basis = set(Ideal(list(self.ideal_basis)).groebner_basis(algorithm=algorithm)) - self.substitute_degree_one() - self.update_equations() + self._substitute_degree_one() + self._update_equations() - def substitute_degree_one(self, eqns=None): + def _substitute_degree_one(self, eqns=None): if eqns is None: eqns = self.ideal_basis @@ -1492,7 +1537,7 @@ def substitute_degree_one(self, eqns=None): self._fvars[sextuple] = rhs.subs(d) return new_knowns, useless - def update_equations(self): + def _update_equations(self): """ Update ideal_basis equations by plugging in known values """ @@ -1540,8 +1585,8 @@ def find_cyclotomic_solution(self, equations=None, algorithm='', verbose=True, o (a1, a1, a2, a1, a2, a0): 1, (a1, a1, a1, a0, a2, a2): 1} - After you successfully run ``get_solution`` you may check - the correctness of the F-matrix by running :meth:`hexagon` + After you successfully run :meth:`find_cyclotomic_solution` you may + check the correctness of the F-matrix by running :meth:`hexagon` and :meth:`pentagon`. These should return empty lists of equations. @@ -1564,7 +1609,7 @@ def find_cyclotomic_solution(self, equations=None, algorithm='', verbose=True, o self.ideal_basis = set(Ideal(equations).groebner_basis(algorithm=algorithm)) if verbose: print("Solving...") - self.substitute_degree_one() + self._substitute_degree_one() if verbose: print("Fixing the gauge...") self.fix_gauge(algorithm=algorithm) diff --git a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx index 0cb4bd2833a..663249fd952 100644 --- a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx @@ -28,7 +28,7 @@ def executor(params): """ Execute a function defined in this module (sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn) - in a worker process, and supply the factory parameter by constructing a + in a worker process, and supply the `FusionRing` parameter by constructing a reference to the FMatrix object in the worker's memory adress space from its id. @@ -149,7 +149,6 @@ cpdef odd_one_out_ij(fusion_ring,xi,xj,a,b): mid_sig_ij = cached_function(mid_sig_ij, name='mid_sig_ij') odd_one_out_ij = cached_function(odd_one_out_ij, name='odd_one_out_ij') -#@cython.wraparound(False) @cython.nonecheck(False) @cython.cdivision(True) cpdef sig_2k(fusion_ring, tuple args): @@ -227,7 +226,6 @@ cpdef sig_2k(fusion_ring, tuple args): worker_results.append(((basis_dict[nnz_pos],i), entry)) -#@cython.wraparound(False) @cython.nonecheck(False) @cython.cdivision(True) cpdef odd_one_out(fusion_ring, tuple args): @@ -312,7 +310,7 @@ def collect_results(proc): """ #Discard the zero polynomial global worker_results - reduced = worker_results #set(worker_results)-set([tuple()]) + reduced = worker_results worker_results = list() return reduced @@ -321,6 +319,13 @@ def collect_results(proc): ###################################### cdef _flatten_entry(fusion_ring, entry): + """ + Flatten cyclotomic coefficients to a representation as a tuple of rational + coefficients. + + This is used to avoid pickling cyclotomic coefficient objects, which fails + with new PARI settings introduced in trac ticket #30537 + """ #The entry is either a polynomial or a base field element if entry.parent() == fusion_ring.fmats._poly_ring: entry = _flatten_coeffs(poly_to_tup(entry)) @@ -328,9 +333,27 @@ cdef _flatten_entry(fusion_ring, entry): entry = entry.list() return entry -cpdef _unflatten_entries(factory, list entries): - F = factory.fvars_field() - fm = factory.fmats +cpdef _unflatten_entries(fusion_ring, list entries): + """ + Restore cyclotomic coefficient object from its tuple of rational + coefficients representation. + + Used to circumvent pickling issue introduced by PARI settigs in trac + ticket #30537 + + EXAMPLES:: + + sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import _unflatten_entries + sage: fr = FusionRing("B2",2) + sage: F = fr.field() + sage: coeff = [F.random_element() for i in range(2)] + sage: entries = [((0,0), coeff[0].list()), ((0,1), coeff[1].list())] + sage: _unflatten_entries(fr, entries) + sage: all(cyc_elt_obj == c for (coord, cyc_elt_obj), c in zip(entries, coeff)) + True + """ + F = fusion_ring.fvars_field() + fm = fusion_ring.fmats must_unflatten = F != QQbar if must_unflatten: for i, (coord, entry) in enumerate(entries): diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index e0d62ac0d58..1f3a11adbbf 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -544,6 +544,10 @@ def fvars_field(self): Number Field in a with defining polynomial y^32 - 8*y^30 + 18*y^28 - 44*y^26 + 93*y^24 - 56*y^22 + 2132*y^20 - 1984*y^18 + 19738*y^16 - 28636*y^14 + 77038*y^12 - 109492*y^10 + 92136*y^8 - 32300*y^6 + 5640*y^4 - 500*y^2 + 25 sage: A13.field() Cyclotomic Field of order 40 and degree 16 + + In some cases, the :meth:`NumberField.optimized_representation` + may be used to obtain a better defining polynomial for the + computed ``NumberField``. """ if self.is_multiplicity_free(): return self.fmats.field() @@ -1275,8 +1279,8 @@ def gens_satisfy_braid_gp_rels(self,sig): Return True if the matrices in the list ``sig`` satisfy the braid relations. This if `n` is the cardinality of ``sig``, this confirms that these matrices define a representation of - the Artin braid group on `n+1` strands.Tests correctness of - get_braid_generators method. + the Artin braid group on `n+1` strands. Tests correctness of + :meth:`get_braid_generators`. EXAMPLES:: diff --git a/src/sage/combinat/root_system/poly_tup_engine.pyx b/src/sage/combinat/root_system/poly_tup_engine.pyx index 2e6dec0db4a..fe4b6bf7799 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pyx +++ b/src/sage/combinat/root_system/poly_tup_engine.pyx @@ -104,6 +104,19 @@ cpdef tuple _unflatten_coeffs(field, tuple eq_tup): Used to circumvent pickling issue introduced by PARI settigs in trac ticket #30537 + + EXAMPLES:: + + sage: from sage.combinat.root_system.poly_tup_engine import _unflatten_coeffs + sage: fm = FMatrix(FusionRing("A2",2)) + sage: p = fm._poly_ring.random_element() + sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: flat_poly_tup = list() + sage: for exp, cyc_coeff in poly_to_tup(p): + ....: flat_poly_tup.append((exp, tuple(cyc_coeff._coefficients()))) + sage: flat_poly_tup = tuple(flat_poly_tup) + sage: _unflatten_coeffs(fm.field(), flat_poly_tup) == poly_to_tup(p) + True """ cdef list unflat = list() for exp, coeff_tup in eq_tup: From 49e27efd33dc53c9e61d50a3ff210e9f3086ac11 Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Mon, 5 Apr 2021 16:15:04 -0700 Subject: [PATCH 033/632] Add CW2015 to index.rst --- src/doc/en/reference/references/index.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 26769808182..7aa2c0abb6f 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1763,6 +1763,9 @@ REFERENCES: .. [CHW2015] Shawn X.; Hong, Seung-Moon; Wang, Zhenghan Universal quantum computation with weakly integral anyons. Quantum Inf. Process. 14 (2015), no. 8, 2687–2727. +.. [CW2015] Cui, S. X. and Wang, Z. (2015). Universal quantum computation with + metaplectic anyons. Journal of Mathematical Physics, 56(3), 032202. + doi:10.1063/1.4914941 .. _ref-D: From c178754d86ae1ec7974e5507d08271abd7f65dd4 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Tue, 6 Apr 2021 18:58:38 -0400 Subject: [PATCH 034/632] testing push to trac --- src/sage/combinat/root_system/f_matrix.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 2e78fc0e108..694f5544230 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -11,6 +11,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** +#Testing git push from itertools import product, zip_longest import sage.combinat.root_system.fusion_ring as FusionRing import sage.graphs From 0b24ef9f7d3ed0131a317efe9eb9e87bc196e54a Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Tue, 6 Apr 2021 19:19:59 -0400 Subject: [PATCH 035/632] faster doctests, full coverage --- src/sage/combinat/root_system/f_matrix.py | 1795 ++++++++++++----- .../fast_parallel_fmats_methods.pxd | 6 +- .../fast_parallel_fmats_methods.pyx | 417 +++- .../fast_parallel_fusion_ring_braid_repn.pxd | 4 +- .../fast_parallel_fusion_ring_braid_repn.pyx | 230 ++- src/sage/combinat/root_system/fusion_ring.py | 221 +- .../combinat/root_system/poly_tup_engine.pxd | 21 +- .../combinat/root_system/poly_tup_engine.pyx | 421 ++-- 8 files changed, 2106 insertions(+), 1009 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 694f5544230..a7d276c2a0d 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -11,251 +11,268 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -#Testing git push -from itertools import product, zip_longest -import sage.combinat.root_system.fusion_ring as FusionRing -import sage.graphs -from sage.graphs.generators.basic import EmptyGraph -from sage.matrix.constructor import matrix -from sage.misc.misc import inject_variable -from sage.rings.polynomial.all import PolynomialRing -from sage.rings.ideal import Ideal -from sage.misc.misc import get_main_globals -from copy import deepcopy -#Import pickle for checkpointing and loading certain variables +#Import pickle for checkpointing and loading try: import cPickle as pickle except: import pickle - +from copy import deepcopy +from itertools import product, zip_longest from multiprocessing import cpu_count, Pool, set_start_method import numpy as np import os -from sage.combinat.root_system.fast_parallel_fmats_methods import * -from sage.combinat.root_system.poly_tup_engine import * -#Import faster unsafe method (not for client use) -from sage.combinat.root_system.poly_tup_engine import _tup_to_poly, _unflatten_coeffs + +from sage.combinat.root_system.fast_parallel_fmats_methods import ( + _backward_subs, _solve_for_linear_terms, + collect_eqns, executor +) +from sage.combinat.root_system.poly_tup_engine import ( + apply_coeff_map, constant_coeff, + compute_known_powers, + get_variables_degrees, variables, + poly_to_tup, _tup_to_poly, tup_to_univ_poly, + _unflatten_coeffs, + poly_tup_sortkey, + tup_fixes_sq, + resize, +) +from sage.graphs.graph import Graph +from sage.matrix.constructor import matrix +from sage.misc.misc import get_main_globals +from sage.rings.ideal import Ideal +from sage.rings.polynomial.all import PolynomialRing from sage.rings.polynomial.polydict import ETuple from sage.rings.qqbar import AA, QQbar, number_field_elements_from_algebraics from sage.rings.real_double import RDF - class FMatrix(): - r"""Return an F-Matrix factory for a :class:`FusionRing`. - - INPUT: - - - ``FR`` -- a :class:`FusionRing`. - - The :class:`FusionRing` or Verlinde algebra is the - Grothendieck ring of a modular tensor category [BaKi2001]_. - Such categories arise in conformal field theory or in the - representation theories of affine Lie algebras, or - quantum groups at roots of unity. They have applications - to low dimensional topology and knot theory, to conformal - field theory and to topological quantum computing. The - :class:`FusionRing` captures much information about a fusion - category, but to complete the picture, the F-matrices or - 6j-symbols are needed. For example these are required in - order to construct braid group representations. - - We only undertake to compute the F-matrix if the - :class:`FusionRing` is *multiplicity free* meaning that - the Fusion coefficients `N^{ij}_k` are bounded - by 1. For Cartan Types `X_r` and level `k`, - the multiplicity-free cases are given by the - following table. - -+------------------------+----------+ -| Cartan Type | `k` | -+========================+==========+ -| `A_1` | any | -+------------------------+----------+ -| `A_r, r\geq 2` | `\leq 2` | -+------------------------+----------+ -| `B_r, r\geq 2` | `\leq 2` | -+------------------------+----------+ -| `C_2` | `\leq 2` | -+------------------------+----------+ -| `C_r, r\geq 3` | `\leq 1` | -+------------------------+----------+ -| `D_r, r\geq 4` | `\leq 2` | -+------------------------+----------+ -| `G_2,F_4,E_r` | `\leq 2` | -+------------------------+----------+ - - Beyond this limitation, computation of the F-matrix - can involve very large systems of equations. A - rule of thumb is that this code can compute the - F-matrix for systems with `\leq 14` primary fields - (simple objects), on a machine with 16 GB of memory. - Larger examples can be quite time consuming. - - The :class:`FusionRing` and its methods capture much - of the structure of the underlying tensor category. - But an important aspect that is not encoded in the - fusion ring is the associator, which is a homomorphism - `(A\otimes B)\otimes C\to A\otimes(B\otimes C)` - requires an additional tool, the F-matrix or 6j-symbol. - To specify this, we fix a simple object `D` - and represent the transformation - - .. MATH:: - - \text{Hom}(D,(A\otimes B)\otimes C) \to \text{Hom}(D,A\otimes(B\otimes C)) - - by a matrix `F^{ABC}_D`. This depends on a pair of - additional simple objects `X` and `Y`. Indeed, we can - get a basis for `\text{Hom}(D,(A\otimes B)\otimes C)` - indexed by simple objects `X` in which the corresponding - homomorphism factors through `X\otimes C`, and similarly - `\text{Hom}(D,A\otimes(B\otimes C))` has a basis indexed - by `Y`, in which the basis vector factors through `A\otimes Y`. - - See [TTWL2009]_ for an introduction to this topic, - [EGNO2015]_ Section 4.9 for a precise mathematical - definition and [Bond2007]_ Section 2.5 for a discussion - of how to compute the F-matrix. In addition to - [Bond2007]_ worked out F-matrices may be found in - [RoStWa2009]_ and [CHW2015]_. - - The F-matrix is only determined up to a *gauge*. This - is a family of embeddings `C\to A\otimes B` for - simple objects `A,B,C` such that `\text{Hom}(C,A\otimes B)` - is nonzero. Changing the gauge changes the F-matrix though - not in a very essential way. By varying the gauge it is - possible to make the F-matrices unitary, or it is possible - to make them cyclotomic. - - Due to the large number of equations we may fail to find a - Groebner basis if there are too many variables. - - - EXAMPLES:: - - sage: I=FusionRing("E8",2,conjugate=True) - sage: I.fusion_labels(["i0","p","s"],inject_variables=True) - sage: f = FMatrix(I,inject_variables=True); f - creating variables fx1..fx14 - Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 - F-Matrix factory for The Fusion Ring of Type E8 and level 2 with Integer Ring coefficients - - We've exported two sets of variables to the global namespace. - We created three variables ``i0, p, s`` to represent the - primary fields (simple elements) of the FusionRing. Creating - the FMatrix factory also created variables ``fx1,fx2, ... , fx14`` - in order to solve the hexagon and pentagon equations describing - the F-matrix. Since we called ``FMatrix`` with the parameter ``inject_variables`` - set true, these have been exported into the global namespace. This - is not necessary for the code to work but if you want to - run the code experimentally you may want access to these - variables. - - EXAMPLES:: - - sage: f.fmatrix(s,s,s,s) - [fx10 fx11] - [fx12 fx13] - - The F-matrix has not been computed at this stage, so - the F-matrix `F^{sss}_s` is filled with variables - ``fx10``, ``fx11``, ``fx12``, ``fx13``. The task is - to solve for these. - - As explained above The F-matrix `(F^{ABC}_D)_{X,Y}` - two other variables `X` and `Y`. We have methods to - tell us (depending on `A,B,C,D`) what the possibilities - for these are. In this example with `A=B=C=D=s` - both `X` and `Y` are allowed to be `i_0` or `s`. - - EXAMPLES:: - - sage: f.f_from(s,s,s,s), f.f_to(s,s,s,s) - ([i0, p], [i0, p]) - - The last two statments show that the possible values of - `X` and `Y` when `A=B=C=D=s` are `i_0` and `p`. - - The F-matrix is computed by solving the so-called - pentagon and hexagon equations. The *pentagon - equations* reflect the Mac Lane pentagon axiom in the - definition of a monoidal category. The hexagon relations - reflect the axioms of a *braided monoidal category*, - which are constraints on both the F-matrix and on - the R-matrix. Optionally, orthogonality constraints - may be imposed to obtain an orthogonal F-matrix. - - EXAMPLES:: - - sage: f.get_defining_equations("pentagons")[1:3] - [fx9*fx12 - fx2*fx13, fx3*fx8 - fx4*fx9] - sage: f.get_defining_equations("hexagons")[1:3] - [fx11*fx12 + (-zeta128^32)*fx13^2 + (-zeta128^48)*fx13, - fx10*fx11 + (-zeta128^32)*fx11*fx13 + (-zeta128^16)*fx11] - sage: f.get_orthogonality_constraints()[1:3] - [fx1^2 - 1, fx2^2 - 1] - - There are two methods available to compute an F-matrix. - The first, :meth:`find_cyclotomic_solution` uses only - the pentagon and hexagon relations. The second, - :meth:`find_orthogonal_solution` uses additionally - the orthogonality relations. There are some differences - that should be kept in mind. - - :meth:`find_cyclotomic_solution` currently works only with - smaller examples. For example the :class:`FusionRing` for A2 at - level 2 is too large. When it is available, this method - produces an F-matrix whose entries are in the same - cyclotomic field as the underlying :class:`FusionRing`. - - EXAMPLES:: - - sage: f.find_cyclotomic_solution() - Setting up hexagons and pentagons... - Finding a Groebner basis... - Solving... - Fixing the gauge... - adding equation... fx1 - 1 - adding equation... fx11 - 1 - Done! - - We now have access to the values of the F-mstrix using - the methods :meth:`fmatrix` and :meth:`fmat`. - - EXAMPLES:: - - sage: f.fmatrix(s,s,s,s) - [(-1/2*zeta128^48 + 1/2*zeta128^16) 1] - [ 1/2 (1/2*zeta128^48 - 1/2*zeta128^16)] - sage: f.fmat(s,s,s,s,p,p) - (1/2*zeta128^48 - 1/2*zeta128^16) - - :meth:`find_orthogonal_solution` is much more powerful - and is capable of handling large cases, sometimes - quickly but sometimes (in larger cases) after hours of - computation. Its F-matrices are not always in the - cyclotomic field that is the base ring of the underlying - :class:`FusionRing`, but sometimes in an extension field adjoining - some square roots. When this happens, the :class:`FusionRing` is - modified, adding an attribute :attr:`_basecoer` that is - a coercion from the cyclotomic field to the field - containing the F-matrix. The field containing the F-matrix - is available through :meth:`field`. - - EXAMPLES:: - - sage: f = FMatrix(FusionRing("B3",2)) - sage: f.find_orthogonal_solution(verbose=False,checkpoint=True) # long time (~100 s) - sage: all(v in CyclotomicField(56) for v in f.get_fvars().values()) # long time - True - - sage: f = FMatrix(FusionRing("G2",2)) - sage: f.find_orthogonal_solution(verbose=False) # long time (~18 s) - sage: f.field() # long time - Algebraic Field - """ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variables=False): + r"""Return an F-Matrix factory for a :class:`FusionRing`. + + INPUT: + + - ``FR`` -- a :class:`FusionRing`. + + - ``fusion_label`` -- (optional) a string used to label basis elements + of the :class:`FusionRing` associated to ``self``. + + See :meth:`FusionRing.fusion_labels`. + + - ``var_prefix`` -- (optional) a string indicating the desired prefix + for variables denoting F-symbols to be solved. + + - ``inject_variables`` -- (default: ``False``) a boolean indicating + whether to inject variables (:class:`FusionRing` basis element + labels and F-symbols) into the global namespace. + + The :class:`FusionRing` or Verlinde algebra is the + Grothendieck ring of a modular tensor category [BaKi2001]_. + Such categories arise in conformal field theory or in the + representation theories of affine Lie algebras, or + quantum groups at roots of unity. They have applications + to low dimensional topology and knot theory, to conformal + field theory and to topological quantum computing. The + :class:`FusionRing` captures much information about a fusion + category, but to complete the picture, the F-matrices or + 6j-symbols are needed. For example these are required in + order to construct braid group representations. + + We only undertake to compute the F-matrix if the + :class:`FusionRing` is *multiplicity free* meaning that + the Fusion coefficients `N^{ij}_k` are bounded + by 1. For Cartan Types `X_r` and level `k`, + the multiplicity-free cases are given by the + following table. + + +------------------------+----------+ + | Cartan Type | `k` | + +========================+==========+ + | `A_1` | any | + +------------------------+----------+ + | `A_r, r\geq 2` | `\leq 2` | + +------------------------+----------+ + | `B_r, r\geq 2` | `\leq 2` | + +------------------------+----------+ + | `C_2` | `\leq 2` | + +------------------------+----------+ + | `C_r, r\geq 3` | `\leq 1` | + +------------------------+----------+ + | `D_r, r\geq 4` | `\leq 2` | + +------------------------+----------+ + | `G_2,F_4,E_r` | `\leq 2` | + +------------------------+----------+ + + Beyond this limitation, computation of the F-matrix + can involve very large systems of equations. A + rule of thumb is that this code can compute the + F-matrix for systems with `\leq 14` simple objects + (primary fields) on a machine with 16 GB of memory. + (Larger examples can be quite time consuming.) + + The :class:`FusionRing` and its methods capture much + of the structure of the underlying tensor category. + But an important aspect that is not encoded in the + fusion ring is the associator, which is a homomorphism + `(A\otimes B)\otimes C\to A\otimes(B\otimes C)` that + requires an additional tool, the F-matrix or 6j-symbol. + To specify this, we fix a simple object `D` + and represent the transformation + + .. MATH:: + + \text{Hom}(D,(A\otimes B)\otimes C) \to \text{Hom}(D,A\otimes(B\otimes C)) + + by a matrix `F^{ABC}_D`. This depends on a pair of + additional simple objects `X` and `Y`. Indeed, we can + get a basis for `\text{Hom}(D,(A\otimes B)\otimes C)` + indexed by simple objects `X` in which the corresponding + homomorphism factors through `X\otimes C`, and similarly + `\text{Hom}(D,A\otimes(B\otimes C))` has a basis indexed + by `Y`, in which the basis vector factors through `A\otimes Y`. + + See [TTWL2009]_ for an introduction to this topic, + [EGNO2015]_ Section 4.9 for a precise mathematical + definition, and [Bond2007]_ Section 2.5 for a discussion + of how to compute the F-matrix. In addition to + [Bond2007]_, worked out F-matrices may be found in + [RoStWa2009]_ and [CHW2015]_. + + The F-matrix is only determined up to a *gauge*. This + is a family of embeddings `C\to A\otimes B` for + simple objects `A,B,C` such that `\text{Hom}(C,A\otimes B)` + is nonzero. Changing the gauge changes the F-matrix though + not in a very essential way. By varying the gauge it is + possible to make the F-matrices unitary, or it is possible + to make them cyclotomic. + + Due to the large number of equations we may fail to find a + Groebner basis if there are too many variables. + + + EXAMPLES:: + + sage: I = FusionRing("E8",2,conjugate=True) + sage: I.fusion_labels(["i0","p","s"],inject_variables=True) + sage: f = FMatrix(I,inject_variables=True); f + creating variables fx1..fx14 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 + F-Matrix factory for The Fusion Ring of Type E8 and level 2 with Integer Ring coefficients + + We've exported two sets of variables to the global namespace. + We created three variables ``i0, p, s`` to represent the + primary fields (simple elements) of the FusionRing. Creating + the FMatrix factory also created variables ``fx1,fx2, ... , fx14`` + in order to solve the hexagon and pentagon equations describing + the F-matrix. Since we called :class:`FMatrix` with the parameter + ``inject_variables=True``, these have been exported into the global + namespace. This is not necessary for the code to work but if you want + to run the code experimentally you may want access to these + variables. + + EXAMPLES:: + + sage: f.fmatrix(s,s,s,s) + [fx10 fx11] + [fx12 fx13] + + The F-matrix has not been computed at this stage, so + the F-matrix `F^{sss}_s` is filled with variables + ``fx10``, ``fx11``, ``fx12``, ``fx13``. The task is + to solve for these. + + As explained above The F-matrix `(F^{ABC}_D)_{X,Y}` + two other variables `X` and `Y`. We have methods to + tell us (depending on `A,B,C,D`) what the possibilities + for these are. In this example with `A=B=C=D=s` + both `X` and `Y` are allowed to be `i_0` or `s`. + + EXAMPLES:: + + sage: f.f_from(s,s,s,s), f.f_to(s,s,s,s) + ([i0, p], [i0, p]) + + The last two statments show that the possible values of + `X` and `Y` when `A=B=C=D=s` are `i_0` and `p`. + + The F-matrix is computed by solving the so-called + pentagon and hexagon equations. The *pentagon + equations* reflect the Mac Lane pentagon axiom in the + definition of a monoidal category. The hexagon relations + reflect the axioms of a *braided monoidal category*, + which are constraints on both the F-matrix and on + the R-matrix. Optionally, orthogonality constraints + may be imposed to obtain an orthogonal F-matrix. + + EXAMPLES:: + + sage: f.get_defining_equations("pentagons")[1:3] + [fx9*fx12 - fx2*fx13, fx3*fx8 - fx4*fx9] + sage: f.get_defining_equations("hexagons")[1:3] + [fx11*fx12 + (-zeta128^32)*fx13^2 + (-zeta128^48)*fx13, + fx10*fx11 + (-zeta128^32)*fx11*fx13 + (-zeta128^16)*fx11] + sage: f.get_orthogonality_constraints()[1:3] + [fx1^2 - 1, fx2^2 - 1] + + There are two methods available to compute an F-matrix. + The first, :meth:`find_cyclotomic_solution` uses only + the pentagon and hexagon relations. The second, + :meth:`find_orthogonal_solution` uses additionally + the orthogonality relations. There are some differences + that should be kept in mind. + + :meth:`find_cyclotomic_solution` currently works only with + smaller examples. For example the :class:`FusionRing` for `G_2` at + level 2 is too large. When it is available, this method + produces an F-matrix whose entries are in the same + cyclotomic field as the underlying :class:`FusionRing`. + + EXAMPLES:: + + sage: f.find_cyclotomic_solution() + Setting up hexagons and pentagons... + Finding a Groebner basis... + Solving... + Fixing the gauge... + adding equation... fx1 - 1 + adding equation... fx11 - 1 + Done! + + We now have access to the values of the F-matrix using + the methods :meth:`fmatrix` and :meth:`fmat`. + + EXAMPLES:: + + sage: f.fmatrix(s,s,s,s) + [(-1/2*zeta128^48 + 1/2*zeta128^16) 1] + [ 1/2 (1/2*zeta128^48 - 1/2*zeta128^16)] + sage: f.fmat(s,s,s,s,p,p) + (1/2*zeta128^48 - 1/2*zeta128^16) + + :meth:`find_orthogonal_solution` is much more powerful + and is capable of handling large cases, sometimes + quickly but sometimes (in larger cases) after hours of + computation. Its F-matrices are not always in the + cyclotomic field that is the base ring of the underlying + :class:`FusionRing`, but sometimes in an extension field adjoining + some square roots. When this happens, the :class:`FusionRing` is + modified, adding an attribute :attr:`_basecoer` that is + a coercion from the cyclotomic field to the field + containing the F-matrix. The field containing the F-matrix + is available through :meth:`field`. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("B3",2)) + sage: f.find_orthogonal_solution(verbose=False,checkpoint=True) # long time (~100 s) + sage: all(v in CyclotomicField(56) for v in f.get_fvars().values()) # long time + True + + sage: f = FMatrix(FusionRing("G2",2)) + sage: f.find_orthogonal_solution(verbose=False) # long time (~18 s) + sage: f.field() # long time + Algebraic Field + """ self._FR = fusion_ring if inject_variables and (self._FR._fusion_labels is None): self._FR.fusion_labels(fusion_label, inject_variables=True) @@ -268,7 +285,8 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab print ("creating variables %s%s..%s%s"%(var_prefix,1,var_prefix,n_vars)) self._poly_ring.inject_variables(get_main_globals()) self._var_to_sextuple, self._fvars = self.findcases(output=True) - self._var_to_idx = { var : idx for idx, var in enumerate(self._poly_ring.gens()) } + self._var_to_idx = {var : idx for idx, var in enumerate(self._poly_ring.gens())} + self._idx_to_sextuple = {i : self._var_to_sextuple[self._poly_ring.gen(i)] for i in range(self._poly_ring.ngens())} self._singles = self.singletons() #Base field attributes @@ -278,6 +296,7 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab self._non_cyc_roots = list() #Useful solver state attributes + self.ideal_basis = list() self._solved = set() self._ks = dict() self._nnz = self._get_known_nonz() @@ -299,29 +318,78 @@ def __repr__(self): """ return "F-Matrix factory for %s"%self._FR - def remaining_vars(self): - """ - Return a list of unknown F-symbols (reflects current stage of computation) - """ - return [var for var in self._poly_ring.gens() if var not in self._solved] - def clear_equations(self): """ - Clear the set of equations to be solved. + Clear the list of equations to be solved. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("E6",1)) + sage: f.get_defining_equations('hexagons', output=False) + sage: len(f.ideal_basis) + 6 + sage: f.clear_equations() + sage: len(f.ideal_basis) == 0 + True """ self.ideal_basis = list() def clear_vars(self): """ - Clear the set of variables. Also reset the set of solved F-symbols. + Reset the F-symbols. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("C4", 1)) + sage: fvars = f.get_fvars() + sage: some_key = sorted(fvars)[0] + sage: fvars[some_key] + fx0 + sage: fvars[some_key] = 1 + sage: f.get_fvars()[some_key] + 1 + sage: f.clear_vars() + sage: f.get_fvars()[some_key] + fx0 """ - self._fvars = { self._var_to_sextuple[key] : key for key in self._var_to_sextuple } + self._fvars = {self._var_to_sextuple[key] : key for key in self._var_to_sextuple} self._solved = set() def _reset_solver_state(self): """ Reset solver state and clear relevant cache. Used to ensure state variables are the same for each orthogonal solver run. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("F4",1)) + sage: K = f.field() + sage: len(f._nnz.nonzero_positions()) + 1 + sage: f.find_orthogonal_solution(verbose=False) + sage: K == f.field() + False + sage: f._reset_solver_state() + sage: K == f.field() + True + sage: f.FR()._basecoer is None + True + sage: f._poly_ring.base_ring() == K + True + sage: len(f._solved) == 0 + True + sage: len(f.ideal_basis) == 0 + True + sage: len(f._ks) == 0 + True + sage: len(f._nnz.nonzero_positions()) == 1 + True + sage: all(len(x.q_dimension.cache) == 0 for x in f.FR().basis()) + True + sage: len(f.FR().r_matrix.cache) == 0 + True + sage: len(f.FR().s_ij.cache) == 0 + True """ self._FR._basecoer = None self._field = self._FR.field() @@ -339,14 +407,25 @@ def _reset_solver_state(self): def _update_poly_ring_base_field(self,field): """ - Change base field of PolynomialRing and the corresponding + Change base field of ``PolynomialRing`` and the corresponding index attributes + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("D4",1)) + sage: f._update_poly_ring_base_field(QQ) + sage: f._poly_ring.base_ring() == QQ + True + sage: all(fx in f._poly_ring for fx in f._var_to_idx) + True + sage: all(fx in f._poly_ring for fx in f._var_to_sextuple) + True """ new_poly_ring = self._poly_ring.change_ring(field) nvars = self._poly_ring.ngens() #Do some appropriate conversions - self._var_to_idx = { new_poly_ring.gen(i) : i for i in range(nvars) } - self._var_to_sextuple = { new_poly_ring.gen(i) : self._var_to_sextuple[self._poly_ring.gen(i)] for i in range(nvars) } + self._var_to_idx = {new_poly_ring.gen(i) : i for i in range(nvars)} + self._var_to_sextuple = {new_poly_ring.gen(i) : self._var_to_sextuple[self._poly_ring.gen(i)] for i in range(nvars)} self._poly_ring = new_poly_ring def fmat(self, a, b, c, d, x, y, data=True): @@ -415,7 +494,7 @@ def fmatrix(self,a,b,c,d): EXAMPLES:: - sage: f=FMatrix(FusionRing("A1",2,fusion_labels="c",inject_variables=True)) + sage: f = FMatrix(FusionRing("A1",2,fusion_labels="c",inject_variables=True)) sage: f.fmatrix(c1,c1,c1,c1) [fx0 fx1] [fx2 fx3] @@ -437,9 +516,29 @@ def fmatrix(self,a,b,c,d): def field(self): r""" - Return the base field containing the F-symbols. When ``self`` is initialized, - the field is set to be the cyclotomic field of the :class:`FusionRing` associated - to ``self``. The field may change after running :meth:`find_orthogonal_solution`. + Return the base field containing the F-symbols. + + When ``self`` is initialized, the field is set to be the + cyclotomic field of the :class:`FusionRing` associated + to ``self``. + + The field may change after running :meth:`find_orthogonal_solution`. + At that point, this method could return the + associated :class:`FusionRing`'s cyclotomic field, an + appropriate :class:`NumberField` that was computed on the fly + by the F-matrix solver, or the :class:`AlgebraicField` ``QQbar``. + + Depending on the ``CartanType`` of ``self``, the solver may need + to compute an extension field containing certain square roots that + do not belong to the associated :class:`FusionRing`'s cyclotomic field. + + In certain cases we revert to ``QQbar`` because the extension field + computation does not seem to terminate. See + :meth:`attempt_number_field_computation` for more details. + + The method :meth:`get_non_cyclotomic_roots` returns a list of + roots defining the extension of the :class:`FusionRing`'s + cyclotomic field needed to contain all F-symbols. EXAMPLES:: @@ -449,6 +548,16 @@ def field(self): sage: f.find_orthogonal_solution(verbose=False) sage: f.field() Number Field in a with defining polynomial y^64 - 16*y^62 + 104*y^60 - 320*y^58 + 258*y^56 + 1048*y^54 - 2864*y^52 - 3400*y^50 + 47907*y^48 - 157616*y^46 + 301620*y^44 - 322648*y^42 + 2666560*y^40 + 498040*y^38 + 54355076*y^36 - 91585712*y^34 + 592062753*y^32 - 1153363592*y^30 + 3018582788*y^28 - 4848467552*y^26 + 7401027796*y^24 - 8333924904*y^22 + 8436104244*y^20 - 7023494736*y^18 + 4920630467*y^16 - 2712058560*y^14 + 1352566244*y^12 - 483424648*y^10 + 101995598*y^8 - 12532920*y^6 + 1061168*y^4 - 57864*y^2 + 1681 + sage: phi = f.get_qqbar_embedding() + sage: [phi(r).n() for r in f.get_non_cyclotomic_roots()] + [-0.786151377757423 + 1.73579267033929e-59*I] + + .. NOTE:: + + Consider using ``self.field().optimized_representation()`` to + obtain an equivalent :class:`NumberField` with a defining + polynomial with smaller coefficients, for a more efficient + element representation. """ return self._field @@ -466,13 +575,15 @@ def FR(self): def findcases(self,output=False): """ - Return unknown F-matrix entries. If run with output=True, + Return unknown F-matrix entries. + + If run with ``output=True``, this returns two dictionaries; otherwise it just returns the number of unknown values. EXAMPLES:: - sage: f=FMatrix(FusionRing("G2",1,fusion_labels=("i0","t"))) + sage: f = FMatrix(FusionRing("G2",1,fusion_labels=("i0","t"))) sage: f.findcases() 5 sage: f.findcases(output=True) @@ -514,9 +625,9 @@ def singletons(self): EXAMPLES:: - sage: fm = FMatrix(FusionRing("E7", 1)) - sage: singles = fm.singletons() - sage: all(fm.fmatrix(*fm._var_to_sextuple[v][:4]).nrows() == 1 for v in singles) + sage: f = FMatrix(FusionRing("E7",1)) + sage: singles = f.singletons() + sage: all(f.fmatrix(*f._var_to_sextuple[v][:4]).nrows() == 1 for v in singles) True """ ret = [] @@ -586,8 +697,9 @@ def get_fvars(self): r""" Return a dictionary of F-symbols. - The keys are sextuples `(a,b,c,d,x,y)` basis elements of ``self`` and - the values are the corresponding F-symbols `(F^{a,b,c}_d)_{xy}`. + The keys are sextuples `(a,b,c,d,x,y)` of basis elements of + ``self.FR()`` and the values are the corresponding F-symbols + `(F^{a,b,c}_d)_{xy}`. These values reflect the current state of a solver's computation. @@ -604,6 +716,18 @@ def get_fvars(self): """ return self._fvars + def get_poly_ring(self): + r""" + Return the polynomial ring whose generators denote F-symbols we seek. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("B6",1)) + sage: f.get_poly_ring() + Multivariate Polynomial Ring in fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 over Cyclotomic Field of order 96 and degree 32 + """ + return self._poly_ring + def get_non_cyclotomic_roots(self): r""" Return a list of roots that define the extension of the associated @@ -612,24 +736,30 @@ def get_non_cyclotomic_roots(self): OUTPUT: - The list of non-cyclotomic roots is given as a list of elements of - ``self.field()``. + The list of non-cyclotomic roots is given as a list of elements of the + field returned by :meth:`field()`. - If ``self.field() == self.FR().field()`` this method returns an empty list. - - When ``self.field()`` is a ``NumberField``, one may use - :meth:`get_qqbar_embedding` to embed the resulting values into ``QQbar``. + If ``self.field() == self.FR().field()`` then this method + returns an empty list. EXAMPLES:: sage: f = FMatrix(FusionRing("E6",1)) sage: f.find_orthogonal_solution(verbose=False) + sage: f.field() == f.FR().field() + True sage: f.get_non_cyclotomic_roots() [] - sage: f = FMatrix(FusionRing("E7",2)) # long time + sage: f = FMatrix(FusionRing("E7",2)) # long time sage: f.find_orthogonal_solution(verbose=False) # long time - sage: f.get_non_cyclotomic_roots() # long time + sage: f.field() == f.FR().field() # long time + False + sage: f.get_non_cyclotomic_roots() # long time [-0.7861513777574233?, -0.5558929702514212?] + + When ``self.field()`` is a ``NumberField``, one may use + :meth:`get_qqbar_embedding` to embed the resulting values into + ``QQbar``. """ return sorted(set(self._non_cyc_roots)) @@ -642,17 +772,30 @@ def get_qqbar_embedding(self): This embedding is useful for getting a better sense for the F-symbols, particularly when they are computed as elements of a - :class:`NumberField`. + :class:`NumberField`. See also :meth:`get_non_cyclotomic_roots`. EXAMPLES:: - sage: fm = FMatrix(FusionRing("C3",1), fusion_label="c", inject_variables=True) - creating variables fx1..fx71 - Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19, fx20, fx21, fx22, fx23, fx24, fx25, fx26, fx27, fx28, fx29, fx30, fx31, fx32, fx33, fx34, fx35, fx36, fx37, fx38, fx39, fx40, fx41, fx42, fx43, fx44, fx45, fx46, fx47, fx48, fx49, fx50, fx51, fx52, fx53, fx54, fx55, fx56, fx57, fx58, fx59, fx60, fx61, fx62, fx63, fx64, fx65, fx66, fx67, fx68, fx69, fx70 - sage: fm.find_orthogonal_solution(verbose=False) - sage: phi = fm.get_qqbar_embedding() - sage: phi(fm.get_fvars()[c1,c2,c2,c1,c1,c2]) - -0.61803399? + 0.?e-8*I + sage: f = FMatrix(FusionRing("F4",1), fusion_label="f", inject_variables=True) + creating variables fx1..fx5 + Defining fx0, fx1, fx2, fx3, fx4 + sage: f.find_orthogonal_solution() + Computing F-symbols for The Fusion Ring of Type F4 and level 1 with Integer Ring coefficients with 5 variables... + Set up 10 hex and orthogonality constraints... + Partitioned 10 equations into 2 components of size: + [4, 1] + Elimination epoch completed... 0 eqns remain in ideal basis + Hex elim step solved for 4 / 5 variables + Set up 0 reduced pentagons... + Pent elim step solved for 4 / 5 variables + Partitioned 0 equations into 0 components of size: + [] + Partitioned 1 equations into 1 components of size: + [1] + Computing appropriate NumberField... + sage: phi = f.get_qqbar_embedding() + sage: phi(f.fmat(f1,f1,f1,f1,f1,f1)).n() + -0.618033988749895 + 3.63089268571980e-21*I """ return self._qqbar_embedding @@ -662,6 +805,39 @@ def get_coerce_map_from_fr_cyclotomic_field(self): cyclotomic field into the base field containing all F-symbols (this could be the :class:`FusionRing`'s :class:`CyclotomicField`, a :class:`NumberField`, or ``QQbar``). + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("G2",1)) + sage: f.find_orthogonal_solution(verbose=False) + sage: f.FR().field() + Cyclotomic Field of order 60 and degree 16 + sage: f.field() + Number Field in a with defining polynomial y^32 - 6*y^30 - 7*y^28 + 62*y^26 - 52*y^24 - 308*y^22 + 831*y^20 + 7496*y^18 + 18003*y^16 - 2252*y^14 + 42259*y^12 - 65036*y^10 + 29368*y^8 - 3894*y^6 + 377*y^4 - 22*y^2 + 1 + sage: phi = f.get_coerce_map_from_fr_cyclotomic_field() + sage: phi.domain() == f.FR().field() + True + sage: phi.codomain() == f.field() + True + + When F-symbols are computed as elements of the associated + :class:`FusionRing`'s base :class:`CyclotomicField`, + we have ``self.field() == self.FR().field()`` and this method + returns the identity map on ``self.field()``. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("A2",1)) + sage: f.find_orthogonal_solution(verbose=False) + sage: phi = f.get_coerce_map_from_fr_cyclotomic_field() + sage: f.field() + Cyclotomic Field of order 48 and degree 16 + sage: f.field() == f.FR().field() + True + sage: phi.domain() == f.field() + True + sage: phi.is_identity() + True """ #If base field is different from associated FusionRing's CyclotomicField, #return coercion map @@ -672,14 +848,29 @@ def get_coerce_map_from_fr_cyclotomic_field(self): F = self._FR.field() return F.hom([F.gen()], F) - def get_fmats_in_alg_field(self): + def get_fvars_in_alg_field(self): r""" Return F-symbols as elements of the :class:`AlgebraicField` ``QQbar``. This method uses the embedding defined by - :meth:`self.get_qqbar_embedding` to coerce + :meth:`get_qqbar_embedding` to coerce F-symbols into ``QQbar``. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("G2",1), fusion_label="g", inject_variables=True) + creating variables fx1..fx5 + Defining fx0, fx1, fx2, fx3, fx4 + sage: f.find_orthogonal_solution(verbose=False) + sage: f.field() + Number Field in a with defining polynomial y^32 - 6*y^30 - 7*y^28 + 62*y^26 - 52*y^24 - 308*y^22 + 831*y^20 + 7496*y^18 + 18003*y^16 - 2252*y^14 + 42259*y^12 - 65036*y^10 + 29368*y^8 - 3894*y^6 + 377*y^4 - 22*y^2 + 1 + sage: f.get_fvars_in_alg_field() + {(g1, g1, g1, g0, g1, g1): 1, + (g1, g1, g1, g1, g0, g0): 0.61803399? + 0.?e-8*I, + (g1, g1, g1, g1, g0, g1): -0.7861514? + 0.?e-8*I, + (g1, g1, g1, g1, g1, g0): -0.7861514? + 0.?e-8*I, + (g1, g1, g1, g1, g1, g1): -0.61803399? + 0.?e-8*I} """ - return { sextuple : self._qqbar_embedding(fvar) for sextuple, fvar in self._fvars.items() } + return {sextuple : self._qqbar_embedding(fvar) for sextuple, fvar in self._fvars.items()} def get_radical_expression(self): """ @@ -687,15 +878,14 @@ def get_radical_expression(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("A1",3), fusion_label="a", inject_variables=True) - creating variables fx1..fx71 - Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19, fx20, fx21, fx22, fx23, fx24, fx25, fx26, fx27, fx28, fx29, fx30, fx31, fx32, fx33, fx34, fx35, fx36, fx37, fx38, fx39, fx40, fx41, fx42, fx43, fx44, fx45, fx46, fx47, fx48, fx49, fx50, fx51, fx52, fx53, fx54, fx55, fx56, fx57, fx58, fx59, fx60, fx61, fx62, fx63, fx64, fx65, fx66, fx67, fx68, fx69, fx70 + sage: f = FMatrix(FusionRing("G2",1)) + sage: f.FR().fusion_labels("g", inject_variables=True) sage: f.find_orthogonal_solution(verbose=False) - sage: fvars = f.get_radical_expression() - sage: fvars[a1, a1, a1, a1, a2, a0] + sage: radical_fvars = f.get_radical_expression() + sage: radical_fvars[g1, g1, g1, g1, g1, g0] -sqrt(1/2*sqrt(5) - 1/2) """ - return { sextuple : val.radical_expression() for sextuple, val in self.get_fmats_in_alg_field().items() } + return {sextuple : val.radical_expression() for sextuple, val in self.get_fvars_in_alg_field().items()} ####################### ### Private helpers ### @@ -705,13 +895,46 @@ def _get_known_vals(self): r""" Construct a dictionary of ``idx``, ``known_val`` pairs used for substituting into remaining equations. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("D4",1)) + sage: len(f._get_known_vals()) == 0 + True + sage: f.find_orthogonal_solution(verbose=False) + sage: len(f._get_known_vals()) == f._poly_ring.ngens() + True """ - return { var_idx : self._fvars[self._var_to_sextuple[self._poly_ring.gen(var_idx)]] for var_idx in self._solved } + return {var_idx : self._fvars[self._idx_to_sextuple[var_idx]] for var_idx in self._solved} def _get_known_sq(self,eqns=None): r""" Update ```self``'s dictionary of known squares. Keys are variable indices and corresponding values are the squares. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("B5",1)) + sage: len(f._ks) == 0 + True + sage: f.get_orthogonality_constraints() + [fx0^2 - 1, + fx1^2 - 1, + fx2^2 - 1, + fx3^2 - 1, + fx4^2 - 1, + fx5^2 - 1, + fx6^2 - 1, + fx7^2 - 1, + fx8^2 - 1, + fx9^2 - 1, + fx10^2 + fx12^2 - 1, + fx10*fx11 + fx12*fx13, + fx10*fx11 + fx12*fx13, + fx11^2 + fx13^2 - 1] + sage: f.get_orthogonality_constraints(output=False) + sage: len(f._get_known_sq()) == 10 + True """ if eqns is None: eqns = self.ideal_basis @@ -726,30 +949,22 @@ def _get_known_nonz(self): r""" Construct an ETuple indicating positions of known nonzero variables. - NOTES: + .. NOTE:: MUST be called after ``self._ks = _get_known_sq()``. - """ - nonz = { self._var_to_idx[var] : 100 for var in self._singles } - for idx in self._ks: - nonz[idx] = 100 - return ETuple(nonz, self._poly_ring.ngens()) - ################################# - ### Useful private predicates ### - ################################# + This method is called by the constructor of ``self``. - def _is_univariate_in_unknown(self,monom_exp): - """ - Determine if monomial exponent is univariate in an unknown F-symbol - """ - return len(monom_exp.nonzero_values()) == 1 and monom_exp.nonzero_positions()[0] not in self._solved + EXAMPLES:: - def _is_uni_linear_in_unkwown(self,monom_exp): + sage: f = FMatrix(FusionRing("D5",1)) # indirect doctest + sage: f._nnz + (100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100) """ - Determine if monomial exponent is univariate and linear in an unknown F-symbol - """ - return monom_exp.nonzero_values() == [1] and monom_exp.nonzero_positions()[0] not in self._solved + nonz = {self._var_to_idx[var] : 100 for var in self._singles} + for idx in self._ks: + nonz[idx] = 100 + return ETuple(nonz, self._poly_ring.ngens()) ############################## ### Variables partitioning ### @@ -774,28 +989,32 @@ def get_fvars_by_size(self,n,indices=False): INPUT: - -``n`` -- positive integer - -``indices`` -- If ``indices`` is ``False`` (default), this method - returns a set of sextuples `(a,b,c,d,x,y)` identifying the - corresponding F-symbol. Each sextuple is a key in the dictionary - returned by :meth:`get_fvars`. + - `n` -- a positive integer + + - ``indices`` -- (default: ``False``) a boolean. + + If ``indices`` is ``False`` (default), + this method returns a set of sextuples `(a,b,c,d,x,y)` identifying + the corresponding F-symbol. Each sextuple is a key in the + dictionary returned by :meth:`get_fvars`. - Otherwise the method returns a list of integer indices that internally - identify the F-symbols. The ``indices=True`` option is meant - for internal use mostly. + Otherwise the method returns a list of integer indices that + internally identify the F-symbols. The ``indices=True`` option is + meant for internal use. EXAMPLES:: - sage: f = FMatrix(FusionRing("E8",2), inject_variables=True) - creating variables fx1..fx14 - Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 + sage: f = FMatrix(FusionRing("A2",2), inject_variables=True) + creating variables fx1..fx287 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19, fx20, fx21, fx22, fx23, fx24, fx25, fx26, fx27, fx28, fx29, fx30, fx31, fx32, fx33, fx34, fx35, fx36, fx37, fx38, fx39, fx40, fx41, fx42, fx43, fx44, fx45, fx46, fx47, fx48, fx49, fx50, fx51, fx52, fx53, fx54, fx55, fx56, fx57, fx58, fx59, fx60, fx61, fx62, fx63, fx64, fx65, fx66, fx67, fx68, fx69, fx70, fx71, fx72, fx73, fx74, fx75, fx76, fx77, fx78, fx79, fx80, fx81, fx82, fx83, fx84, fx85, fx86, fx87, fx88, fx89, fx90, fx91, fx92, fx93, fx94, fx95, fx96, fx97, fx98, fx99, fx100, fx101, fx102, fx103, fx104, fx105, fx106, fx107, fx108, fx109, fx110, fx111, fx112, fx113, fx114, fx115, fx116, fx117, fx118, fx119, fx120, fx121, fx122, fx123, fx124, fx125, fx126, fx127, fx128, fx129, fx130, fx131, fx132, fx133, fx134, fx135, fx136, fx137, fx138, fx139, fx140, fx141, fx142, fx143, fx144, fx145, fx146, fx147, fx148, fx149, fx150, fx151, fx152, fx153, fx154, fx155, fx156, fx157, fx158, fx159, fx160, fx161, fx162, fx163, fx164, fx165, fx166, fx167, fx168, fx169, fx170, fx171, fx172, fx173, fx174, fx175, fx176, fx177, fx178, fx179, fx180, fx181, fx182, fx183, fx184, fx185, fx186, fx187, fx188, fx189, fx190, fx191, fx192, fx193, fx194, fx195, fx196, fx197, fx198, fx199, fx200, fx201, fx202, fx203, fx204, fx205, fx206, fx207, fx208, fx209, fx210, fx211, fx212, fx213, fx214, fx215, fx216, fx217, fx218, fx219, fx220, fx221, fx222, fx223, fx224, fx225, fx226, fx227, fx228, fx229, fx230, fx231, fx232, fx233, fx234, fx235, fx236, fx237, fx238, fx239, fx240, fx241, fx242, fx243, fx244, fx245, fx246, fx247, fx248, fx249, fx250, fx251, fx252, fx253, fx254, fx255, fx256, fx257, fx258, fx259, fx260, fx261, fx262, fx263, fx264, fx265, fx266, fx267, fx268, fx269, fx270, fx271, fx272, fx273, fx274, fx275, fx276, fx277, fx278, fx279, fx280, fx281, fx282, fx283, fx284, fx285, fx286 sage: f.largest_fmat_size() 2 sage: f.get_fvars_by_size(2) - {(f2, f2, f2, f2, f0, f0), - (f2, f2, f2, f2, f0, f1), - (f2, f2, f2, f2, f1, f0), - (f2, f2, f2, f2, f1, f1)} + {(f2, f2, f2, f4, f1, f1), + (f2, f2, f2, f4, f1, f5), + ... + (f4, f4, f4, f4, f4, f0), + (f4, f4, f4, f4, f4, f4)} """ fvars_copy = deepcopy(self._fvars) solved_copy = deepcopy(self._solved) @@ -809,8 +1028,8 @@ def get_fvars_by_size(self,n,indices=False): self._fvars = fvars_copy self._solved = solved_copy if indices: - return { self._var_to_idx[fx] for fx in var_set } - return { self._var_to_sextuple[fx] for fx in var_set } + return {self._var_to_idx[fx] for fx in var_set} + return {self._var_to_sextuple[fx] for fx in var_set} ############################ ### Checkpoint utilities ### @@ -818,31 +1037,93 @@ def get_fvars_by_size(self,n,indices=False): def save_fvars(self, filename): r""" - Save computed F-symbols for later use. This method should only be used - AFTER successfully one of the solvers, e.g. - :meth:`find_cyclotomic_solution` or :meth:`find_orthogonal_solution`. + Save computed F-symbols for later use. - Specify the ``filename`` as a string for storing computed F-symbols in a - pickle file. - """ + INPUT: + + - ``filename`` -- a string specifying the name of the pickle file + to be used. + + The current directory is used unless an absolute path to a file in + a different directory is provided. + + .. NOTE:: + + This method should only be used *after* successfully running one + of the solvers, e.g. :meth:`find_cyclotomic_solution` or + :meth:`find_orthogonal_solution`. + + When used in conjunction with :meth:`load_fvars`, this method may + be used to restore state of an :class:`FMatrix` object at the end + of a successful F-matrix solver run. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("A2",1)) + sage: f.find_orthogonal_solution(verbose=False) + sage: fvars = f.get_fvars() + sage: K = f.field() + sage: filename = f.get_fr_str() + "_solver_results.pickle" + sage: f.save_fvars(filename) + sage: del f + sage: f2 = FMatrix(FusionRing("A2",1)) + sage: f2.load_fvars(filename) + sage: fvars == f2.get_fvars() + True + sage: K == f2.field() + True + sage: os.remove(filename) + """ + final_state = [ + self._fvars, + self._non_cyc_roots, + self.get_coerce_map_from_fr_cyclotomic_field(), + self._qqbar_embedding, + ] with open(filename, 'wb') as f: - pickle.dump(self._fvars, f) + pickle.dump(final_state, f) def load_fvars(self, filename): r""" - Load previously computed F-symbols from a pickle file generated by - :meth:`save_fvars`. This method does not work with intermediate - checkpoint pickles; it only works with pickles containing ALL F-symbols. + Load previously computed F-symbols from a pickle file. + + See :meth:`save_fvars` for more information. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("A2",1)) + sage: f.find_orthogonal_solution(verbose=False) + sage: fvars = f.get_fvars() + sage: K = f.field() + sage: filename = f.get_fr_str() + "_solver_results.pickle" + sage: f.save_fvars(filename) + sage: del f + sage: f2 = FMatrix(FusionRing("A2",1)) + sage: f2.load_fvars(filename) + sage: fvars == f2.get_fvars() + True + sage: K == f2.field() + True + sage: os.remove(filename) + + .. NOTE:: + + :meth:`save_fvars`. This method does not work with intermediate + checkpoint pickles; it only works with pickles containing *all* + F-symbols, i.e. those created by :meth:`save_fvars` and by + specifying an optional ``save_results`` parameter for + :meth:`find_orthogonal_solution`. """ with open(filename, 'rb') as f: - self._fvars = pickle.load(f) - #Update class state attributes - self.symbols_known = True + self._fvars, self._non_cyc_roots, self._coerce_map_from_cyc_field, self._qqbar_embedding = pickle.load(f) + #Update state attributes + self._chkpt_status = 7 self._solved = set(range(self._poly_ring.ngens())) + self._field = self._qqbar_embedding.domain() def get_fr_str(self): """ - Auto-generate identifying key for saving results + Auto-generate an identifying key for saving results EXAMPLES:: @@ -855,14 +1136,74 @@ def get_fr_str(self): def _checkpoint(self,do_chkpt,status,verbose=True): """ - Pickle current solver state + Pickle current solver state. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("A1",3)) + sage: f._reset_solver_state() + sage: f.get_orthogonality_constraints(output=False) + sage: f.get_defining_equations('hexagons',output=False) + sage: f.ideal_basis = f._par_graph_gb(verbose=False) + sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey + sage: f.ideal_basis.sort(key=poly_tup_sortkey) + sage: f._triangular_elim(verbose=False) + sage: f._update_reduction_params(children_need_update=False) + sage: f._checkpoint(do_chkpt=True,status=2) + Checkpoint 2 reached! + sage: del f + sage: f = FMatrix(FusionRing("A1",3)) + sage: f.find_orthogonal_solution(warm_start="fmatrix_solver_checkpoint_A13.pickle") + Computing F-symbols for The Fusion Ring of Type A1 and level 3 with Integer Ring coefficients with 71 variables... + Set up 121 reduced pentagons... + Elimination epoch completed... 18 eqns remain in ideal basis + Elimination epoch completed... 5 eqns remain in ideal basis + Pent elim step solved for 64 / 71 variables + Partitioned 5 equations into 1 components of size: + [4] + Elimination epoch completed... 0 eqns remain in ideal basis + Partitioned 6 equations into 6 components of size: + [1, 1, 1, 1, 1, 1] + Computing appropriate NumberField... + sage: f._chkpt_status == 7 + True + sage: len(f._solved) == f._poly_ring.ngens() + True + sage: os.remove("fmatrix_solver_checkpoint_A13.pickle") + sage: f = FMatrix(FusionRing("A1",2)) + sage: f._reset_solver_state() + sage: f.get_orthogonality_constraints(output=False) + sage: f.get_defining_equations('hexagons',output=False) + sage: f.ideal_basis = f._par_graph_gb(verbose=False) + sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey + sage: f.ideal_basis.sort(key=poly_tup_sortkey) + sage: f._triangular_elim(verbose=False) + sage: f._update_reduction_params(children_need_update=False) + sage: f.get_defining_equations('pentagons',output=False) + sage: f.ideal_basis.sort(key=poly_tup_sortkey) + sage: f._triangular_elim(verbose=False) + sage: f._checkpoint(do_chkpt=True,status=4) + Checkpoint 4 reached! + sage: del f + sage: f = FMatrix(FusionRing("A1",2)) + sage: f.find_orthogonal_solution(warm_start="fmatrix_solver_checkpoint_A12.pickle") + Computing F-symbols for The Fusion Ring of Type A1 and level 2 with Integer Ring coefficients with 14 variables... + Partitioned 0 equations into 0 components of size: + [] + Partitioned 2 equations into 2 components of size: + [1, 1] + sage: f._chkpt_status == 7 + True + sage: len(f._solved) == f._poly_ring.ngens() + True + sage: os.remove("fmatrix_solver_checkpoint_A12.pickle") """ if not do_chkpt: return filename = "fmatrix_solver_checkpoint_" + self.get_fr_str() + ".pickle" - eqns = self.ideal_basis + self._add_square_fixers(output=True) with open(filename, 'wb') as f: - pickle.dump([self._fvars, self._solved, eqns, status], f) + # pickle.dump([self._fvars, self._solved, eqns, status], f) + pickle.dump([self._fvars, self._solved, self._ks, self.ideal_basis, status], f) if verbose: print(f"Checkpoint {status} reached!") @@ -870,15 +1211,59 @@ def _restore_state(self,filename): r""" Load solver state from file. Use this method both for warm-starting :meth:`find_orthogonal_solution` and to load pickled results. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("A1",3)) + sage: f._reset_solver_state() + sage: f.get_orthogonality_constraints(output=False) + sage: f.get_defining_equations('hexagons',output=False) + sage: f.ideal_basis = f._par_graph_gb(verbose=False) + sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey + sage: f.ideal_basis.sort(key=poly_tup_sortkey) + sage: f._triangular_elim(verbose=False) + sage: f._update_reduction_params(children_need_update=False) + sage: fvars = f._fvars + sage: ib = f.ideal_basis + sage: solved = f._solved + sage: ks = f._ks + sage: status = f._chkpt_status + sage: f._checkpoint(do_chkpt=True,status=2) + Checkpoint 2 reached! + sage: del f + sage: f = FMatrix(FusionRing("A1",3)) + sage: f._restore_state("fmatrix_solver_checkpoint_A13.pickle") + sage: fvars == f._fvars + True + sage: ib == f.ideal_basis + True + sage: ks == f._ks + True + sage: solved == f._solved + True + sage: 2 == f._chkpt_status + True + sage: os.remove("fmatrix_solver_checkpoint_A13.pickle") + + TESTS:: + + sage: f = FMatrix(FusionRing("A1",3)) + sage: f.find_orthogonal_solution(save_results="test.pickle",verbose=False) # long time + sage: del f + sage: f = FMatrix(FusionRing("A1",3)) + sage: f.find_orthogonal_solution(warm_start="test.pickle") # long time + sage: f._chkpt_status == 7 # long time + True + sage: os.remove("test.pickle") # long time """ with open(filename, 'rb') as f: state = pickle.load(f) #Loading saved results pickle - if type(state) == type(dict()): + if len(state) == 4: self.load_fvars(filename) self._chkpt_status = 7 return - self._fvars, self._solved, self.ideal_basis, self._chkpt_status = state + self._fvars, self._solved, self._ks, self.ideal_basis, self._chkpt_status = state self._update_reduction_params() ################# @@ -888,15 +1273,14 @@ def _restore_state(self,filename): def _map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_thresh=None): r""" Apply the given mapper to each element of the given input iterable and - return the results (with no duplicates) in a list. This method applies - the mapper in parallel if a worker_pool is provided. + return the results (with no duplicates) in a list. INPUT: -``mapper`` -- string specifying the name of a function defined in the ``fast_parallel_fmats_methods`` module. - NOTES: + .. NOTE:: If ``worker_pool`` is not provided, function maps and reduces on a single process. @@ -904,7 +1288,18 @@ def _map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_t whether it should use multiprocessing based on the length of the input iterable. If it can't determine the length of the input iterable then it uses multiprocessing with the default chunksize of - `1` if chunksize is not explicitly provided. + `1` unless a chunksize is provided. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("A1",2)) + sage: len(f._map_triv_reduce('get_reduced_hexagons',[(0,1)])) + 11 + sage: from multiprocessing import Pool + sage: pool = Pool() + sage: mp_params = [(i,pool._processes) for i in range(pool._processes)] + sage: len(f._map_triv_reduce('get_reduced_pentagons',mp_params,worker_pool=pool,chunksize=1,mp_thresh=0)) + 33 """ if mp_thresh is None: mp_thresh = self.mp_thresh @@ -944,14 +1339,36 @@ def get_orthogonality_constraints(self,output=True): INPUT: - -``output``-- a boolean. + - ``output`` -- a boolean. + + OUTPUT: - If ``output==True``, orthogonality constraints are returned as + If ``output=True``, orthogonality constraints are returned as polynomial objects. Otherwise, the constraints are appended to ``self.ideal_basis``. - They are stored in the internal tuple representation. The ``output=False`` - option is meant mostly for internal use by the F-matrix solver. + They are stored in the internal tuple representation. The + ``output=False`` option is meant mostly for internal use by the + F-matrix solver. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("B4",1)) + sage: f.get_orthogonality_constraints() + [fx0^2 - 1, + fx1^2 - 1, + fx2^2 - 1, + fx3^2 - 1, + fx4^2 - 1, + fx5^2 - 1, + fx6^2 - 1, + fx7^2 - 1, + fx8^2 - 1, + fx9^2 - 1, + fx10^2 + fx12^2 - 1, + fx10*fx11 + fx12*fx13, + fx10*fx11 + fx12*fx13, + fx11^2 + fx13^2 - 1] """ eqns = list() for tup in product(self._FR.basis(), repeat=4): @@ -966,21 +1383,50 @@ def get_defining_equations(self,option,worker_pool=None,output=True): Get the equations defining the ideal generated by the hexagon or pentagon relations. - Use ``option='hexagons'`` to get equations imposed on the F-matrix by the hexagon - relations in the definition of a braided category. + INPUT: + + - ``option`` -- a string determining equations to be set up. + + Use ``option='hexagons'`` to get equations imposed on the F-matrix by + the hexagon relations in the definition of a braided category. - Use ``option='pentagons'`` to get equations imposed on the F-matrix by the pentagon - relations in the definition of a monoidal category. + Use ``option='pentagons'`` to get equations imposed on the F-matrix by + the pentagon relations in the definition of a monoidal category. + + - ``worker_pool`` -- (default: ``None``) a ``Pool`` object of the + Python ``multiprocessing`` module. + + If a ``worker_pool`` object is passed, we distribute the work + amongst processes in the pool. + + - ``output`` -- (default: ``True``) a boolean indicating whether + results should be returned. If ``output=True``, equations are returned as polynomial objects. Otherwise, the constraints are appended to ``self.ideal_basis``. - They are stored in the internal tuple representation. The ``output==False`` - option is meant mostly for internal use by the F-matrix solver. + They are stored in the internal tuple representation. The + ``output=False`` option is meant mostly for internal use by the + F-matrix solver. + + EXAMPLES:: - If a ``worker_pool`` object is passed, then we use multiprocessing. - The ``worker_pool`` object is assumed to be a ``Pool`` object of the - Python ``multiprocessing`` module. + sage: f = FMatrix(FusionRing("B2",1)) + sage: f.get_defining_equations('hexagons') + [fx0 - 1, + fx10^2 + (-zeta32^8)*fx11*fx12 + (-zeta32^12)*fx10, + fx11*fx12 + (-zeta32^8)*fx13^2 + (zeta32^12)*fx13, + fx2 + 1, + fx7 + 1, + fx3*fx8 - fx6, + fx1*fx5 + fx2, + fx6 - 1, + fx4*fx9 + fx7, + fx10*fx11 + (-zeta32^8)*fx11*fx13 + (zeta32^4)*fx11, + fx10*fx12 + (-zeta32^8)*fx12*fx13 + (zeta32^4)*fx12] + sage: pe = f.get_defining_equations('pentagons') + sage: len(pe) + 33 """ n_proc = worker_pool._processes if worker_pool is not None else 1 params = [(child_id, n_proc) for child_id in range(n_proc)] @@ -996,74 +1442,59 @@ def get_defining_equations(self,option,worker_pool=None,output=True): ### Equations processing ### ############################ - def tup_to_fpoly(self,eq_tup): - """ - Assemble a polynomial object from its tuple representation - """ - return tup_to_poly(eq_tup,parent=self._poly_ring) - def _tup_to_fpoly(self,eq_tup): r""" - Faster version of :meth:`tup_to_fpoly`. Unsafe for client use, since it - avoids implicit casting and it may lead to segmentation faults. - """ - return _tup_to_poly(eq_tup,parent=self._poly_ring) + Assemble a polynomial object from its tuple representation. - def _solve_for_linear_terms(self,eqns=None): - """ - Solve for a linear term occurring in a two-term equation. + .. WARNING:: - Also solve for variables that are zero. - """ - if eqns is None: - eqns = self.ideal_basis + This method avoids implicit casting when constructing a + polynomial object, and may therefore lead to SEGFAULTs. + It is meant for internal use by the F-matrix solver. - F = self._field - linear_terms_exist = False - for eq_tup in eqns: + This method is a left inverse of + :meth:`sage.combinat.root_system.poly_tup_engine.poly_to_tup`. - #Only unflatten relevant polynomials - if len(eq_tup) > 2: - continue - eq_tup = _unflatten_coeffs(F, eq_tup) - - if len(eq_tup) == 1: - m = eq_tup[0][0] - if self._is_univariate_in_unknown(m): - var = m.nonzero_positions()[0] - self._fvars[self._var_to_sextuple[self._poly_ring.gen(var)]] = tuple() - self._solved.add(var) - linear_terms_exist = True - if len(eq_tup) == 2: - monomials = [m for m, c in eq_tup] - max_var = monomials[0].emax(monomials[1]).nonzero_positions()[0] - for this, m in enumerate(monomials): - other = (this+1)%2 - if self._is_uni_linear_in_unkwown(m) and m.nonzero_positions()[0] == max_var and monomials[other][m.nonzero_positions()[0]] == 0: - var = m.nonzero_positions()[0] - rhs_key = monomials[other] - rhs_coeff = -eq_tup[other][1] / eq_tup[this][1] - self._fvars[self._var_to_sextuple[self._poly_ring.gen(var)]] = ((rhs_key,rhs_coeff),) - self._solved.add(var) - linear_terms_exist = True - return linear_terms_exist - - def _backward_subs(self): - """ - Backward substitution step. Traverse variables in reverse lexicographical order. - """ - one = self._field.one() - for var in reversed(self._poly_ring.gens()): - sextuple = self._var_to_sextuple[var] - rhs = self._fvars[sextuple] - d = { var_idx : self._fvars[self._var_to_sextuple[self._poly_ring.gen(var_idx)]] for var_idx in variables(rhs) if var_idx in self._solved } - if d: - kp = compute_known_powers(get_variables_degrees([rhs]), d, one) - self._fvars[sextuple] = tuple(subs_squares(subs(rhs,kp,one),self._ks).items()) + EXAMPLES:: + + sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: f = FMatrix(FusionRing("C3",1)) + sage: from multiprocessing import Pool, set_start_method + sage: try: + ....: set_start_method('fork') + ....: except: + ....: pass + sage: pool = Pool() + sage: he = f.get_defining_equations('hexagons',pool) + sage: all(f._tup_to_fpoly(poly_to_tup(h)) for h in he) + True + """ + return _tup_to_poly(eq_tup,parent=self._poly_ring) def _update_reduction_params(self,eqns=None,worker_pool=None,children_need_update=False): """ - Update reduction parameters in all processes + Update reduction parameters that are solver state attributes. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("A1",3)) + sage: f.get_orthogonality_constraints(output=False) + sage: from multiprocessing import Pool, set_start_method + sage: try: + ....: set_start_method('fork') + ....: except: + ....: pass + sage: pool = Pool() + sage: f.get_defining_equations('hexagons',worker_pool=pool,output=False) + sage: f.ideal_basis = f._par_graph_gb(worker_pool=pool,verbose=False) + sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey + sage: f.ideal_basis.sort(key=poly_tup_sortkey) + sage: f.mp_thresh = 0 + sage: f._triangular_elim(worker_pool=pool) # indirect doctest + Elimination epoch completed... 10 eqns remain in ideal basis + Elimination epoch completed... 0 eqns remain in ideal basis + sage: f.ideal_basis + [] """ if eqns is None: eqns = self.ideal_basis @@ -1076,40 +1507,50 @@ def _update_reduction_params(self,eqns=None,worker_pool=None,children_need_updat new_data = [(self._fvars,self._solved,self._ks,self._var_degs)]*n_proc self._map_triv_reduce('update_child_fmats',new_data,worker_pool=worker_pool,chunksize=1,mp_thresh=0) - def _triangular_elim(self,required_vars=None,eqns=None,worker_pool=None,verbose=True): + def _triangular_elim(self,eqns=None,worker_pool=None,verbose=True): """ - Perform triangular elimination of linear terms in two-term equations until no such terms exist - For optimal usage of TRIANGULAR elimination, pass in a SORTED list of equations + Perform triangular elimination of linear terms in two-term equations + until no such terms exist. + + .. NOTE:: + + For optimal usage of TRIANGULAR elimination, pass in a + SORTED list of equations. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("D3",1)) + sage: f.get_defining_equations('hexagons',output=False) + sage: f.get_orthogonality_constraints(output=False) + sage: gb = f._par_graph_gb(verbose=False) + sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey + sage: f.ideal_basis = sorted(gb, key=poly_tup_sortkey) + sage: f._triangular_elim() + Elimination epoch completed... 0 eqns remain in ideal basis + sage: f.ideal_basis + [] """ ret = True if eqns is None: eqns = self.ideal_basis ret = False - if required_vars is None: - required_vars = self._poly_ring.gens() - poly_sortkey = poly_tup_sortkey_degrevlex - #Unzip polynomials - self._fvars = { sextuple : poly_to_tup(rhs) for sextuple, rhs in self._fvars.items() } + self._fvars = {sextuple : poly_to_tup(rhs) for sextuple, rhs in self._fvars.items()} while True: - linear_terms_exist = self._solve_for_linear_terms(eqns) + linear_terms_exist = _solve_for_linear_terms(self,eqns) if not linear_terms_exist: break - self._backward_subs() - - #Support early termination in case only some F-symbols are needed - req_vars_known = all(self._fvars[self._var_to_sextuple[var]] in self._FR.field() for var in required_vars) - if req_vars_known: return 1 + _backward_subs(self) #Compute new reduction params, send to child processes if any, and update eqns self._update_reduction_params(eqns=eqns,worker_pool=worker_pool,children_need_update=len(eqns)>self.mp_thresh) eqns = self._map_triv_reduce('update_reduce',eqns,worker_pool=worker_pool) - eqns.sort(key=poly_sortkey) + eqns.sort(key=poly_tup_sortkey) if verbose: print("Elimination epoch completed... {} eqns remain in ideal basis".format(len(eqns))) #Zip up _fvars before exiting - self._fvars = { sextuple : self._tup_to_fpoly(rhs_tup) for sextuple, rhs_tup in self._fvars.items() } + self._fvars = {sextuple : self._tup_to_fpoly(rhs_tup) for sextuple, rhs_tup in self._fvars.items()} if ret: return eqns self.ideal_basis = eqns @@ -1120,34 +1561,111 @@ def _triangular_elim(self,required_vars=None,eqns=None,worker_pool=None,verbose= def equations_graph(self,eqns=None): """ - Construct a graph corresponding to equations. The nodes in the graph - are indices corresponding to variables in the equations and two - nodes are connected if the corresponding variables appear together in - a given equation. + Construct a graph corresponding to the given equations. + + Every node corresponds to a variable and nodes are connected when + the corresponding variables appear together in an equation. + + INPUT: - If no list of equations is passed, the graph is built from equations in - ``self.ideal_basis``. + - ``eqns`` -- a list of polynomials. + + Each polynomial is either an object in the ring returned by + :meth:`get_poly_ring` or it is a tuple of pairs representing + a polynomial using the internal representation. + + If no list of equations is passed, the graph is built from the + polynomials in ``self.ideal_basis``. In this case the method assumes + the internal representation of a polynomial as a tuple of pairs is + used. + + This method is crucial to :meth:`find_orthogonal_solution`. The + hexagon equations, obtained using :meth:`get_defining_equations`, + define a disconnected graph that breaks up into many small components. + The :meth:`find_orthogonal_solution` solver exploits this when + undertaking a Groebner basis computation. + + OUTPUT: + + A ``Graph`` object. If a list of polynomial objects was given, + the set of nodes in the output graph is the subset polynomial + ring generators appearing in the equations. + + If the internal representation was used, the set of nodes is + the subset of indices corresponding to polynomial ring generators. + This option is meant for internal use by the F-matrix solver. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("A3",1)) + sage: f.get_poly_ring().ngens() + 27 + sage: he = f.get_defining_equations('hexagons') + sage: graph = f.equations_graph(he) + sage: graph.connected_components_sizes() + [6, 3, 3, 3, 3, 3, 3, 1, 1, 1] """ if eqns is None: eqns = self.ideal_basis - G = sage.graphs.generators.basic.EmptyGraph() - G.add_vertices([x for eq_tup in eqns for x in variables(eq_tup)]) - for eq_tup in eqns: - s = [v for v in variables(eq_tup)] + G = Graph() + if not eqns: return G + + #Eqns could be a list of poly objects or poly tuples stored in internal repn + if type(eqns[0]) == tuple: + G.add_vertices([x for eq_tup in eqns for x in variables(eq_tup)]) + else: + G.add_vertices([x for eq in eqns for x in eq.variables()]) + for eq in eqns: + #Eqns could be a list of poly objects or poly tuples stored in internal repn + if type(eq) == tuple: + s = [v for v in variables(eq)] + else: + s = [v for v in eq.variables()] for x in s: for y in s: if y!=x: G.add_edge(x,y) return G - def partition_eqns(self,graph,eqns=None,verbose=True): + def _partition_eqns(self,eqns=None,verbose=True): """ - Partition equations corresponding to edges in a disconnected graph + Partition equations corresponding to edges in a disconnected graph. + + OUTPUT: + + This method returns a dictionary of (c, e) pairs, where + c is a tuple denoting a connected component in the graph produced + by calling :meth:`equations_graph` with the given ``eqns`` and + e is a list of all equations with variables in c. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("C2",1)) + sage: f.get_defining_equations('hexagons',output=False) + sage: partition = f._partition_eqns() + Partitioned 11 equations into 5 components of size: + [4, 3, 3, 3, 1] + sage: from sage.combinat.root_system.poly_tup_engine import variables + sage: for c, e in partition.items(): + ....: assert set(v for eq_tup in e for v in variables(eq_tup)) == set(c) + sage: vars_in_partition = set() + sage: eqns_in_partition = set() + sage: for c, e in partition.items(): + ....: vars_in_partition.update(c) + ....: eqns_in_partition.update(e) + sage: vars_in_partition == set(v for eq_tup in f.ideal_basis for v in variables(eq_tup)) + True + sage: eqns_in_partition == set(f.ideal_basis) + True + sage: from itertools import product + sage: for e1, e2 in product(partition.values(),repeat=2): + ....: assert e1 == e2 or set(e1).isdisjoint(set(e2)) """ if eqns is None: eqns = self.ideal_basis - partition = { tuple(c) : [] for c in graph.connected_components() } + graph = self.equations_graph(eqns) + partition = {tuple(c) : [] for c in graph.connected_components()} for eq_tup in eqns: partition[tuple(graph.connected_component_containing_vertex(variables(eq_tup)[0]))].append(eq_tup) if verbose: @@ -1155,27 +1673,43 @@ def partition_eqns(self,graph,eqns=None,verbose=True): print(graph.connected_components_sizes()) return partition - def _add_square_fixers(self,output=False): - """ - Add square fixing equations back to ideal basis - """ - sq_fixers = list() - n = self._poly_ring.ngens() - one = self._field.one() - for fx, rhs in self._ks.items(): - if fx not in self._solved: - lt = (ETuple({ fx : 2 },n), one) - sq_fixers.append((lt, (ETuple({},n), -rhs))) - if output: - return sq_fixers - self.ideal_basis.extend(sq_fixers) - def _par_graph_gb(self,worker_pool=None,eqns=None,term_order="degrevlex",verbose=True): """ - Compute a Groebner basis for a set of equations partitioned according to their corresponding graph + Compute a Groebner basis for a list of equations partitioned according + to their corresponding graph. + + .. NOTE:: + + If the graph has more than 50 components, this method computes the + Groebner basis in parallel when a ``worker_pool`` is provided. + + This method will refuse to find a Groebner basis for a component + of size larger than 60, since such a calculation does not seem to + terminate. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("F4",1)) + sage: f.get_orthogonality_constraints(output=False) + sage: from multiprocessing import Pool, set_start_method + sage: try: + ....: set_start_method('fork') # context can be set only once + ....: except RuntimeError: + ....: pass + sage: pool = Pool() + sage: f.get_defining_equations('hexagons',worker_pool=pool,output=False) + sage: gb = f._par_graph_gb(worker_pool=pool) + Partitioned 10 equations into 2 components of size: + [4, 1] + sage: from sage.combinat.root_system.poly_tup_engine import _unflatten_coeffs + sage: [f._tup_to_fpoly(_unflatten_coeffs(f.field(), t)) for t in gb] + [fx0 - 1, + fx2 - fx3, + fx3^2 + (zeta80^24 - zeta80^16), + fx4 + (-zeta80^24 + zeta80^16), + fx1 + (zeta80^24 - zeta80^16)] """ if eqns is None: eqns = self.ideal_basis - graph = self.equations_graph(eqns) small_comps = list() temp_eqns = list() @@ -1185,7 +1719,7 @@ def _par_graph_gb(self,worker_pool=None,eqns=None,term_order="degrevlex",verbose # for i in range(nmax+1): # vars_by_size.append(self.get_fvars_by_size(i)) - for comp, comp_eqns in self.partition_eqns(graph,verbose=verbose).items(): + for comp, comp_eqns in self._partition_eqns(verbose=verbose).items():#self._partition_eqns(graph,verbose=verbose).items(): #Check if component is too large to process if len(comp) > 60: # fmat_size = 0 @@ -1206,19 +1740,44 @@ def _get_component_variety(self,var,eqns): """ Translate equations in each connected component to smaller polynomial rings so we can call built-in variety method. + + INPUT: + + - ``var`` -- a list of variable indices + - ``eqns`` -- a list of polynomial equations in the internal + tuple of pairs representation + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("G2",2)) + sage: from multiprocessing import Pool, set_start_method + sage: try: + ....: set_start_method('fork') # context can be set only once + ....: except RuntimeError: + ....: pass + sage: f.get_defining_equations('hexagons',worker_pool=Pool(),output=False) # long time + sage: partition = f._partition_eqns() # long time + Partitioned 327 equations into 35 components of size: + [27, 27, 27, 24, 24, 16, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 9, 9, 6, 6, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1] + sage: c = (216, 292, 319) + sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: eqns = partition[c] + [poly_to_tup(f._poly_ring.gen(216)-1)] # long time + sage: f._get_component_variety(c,eqns) # long time + [{216: -1, 292: -1, 319: 1}] """ #Define smaller poly ring in component vars R = PolynomialRing(self._FR.field(),len(var),'a',order='lex') #Zip tuples into R and compute Groebner basis - idx_map = { old : new for new, old in enumerate(sorted(var)) } + idx_map = {old : new for new, old in enumerate(sorted(var))} nvars = len(var) - polys = [tup_to_poly(resize(eq_tup,idx_map,nvars),parent=R) for eq_tup in eqns] + eqns = [_unflatten_coeffs(self._field,eq_tup) for eq_tup in eqns] + polys = [_tup_to_poly(resize(eq_tup,idx_map,nvars),parent=R) for eq_tup in eqns] var_in_R = Ideal(sorted(polys)).variety(ring=AA) #Change back to fmats poly ring and append to temp_eqns - inv_idx_map = { v : k for k, v in idx_map.items() } - return [{ inv_idx_map[i] : value for i, (key, value) in enumerate(sorted(soln.items())) } for soln in var_in_R] + inv_idx_map = {v : k for k, v in idx_map.items()} + return [{inv_idx_map[i] : value for i, (key, value) in enumerate(sorted(soln.items()))} for soln in var_in_R] ####################### ### Solution method ### @@ -1226,23 +1785,36 @@ def _get_component_variety(self,var,eqns): def attempt_number_field_computation(self): """ - Based on the ``CartanType`` of ``self``, determine whether to attempt - to find a :class:`NumberField` containing all the F-symbols based on data - known on March 17, 2021. + Based on the ``CartanType`` of ``self`` and data + known on March 17, 2021, determine whether to attempt + to find a :class:`NumberField` containing all the F-symbols. + + This method is used by :meth:`find_orthogonal_solution` + to determine a field containing all F-symbols. + See :meth:`field` and :meth:`get_non_cyclotomic_roots`. - For certain :class:`FusionRing` 's, the number field computation does - not terminate in a reasonable amount of time. + For certain :class:`FusionRing`'s, the number field computation does + not terminate in reasonable time. In these cases, we report F-symbols as elements - of the ``AlgebraicField`` :class:`QQbar`. + of the :class:`AlgebraicField` ``QQbar``. EXAMPLES:: - sage: f = FMatrix(FusionRing("E6",2)) + sage: f = FMatrix(FusionRing("F4",2)) sage: f.attempt_number_field_computation() False sage: f = FMatrix(FusionRing("G2",1)) sage: f.attempt_number_field_computation() True + + .. NOTE:: + + In certain cases, F-symbols are found in the associated + :class:`FusionRing`'s cyclotomic field and a + :class:`NumberField` computation is not needed. In these + cases this method returns ``True`` but the + :meth:`find_orthogonal_solution` solver does *not* + undertake a :class:`NumberField` computation. """ ct = self._FR.cartan_type() k = self._FR._k @@ -1268,37 +1840,69 @@ def _get_explicit_solution(self,eqns=None,verbose=True): terms of Groeber basis. A few degrees of freedom remain. By specializing the free variables and back substituting, a solution in the base field is now obtained. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("A2",2)) # indirect doctest + sage: f.find_orthogonal_solution() # long time + Computing F-symbols for The Fusion Ring of Type A2 and level 2 with Integer Ring coefficients with 287 variables... + Set up 548 hex and orthogonality constraints... + Partitioned 548 equations into 57 components of size: + [24, 12, 12, 12, 12, 12, 12, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1] + Elimination epoch completed... 53 eqns remain in ideal basis + Elimination epoch completed... 5 eqns remain in ideal basis + Elimination epoch completed... 0 eqns remain in ideal basis + Hex elim step solved for 203 / 287 variables + Set up 994 reduced pentagons... + Elimination epoch completed... 699 eqns remain in ideal basis + Elimination epoch completed... 279 eqns remain in ideal basis + Elimination epoch completed... 9 eqns remain in ideal basis + Pent elim step solved for 270 / 287 variables + Partitioned 9 equations into 1 components of size: + [5] + Elimination epoch completed... 0 eqns remain in ideal basis + Partitioned 16 equations into 16 components of size: + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + Computing appropriate NumberField... """ if eqns is None: eqns = self.ideal_basis #Don't add square fixers when warm starting from a late-stage checkpoint if self._chkpt_status < 5: - self._add_square_fixers() - eqns_partition = self.partition_eqns(self.equations_graph(eqns),verbose=verbose) + # self._add_square_fixers() + n = self._poly_ring.ngens() + one = self._field.one() + for fx, rhs in self._ks.items(): + if fx not in self._solved: + lt = (ETuple({fx : 2},n), one) + eqns.append((lt, (ETuple({},n), -rhs))) + eqns_partition = self._partition_eqns(verbose=verbose) F = self._field - x = F['x'].gen() + R = F['x'] numeric_fvars = dict() non_cyclotomic_roots = list() must_change_base_field = False phi = F.hom([F.gen()],F) for comp, part in eqns_partition.items(): - #If component have only one equation in a single variable, get a root + #If component has only one equation in a single variable, get a root if len(comp) == 1 and len(part) == 1: #Attempt to find cyclotomic root - univ_poly = tup_to_univ_poly(part[0],x) - real_roots = univ_poly.roots(ring=AA,multiplicities=False) - assert real_roots, "No real solution exists... {} has no real roots".format(univ_poly) + univ_poly = tup_to_univ_poly(part[0],R) roots = univ_poly.roots(multiplicities=False) if roots: numeric_fvars[comp[0]] = roots[0] else: - non_cyclotomic_roots.append((comp[0],real_roots[0])) + #A real solution is preferred + roots = univ_poly.roots(ring=AA,multiplicities=False) + if not roots: + roots = univ_poly.roots(ring=QQbar,multiplicities=False) + non_cyclotomic_roots.append((comp[0],roots[0])) must_change_base_field = True #Otherwise, compute the component variety and select a point to obtain a numerical solution else: sols = self._get_component_variety(comp,part) - assert len(sols) > 1, "No real solution exists... component with variables {} has no real points".format(comp) + # assert len(sols) > 1, "No real solution exists... component with variables {} has no real points".format(comp) for fx, rhs in sols[0].items(): non_cyclotomic_roots.append((fx,rhs)) must_change_base_field = True @@ -1322,7 +1926,7 @@ def _get_explicit_solution(self,eqns=None,verbose=True): cyc_gen_as_bf_elt = bf_elts.pop(0) phi = self._FR.field().hom([cyc_gen_as_bf_elt], self._field) self._coerce_map_from_cyc_field = phi - numeric_fvars = { k : phi(v) for k, v in numeric_fvars.items() } + numeric_fvars = {k : phi(v) for k, v in numeric_fvars.items()} for i, elt in enumerate(bf_elts): numeric_fvars[non_cyclotomic_roots[i][0]] = elt @@ -1335,20 +1939,21 @@ def _get_explicit_solution(self,eqns=None,verbose=True): assert len(self._solved) == nvars, "Some F-symbols are still missing...{}".format([self._poly_ring.gen(fx) for fx in set(range(nvars)).difference(self._solved)]) #Backward substitution step. Traverse variables in reverse lexicographical order. (System is in triangular form) - self._fvars = { sextuple : apply_coeff_map(poly_to_tup(rhs),phi) for sextuple, rhs in self._fvars.items() } + self._fvars = {sextuple : apply_coeff_map(poly_to_tup(rhs),phi) for sextuple, rhs in self._fvars.items()} for fx, rhs in numeric_fvars.items(): - self._fvars[self._var_to_sextuple[self._poly_ring.gen(fx)]] = ((ETuple({},nvars),rhs),) - self._backward_subs() - self._fvars = { sextuple : constant_coeff(rhs) for sextuple, rhs in self._fvars.items() } + self._fvars[self._idx_to_sextuple[fx]] = ((ETuple({},nvars),rhs),) + # self._backward_subs() + _backward_subs(self) + self._fvars = {sextuple : constant_coeff(rhs) for sextuple, rhs in self._fvars.items()} #Update base field attributes self._FR._field = self.field() self._FR._basecoer = self.get_coerce_map_from_fr_cyclotomic_field() - def find_orthogonal_solution(self,checkpoint=False,save_results="",warm_start="",verbose=True,use_mp=True): + def find_orthogonal_solution(self,checkpoint=False,save_results="",warm_start="",use_mp=True,verbose=True): r""" - Find an orthogonal solution to the pentagon equations associated to the - monoidal category represented by ``self``. + Solve the the hexagon and pentagon relations, along with + orthogonality constraints, to evaluate an orthogonal F-matrix. INPUT: @@ -1387,20 +1992,59 @@ def find_orthogonal_solution(self,checkpoint=False,save_results="",warm_start="" ``True`` is highly recommended, since parallel processing yields results much more quickly. + - ``verbose`` -- (default: ``True``) a boolean indicating whether the + solver should print out intermediate progress reports. + OUTPUT: This method returns ``None``. If the solver runs successfully, the results may be accessed through various methods, such as :meth:`get_fvars`, :meth:`fmatrix`, :meth:`fmat`, etc. - In many cases the F-symbols obtained are in fact real. In any case, the - F-symbols are obtained as elements of the associated + EXAMPLES:: + + sage: f = FMatrix(FusionRing("B5",1), fusion_label="b", inject_variables=True) + creating variables fx1..fx14 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 + sage: f.find_orthogonal_solution() + Computing F-symbols for The Fusion Ring of Type B5 and level 1 with Integer Ring coefficients with 14 variables... + Set up 25 hex and orthogonality constraints... + Partitioned 25 equations into 5 components of size: + [4, 3, 3, 3, 1] + Elimination epoch completed... 0 eqns remain in ideal basis + Hex elim step solved for 10 / 14 variables + Set up 7 reduced pentagons... + Elimination epoch completed... 0 eqns remain in ideal basis + Pent elim step solved for 12 / 14 variables + Partitioned 0 equations into 0 components of size: + [] + Partitioned 2 equations into 2 components of size: + [1, 1] + sage: f.fmatrix(b2, b2, b2, b2) + [ 1/2*zeta80^30 - 1/2*zeta80^10 -1/2*zeta80^30 + 1/2*zeta80^10] + [ 1/2*zeta80^30 - 1/2*zeta80^10 1/2*zeta80^30 - 1/2*zeta80^10] + sage: f.fmat(b2, b2, b2, b2, b0, b1) + -1/2*zeta80^30 + 1/2*zeta80^10 + + Every F-matrix `F^{a,b,c}_d` is orthogonal and in many cases real. + We may use :meth:`fmats_are_orthogonal` and :meth:`fvars_are_real` + to obtain correctness certificates. + + EXAMPLES:: + + sage: f.fmats_are_orthogonal() + True + sage: f.fvars_are_real() + True + + In any case, the F-symbols are obtained as elements of the associated :class:`FusionRing`'s :class:`CyclotomicField`, a computed - :class:`NumberField`, or ``QQbar``. Currently, the output field is - determined based on the ``CartanType`` associated to ``self``. - See :meth:`attempt_number_field_computation` for details. + :class:`NumberField`, or ``QQbar``. Currently, the field containing + the F-symbols is determined based on the ``CartanType`` associated + to ``self``. See :meth:`attempt_number_field_computation` for details. """ self._reset_solver_state() + if self._poly_ring.ngens() == 0: return #Resume computation from checkpoint if warm_start: @@ -1417,7 +2061,6 @@ def find_orthogonal_solution(self,checkpoint=False,save_results="",warm_start="" if verbose: print("Computing F-symbols for {} with {} variables...".format(self._FR, len(self._fvars))) - poly_sortkey = poly_tup_sortkey_degrevlex if self._chkpt_status < 1: #Set up hexagon equations and orthogonality constraints self.get_orthogonality_constraints(output=False) @@ -1432,7 +2075,7 @@ def find_orthogonal_solution(self,checkpoint=False,save_results="",warm_start="" if self._chkpt_status < 2: #Set up equations graph. Find GB for each component in parallel. Eliminate variables self.ideal_basis = self._par_graph_gb(worker_pool=pool,verbose=verbose) - self.ideal_basis.sort(key=poly_sortkey) + self.ideal_basis.sort(key=poly_tup_sortkey) self._triangular_elim(worker_pool=pool,verbose=verbose) #Update reduction parameters, also in children if any @@ -1447,7 +2090,7 @@ def find_orthogonal_solution(self,checkpoint=False,save_results="",warm_start="" if self._chkpt_status < 3: #Set up pentagon equations in parallel self.get_defining_equations('pentagons',worker_pool=pool,output=False) - self.ideal_basis.sort(key=poly_sortkey) + self.ideal_basis.sort(key=poly_tup_sortkey) #Report progress if verbose: @@ -1474,7 +2117,7 @@ def find_orthogonal_solution(self,checkpoint=False,save_results="",warm_start="" #Set up new equations graph and compute variety for each component if self._chkpt_status < 5: self.ideal_basis = self._par_graph_gb(term_order="lex",verbose=verbose) - self.ideal_basis.sort(key=poly_sortkey) + self.ideal_basis.sort(key=poly_tup_sortkey) self._triangular_elim(verbose=verbose) self._checkpoint(checkpoint,5,verbose=verbose) @@ -1494,11 +2137,26 @@ def find_orthogonal_solution(self,checkpoint=False,save_results="",warm_start="" ### Cyclotomic method ### ######################### - def fix_gauge(self, algorithm=''): + def _fix_gauge(self, algorithm=""): """ Fix the gauge by forcing F-symbols not already fixed to equal 1. - This method should be used AFTER adding hex and pentagon eqns to - ``self.ideal_basis`` + + .. NOTE:: + + This method should be used AFTER adding hexagon and pentagon + equations to ``self.ideal_basis``. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("A3",1)) # long time + sage: f._reset_solver_state() # long time + sage: eqns = f.get_defining_equations("hexagons")+f.get_defining_equations("pentagons") # long time + sage: f.ideal_basis = set(Ideal(eqns).groebner_basis()) # long time + sage: _, _ = f._substitute_degree_one() # long time + sage: f._fix_gauge() # long time + adding equation... fx1 - 1 + adding equation... fx18 - 1 + adding equation... fx21 - 1 """ while len(self._solved) < len(self._poly_ring.gens()): #Get a variable that has not been fixed @@ -1515,15 +2173,29 @@ def fix_gauge(self, algorithm=''): self._update_equations() def _substitute_degree_one(self, eqns=None): + """ + Substitute known value from linear univariate polynomial and + solve, following [Bond2007]_ p.37, for two-term linear equation + for one of the variables. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("D3",1), inject_variables=True) + creating variables fx1..fx27 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19, fx20, fx21, fx22, fx23, fx24, fx25, fx26 + sage: f.ideal_basis = [fx0 - 8, fx4**2 - 3, fx4 + fx10 + 3, fx4 + fx9] + sage: _, _ = f._substitute_degree_one() + sage: f._fvars[f._var_to_sextuple[fx0]] + 8 + sage: f._fvars[f._var_to_sextuple[fx4]] + -fx9 + """ if eqns is None: eqns = self.ideal_basis new_knowns = set() useless = set() for eq in eqns: - #Substitute known value from univariate degree 1 polynomial or, - #Following Bonderson, p. 37, solve linear equation with two terms - #for one of the variables if eq.degree() == 1 and sum(eq.degrees()) <= 2 and eq.lm() not in self._solved: self._fvars[self._var_to_sextuple[eq.lm()]] = -sum(c * m for c, m in zip(eq.coefficients()[1:], eq.monomials()[1:])) / eq.lc() #Add variable to set of known values and remove this equation @@ -1533,26 +2205,38 @@ def _substitute_degree_one(self, eqns=None): #Update fvars depending on other variables self._solved.update(new_knowns) for sextuple, rhs in self._fvars.items(): - d = { var : self._fvars[self._var_to_sextuple[var]] for var in rhs.variables() if var in self._solved } + d = {var : self._fvars[self._var_to_sextuple[var]] for var in rhs.variables() if var in self._solved} if d: self._fvars[sextuple] = rhs.subs(d) return new_knowns, useless def _update_equations(self): """ - Update ideal_basis equations by plugging in known values + Perform backward substitution on equations in ``self.ideal_basis``. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("D3",1), inject_variables=True) + creating variables fx1..fx27 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19, fx20, fx21, fx22, fx23, fx24, fx25, fx26 + sage: f.ideal_basis = [fx0 - 8, fx4 + fx9, fx4**2 + fx3 - fx9**2] + sage: _, _ = f._substitute_degree_one() + sage: f._update_equations() + sage: f.ideal_basis + {fx3} """ - special_values = { known : self._fvars[self._var_to_sextuple[known]] for known in self._solved } + special_values = {known : self._fvars[self._var_to_sextuple[known]] for known in self._solved} self.ideal_basis = set(eq.subs(special_values) for eq in self.ideal_basis) self.ideal_basis.discard(0) def find_cyclotomic_solution(self, equations=None, algorithm='', verbose=True, output=False): """ Solve the the hexagon and pentagon relations to evaluate the F-matrix. + This method (omitting the orthogonality constraints) produces output in the cyclotomic field, but it is very limited in the size - of examples it can handle: for example, `A_2` at level 2 is - too large for this method. You may use :meth:`find_real_orthogonal_solution` + of examples it can handle: for example, `G_2` at level 2 is + too large for this method. You may use :meth:`find_orthogonal_solution` to solve much larger examples. INPUT: @@ -1567,7 +2251,7 @@ def find_cyclotomic_solution(self, equations=None, algorithm='', verbose=True, o EXAMPLES:: - sage: f=FMatrix(FusionRing("A2",1,fusion_labels="a",inject_variables=True),inject_variables=True) + sage: f = FMatrix(FusionRing("A2",1,fusion_labels="a",inject_variables=True),inject_variables=True) creating variables fx1..fx8 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 sage: f.find_cyclotomic_solution(output=True) @@ -1587,8 +2271,9 @@ def find_cyclotomic_solution(self, equations=None, algorithm='', verbose=True, o (a1, a1, a1, a0, a2, a2): 1} After you successfully run :meth:`find_cyclotomic_solution` you may - check the correctness of the F-matrix by running :meth:`hexagon` - and :meth:`pentagon`. These should return empty lists + check the correctness of the F-matrix by running + :meth:`get_defining_equations` with ``option='hexagons'`` and + ``option='pentagons'``. These should return empty lists of equations. EXAMPLES:: @@ -1600,6 +2285,7 @@ def find_cyclotomic_solution(self, equations=None, algorithm='', verbose=True, o """ self._reset_solver_state() + if self._poly_ring.ngens() == 0: return if equations is None: if verbose: @@ -1613,46 +2299,62 @@ def find_cyclotomic_solution(self, equations=None, algorithm='', verbose=True, o self._substitute_degree_one() if verbose: print("Fixing the gauge...") - self.fix_gauge(algorithm=algorithm) + self._fix_gauge(algorithm=algorithm) if verbose: print("Done!") if output: return self._fvars - ##################### ### Verifications ### ##################### - def verify_hexagons(self): - """ - Ensure the hexagon equations are satisfied + def certify_pentagons(self,use_mp=True,verbose=False): """ - hex = [] - for a,b,c,d,e,g in product(self._FR.basis(),repeat=6): - lhs = self._field(self._FR.r_matrix(a,c,e))*self.fmat(a,c,b,d,e,g)*self._field(self._FR.r_matrix(b,c,g)) - rhs = sum(self.fmat(c,a,b,d,e,f)*self._field(self._FR.r_matrix(f,c,d))*self.fmat(a,b,c,d,f,g) for f in self._FR.basis()) - hex.append(lhs-rhs) - if all(h == self._field.zero() for h in hex): - print("Success!!! Found valid F-symbols for {}".format(self._FR)) - else: - print("Ooops... something went wrong... These pentagons remain:") - print(hex) - return hex + Obtain a certificate of satisfaction for the pentagon equations, + up to floating-point error. - def verify_pentagons(self,use_mp=True,prune=False): - """ - Ensure the pentagon equations are satisfied + This method converts the computed F-symbols (available through + :meth:`get_fvars`) to native Python floats and then checks whether + the pentagon equations are satisfied using floating point arithmetic. + + When ``self.FR().basis()`` has many elements, verifying satisfaction + of the pentagon relations exactly using :meth:`get_defining_equations` + with ``option="pentagons"`` may take a long time. This method is + faster, but it cannot provide mathematical guarantees. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("C3",1)) # long time + sage: f.find_orthogonal_solution() # long time + Computing F-symbols for The Fusion Ring of Type C3 and level 1 with Integer Ring coefficients with 71 variables... + Set up 134 hex and orthogonality constraints... + Partitioned 134 equations into 17 components of size: + [12, 12, 6, 6, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1] + Elimination epoch completed... 10 eqns remain in ideal basis + Elimination epoch completed... 0 eqns remain in ideal basis + Hex elim step solved for 51 / 71 variables + Set up 121 reduced pentagons... + Elimination epoch completed... 18 eqns remain in ideal basis + Elimination epoch completed... 5 eqns remain in ideal basis + Pent elim step solved for 64 / 71 variables + Partitioned 5 equations into 1 components of size: + [4] + Elimination epoch completed... 0 eqns remain in ideal basis + Partitioned 6 equations into 6 components of size: + [1, 1, 1, 1, 1, 1] + Computing appropriate NumberField... + sage: f.certify_pentagons() # long time + Success!!! Found valid F-symbols for The Fusion Ring of Type C3 and level 1 with Integer Ring coefficients """ - print("Testing F-symbols for {}...".format(self._FR)) fvars_copy = deepcopy(self._fvars) - self._fvars = { sextuple : float(RDF(rhs)) for sextuple, rhs in self.get_fmats_in_alg_field().items() } + self._fvars = {sextuple : float(rhs) for sextuple, rhs in self.get_fvars_in_alg_field().items()} if use_mp: pool = Pool(processes=cpu_count()) else: pool = None n_proc = pool._processes if pool is not None else 1 - params = [(child_id,n_proc) for child_id in range(n_proc)] + params = [(child_id,n_proc,verbose) for child_id in range(n_proc)] pe = self._map_triv_reduce('pent_verify',params,worker_pool=pool,chunksize=1,mp_thresh=0) if np.all(np.isclose(np.array(pe),0,atol=1e-7)): print("Success!!! Found valid F-symbols for {}".format(self._FR)) @@ -1665,10 +2367,39 @@ def verify_pentagons(self,use_mp=True,prune=False): def fmats_are_orthogonal(self): """ - Verify that all F-matrices are orthogonal + Verify that all F-matrices are orthogonal. + + This method should always return ``True`` when called after running + :meth:`find_orthogonal_solution`. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("D4",1)) + sage: f.find_orthogonal_solution(verbose=False) + sage: f.fmats_are_orthogonal() + True """ is_orthog = [] for a,b,c,d in product(self._FR.basis(),repeat=4): mat = self.fmatrix(a,b,c,d) is_orthog.append(mat.T * mat == matrix.identity(mat.nrows())) return all(is_orthog) + + def fvars_are_real(self): + """ + Test whether all F-symbols are real. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("A1",3)) # long time + sage: f.find_orthogonal_solution(verbose=False) # long time + sage: f.fvars_are_real() # long time + True + """ + try: + for k, v in self._fvars.items(): + AA(self._qqbar_embedding(v)) + except ValueError: + print("The F-symbol {} (key {}) has a nonzero imaginary part!".format(v,k)) + return False + return True diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd index 9eebf2e5c58..2092478205b 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd @@ -1,10 +1,8 @@ -cpdef get_reduced_hexagons(factory, tuple mp_params) -cpdef get_reduced_pentagons(factory, tuple mp_params, bint prune=*) cpdef update_reduce(factory, tuple eq_tup) cpdef compute_gb(factory, tuple args) cpdef update_child_fmats(factory, tuple data_tup) -# cpdef pent_verify(factory, tuple mp_params) cdef _fmat(fvars, Nk_ij, one, a, b, c, d, x, y) -# cpdef update_reduce_chunk(factory, args) +cpdef executor(params) +cpdef collect_eqns(proc) diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index 9b8e013e93e..3030347ff8c 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -9,56 +9,135 @@ Fast FMatrix methods # **************************************************************************** cimport cython +from sage.combinat.root_system.poly_tup_engine cimport ( + compute_known_powers, + get_variables_degrees, variables, + poly_to_tup, _tup_to_poly, + subs, subs_squares, reduce_poly_dict, resize, + _flatten_coeffs, _unflatten_coeffs, + has_appropriate_linear_term, + resize +) +from sage.rings.number_field.number_field_element cimport NumberFieldElement_absolute +from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular, MPolynomialRing_libsingular +from sage.rings.polynomial.polydict cimport ETuple + import ctypes from itertools import product -from os import getpid -import sage -from sage.combinat.root_system.poly_tup_engine cimport * from sage.rings.ideal import Ideal -from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -# from sage.rings.qqbar import number_field_elements_from_algebraics - -#Define a global temporary worker results repository -cdef list worker_results = list() -############################## -### Parallel code executor ### -############################## +########################## +### Fast class methods ### +########################## -def executor(params): +cpdef _solve_for_linear_terms(factory,eqns=None): """ - Execute a function defined in this module (sage.combinat.root_system.fast_parallel_fmats_methods) - in a worker process, and supply the factory parameter by constructing a reference - to the FMatrix object in the worker's memory adress space from its id - ## NOTE: When the parent process is forked, each worker gets a copy of - every global variable. The virtual memory address of object X in the parent - process equals the VIRTUAL memory address of the copy of object X in each - worker, so we may construct references to forked copies of X using an id - obtained in the parent process - - TESTS: + Solve for a linear term occurring in a two-term equation, and for + variables appearing in univariate single-term equations. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("D3",1), inject_variables=True) + creating variables fx1..fx27 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19, fx20, fx21, fx22, fx23, fx24, fx25, fx26 + sage: f.ideal_basis = [fx0**3, fx0 + fx3**4, fx2**2 - fx3, fx2 - fx3**2, fx4 - fx2] + sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: f.ideal_basis = [poly_to_tup(p) for p in f.ideal_basis] + sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey + sage: f.ideal_basis.sort(key=poly_tup_sortkey) + sage: f._fvars = {sextuple : poly_to_tup(rhs) for sextuple, rhs in f._fvars.items()} + sage: from sage.combinat.root_system.fast_parallel_fmats_methods import _solve_for_linear_terms + sage: _solve_for_linear_terms(f) + True + sage: f._tup_to_fpoly(f._fvars[f._idx_to_sextuple[0]]) + 0 + sage: f._tup_to_fpoly(f._fvars[f._idx_to_sextuple[2]]) + fx4 + sage: f._tup_to_fpoly(f._fvars[f._idx_to_sextuple[3]]) + fx3 + sage: f._tup_to_fpoly(f._fvars[f._idx_to_sextuple[4]]) + fx4 + """ + if eqns is None: + eqns = factory.ideal_basis - sage: from sage.combinat.root_system.fast_parallel_fmats_methods import executor - sage: fmats = FMatrix(FusionRing("A1",3)) - sage: params = (('get_reduced_hexagons', id(fmats)), (0,1)) - sage: executor(params) - sage: from sage.combinat.root_system.fast_parallel_fmats_methods import collect_eqns - sage: len(collect_eqns(0)) == 63 + linear_terms_exist = False + for eq_tup in eqns: + #Only unflatten relevant polynomials + if len(eq_tup) > 2: + continue + eq_tup = _unflatten_coeffs(factory._field, eq_tup) + + if len(eq_tup) == 1: + vars = variables(eq_tup) + if len(vars) == 1 and vars[0] not in factory._solved: + factory._fvars[factory._idx_to_sextuple[vars[0]]] = tuple() + factory._solved.add(vars[0]) + linear_terms_exist = True + if len(eq_tup) == 2: + idx = has_appropriate_linear_term(eq_tup) + if idx < 0: continue + #The chosen term is guaranteed to be univariate in the largest variable + max_var = eq_tup[idx][0].nonzero_positions()[0] + if max_var not in factory._solved: + rhs_exp = eq_tup[(idx+1) % 2][0] + rhs_coeff = -eq_tup[(idx+1) % 2][1] / eq_tup[idx][1] + factory._fvars[factory._idx_to_sextuple[max_var]] = ((rhs_exp,rhs_coeff),) + factory._solved.add(max_var) + linear_terms_exist = True + return linear_terms_exist + +cpdef _backward_subs(factory): + """ + Perform backward substitution on ``self.ideal_basis``, traversing + variables in reverse lexicographical order. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("D3",1), inject_variables=True) + creating variables fx1..fx27 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19, fx20, fx21, fx22, fx23, fx24, fx25, fx26 + sage: f.ideal_basis = [fx0**3, fx0 + fx3**4, fx2**2 - fx3, fx2 - fx3**2, fx4 - fx2] + sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: f.ideal_basis = [poly_to_tup(p) for p in f.ideal_basis] + sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey + sage: f.ideal_basis.sort(key=poly_tup_sortkey) + sage: f._fvars = {sextuple : poly_to_tup(rhs) for sextuple, rhs in f._fvars.items()} + sage: from sage.combinat.root_system.fast_parallel_fmats_methods import _solve_for_linear_terms + sage: _solve_for_linear_terms(f) True - sage: fmats = FMatrix(FusionRing("E8",2)) - sage: params = (('get_reduced_hexagons', id(fmats)), (0,1)) - sage: executor(params) - sage: len(collect_eqns(0)) == 11 + sage: f._tup_to_fpoly(f._fvars[f._idx_to_sextuple[0]]) + 0 + sage: f._tup_to_fpoly(f._fvars[f._idx_to_sextuple[2]]) + fx4 + sage: f._tup_to_fpoly(f._fvars[f._idx_to_sextuple[3]]) + fx3 + sage: f._tup_to_fpoly(f._fvars[f._idx_to_sextuple[4]]) + fx4 + sage: f.ideal_basis.append(poly_to_tup(fx4-1)) + sage: f.ideal_basis.sort(key=poly_tup_sortkey) + sage: _solve_for_linear_terms(f) True + sage: from sage.combinat.root_system.fast_parallel_fmats_methods import _backward_subs + sage: _backward_subs(f) + sage: f._tup_to_fpoly(f._fvars[f._idx_to_sextuple[0]]) + 0 + sage: f._tup_to_fpoly(f._fvars[f._idx_to_sextuple[2]]) + 1 + sage: f._tup_to_fpoly(f._fvars[f._idx_to_sextuple[3]]) + fx3 + sage: f._tup_to_fpoly(f._fvars[f._idx_to_sextuple[4]]) + 1 """ - (fn_name, fmats_id), args = params - #Construct a reference to global FMatrix object in this worker's memory - fmats_obj = ctypes.cast(fmats_id, ctypes.py_object).value - mod = sage.combinat.root_system.fast_parallel_fmats_methods - #Bind module method to FMatrix object in worker process, and call the method - return getattr(mod,fn_name)(fmats_obj,args) - + one = factory._field.one() + for var in reversed(factory._poly_ring.gens()): + sextuple = factory._var_to_sextuple[var] + rhs = factory._fvars[sextuple] + d = {var_idx : factory._fvars[factory._idx_to_sextuple[var_idx]] for var_idx in variables(rhs) if var_idx in factory._solved} + if d: + kp = compute_known_powers(get_variables_degrees([rhs]), d, one) + factory._fvars[sextuple] = tuple(subs_squares(subs(rhs,kp,one),factory._ks).items()) ###################################### ### Fast fusion coefficients cache ### @@ -84,11 +163,13 @@ def executor(params): # # _Nk_ij = cached_function(_Nk_ij, name='_Nk_ij') - ############### ### Mappers ### ############### +#Define a global temporary worker results repository +cdef list worker_results = list() + cdef _fmat(fvars, _Nk_ij, id_anyon, a, b, c, d, x, y): """ Cython version of fmat class method. Using cdef for fastest dispatch @@ -129,7 +210,7 @@ cdef req_cy(tuple basis, r_matrix, dict fvars, Nk_ij, id_anyon, tuple sextuple): @cython.wraparound(False) @cython.nonecheck(False) @cython.cdivision(True) -cpdef get_reduced_hexagons(factory, tuple mp_params): +cdef get_reduced_hexagons(factory, tuple mp_params): """ Set up and reduce the hexagon equations corresponding to this worker """ @@ -178,7 +259,7 @@ cdef MPolynomial_libsingular feq_cy(tuple basis, fvars, Nk_ij, id_anyon, zero, t @cython.wraparound(False) @cython.nonecheck(False) @cython.cdivision(True) -cpdef get_reduced_pentagons(factory, tuple mp_params, bint prune=True): +cdef get_reduced_pentagons(factory, tuple mp_params): """ Set up and reduce the pentagon equations corresponding to this worker """ @@ -201,9 +282,11 @@ cpdef get_reduced_pentagons(factory, tuple mp_params, bint prune=True): cdef MPolynomial_libsingular zero = factory._poly_ring.zero() #Computation loop - for i, nonuple in enumerate(product(basis,repeat=9)): + it = product(basis,repeat=9) + for i in range(len(basis)**9): + nonuple = next(it) if i % n_proc == child_id: - pe = feq_cy(basis,fvars,_Nk_ij,id_anyon,zero,nonuple,prune=prune) + pe = feq_cy(basis,fvars,_Nk_ij,id_anyon,zero,nonuple,prune=True) if pe: red = reduce_poly_dict(pe.dict(),_nnz,_ks,one) @@ -215,14 +298,34 @@ cpdef get_reduced_pentagons(factory, tuple mp_params, bint prune=True): cpdef update_reduce(factory, tuple eq_tup): """ Substitute known values, known squares, and reduce! + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("B4",1), fusion_label="b", inject_variables=True) + creating variables fx1..fx14 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 + sage: p = 2*fx0**2 - 3/7*fx0*fx1*fx3**3 + 5*fx0*fx4 + sage: from sage.combinat.root_system.poly_tup_engine import * + sage: f.ideal_basis = [poly_to_tup(p)] + sage: f._ks = {3: 2} + sage: f._var_degs = get_variables_degrees(f.ideal_basis) + sage: known_vals = {4: poly_to_tup(fx11**2)} + sage: f._nnz + (100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 0, 0, 0, 0) + sage: f._kp = compute_known_powers(f._var_degs,known_vals,f._field.one()) + sage: from sage.combinat.root_system.fast_parallel_fmats_methods import update_reduce, collect_eqns + sage: update_reduce(f, f.ideal_basis[0]) + sage: from sage.combinat.root_system.poly_tup_engine import _unflatten_coeffs + sage: [f._tup_to_fpoly(_unflatten_coeffs(f._field,p)) for p in collect_eqns(0)] + [fx1*fx3 - 35/6*fx11^2 - 7/3*fx0] """ global worker_results cdef NumberFieldElement_absolute one = factory._field.one() #Construct cyclotomic field elts from list repn - eq_tup = _unflatten_coeffs(factory._field, eq_tup) + cdef tuple unflat = _unflatten_coeffs(factory._field, eq_tup) - cdef dict eq_dict = subs(eq_tup,factory._kp,one) + cdef dict eq_dict = subs(unflat,factory._kp,one) cdef tuple red = reduce_poly_dict(eq_dict,factory._nnz,factory._ks,one) #Avoid pickling cyclotomic coefficients @@ -232,17 +335,48 @@ cpdef update_reduce(factory, tuple eq_tup): cpdef compute_gb(factory, tuple args): """ - Compute Groebner basis for given equations iterable + Compute the reduced Groebner basis for given equations iterable. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("A2",2)) + sage: from multiprocessing import Pool, set_start_method + sage: try: + ....: set_start_method('fork') # context can be set only once + ....: except RuntimeError: + ....: pass + sage: f.get_defining_equations('hexagons',worker_pool=Pool(),output=False) + sage: partition = f._partition_eqns() + Partitioned 261 equations into 57 components of size: + [24, 12, 12, 12, 12, 12, 12, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1] + sage: from sage.combinat.root_system.fast_parallel_fmats_methods import compute_gb, collect_eqns + sage: args = (partition[(29, 40, 83, 111, 148, 154)], "degrevlex") + sage: compute_gb(f, args) + sage: from sage.combinat.root_system.poly_tup_engine import _unflatten_coeffs + sage: [f._tup_to_fpoly(_unflatten_coeffs(f._field,t)) for t in collect_eqns(0)] + [fx83*fx154 - fx111, + fx29^2*fx154 + fx29, + fx29*fx148 - fx40, + fx29*fx154^2 + fx154, + fx83*fx148 + fx29*fx154, + fx40*fx83 - fx29, + fx111*fx148 - fx154, + fx40*fx154 + fx148, + fx40*fx111 - fx29*fx154, + fx29*fx111 + fx83] """ global worker_results + cdef list eqns, sorted_vars eqns, term_order = args #Define smaller poly ring in component vars sorted_vars = list() + cdef tuple eq_tup + cdef int fx for eq_tup in eqns: for fx in variables(eq_tup): sorted_vars.append(fx) sorted_vars = sorted(set(sorted_vars)) - R = PolynomialRing(factory._FR.field(),len(sorted_vars),'a',order=term_order) + cdef MPolynomialRing_libsingular R = PolynomialRing(factory._FR.field(),len(sorted_vars),'a',order=term_order) #Zip tuples into R and compute Groebner basis cdef idx_map = { old : new for new, old in enumerate(sorted_vars) } @@ -251,7 +385,7 @@ cpdef compute_gb(factory, tuple args): cdef list polys = list() for eq_tup in eqns: eq_tup = _unflatten_coeffs(F, eq_tup) - polys.append(tup_to_poly(resize(eq_tup,idx_map,nvars),parent=R)) + polys.append(_tup_to_poly(resize(eq_tup,idx_map,nvars),parent=R)) gb = Ideal(sorted(polys)).groebner_basis(algorithm="libsingular:slimgb") #Change back to fmats poly ring and append to temp_eqns @@ -271,6 +405,27 @@ cpdef update_child_fmats(factory, tuple data_tup): One-to-all communication used to update FMatrix object after each triangular elim step. We must update the algorithm's state values. These are: _fvars, _solved, _ks, _var_degs, _nnz, and _kp. + + TESTS:: + + sage: f = FMatrix(FusionRing("A1",3)) + sage: f.get_orthogonality_constraints(output=False) + sage: from multiprocessing import Pool, set_start_method + sage: try: + ....: set_start_method('fork') # context can be set only once + ....: except RuntimeError: + ....: pass + sage: pool = Pool() + sage: f.get_defining_equations('hexagons',worker_pool=pool,output=False) + sage: f.ideal_basis = f._par_graph_gb(worker_pool=pool,verbose=False) + sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey + sage: f.ideal_basis.sort(key=poly_tup_sortkey) + sage: f.mp_thresh = 0 + sage: f._triangular_elim(worker_pool=pool) # indirect doctest + Elimination epoch completed... 10 eqns remain in ideal basis + Elimination epoch completed... 0 eqns remain in ideal basis + sage: f.ideal_basis + [] """ #factory object is assumed global before forking used to create the Pool object, #so each child has a global fmats variable. So it's enough to update that object @@ -278,21 +433,32 @@ cpdef update_child_fmats(factory, tuple data_tup): factory._nnz = factory._get_known_nonz() factory._kp = compute_known_powers(factory._var_degs,factory._get_known_vals(),factory._field.one()) -# def get_appropriate_number_field(factory,non_cyclotomic_roots): -# """ -# If needed, find a NumberField containing the roots given roots -# """ -# roots = [factory._FR.field().gen()]+[r[1] for r in non_cyclotomic_roots] -# return number_field_elements_from_algebraics(roots,minimal=True) - ################ ### Reducers ### ################ -def collect_eqns(proc): +cpdef collect_eqns(proc): """ Helper function for returning processed results back to parent process. - Trivial reducer: simply collects objects with the same key in the worker + + Trivial reducer: simply collects objects with the same key in the worker. + This method is only useful when called after :meth:`executor`, whose + function argument appends output to the ``worker_results`` list. + + EXAMPLES:: + + sage: from sage.combinat.root_system.fast_parallel_fmats_methods import executor + sage: fmats = FMatrix(FusionRing("A1",3)) + sage: params = (('get_reduced_hexagons', id(fmats)), (0,1)) + sage: executor(params) + sage: from sage.combinat.root_system.fast_parallel_fmats_methods import collect_eqns + sage: len(collect_eqns(0)) == 63 + True + sage: fmats = FMatrix(FusionRing("G2",2)) + sage: params = (('get_reduced_pentagons', id(fmats)), (0,1)) + sage: executor(params) + sage: len(collect_eqns(0)) == 4911 + True """ #Discard the zero polynomial global worker_results @@ -300,39 +466,102 @@ def collect_eqns(proc): worker_results = list() return list(reduced) -# #################### -# ### Verification ### -# #################### -# -# cdef feq_verif(factory, tuple nonuple, float tol=5e-8): -# """ -# Check the pentagon equation corresponding to the given nonuple -# """ -# global worker_results -# a, b, c, d, e, f, g, k, l = nonuple -# cdef float diff, lhs, rhs -# lhs = _fmat(factory,f,c,d,e,g,l)*_fmat(factory,a,b,l,e,f,k) -# rhs = 0.0 -# for h in factory._FR.basis(): -# rhs += _fmat(factory,a,b,c,g,f,h)*_fmat(factory,a,h,d,e,g,k)*_fmat(factory,b,c,d,k,h,l) -# diff = lhs - rhs -# if diff > tol or diff < -tol: -# worker_results.append(diff) -# -# @cython.wraparound(False) -# @cython.nonecheck(False) -# @cython.cdivision(True) -# cpdef pent_verify(factory, tuple mp_params): -# """ -# Generate all the pentagon equations assigned to this process, and reduce them -# """ -# child_id, n_proc = mp_params -# cdef float t0 -# cdef tuple nonuple -# cdef unsigned long i -# for i, nonuple in enumerate(product(factory._FR.basis(),repeat=9)): -# if i % n_proc == child_id: -# feq_verif(factory,nonuple) -# if i % 50000000 == 0 and i: -# print("{:5d}m equations checked... {} potential misses so far...".format(i // 1000000,len(worker_results))) -# print("Ran through {} pentagons... Worker {} with pid {} reporting {} potential misses...".format(i,child_id,getpid(),len(worker_results))) +############################## +### Parallel code executor ### +############################## + +#Hard-coded module __dict__-style attribute with visible cdef methods +cdef dict mappers = { + "get_reduced_hexagons": get_reduced_hexagons, + "get_reduced_pentagons": get_reduced_pentagons, + "update_reduce": update_reduce, + "compute_gb": compute_gb, + "update_child_fmats": update_child_fmats, + "pent_verify": pent_verify + } + +cpdef executor(params): + r""" + Execute a function defined in this module + (``sage.combinat.root_system.fast_parallel_fmats_methods``) in a worker + process, and supply the factory parameter by constructing a reference + to the ``FMatrix`` object in the worker's memory adress space from + its ``id``. + + INPUT: + + - ``params`` -- a tuple ``((fn_name, fmats_id), fn_args)`` where + ``fn_name`` is the name of the function to be executed, ``fmats_id`` + is the ``id`` of the :class:`FMatrix` object, and ``fn_args`` is a + tuple containing all arguments to be passed to the function ``fn_name``. + + .. NOTES:: + + When the parent process is forked, each worker gets a copy of + every global variable. The virtual memory address of object `X` in + the parent process equals the *virtual* memory address of the copy + of object `X` in each worker, so we may construct references to + forked copies of `X` using an ``id`` obtained in the parent process. + + TESTS: + + sage: from sage.combinat.root_system.fast_parallel_fmats_methods import executor + sage: fmats = FMatrix(FusionRing("A1",3)) + sage: params = (('get_reduced_hexagons', id(fmats)), (0,1)) + sage: executor(params) + sage: from sage.combinat.root_system.fast_parallel_fmats_methods import collect_eqns + sage: len(collect_eqns(0)) == 63 + True + sage: fmats = FMatrix(FusionRing("E8",2)) + sage: params = (('get_reduced_hexagons', id(fmats)), (0,1)) + sage: executor(params) + sage: len(collect_eqns(0)) == 11 + True + """ + (fn_name, fmats_id), args = params + #Construct a reference to global FMatrix object in this worker's memory + fmats_obj = ctypes.cast(fmats_id, ctypes.py_object).value + #Bind module method to FMatrix object in worker process, and call the method + return mappers[fn_name](fmats_obj,args) + +#################### +### Verification ### +#################### + +cdef feq_verif(factory, fvars, Nk_ij, id_anyon, tuple nonuple, float tol=5e-8): + """ + Check the pentagon equation corresponding to the given nonuple + """ + global worker_results + a, b, c, d, e, f, g, k, l = nonuple + cdef float diff, lhs, rhs + + lhs = _fmat(fvars,Nk_ij,id_anyon,f,c,d,e,g,l)*_fmat(fvars,Nk_ij,id_anyon,a,b,l,e,f,k) + rhs = 0.0 + for h in factory._FR.basis(): + rhs += _fmat(fvars,Nk_ij,id_anyon,a,b,c,g,f,h)*_fmat(fvars,Nk_ij,id_anyon,a,h,d,e,g,k)*_fmat(fvars,Nk_ij,id_anyon,b,c,d,k,h,l) + diff = lhs - rhs + if diff > tol or diff < -tol: + worker_results.append(diff) + +@cython.wraparound(False) +@cython.nonecheck(False) +@cython.cdivision(True) +cdef pent_verify(factory, tuple mp_params): + """ + Generate all the pentagon equations assigned to this process, and reduce them + """ + child_id, n_proc, verbose = mp_params + cdef float t0 + cdef tuple nonuple + cdef unsigned long long i + + #Pre-compute common parameters for speed + Nk_ij = factory._FR.Nk_ij + cdef dict fvars = factory._fvars + id_anyon = factory._FR.one() + for i, nonuple in enumerate(product(factory._FR.basis(),repeat=9)): + if i % n_proc == child_id: + feq_verif(factory,fvars,Nk_ij,id_anyon,nonuple) + if i % 50000000 == 0 and i and verbose: + print("{:5d}m equations checked... {} potential misses so far...".format(i // 1000000,len(worker_results))) diff --git a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pxd b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pxd index 134b6b57f2d..4dd122a7fbf 100644 --- a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pxd +++ b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pxd @@ -1,3 +1,3 @@ -cpdef sig_2k(fusion_ring, tuple args) -cpdef odd_one_out(fusion_ring, tuple args) cpdef _unflatten_entries(factory, list entries) +cpdef executor(params) +cpdef collect_results(proc) diff --git a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx index 663249fd952..f6e8b40f1a7 100644 --- a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx @@ -8,62 +8,17 @@ Fast FusionRing methods for computing braid group representations # https://www.gnu.org/licenses/ # **************************************************************************** -cimport cython import ctypes -from itertools import product -import sage -from sage.combinat.root_system.poly_tup_engine cimport _flatten_coeffs, _unflatten_coeffs, poly_to_tup +cimport cython from sage.combinat.root_system.fast_parallel_fmats_methods cimport _fmat + +from itertools import product from sage.misc.cachefunc import cached_function from sage.rings.qqbar import QQbar #Define a global temporary worker results repository worker_results = list() -############################## -### Parallel code executor ### -############################## - -def executor(params): - """ - Execute a function defined in this module - (sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn) - in a worker process, and supply the `FusionRing` parameter by constructing a - reference to the FMatrix object in the worker's memory adress space - from its id. - - NOTES: - - When the parent process is forked, each worker gets a copy of - every global variable. The virtual memory address of object X in the parent - process equals the VIRTUAL memory address of the copy of object X in each - worker, so we may construct references to forked copies of X - - TESTS: - - sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import executor - sage: FR = FusionRing("A1",4) - sage: FR.fusion_labels(['idd','one','two','three','four'],inject_variables=True) - sage: params = (('sig_2k',id(FR)),(0,1,(1,one,one,5))) - sage: executor(params) - sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import collect_results - sage: len(collect_results(0)) == 13 - True - sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import executor, collect_results - sage: FR = FusionRing("B2",2) - sage: FR.fusion_labels(['I0','Y1','X','Z','Xp','Y2'],inject_variables=True) - sage: params = (('odd_one_out',id(FR)),(0,1,(X,Xp,5))) - sage: executor(params) - sage: len(collect_results(0)) == 54 - True - """ - (fn_name, fr_id), args = params - #Construct a reference to global FMatrix object in this worker's memory - fusion_ring_obj = ctypes.cast(fr_id, ctypes.py_object).value - mod = sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn - #Bind module method to FMatrix object in worker process, and call the method - getattr(mod,fn_name)(fusion_ring_obj,args) - ############### ### Mappers ### ############### @@ -87,8 +42,32 @@ cpdef mid_sig_ij(fusion_ring,row,col,a,b): (1/2, -1/2) sage: FR.get_computational_basis(one,two,4) [(two, two), (two, idd), (idd, two)] + sage: FR.fmats.find_orthogonal_solution(verbose=False) sage: mid_sig_ij(FR, (two, two), (two, idd), one, two) - (zeta48^10 - zeta48^2)*fx0*fx1*fx8 + (zeta48^2)*fx2*fx3*fx8 + 1/3*zeta48^10 - 2/3*zeta48^2 + + This method works for all possible types of fields returned by + ``self.fmats.field()``. + + TESTS:: + + sage: FR = FusionRing("A1",3) + sage: FR.fusion_labels("a",inject_variables=True) + sage: FR.fmats.find_orthogonal_solution(verbose=False) + sage: _, _, to_opt = FR.fmats.field().optimized_representation() + sage: a2**4 + 2*a0 + 3*a2 + sage: FR.get_computational_basis(a2,a2,4) + [(a2, a2), (a2, a0), (a0, a2)] + sage: to_opt(mid_sig_ij(FR,(a2, a0),(a2, a2),a2,a2)) + -2024728666370660589/10956322398441542221*a1^30 - 34142146914395596291/21912644796883084442*a1^28 - 21479437628091413631/21912644796883084442*a1^26 + 260131910217202103829/21912644796883084442*a1^24 + 69575612911670713183/10956322398441542221*a1^22 + 25621808994337724689/1992058617898462222*a1^20 - 1975139725303994650417/21912644796883084442*a1^18 - 1315664901396537703585/21912644796883084442*a1^16 - 2421451803369354765026/10956322398441542221*a1^14 - 5963323855935165859057/21912644796883084442*a1^12 - 4477124943233705460859/21912644796883084442*a1^10 - 2001454824483021618178/10956322398441542221*a1^8 - 2120319455379289595185/21912644796883084442*a1^6 - 15722612944437234961/755608441271830498*a1^4 - 39862668562651453480/10956322398441542221*a1^2 - 6967145776903524195/10956322398441542221 + sage: FR = FusionRing("G2",2) + sage: FR.fusion_labels("g",inject_variables=True) + sage: FR.get_computational_basis(g1,g2,4) + [(g3, g2), (g3, g1), (g2, g3), (g2, g0), (g1, g3), (g1, g1), (g0, g2)] + sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time (~11 s) + sage: mid_sig_ij(FR,(g2, g3),(g1, g1),g1,g2) # long time + -0.4566723195695565? + 0.0805236512828312?*I """ #Pre-compute common parameters for efficiency _fvars = fusion_ring.fmats._fvars @@ -122,14 +101,38 @@ cpdef odd_one_out_ij(fusion_ring,xi,xj,a,b): EXAMPLES:: sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import odd_one_out_ij - sage: FR = FusionRing("B2",2) - sage: FR.fusion_labels(['I0','Y1','X','Z','Xp','Y2'],inject_variables=True) - sage: X.weight() - (1/2, 1/2) - sage: FR.get_computational_basis(X,X,3) - [(Y2,), (Y1,), (I0,)] - sage: odd_one_out_ij(FR,Y2,Y1,X,X) - (zeta40^10)*fx205*fx208 + (zeta40^14 - zeta40^10 + zeta40^6 - zeta40^2)*fx206*fx209 + (zeta40^2)*fx207*fx210 + sage: FR = FusionRing("A1",4) + sage: FR.fusion_labels(["one","two","three","four","five"],inject_variables=True) + sage: FR.get_computational_basis(two,two,5) + [(three, three, one), + (three, three, three), + (three, one, three), + (one, three, three), + (one, one, one)] + sage: FR.fmats.find_orthogonal_solution(verbose=False) + sage: odd_one_out_ij(FR,one,three,two,two) + 2/3*zeta48^12 - 1/3*zeta48^8 - 1/3*zeta48^4 - 1/3 + + This method works for all possible types of fields returned by + ``self.fmats.field()``. + + TESTS:: + + sage: FR = FusionRing("A1",3) + sage: FR.fusion_labels("a",inject_variables=True) + sage: FR.fmats.find_orthogonal_solution(verbose=False) + sage: _, _, to_opt = FR.fmats.field().optimized_representation() + sage: a2**3 + a0 + 2*a2 + sage: FR.get_computational_basis(a2,a2,3) + [(a2,), (a0,)] + sage: to_opt(odd_one_out_ij(FR,a0,a2,a2,a2)) + 6341990144855406911/21912644796883084442*a1^30 + 47313529044493641571/21912644796883084442*a1^28 - 6964289120109414595/10956322398441542221*a1^26 - 406719371329322780627/21912644796883084442*a1^24 + 87598732372849355687/10956322398441542221*a1^22 - 456724726845194775/19723352652460022*a1^20 + 3585892725441116840515/21912644796883084442*a1^18 - 645866255979227573282/10956322398441542221*a1^16 + 7958479159087829772639/21912644796883084442*a1^14 + 789748976956837633826/10956322398441542221*a1^12 + 3409710648897945752185/21912644796883084442*a1^10 + 903956381582048110980/10956322398441542221*a1^8 + 192973084151342020307/21912644796883084442*a1^6 - 9233312083438019435/755608441271830498*a1^4 + 667869266552877781/10956322398441542221*a1^2 + 17644302696056968099/21912644796883084442 + sage: FR = FusionRing("G2",2) + sage: FR.fusion_labels("g",inject_variables=True) + sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time (~11 s) + sage: odd_one_out_ij(FR,g1,g2,g1,g1) # long time + -0.2636598866349343? + 0.4566723195695565?*I """ #Pre-compute common parameters for efficiency _fvars = fusion_ring.fmats._fvars @@ -151,7 +154,7 @@ odd_one_out_ij = cached_function(odd_one_out_ij, name='odd_one_out_ij') @cython.nonecheck(False) @cython.cdivision(True) -cpdef sig_2k(fusion_ring, tuple args): +cdef sig_2k(fusion_ring, tuple args): """ Compute entries of the 2k-th braid generator """ @@ -204,7 +207,7 @@ cpdef sig_2k(fusion_ring, tuple args): #Avoid pickling cyclotomic field element objects if must_flatten_coeff: - entry = _flatten_entry(fusion_ring, entry) + entry = entry.list() worker_results.append(((basis_dict[nnz_pos],i), entry)) coords.add((basis_dict[nnz_pos],i)) @@ -218,17 +221,13 @@ cpdef sig_2k(fusion_ring, tuple args): #Avoid pickling cyclotomic field element objects if must_flatten_coeff: - #The entry is either a polynomial or a base field element - if entry.parent() == fusion_ring.fmats._poly_ring: - entry = _flatten_coeffs(poly_to_tup(entry)) - else: - entry = entry.list() + entry = entry.list() worker_results.append(((basis_dict[nnz_pos],i), entry)) @cython.nonecheck(False) @cython.cdivision(True) -cpdef odd_one_out(fusion_ring, tuple args): +cdef odd_one_out(fusion_ring, tuple args): """ Compute entries of the rightmost braid generator, in case we have an odd number of strands @@ -273,11 +272,7 @@ cpdef odd_one_out(fusion_ring, tuple args): #Avoid pickling cyclotomic field element objects if must_flatten_coeff: - #The entry is either a polynomial or a base field element - if entry.parent() == fusion_ring.fmats._poly_ring: - entry = _flatten_coeffs(poly_to_tup(entry)) - else: - entry = entry.list() + entry = entry.list() worker_results.append(((basis_dict[nnz_pos],i), entry)) continue @@ -295,7 +290,7 @@ cpdef odd_one_out(fusion_ring, tuple args): #Avoid pickling cyclotomic field element objects if must_flatten_coeff: - entry = _flatten_entry(fusion_ring, entry) + entry = entry.list() worker_results.append(((basis_dict[nnz_pos],i), entry)) @@ -303,10 +298,25 @@ cpdef odd_one_out(fusion_ring, tuple args): ### Reducers ### ################ -def collect_results(proc): +cpdef collect_results(proc): """ Helper function for returning processed results back to parent process. - Trivial reducer: simply collects objects with the same key in the worker + + Trivial reducer: simply collects objects with the same key in the worker. + This method is only useful when called after :meth:`executor`, whose + function argument appends output to the ``worker_results`` list. + + EXAMPLES:: + + sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import executor + sage: FR = FusionRing("A1",4) + sage: FR.fusion_labels(['idd','one','two','three','four'],inject_variables=True) + sage: FR.fmats.find_orthogonal_solution(verbose=False) + sage: params = (('sig_2k',id(FR)),(0,1,(2,one,one,9))) + sage: executor(params) + sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import collect_results + sage: len(collect_results(0)) == 171 + True """ #Discard the zero polynomial global worker_results @@ -314,24 +324,60 @@ def collect_results(proc): worker_results = list() return reduced -###################################### -### Pickling circumvention helpers ### -###################################### +############################## +### Parallel code executor ### +############################## -cdef _flatten_entry(fusion_ring, entry): - """ - Flatten cyclotomic coefficients to a representation as a tuple of rational - coefficients. +#Hard-coded module __dict__-style attribute with visible cdef methods +cdef dict mappers = { + "sig_2k": sig_2k, + "odd_one_out": odd_one_out +} + +cpdef executor(params): + r""" + Execute a function registered in this module's ``mappers`` + in a worker process, and supply the ``FusionRing`` parameter by + constructing a reference to the FMatrix object in the worker's memory + adress space from its ``id``. + + .. NOTES:: + + When the parent process is forked, each worker gets a copy of + every global variable. The virtual memory address of object `X` in + the parent process equals the *virtual* memory address of the copy of + object `X` in each worker, so we may construct references to forked + copies of `X`. - This is used to avoid pickling cyclotomic coefficient objects, which fails - with new PARI settings introduced in trac ticket #30537 + TESTS:: + + sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import executor + sage: FR = FusionRing("A1",4) + sage: FR.fusion_labels(['idd','one','two','three','four'],inject_variables=True) + sage: FR.fmats.find_orthogonal_solution(verbose=False) + sage: params = (('sig_2k',id(FR)),(0,1,(1,one,one,5))) + sage: executor(params) + sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import collect_results + sage: len(collect_results(0)) == 13 + True + sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import executor, collect_results + sage: FR = FusionRing("B2",2) + sage: FR.fusion_labels(['I0','Y1','X','Z','Xp','Y2'],inject_variables=True) + sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time (~23 s) + sage: params = (('odd_one_out',id(FR)),(0,1,(X,Xp,5))) + sage: executor(params) # long time + sage: len(collect_results(0)) == 54 # long time + True """ - #The entry is either a polynomial or a base field element - if entry.parent() == fusion_ring.fmats._poly_ring: - entry = _flatten_coeffs(poly_to_tup(entry)) - else: - entry = entry.list() - return entry + (fn_name, fr_id), args = params + #Construct a reference to global FMatrix object in this worker's memory + fusion_ring_obj = ctypes.cast(fr_id, ctypes.py_object).value + #Bind module method to FMatrix object in worker process, and call the method + mappers[fn_name](fusion_ring_obj,args) + +###################################### +### Pickling circumvention helpers ### +###################################### cpdef _unflatten_entries(fusion_ring, list entries): """ @@ -357,10 +403,4 @@ cpdef _unflatten_entries(fusion_ring, list entries): must_unflatten = F != QQbar if must_unflatten: for i, (coord, entry) in enumerate(entries): - #In this case entry represents a polynomial - if type(entry) == type(tuple()): - entry = fm.tup_to_fpoly(_unflatten_coeffs(F,entry)) - #Otherwise entry belongs to base field - else: - entry = F(entry) - entries[i] = (coord, entry) + entries[i] = (coord, F(entry)) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 1f3a11adbbf..3a033f56d16 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -15,7 +15,10 @@ from multiprocessing import Pool, set_start_method from sage.combinat.q_analogues import q_int import sage.combinat.root_system.f_matrix as FMatrix -from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import collect_results, executor, _unflatten_entries +from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import ( + collect_results, executor, + _unflatten_entries +) from sage.combinat.root_system.weyl_characters import WeylCharacterRing from sage.matrix.constructor import matrix from sage.matrix.special import diagonal_matrix @@ -24,7 +27,6 @@ from sage.misc.misc import inject_variable from sage.rings.integer_ring import ZZ from sage.rings.number_field.number_field import CyclotomicField - from sage.rings.qqbar import QQbar class FusionRing(WeylCharacterRing): @@ -374,26 +376,36 @@ def _test_total_q_order(self, **options): tester.assertTrue(tqo.is_real_positive()) tester.assertEqual(tqo**2, self.global_q_dimension(base_coercion=False)) - def test_braid_representation(self, **options): + def test_braid_representation(self, max_strands=6): """ Check that we can compute valid braid group representations. - This test indirectly partially verifies the correctness of the orthogonal - F-matrix solver. + INPUT: + + - ``max_strands`` -- (default: 6): maximum number of braid group strands + + Create a braid group representation using :meth:`get_braid_generators` + and confirms the braid relations. This test indirectly partially verifies + the correctness of the orthogonal F-matrix solver. If the code were + incorrect the method would not be deterministic because the fusing anyon + is chosen randomly. (A different choice is made for each number of strands tested.) + However the doctest is deterministic since it will always return True. EXAMPLES:: sage: F41 = FusionRing("F4",1) - sage: F41.test_braid_representation() + sage: F41.test_braid_representation(max_strands=4) + True sage: B22 = FusionRing("B2",2) # long time - sage: B22.test_braid_representation() # long time (~45s) + sage: B22.test_braid_representation() # long time + True """ if not self.is_multiplicity_free(): # Braid group representation is not available if self is not multiplicity free return True - tester = self._tester(**options) b = self.basis() + results = [] #Test with different numbers of strands - for n_strands in range(3,7): + for n_strands in range(3,max_strands+1): #Randomly select a fusing anyon. Skip the identity element, since #its braiding matrices are trivial while True: @@ -408,8 +420,9 @@ def test_braid_representation(self, **options): d = self(k) break comp_basis, sig = self.get_braid_generators(a,d,n_strands,verbose=False) - tester.assertTrue(len(comp_basis) > 0) - tester.assertTrue(self.gens_satisfy_braid_gp_rels(sig)) + results.append(len(comp_basis) > 0) + results.append(self.gens_satisfy_braid_gp_rels(sig)) + return all(results) def fusion_labels(self, labels=None, inject_variables=False): r""" @@ -537,7 +550,7 @@ def fvars_field(self): Cyclotomic Field of order 40 and degree 16 sage: a2**4 2*a0 + 3*a2 - sage: comp_basis, sig = A13.get_braid_generators(a2,a2,4,verbose=False) + sage: comp_basis, sig = A13.get_braid_generators(a2,a2,3,verbose=False) sage: A13.fvars_field() Number Field in a with defining polynomial y^32 - 8*y^30 + 18*y^28 - 44*y^26 + 93*y^24 - 56*y^22 + 2132*y^20 - 1984*y^18 + 19738*y^16 - 28636*y^14 + 77038*y^12 - 109492*y^10 + 92136*y^8 - 32300*y^6 + 5640*y^4 - 500*y^2 + 25 sage: a2.q_dimension().parent() @@ -814,6 +827,37 @@ def s_ij(self, elt_i, elt_j, base_coercion=True): def s_ijconj(self, elt_i, elt_j, base_coercion=True): """ + Return the conjugate of the element of the S-matrix given by + ``self.s_ij(elt_i,elt_j,base_coercion=base_coercion)``. + + See :meth:`s_ij`. + + EXAMPLES:: + + sage: G21 = FusionRing("G2", 1) + sage: b = G21.basis() + sage: [G21.s_ijconj(x, y) for x in b for y in b] + [1, -zeta60^14 + zeta60^6 + zeta60^4, -zeta60^14 + zeta60^6 + zeta60^4, -1] + + This method works with all possible types of fields returned by + ``self.fmats.field()``. + + TESTS:: + + sage: E62 = FusionRing("E6",2) + sage: E62.fusion_labels("e", inject_variables=True) + sage: E62.s_ij(e8,e1).conjugate() == E62.s_ijconj(e8,e1) + True + sage: F41 = FusionRing("F4",1) + sage: F41.fmats.find_orthogonal_solution(verbose=False) + sage: b = F41.basis() + sage: all(F41.s_ijconj(x,y) == F41._basecoer(F41.s_ij(x,y,base_coercion=False).conjugate()) for x in b for y in b) + True + sage: G22 = FusionRing("G2",2) + sage: G22.fmats.find_orthogonal_solution(verbose=False) # long time (~11 s) + sage: b = G22.basis() + sage: all(G22.s_ijconj(x,y) == G22.fmats.field()(G22.s_ij(x,y,base_coercion=False).conjugate()) for x in b for y in b) + True """ ret = self.s_ij(elt_i, elt_j, base_coercion=False).conjugate() if (not base_coercion) or (self._basecoer is None): @@ -1035,36 +1079,36 @@ def is_multiplicity_free(self): ### Braid group representations ### ################################### - def get_trees(self,top_row,root): - """ - Recursively enumerate all the admissible trees with given top row and root. - - INPUT: - - - ``top_row`` -- a list of basis elements of self - - ``root`` -- a simple element of self - - Let `k` denote the length ``top_row``. This method returns - Returns a list of tuples `(l_1,...,l_{k-2})` such that - - .. MATH:: - - \\begin{array}{l} - root \\to l_{k-2} \otimes m_{k},\\ - l_{k-2} \\to l_{k-3} \otimes m_{k-1},\\ - \\cdots\\ - l_2\\to l_1\otimes m_3\\ - l_1\\to m_1\otimes m_2 - \end{array} - - where `a \\to b\otimes c` means `N_{bc}^a\\neq 0`. - """ - if len(top_row) == 2: - m1, m2 = top_row - return [[]] if self.Nk_ij(m1,m2,root) else [] - else: - m1, m2 = top_row[:2] - return [tuple([l,*b]) for l in self.basis() for b in self.get_trees([l]+top_row[2:],root) if self.Nk_ij(m1,m2,l)] + # def get_trees(self,top_row,root): + # """ + # Recursively enumerate all the admissible trees with given top row and root. + # + # INPUT: + # + # - ``top_row`` -- a list of basis elements of self + # - ``root`` -- a simple element of self + # + # Let `k` denote the length ``top_row``. This method returns + # Returns a list of tuples `(l_1,...,l_{k-2})` such that + # + # .. MATH:: + # + # \\begin{array}{l} + # root \\in l_{k-2} \otimes m_{k},\\ + # l_{k-2} \\in l_{k-3} \otimes m_{k-1},\\ + # \\cdots\\ + # l_2 \\in l_1\otimes m_3\\ + # l_1 \\in m_1\otimes m_2 + # \end{array} + # + # where `a \\to b\otimes c` means `N_{bc}^a\\neq 0`. + # """ + # if len(top_row) == 2: + # m1, m2 = top_row + # return [[]] if self.Nk_ij(m1,m2,root) else [] + # else: + # m1, m2 = top_row[:2] + # return [tuple([l,*b]) for l in self.basis() for b in self.get_trees([l]+top_row[2:],root) if self.Nk_ij(m1,m2,l)] def get_computational_basis(self,a,b,n_strands): """ @@ -1073,38 +1117,79 @@ def get_computational_basis(self,a,b,n_strands): INPUT: - ``a`` -- a basis element - - ``b`` -- another basis elements + - ``b`` -- another basis element - ``n_strands`` -- the number of strands for a braid group Let `n=` ``n_strands`` and let `k` be the greatest integer `\leqslant n/2`. The braid group acts on `\\text{Hom}(b,a^n)`. This action is computed in :meth:`get_braid_generators`. This method - returns the computational basis in the form of a set of - fusion trees. Each tree is represented by a `(n-2)`-tuple + returns the computational basis in the form of a list of + fusion trees. Each tree is represented by an `(n-2)`-tuple .. MATH:: - (m_1,\cdots,m_k,l_1,\cdots,l_{k-2}) + (m_1,\ldots,m_k,l_1,\ldots,l_{k-2}) + + such that each `m_j` is an irreducible constituent in `a \otimes a` + and + + .. MATH:: + + \\begin{array}{l} + b \\in l_{k-2} \otimes m_{k},\\\\ + l_{k-2} \\in l_{k-3} \otimes m_{k-1},\\\\ + \\cdots,\\\\ + l_2 \\in l_1\otimes m_3,\\\\ + l_1 \\in m_1\otimes m_2. + \end{array} + + where `z \\in x\otimes y` means `N_{xy}^z\\neq 0`. + + As a computational device when ``n_strands`` is odd, we pad the + vector `(m_1,\ldots,m_k)` with an additional `m_{k+1}` equal to `a`. + However, this `m_{k+1}` does *not* appear in the output of this method. + + The following example appears in Section 3.1 of [CW2015]_. + + EXAMPLES:: - These are computed by :meth:`get_trees`. As a computational - device when ``n_strands`` is odd, we pad the vector `(m_1,\cdots,m_k)` - with an additional `m_{k+1}` equal to `a` before passing it to - :meth:`get_trees`. This `m_{k+1}` does not appear in the output - of this method. + sage: A14 = FusionRing("A1",4) + sage: A14.get_order() + [(0, 0), (1/2, -1/2), (1, -1), (3/2, -3/2), (2, -2)] + sage: A14.fusion_labels(["zero","one","two","three","four"],inject_variables=True) + sage: [A14(x) for x in A14.get_order()] + [zero, one, two, three, four] + sage: A14.get_computational_basis(one,two,4) + [(two, two), (two, zero), (zero, two)] """ + def _get_trees(fr,top_row,root): + if len(top_row) == 2: + m1, m2 = top_row + return [[]] if fr.Nk_ij(m1,m2,root) else [] + else: + m1, m2 = top_row[:2] + return [tuple([l,*b]) for l in fr.basis() for b in _get_trees(fr,[l]+top_row[2:],root) if fr.Nk_ij(m1,m2,l)] + comp_basis = list() for top in product((a*a).monomials(),repeat=n_strands//2): #If the n_strands is odd, we must extend the top row by a fusing anyon top_row = list(top)+[a]*(n_strands%2) - comp_basis.extend(tuple([*top,*levels]) for levels in self.get_trees(top_row,b)) + comp_basis.extend(tuple([*top,*levels]) for levels in _get_trees(self,top_row,b)) return comp_basis @lazy_attribute def fmats(self): """ Construct an FMatrix factory to solve the pentagon relations and - organize the resulting F-symbols. We only need this attribute to compute - braid group representations. + organize the resulting F-symbols. + + We only need this attribute to compute braid group representations. + + EXAMPLES:: + + sage: A15 = FusionRing("A1",5) + sage: A15.fmats + F-Matrix factory for The Fusion Ring of Type A1 and level 5 with Integer Ring coefficients """ return FMatrix.FMatrix(self) @@ -1120,7 +1205,7 @@ def _emap(self,mapper,input_args,worker_pool=None): ``fast_parallel_fusion_ring_braid_repn`` module. - ``input_args`` -- a tuple of arguments to be passed to mapper - NOTES: + .. NOTE:: If ``worker_pool`` is not provided, function maps and reduces on a single process. @@ -1129,6 +1214,19 @@ def _emap(self,mapper,input_args,worker_pool=None): input iterable. If it can't determine the length of the input iterable then it uses multiprocessing with the default chunksize of `1` if chunksize is not explicitly provided. + + EXAMPLES:: + + sage: FR = FusionRing("A1",4) + sage: FR.fusion_labels(['idd','one','two','three','four'],inject_variables=True) + sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time + sage: len(FR._emap('sig_2k',(1,one,one,5))) # long time + 13 + sage: FR = FusionRing("A1",2) + sage: FR.fusion_labels("a",inject_variables=True) + sage: FR.fmats.find_orthogonal_solution(verbose=False) + sage: len(FR._emap('odd_one_out',(a1,a1,7))) + 16 """ n_proc = worker_pool._processes if worker_pool is not None else 1 input_iter = [(child_id, n_proc, input_args) for child_id in range(n_proc)] @@ -1147,7 +1245,6 @@ def _emap(self,mapper,input_args,worker_pool=None): results = list() for worker_results in worker_pool.imap_unordered(collect_results,range(worker_pool._processes),chunksize=1): results.extend(worker_results) - # results = list(results) return results def get_braid_generators(self, @@ -1198,7 +1295,7 @@ def get_braid_generators(self, ``comp_basis`` is a list of basis elements of the braid group module, parametrized by a list of fusion ring elements describing a fusion tree. For example with 5 strands the fusion tree - is as follows. See :meth:`get_computational_basis` and :meth:`get_trees` + is as follows. See :meth:`get_computational_basis` for more information. .. image:: ../../../media/fusiontree.png @@ -1220,10 +1317,10 @@ def get_braid_generators(self, [one, two, three, four, five] sage: two ** 5 5*two + 4*four - sage: comp_basis, sig = A14.get_braid_generators(two,two,5,verbose=False) - sage: A14.gens_satisfy_braid_gp_rels(sig) + sage: comp_basis, sig = A14.get_braid_generators(two,two,5,verbose=False) # long time + sage: A14.gens_satisfy_braid_gp_rels(sig) # long time True - sage: len(comp_basis) == 5 + sage: len(comp_basis) == 5 # long time True """ @@ -1406,9 +1503,9 @@ def ribbon(self,base_coercion=True): sage: F = FusionRing("A1",3) sage: [x.twist() for x in F.basis()] [0, 3/10, 4/5, 3/2] - sage: [x.ribbon() for x in F.basis()] + sage: [x.ribbon(base_coercion=False) for x in F.basis()] [1, zeta40^6, zeta40^12 - zeta40^8 + zeta40^4 - 1, -zeta40^10] - sage: [F.root_of_unity(x) for x in [0, 3/10, 4/5, 3/2]] + sage: [F.root_of_unity(x,base_coercion=False) for x in [0, 3/10, 4/5, 3/2]] [1, zeta40^6, zeta40^12 - zeta40^8 + zeta40^4 - 1, -zeta40^10] """ ret = self.parent().root_of_unity(self.twist(),base_coercion=False) diff --git a/src/sage/combinat/root_system/poly_tup_engine.pxd b/src/sage/combinat/root_system/poly_tup_engine.pxd index 5eb7e11ad18..62f7401e7b0 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pxd +++ b/src/sage/combinat/root_system/poly_tup_engine.pxd @@ -1,24 +1,21 @@ -from sage.rings.polynomial.polydict cimport ETuple -from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular, MPolynomialRing_libsingular from sage.rings.number_field.number_field_element cimport NumberFieldElement_absolute +from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular, MPolynomialRing_libsingular +from sage.rings.polynomial.polydict cimport ETuple cpdef tuple poly_to_tup(MPolynomial_libsingular poly) -cpdef MPolynomial_libsingular tup_to_poly(tuple eq_tup, MPolynomialRing_libsingular parent) +cpdef MPolynomial_libsingular _tup_to_poly(tuple eq_tup, MPolynomialRing_libsingular parent) cpdef tuple resize(tuple eq_tup, dict idx_map, int nvars) cpdef ETuple get_variables_degrees(list eqns) cpdef list variables(tuple eq_tup) cpdef constant_coeff(tuple eq_tup) cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map) cpdef bint tup_fixes_sq(tuple eq_tup) -cpdef dict subs_squares(dict eq_dict, dict known_sq) +cdef dict subs_squares(dict eq_dict, dict known_sq) cpdef dict compute_known_powers(ETuple max_deg, dict val_dict, one) -cpdef dict subs(tuple poly_tup, dict known_powers, one) -# cpdef int poly_tup_cmp(tuple tleft, tuple tright) -cpdef tuple poly_tup_sortkey_degrevlex(tuple eq_tup) - -cpdef MPolynomial_libsingular _tup_to_poly(tuple eq_tup, MPolynomialRing_libsingular parent) -cpdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq, NumberFieldElement_absolute one) - - +cdef dict subs(tuple poly_tup, dict known_powers, one) +cpdef tup_to_univ_poly(tuple eq_tup, univ_poly_ring) +cpdef tuple poly_tup_sortkey(tuple eq_tup) +cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq, NumberFieldElement_absolute one) cdef tuple _flatten_coeffs(tuple eq_tup) cpdef tuple _unflatten_coeffs(field, tuple eq_tup) +cdef int has_appropriate_linear_term(tuple eq_tup) diff --git a/src/sage/combinat/root_system/poly_tup_engine.pyx b/src/sage/combinat/root_system/poly_tup_engine.pyx index fe4b6bf7799..2c7b034db1d 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pyx +++ b/src/sage/combinat/root_system/poly_tup_engine.pyx @@ -8,19 +8,11 @@ Arithmetic Engine for polynomials as tuples # https://www.gnu.org/licenses/ # **************************************************************************** -from functools import cmp_to_key -from sage.rings.polynomial.term_order import TermOrder - -# from sage.arith.functions cimport LCM_list - -#Pre-compute common values for speed -degrevlex_sortkey = TermOrder().sortkey_degrevlex - ########### ### API ### ########### -cpdef tuple poly_to_tup(MPolynomial_libsingular poly): +cpdef inline tuple poly_to_tup(MPolynomial_libsingular poly): """ Convert a polynomial object into the internal representation as tuple of (ETuple exp, NumberFieldElement coeff) pairs @@ -36,55 +28,55 @@ cpdef tuple poly_to_tup(MPolynomial_libsingular poly): """ return tuple(poly.dict().items()) -cpdef MPolynomial_libsingular tup_to_poly(tuple eq_tup, MPolynomialRing_libsingular parent): - """ - Return a polynomial object from its tuple representation. Inverse of poly_to_tup. - poly_to_tup(tup_to_poly(eq_tup, ring)) == eq_tup && tup_to_poly(poly_to_tup(eq), eq.parent()) == eq +cpdef inline MPolynomial_libsingular _tup_to_poly(tuple eq_tup, MPolynomialRing_libsingular parent): + r""" + Return a polynomial object from its tuple of pairs representation. + + Inverse of :meth:`poly_to_tup`: + + ``poly_to_tup(tup_to_poly(eq_tup, ring)) == eq_tup`` and + + ``tup_to_poly(poly_to_tup(eq), eq.parent()) == eq``. + + .. NOTE:: - NOTE: - Assumes parent.ngens() == len(exp_tup) for exp_tup, c in eq_tup + Assumes ``parent.ngens() == len(exp_tup) for exp_tup, c in eq_tup``. + + This method is meant for internal use. + + .. WARNING:: + + Unsafe for client use, since it avoids implicit casting and + it may lead to segmentation faults. EXAMPLES:: - sage: from sage.combinat.root_system.poly_tup_engine import tup_to_poly - sage: R. = PolynomialRing(CyclotomicField(20)) - sage: poly_tup = (((2,0),1), ((0,0),1)) - sage: tup_to_poly(poly_tup, parent=R) + sage: from sage.combinat.root_system.poly_tup_engine import _tup_to_poly + sage: K = CyclotomicField(20) + sage: R. = PolynomialRing(K) + sage: poly_tup = (((2,0),K.one()), ((0,0),K.one())) + sage: _tup_to_poly(poly_tup, parent=R) x^2 + 1 sage: poly = x**2*y**4 - 4/5*x*y**2 + 1/3 * y sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup - sage: tup_to_poly(poly_to_tup(poly), parent=R) == poly + sage: _tup_to_poly(poly_to_tup(poly), parent=R) == poly True TESTS: - sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup, tup_to_poly + sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup, _tup_to_poly sage: R. = PolynomialRing(CyclotomicField(20)) sage: r = R.random_element() - sage: tup_to_poly(poly_to_tup(r), parent=R) == r + sage: _tup_to_poly(poly_to_tup(r), parent=R) == r True sage: R = PolynomialRing(QQ, 'fx', 100) sage: r = R.random_element() - sage: tup_to_poly(poly_to_tup(r), parent=R) == r + sage: _tup_to_poly(poly_to_tup(r), parent=R) == r True """ - # Maybe the following is faster but we need to ensure all coefficients are - # already in fmats._poly_ring.base_ring() so that implicit casting is avoided - # (this is pretty slow) - # return parent._element_constructor_(dict(eq_tup), check=False) - return parent(dict(eq_tup)) - -cpdef MPolynomial_libsingular _tup_to_poly(tuple eq_tup, MPolynomialRing_libsingular parent): - r""" - Faster version of :meth:`tup_to_poly`. Unsafe for client use, since it - avoids implicit casting and it may lead to segmentation faults. - - Safe for internal use because our methods ensure that - polynomial coefficients always lie in the same base ring. - """ return parent._element_constructor_(dict(eq_tup), check=False) -cdef tuple _flatten_coeffs(tuple eq_tup): +cdef inline tuple _flatten_coeffs(tuple eq_tup): """ Flatten cyclotomic coefficients to a representation as a tuple of rational coefficients. @@ -93,6 +85,8 @@ cdef tuple _flatten_coeffs(tuple eq_tup): with new PARI settings introduced in trac ticket #30537 """ cdef list flat = list() + cdef ETuple exp + cdef NumberFieldElement_absolute cyc_coeff for exp, cyc_coeff in eq_tup: flat.append((exp, tuple(cyc_coeff._coefficients()))) return tuple(flat) @@ -119,63 +113,104 @@ cpdef tuple _unflatten_coeffs(field, tuple eq_tup): True """ cdef list unflat = list() + cdef ETuple exp for exp, coeff_tup in eq_tup: unflat.append((exp, field(list(coeff_tup)))) return tuple(unflat) +################################# +### Useful private predicates ### +################################# + +cdef inline int has_appropriate_linear_term(tuple eq_tup): + """ + Determine whether the given tuple of pairs (of length 2) contains + an *appropriate* linear term. + + In this context, a linear term is said to be *appropriate* if the + it is in the largest variable in the given polynomial (w.r.t. + the degrevlex ordering), the monomial in which the linear term + appears is univariate, and the linear term is not a common factor in + the polynomial. + + OUTPUT: + + If the given polynomial contains an appropriate linear term, this method + returns the index of the monomial in which the term appears. + + Otherwise, the method returns -1. + """ + max_var = variables(eq_tup)[0] + cdef ETuple m + for i in range(2): + m = eq_tup[i][0] + if m._nonzero == 1 and m._data[1] == 1 and m._data[0] == max_var and eq_tup[(i+1) % 2][0][max_var] == 0: + return i + return -1 + ###################### ### "Change rings" ### ###################### -def tup_to_univ_poly(tuple eq_tup, gen): +cpdef inline tup_to_univ_poly(tuple eq_tup, univ_poly_ring): """ Given a tuple of pairs representing a univariate polynomial and a univariate - polynomial ring generator, return a univariate polynomial object. + polynomial ring, return a univariate polynomial object. - Each pair in the tuple is assumed to be of the form (ETuple, coeff), where - coeff is an element of gen.parent().base_ring(). + Each pair in the tuple is assumed to be of the form ``(ETuple, coeff)``, + where ``coeff`` is an element of ``univ_poly_ring.base_ring()``. EXAMPLES:: sage: from sage.combinat.root_system.poly_tup_engine import tup_to_univ_poly sage: from sage.rings.polynomial.polydict import ETuple - sage: poly_tup = ((ETuple([0,3,0]),2), (ETuple([0,1,0]),-1), (ETuple([0,0,0]),-2/3)) - sage: b = QQ['b'].gen() - sage: tup_to_univ_poly(poly_tup, b) + sage: K = CyclotomicField(56) + sage: poly_tup = ((ETuple([0,3,0]),K(2)), (ETuple([0,1,0]),K(-1)), (ETuple([0,0,0]),K(-2/3))) + sage: R = K['b'] + sage: tup_to_univ_poly(poly_tup, R) 2*b^3 - b - 2/3 - sage: poly_tup = ((ETuple([0, 0, 0]), -1/5),) - sage: tup_to_univ_poly(poly_tup, b) + + TESTS:: + + sage: poly_tup = ((ETuple([0, 0, 0]), K(-1/5)),) + sage: tup_to_univ_poly(poly_tup, R) -1/5 """ - univ_tup = tuple((exp.nonzero_values()[0] if exp.nonzero_values() else 0, c) for exp, c in eq_tup) - return sum(c * gen ** p for p, c in univ_tup) + cdef ETuple exp + cdef NumberFieldElement_absolute c + return univ_poly_ring({ exp._data[1] if exp._nonzero else 0 : c for exp, c in eq_tup }) -cpdef tuple resize(tuple eq_tup, dict idx_map, int nvars): +cpdef inline tuple resize(tuple eq_tup, dict idx_map, int nvars): """ - Return a tuple representing a polynomial in a ring with len(sorted_vars) generators - This method is used for creating polynomial objects with the "right number" of - variables for computing Groebner bases of the partitioned equations graph - and for adding constraints ensuring certain F-symbols are nonzero + Return a tuple representing a polynomial in a ring with + ``len(sorted_vars)`` generators. + + This method is used for creating polynomial objects with the + "right number" of variables for computing Groebner bases of the + partitioned equations graph and for adding constraints ensuring certain + F-symbols are nonzero. EXAMPLES:: sage: from sage.combinat.root_system.poly_tup_engine import resize sage: from sage.rings.polynomial.polydict import ETuple - sage: poly_tup = ((ETuple([0,3,0,2]),2), (ETuple([0,1,0,1]),-1), (ETuple([0,0,0,0]),-2/3)) + sage: K = CyclotomicField(56) + sage: poly_tup = ((ETuple([0,3,0,2]),K(2)), (ETuple([0,1,0,1]),K(-1)), (ETuple([0,0,0,0]),K(-2/3))) sage: idx_map = { 1 : 0, 3 : 1 } sage: resize(poly_tup,idx_map,2) (((3, 2), 2), ((1, 1), -1), ((0, 0), -2/3)) - sage: R = PolynomialRing(ZZ, 'fx', 20) + sage: R = PolynomialRing(K, 'fx', 20) sage: R.inject_variables() Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19 - sage: sparse_poly = fx0**2 * fx17 + fx3 - sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup, tup_to_poly - sage: S. = PolynomialRing(ZZ) - sage: tup_to_poly(resize(poly_to_tup(sparse_poly),{0:0,3:1,17:2},3), parent=S) + sage: sparse_poly = R(fx0**2 * fx17 + fx3) + sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup, _tup_to_poly + sage: S. = PolynomialRing(K) + sage: _tup_to_poly(resize(poly_to_tup(sparse_poly),{0:0,3:1,17:2},3), parent=S) x^2*z + y """ - cdef ETuple new_e + cdef ETuple exp, new_e + cdef NumberFieldElement_absolute c cdef list resized = list() for exp, c in eq_tup: new_e = ETuple({ idx_map[pos] : d for pos, d in exp.sparse_iter() }, nvars) @@ -186,7 +221,7 @@ cpdef tuple resize(tuple eq_tup, dict idx_map, int nvars): ### Convenience methods ### ########################### -cdef ETuple degrees(tuple poly_tup): +cdef inline ETuple degrees(tuple poly_tup): """ Return the maximal degree of each variable in the polynomial """ @@ -241,7 +276,8 @@ cpdef list variables(tuple eq_tup): cpdef constant_coeff(tuple eq_tup): """ - Return the constant coefficient of the polynomial represented by given tuple + Return the constant coefficient of the polynomial represented by + given tuple. EXAMPLES:: @@ -263,15 +299,15 @@ cpdef constant_coeff(tuple eq_tup): cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map): """ - Apply `coeff_map` to coefficients + Apply ``coeff_map`` to coefficients. EXAMPLES:: sage: from sage.combinat.root_system.poly_tup_engine import apply_coeff_map sage: sq = lambda x : x**2 - sage: R. = PolynomialRing(QQ) - sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup, tup_to_poly - sage: tup_to_poly(apply_coeff_map(poly_to_tup(x + 2*y + 3*z), sq), parent=R) + sage: R. = PolynomialRing(ZZ) + sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup, _tup_to_poly + sage: _tup_to_poly(apply_coeff_map(poly_to_tup(x + 2*y + 3*z), sq), parent=R) x + 4*y + 9*z """ cdef ETuple exp @@ -280,10 +316,12 @@ cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map): new_tup.append((exp, coeff_map(coeff))) return tuple(new_tup) -cpdef bint tup_fixes_sq(tuple eq_tup): - """ - Determine if given equation fixes the square of a variable. An equation - fixes the sq of a variable if it is of the form `a*x^2 + c` for *nonzero* constants `a`, `c` +cpdef inline bint tup_fixes_sq(tuple eq_tup): + r""" + Determine if given equation fixes the square of a variable. + + An equation fixes the sq of a variable if it is of the form `a*x^2 + c` + for *nonzero* constants `a`, `c`. EXAMPLES:: @@ -301,27 +339,38 @@ cpdef bint tup_fixes_sq(tuple eq_tup): False """ #Make this faster by combining two conditions into one... don't create temp variables - return len(eq_tup) == 2 and len(variables(eq_tup)) == 1 and eq_tup[0][0].nonzero_values() == [2] + # return len(eq_tup) == 2 and len(variables(eq_tup)) == 1 and eq_tup[0][0].nonzero_values() == [2] + # return len(eq_tup) == 2 and eq_tup[0][0].emax(eq_tup[1][0]).nonzero_values() == [2] + if len(eq_tup) != 2: + return False + #In order to access _attributes, we must cdef ETuple + cdef ETuple lm = eq_tup[0][0] + if lm._nonzero != 1 or lm._data[1] != 2: + return False + cdef ETuple tm = eq_tup[1][0] + if tm._nonzero != 0: + return False + return True ###################### ### Simplification ### ###################### -cpdef dict subs_squares(dict eq_dict, dict known_sq): - """ - Substitutes for known squares in a given polynomial. - The parameter known_sq is a dictionary of (int i, NumberFieldElement a) pairs such that x_i^2 - a == 0 - Returns a dictionary of (ETuple, coeff) pairs representing polynomial +cdef dict subs_squares(dict eq_dict, dict known_sq): + r""" + Substitute for known squares into a given polynomial. - EXAMPLES:: + INPUT: - sage: from sage.combinat.root_system.poly_tup_engine import subs_squares - sage: R. = PolynomialRing(QQ) - sage: poly = x**2 + y**3 + x*z**3 - sage: known_sq = { 0 : 2, 1 : -1, 2 : -1/2 } - sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup - sage: subs_squares(dict(poly_to_tup(poly)), known_sq) - {(0, 0, 0): 2, (0, 1, 0): -1, (1, 0, 1): -1/2} + - ``eq_dict`` -- a dictionary of ``(ETuple, coeff)`` pairs representing + a polynomial. + + - ``known_sq`` -- a dictionary of ``(int i, NumberFieldElement a)`` pairs + such that `x_i^2 - a = 0`. + + OUTPUT: + + Returns a dictionary of ``(ETuple, coeff)`` pairs representing polynomial """ cdef dict subbed, new_e cdef ETuple exp, lm @@ -346,13 +395,17 @@ cpdef dict subs_squares(dict eq_dict, dict known_sq): return subbed cdef dict remove_gcf(dict eq_dict, ETuple nonz): - """ - Returns a dictionary of (ETuple, coeff) pairs describing the polynomial eq / GCF(eq) - The input nonz is an ETuple indicating the positions of variables known to be nonzero. - The entries of nonz are assumed to be some relatively large number, like 100 + r""" + Returns a dictionary of ``(ETuple, coeff)`` pairs describing the + polynomial ``eq / GCF(eq)``. + + The input ``nonz`` is an ``ETuple`` indicating the positions of + variables known to be nonzero. The entries of ``nonz`` are assumed to + be some relatively large number, like 100. """ #Find common variables, filtered according to known nonzeros cdef ETuple common_powers, exp + cdef NumberFieldElement_absolute c common_powers = nonz for exp, c in eq_dict.items(): common_powers = common_powers.emin(exp) @@ -363,26 +416,29 @@ cdef dict remove_gcf(dict eq_dict, ETuple nonz): cdef tuple to_monic(dict eq_dict, one): """ - Return tuple of pairs (ETuple, coeff) describing the monic polynomial associated to eq_dict - Here, the leading coefficient is chosen according to the degree reverse lexicographic ordering - (default for multivariate polynomial rings) + Return tuple of pairs ``(ETuple, coeff)`` describing the monic polynomial + associated to ``eq_dict``. + + Here, the leading coefficient is chosen according to the degree reverse + lexicographic ordering (default for multivariate polynomial rings). """ if not eq_dict: return tuple() - cdef list ord_monoms = sorted(eq_dict, key=degrevlex_sortkey) + cdef list ord_monoms = sorted(eq_dict, key=monom_sortkey) cdef ETuple lm = ord_monoms[-1] - lc = eq_dict[lm] + cdef NumberFieldElement_absolute lc = eq_dict[lm] if not lc: return tuple() cdef list ret = [(lm, one)] inv_lc = lc.inverse_of_unit() - cdef ETuple exp - for exp in reversed(ord_monoms[:-1]): - ret.append((exp, inv_lc * eq_dict[exp])) + cdef int i, n + n = len(ord_monoms) + for i in range(n-1): + ret.append((ord_monoms[n-2-i], inv_lc * eq_dict[ord_monoms[n-2-i]])) return tuple(ret) -cpdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq, NumberFieldElement_absolute one): +cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq, NumberFieldElement_absolute one): """ - Return a dictionary describing a monic polynomial with no known nonzero gcd and - no known squares + Return a tuple describing a monic polynomial with no known nonzero + gcf and no known squares. """ if not eq_dict: return tuple() cdef dict sq_rmvd = subs_squares(eq_dict, known_sq) @@ -395,14 +451,17 @@ cpdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq, NumberFie cpdef dict compute_known_powers(ETuple max_deg, dict val_dict, one): """ - Pre-compute powers of known values for efficiency when preparing to substitute - into a list of polynomials. + Pre-compute powers of known values for efficiency when preparing to + substitute into a list of polynomials. INPUTS: - max_deg is an ETuple indicating the maximal degree of each variable - val_dict is a dictionary of (var_idx, poly_tup) key-value pairs. poly_tup - is a tuple of (ETuple, coeff) pairs reperesenting a multivariate polynomial + - ``max_deg`` -- an ``ETuple`` indicating the maximal degree of + each variable. + - ``val_dict`` -- a dictionary of ``(var_idx, poly_tup)`` key-value + pairs. + - ``poly_tup`` -- a tuple of (ETuple, coeff) pairs reperesenting a + multivariate polynomial. EXAMPLES:: @@ -436,25 +495,9 @@ cpdef dict compute_known_powers(ETuple max_deg, dict val_dict, one): known_powers[var_idx][power+1] = tup_mul(known_powers[var_idx][power],val_dict[var_idx]) return known_powers -cpdef dict subs(tuple poly_tup, dict known_powers, one): +cdef dict subs(tuple poly_tup, dict known_powers, one): """ Substitute given variables into a polynomial tuple - - EXAMPLES:: - - sage: from sage.combinat.root_system.poly_tup_engine import subs - sage: R. = PolynomialRing(QQ) - sage: polys = [x**3 + 1, x**2*y + z**3, y**2 - 3*y] - sage: from sage.combinat.root_system.poly_tup_engine import compute_known_powers, get_variables_degrees, poly_to_tup - sage: known_val = { 0 : poly_to_tup(R(-1)), 2 : poly_to_tup(y**2) } - sage: max_deg = get_variables_degrees([poly_to_tup(p) for p in polys]) - sage: poly_tup = poly_to_tup(polys[0]) - sage: one = R.base_ring().one() - sage: subs(poly_tup, compute_known_powers(max_deg, known_val, one), one) - {(0, 0, 0): 0} - sage: poly_tup = poly_to_tup(polys[1]) - sage: subs(poly_tup, compute_known_powers(max_deg, known_val, one), one) - {(0, 1, 0): 1, (0, 6, 0): 1} """ cdef dict subbed = {} cdef ETuple exp, m, shifted_exp @@ -494,106 +537,68 @@ cdef tuple tup_mul(tuple p1, tuple p2): ### Sorting ### ############### -#Implement richcmp comparator object that can be passed in as key to sorted method +cdef tuple monom_sortkey(ETuple exp): + """ + Produce a sortkey for a monomial exponent w.r.t. degree reversed + lexicographic ordering. + """ + cdef int deg = exp.unweighted_degree() + # for i in range(exp._nonzero): + # exp._data[2*i+1] = -exp._data[2*i+1] + cdef ETuple rev = exp.reversed().emul(-1) + return (deg, rev) -# cpdef int poly_tup_cmp(tuple tleft, tuple tright): -# """ -# Determine which polynomial is larger with respect to the degrevlex ordering -# -# EXAMPLES:: -# -# sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_cmp -# sage: R. = PolynomialRing(QQ) -# sage: p1 = x*y*z - x**2 + 3/2 -# sage: p2 = x*y*z - x * y +1/2 -# sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup -# sage: (p1 < p2) == (poly_tup_cmp(poly_to_tup(p1), poly_to_tup(p2)) < 0) -# True -# sage: R. = PolynomialRing(CyclotomicField(20)) -# sage: zeta20 = R.base_ring().gen() -# sage: p1 = zeta20**2 * x*z**2 - 2*zeta20 -# sage: p2 = y**3 + 1/4 -# sage: (p1 < p2) == (poly_tup_cmp(poly_to_tup(p1), poly_to_tup(p2)) < 0) -# True -# -# TESTS: -# -# sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_cmp, poly_to_tup -# sage: R. = PolynomialRing(CyclotomicField(20)) -# sage: p1 = R.random_element() -# sage: p2 = R.random_element() -# sage: (p1 < p2) == (poly_tup_cmp(poly_to_tup(p1), poly_to_tup(p2)) < 0) -# True -# sage: (p1 > p2) == (poly_tup_cmp(poly_to_tup(p1), poly_to_tup(p2)) > 0) -# True -# sage: poly_tup_cmp(poly_to_tup(p1), poly_to_tup(p1)) == 0 -# True -# """ -# cdef int i, ret, sf, sg, val -# cdef ETuple f, g -# ret = 0 -# for i in range(min(len(tleft),len(tright))): -# f, g = tleft[i][0], tright[i][0] -# if f == g: -# if tleft[i][1] != tright[i][1]: -# ret = -1 + 2*(tleft[i][1] > tright[i][1]) -# else: -# sf, sg = 0, 0 -# for val in f.nonzero_values(sort=False): -# sf += val -# for val in g.nonzero_values(sort=False): -# sg += val -# ret = -1 + 2*(sf > sg or ( sf == sg and f.reversed() < g.reversed() )) -# if ret != 0: -# return ret -# return len(tleft) - len(tright) - -import numpy as np -cpdef tuple poly_tup_sortkey_degrevlex(tuple eq_tup): - """ - Return the sortkey of a polynomial represented as a tuple of (ETuple, coeff) - pairs with respect to the degree reversed lexicographical term order. +cpdef tuple poly_tup_sortkey(tuple eq_tup): + """ + Return the sortkey of a polynomial represented as a tuple of + ``(ETuple, coeff)`` pairs with respect to the degree + lexicographical term order. Using this key to sort polynomial tuples results in comparing polynomials term by term (we assume the tuple representation is sorted so that the - leading term with respect to the degree reverse lexicographical order comes - first). For each term, we first compare degrees, then the monomials themselves, - then the corresponding coefficient. + leading term with respect to the degree reverse lexicographical order + comes first). For each term, we first compare degrees, then the monomials + themselves. + + EXAMPLES:: - This method relies on the built-in comparison of ETuple's. + sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey + sage: R. = PolynomialRing(QQ, order='deglex') + sage: p1 = x*y*z - x**2 + 3/2 + sage: p2 = x*y*z - x*y + 1/2 + sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: (p1 < p2) == (poly_tup_sortkey(poly_to_tup(p1)) < poly_tup_sortkey(poly_to_tup(p2))) + True + sage: R. = PolynomialRing(CyclotomicField(20), order='deglex') + sage: zeta20 = R.base_ring().gen() + sage: p1 = zeta20**2 * x*z**2 - 2*zeta20 + sage: p2 = y**3 + 1/4 + sage: (p1 < p2) == (poly_tup_sortkey(poly_to_tup(p1)) < poly_tup_sortkey(poly_to_tup(p2))) + True - EXAMPLES :: + TESTS: + sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey, poly_to_tup + sage: R. = PolynomialRing(CyclotomicField(20)) + sage: p1 = R.random_element() + sage: p2 = R.random_element() + sage: (p1 < p2) == (poly_tup_sortkey(poly_to_tup(p1)) < poly_tup_sortkey(poly_to_tup(p2))) + True + sage: (p1 > p2) == (poly_tup_sortkey(poly_to_tup(p1)) > poly_tup_sortkey(poly_to_tup(p2))) + True + sage: poly_tup_sortkey(poly_to_tup(p1)) == poly_tup_sortkey(poly_to_tup(p1)) + True """ cdef ETuple exp cdef int i, l, nnz - key = list() - # for exp, c in eq_tup: - # key.extend([sum(exp.nonzero_values(sort=False)),exp.reversed().emul(-1),c._coefficients()]) - # for exp, c in eq_tup: - # #Compare by term degree - # key.append(exp.unweighted_degree()) - # revlex_exp = exp.reversed().emul(-1) - # #Then by term w.r.t. revlex order - # revlex_exp_key = list() - # for i from 0 <= i < exp._nonzero: - # #Reverse tuple and negate values - # # key.append(-(exp._length - exp._data[2*(exp._nonzero-i-1)] - 1)) - # # key.append(-exp._data[2*(exp._nonzero-i-1)+1]) - # key.append(-exp._data[2*i]) - # key.append(exp._data[2*i+1]) - # #Finally by coefficient - # key.extend(c._coefficients()) + cdef list key = list() for exp, c in eq_tup: #Compare by term degree key.append(exp.unweighted_degree()) - #Next compare by term w.r.t. revlex order - l = exp._length - nnz = exp._nonzero - for i from 0 <= i < nnz: - # key.append(l-1-exp._data[2*(nnz-i-1)]) + #Next compare by term w.r.t. lex order + for i in range(exp._nonzero): + # key.append(exp._length-1-exp._data[2*(nnz-i-1)]) # key.append(-exp._data[2*(nnz-i-1)+1]) - # Try sorting in lex order instead key.append(-exp._data[2*i]) key.append(exp._data[2*i+1]) return tuple(key) From 7c4ecb21a043bf6ccaefe692e9aed876bf85fe69 Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Tue, 6 Apr 2021 20:22:12 -0700 Subject: [PATCH 036/632] make tests in fast_parallel_fusion_ring_braid_repn.pyx long to avoid excessive test time. --- .../fast_parallel_fusion_ring_braid_repn.pyx | 86 +++++++++---------- 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx index f6e8b40f1a7..c02f49588d0 100644 --- a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx @@ -36,14 +36,14 @@ cpdef mid_sig_ij(fusion_ring,row,col,a,b): EXAMPLES:: sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import mid_sig_ij - sage: FR = FusionRing("A1",4) - sage: FR.fusion_labels(['idd','one','two','three','four'],inject_variables=True) - sage: one.weight() + sage: FR = FusionRing("A1",4) # long time + sage: FR.fusion_labels(['idd','one','two','three','four'],inject_variables=True) # long time + sage: one.weight() # long time (1/2, -1/2) - sage: FR.get_computational_basis(one,two,4) + sage: FR.get_computational_basis(one,two,4) # long time [(two, two), (two, idd), (idd, two)] - sage: FR.fmats.find_orthogonal_solution(verbose=False) - sage: mid_sig_ij(FR, (two, two), (two, idd), one, two) + sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time (~7s) + sage: mid_sig_ij(FR, (two, two), (two, idd), one, two) # long time 1/3*zeta48^10 - 2/3*zeta48^2 This method works for all possible types of fields returned by @@ -51,19 +51,19 @@ cpdef mid_sig_ij(fusion_ring,row,col,a,b): TESTS:: - sage: FR = FusionRing("A1",3) - sage: FR.fusion_labels("a",inject_variables=True) - sage: FR.fmats.find_orthogonal_solution(verbose=False) - sage: _, _, to_opt = FR.fmats.field().optimized_representation() - sage: a2**4 + sage: FR = FusionRing("A1",3) # long time + sage: FR.fusion_labels("a",inject_variables=True) # long time + sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time + sage: _, _, to_opt = FR.fmats.field().optimized_representation() # long time + sage: a2**4 # long time 2*a0 + 3*a2 - sage: FR.get_computational_basis(a2,a2,4) + sage: FR.get_computational_basis(a2,a2,4) # long time [(a2, a2), (a2, a0), (a0, a2)] - sage: to_opt(mid_sig_ij(FR,(a2, a0),(a2, a2),a2,a2)) + sage: to_opt(mid_sig_ij(FR,(a2, a0),(a2, a2),a2,a2)) # long time -2024728666370660589/10956322398441542221*a1^30 - 34142146914395596291/21912644796883084442*a1^28 - 21479437628091413631/21912644796883084442*a1^26 + 260131910217202103829/21912644796883084442*a1^24 + 69575612911670713183/10956322398441542221*a1^22 + 25621808994337724689/1992058617898462222*a1^20 - 1975139725303994650417/21912644796883084442*a1^18 - 1315664901396537703585/21912644796883084442*a1^16 - 2421451803369354765026/10956322398441542221*a1^14 - 5963323855935165859057/21912644796883084442*a1^12 - 4477124943233705460859/21912644796883084442*a1^10 - 2001454824483021618178/10956322398441542221*a1^8 - 2120319455379289595185/21912644796883084442*a1^6 - 15722612944437234961/755608441271830498*a1^4 - 39862668562651453480/10956322398441542221*a1^2 - 6967145776903524195/10956322398441542221 - sage: FR = FusionRing("G2",2) - sage: FR.fusion_labels("g",inject_variables=True) - sage: FR.get_computational_basis(g1,g2,4) + sage: FR = FusionRing("G2",2) # long time + sage: FR.fusion_labels("g",inject_variables=True) # long time + sage: FR.get_computational_basis(g1,g2,4) # long time [(g3, g2), (g3, g1), (g2, g3), (g2, g0), (g1, g3), (g1, g1), (g0, g2)] sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time (~11 s) sage: mid_sig_ij(FR,(g2, g3),(g1, g1),g1,g2) # long time @@ -101,16 +101,16 @@ cpdef odd_one_out_ij(fusion_ring,xi,xj,a,b): EXAMPLES:: sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import odd_one_out_ij - sage: FR = FusionRing("A1",4) - sage: FR.fusion_labels(["one","two","three","four","five"],inject_variables=True) - sage: FR.get_computational_basis(two,two,5) + sage: FR = FusionRing("A1",4) # long time + sage: FR.fusion_labels(["one","two","three","four","five"],inject_variables=True) # long time + sage: FR.get_computational_basis(two,two,5) # long time [(three, three, one), (three, three, three), (three, one, three), (one, three, three), (one, one, one)] - sage: FR.fmats.find_orthogonal_solution(verbose=False) - sage: odd_one_out_ij(FR,one,three,two,two) + sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time + sage: odd_one_out_ij(FR,one,three,two,two) # long time 2/3*zeta48^12 - 1/3*zeta48^8 - 1/3*zeta48^4 - 1/3 This method works for all possible types of fields returned by @@ -118,18 +118,18 @@ cpdef odd_one_out_ij(fusion_ring,xi,xj,a,b): TESTS:: - sage: FR = FusionRing("A1",3) - sage: FR.fusion_labels("a",inject_variables=True) - sage: FR.fmats.find_orthogonal_solution(verbose=False) - sage: _, _, to_opt = FR.fmats.field().optimized_representation() - sage: a2**3 + sage: FR = FusionRing("A1",3) # long time + sage: FR.fusion_labels("a",inject_variables=True) # long time + sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time + sage: _, _, to_opt = FR.fmats.field().optimized_representation() # long time + sage: a2**3 # long time a0 + 2*a2 - sage: FR.get_computational_basis(a2,a2,3) + sage: FR.get_computational_basis(a2,a2,3) # long time [(a2,), (a0,)] - sage: to_opt(odd_one_out_ij(FR,a0,a2,a2,a2)) + sage: to_opt(odd_one_out_ij(FR,a0,a2,a2,a2)) # long time 6341990144855406911/21912644796883084442*a1^30 + 47313529044493641571/21912644796883084442*a1^28 - 6964289120109414595/10956322398441542221*a1^26 - 406719371329322780627/21912644796883084442*a1^24 + 87598732372849355687/10956322398441542221*a1^22 - 456724726845194775/19723352652460022*a1^20 + 3585892725441116840515/21912644796883084442*a1^18 - 645866255979227573282/10956322398441542221*a1^16 + 7958479159087829772639/21912644796883084442*a1^14 + 789748976956837633826/10956322398441542221*a1^12 + 3409710648897945752185/21912644796883084442*a1^10 + 903956381582048110980/10956322398441542221*a1^8 + 192973084151342020307/21912644796883084442*a1^6 - 9233312083438019435/755608441271830498*a1^4 + 667869266552877781/10956322398441542221*a1^2 + 17644302696056968099/21912644796883084442 - sage: FR = FusionRing("G2",2) - sage: FR.fusion_labels("g",inject_variables=True) + sage: FR = FusionRing("G2",2) # long time + sage: FR.fusion_labels("g",inject_variables=True) # long time sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time (~11 s) sage: odd_one_out_ij(FR,g1,g2,g1,g1) # long time -0.2636598866349343? + 0.4566723195695565?*I @@ -309,13 +309,13 @@ cpdef collect_results(proc): EXAMPLES:: sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import executor - sage: FR = FusionRing("A1",4) - sage: FR.fusion_labels(['idd','one','two','three','four'],inject_variables=True) - sage: FR.fmats.find_orthogonal_solution(verbose=False) - sage: params = (('sig_2k',id(FR)),(0,1,(2,one,one,9))) - sage: executor(params) + sage: FR = FusionRing("A1",4) # long time + sage: FR.fusion_labels(['idd','one','two','three','four'],inject_variables=True) # long time + sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time + sage: params = (('sig_2k',id(FR)),(0,1,(2,one,one,9))) # long time + sage: executor(params) # long time sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import collect_results - sage: len(collect_results(0)) == 171 + sage: len(collect_results(0)) == 171 # long time True """ #Discard the zero polynomial @@ -354,17 +354,17 @@ cpdef executor(params): sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import executor sage: FR = FusionRing("A1",4) sage: FR.fusion_labels(['idd','one','two','three','four'],inject_variables=True) - sage: FR.fmats.find_orthogonal_solution(verbose=False) - sage: params = (('sig_2k',id(FR)),(0,1,(1,one,one,5))) - sage: executor(params) + sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time + sage: params = (('sig_2k',id(FR)),(0,1,(1,one,one,5))) # long time + sage: executor(params) # long time sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import collect_results - sage: len(collect_results(0)) == 13 + sage: len(collect_results(0)) == 13 # long time True sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import executor, collect_results - sage: FR = FusionRing("B2",2) - sage: FR.fusion_labels(['I0','Y1','X','Z','Xp','Y2'],inject_variables=True) + sage: FR = FusionRing("B2",2) # long time + sage: FR.fusion_labels(['I0','Y1','X','Z','Xp','Y2'],inject_variables=True) # long time sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time (~23 s) - sage: params = (('odd_one_out',id(FR)),(0,1,(X,Xp,5))) + sage: params = (('odd_one_out',id(FR)),(0,1,(X,Xp,5))) # long time sage: executor(params) # long time sage: len(collect_results(0)) == 54 # long time True From da6c2021b4305196e595c76eed8fdd453d2e21f9 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Wed, 7 Apr 2021 13:42:54 -0400 Subject: [PATCH 037/632] shorter doctests in f_matrix.py --- src/sage/combinat/root_system/f_matrix.py | 69 +++++++++---------- .../fast_parallel_fmats_methods.pyx | 2 +- .../fast_parallel_fusion_ring_braid_repn.pyx | 2 +- src/sage/combinat/root_system/fusion_ring.py | 31 --------- .../combinat/root_system/poly_tup_engine.pyx | 4 +- 5 files changed, 38 insertions(+), 70 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index a7d276c2a0d..48872ce5480 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -44,7 +44,6 @@ from sage.rings.polynomial.all import PolynomialRing from sage.rings.polynomial.polydict import ETuple from sage.rings.qqbar import AA, QQbar, number_field_elements_from_algebraics -from sage.rings.real_double import RDF class FMatrix(): def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variables=False): @@ -362,7 +361,7 @@ def _reset_solver_state(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("F4",1)) + sage: f = FMatrix(FusionRing("G2",1)) sage: K = f.field() sage: len(f._nnz.nonzero_positions()) 1 @@ -542,15 +541,15 @@ def field(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("F4",1,conjugate=True)) + sage: f = FMatrix(FusionRing("G2",1)) sage: f.field() - Cyclotomic Field of order 80 and degree 32 + Cyclotomic Field of order 60 and degree 16 sage: f.find_orthogonal_solution(verbose=False) sage: f.field() - Number Field in a with defining polynomial y^64 - 16*y^62 + 104*y^60 - 320*y^58 + 258*y^56 + 1048*y^54 - 2864*y^52 - 3400*y^50 + 47907*y^48 - 157616*y^46 + 301620*y^44 - 322648*y^42 + 2666560*y^40 + 498040*y^38 + 54355076*y^36 - 91585712*y^34 + 592062753*y^32 - 1153363592*y^30 + 3018582788*y^28 - 4848467552*y^26 + 7401027796*y^24 - 8333924904*y^22 + 8436104244*y^20 - 7023494736*y^18 + 4920630467*y^16 - 2712058560*y^14 + 1352566244*y^12 - 483424648*y^10 + 101995598*y^8 - 12532920*y^6 + 1061168*y^4 - 57864*y^2 + 1681 + Number Field in a with defining polynomial y^32 - 6*y^30 - 7*y^28 + 62*y^26 - 52*y^24 - 308*y^22 + 831*y^20 + 7496*y^18 + 18003*y^16 - 2252*y^14 + 42259*y^12 - 65036*y^10 + 29368*y^8 - 3894*y^6 + 377*y^4 - 22*y^2 + 1 sage: phi = f.get_qqbar_embedding() sage: [phi(r).n() for r in f.get_non_cyclotomic_roots()] - [-0.786151377757423 + 1.73579267033929e-59*I] + [-0.786151377757423 - 8.92806368517581e-31*I] .. NOTE:: @@ -750,12 +749,13 @@ def get_non_cyclotomic_roots(self): True sage: f.get_non_cyclotomic_roots() [] - sage: f = FMatrix(FusionRing("E7",2)) # long time - sage: f.find_orthogonal_solution(verbose=False) # long time - sage: f.field() == f.FR().field() # long time + sage: f = FMatrix(FusionRing("F4",1)) + sage: f.find_orthogonal_solution(verbose=False) + sage: f.field() == f.FR().field() False - sage: f.get_non_cyclotomic_roots() # long time - [-0.7861513777574233?, -0.5558929702514212?] + sage: phi = f.get_qqbar_embedding() + sage: [phi(r).n() for r in f.get_non_cyclotomic_roots()] + [-0.786151377757423 + 1.73579267033929e-59*I] When ``self.field()`` is a ``NumberField``, one may use :meth:`get_qqbar_embedding` to embed the resulting values into @@ -881,8 +881,8 @@ def get_radical_expression(self): sage: f = FMatrix(FusionRing("G2",1)) sage: f.FR().fusion_labels("g", inject_variables=True) sage: f.find_orthogonal_solution(verbose=False) - sage: radical_fvars = f.get_radical_expression() - sage: radical_fvars[g1, g1, g1, g1, g1, g0] + sage: radical_fvars = f.get_radical_expression() # long time (~1.5s) + sage: radical_fvars[g1, g1, g1, g1, g1, g0] # long time -sqrt(1/2*sqrt(5) - 1/2) """ return {sextuple : val.radical_expression() for sextuple, val in self.get_fvars_in_alg_field().items()} @@ -1202,7 +1202,6 @@ def _checkpoint(self,do_chkpt,status,verbose=True): return filename = "fmatrix_solver_checkpoint_" + self.get_fr_str() + ".pickle" with open(filename, 'wb') as f: - # pickle.dump([self._fvars, self._solved, eqns, status], f) pickle.dump([self._fvars, self._solved, self._ks, self.ideal_basis, status], f) if verbose: print(f"Checkpoint {status} reached!") @@ -1214,7 +1213,7 @@ def _restore_state(self,filename): EXAMPLES:: - sage: f = FMatrix(FusionRing("A1",3)) + sage: f = FMatrix(FusionRing("A1",2)) sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) sage: f.get_defining_equations('hexagons',output=False) @@ -1231,8 +1230,8 @@ def _restore_state(self,filename): sage: f._checkpoint(do_chkpt=True,status=2) Checkpoint 2 reached! sage: del f - sage: f = FMatrix(FusionRing("A1",3)) - sage: f._restore_state("fmatrix_solver_checkpoint_A13.pickle") + sage: f = FMatrix(FusionRing("A1",2)) + sage: f._restore_state("fmatrix_solver_checkpoint_A12.pickle") sage: fvars == f._fvars True sage: ib == f.ideal_basis @@ -1243,7 +1242,7 @@ def _restore_state(self,filename): True sage: 2 == f._chkpt_status True - sage: os.remove("fmatrix_solver_checkpoint_A13.pickle") + sage: os.remove("fmatrix_solver_checkpoint_A12.pickle") TESTS:: @@ -1843,27 +1842,27 @@ def _get_explicit_solution(self,eqns=None,verbose=True): EXAMPLES:: - sage: f = FMatrix(FusionRing("A2",2)) # indirect doctest + sage: f = FMatrix(FusionRing("A1",4)) # indirect doctest sage: f.find_orthogonal_solution() # long time - Computing F-symbols for The Fusion Ring of Type A2 and level 2 with Integer Ring coefficients with 287 variables... - Set up 548 hex and orthogonality constraints... - Partitioned 548 equations into 57 components of size: - [24, 12, 12, 12, 12, 12, 12, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1] - Elimination epoch completed... 53 eqns remain in ideal basis + Computing F-symbols for The Fusion Ring of Type A1 and level 4 with Integer Ring coefficients with 238 variables... + Set up 460 hex and orthogonality constraints... + Partitioned 460 equations into 41 components of size: + [24, 12, 12, 12, 12, 12, 12, 12, 12, 9, 6, 6, 6, 6, 6, 6, 6, 6, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1] + Elimination epoch completed... 63 eqns remain in ideal basis Elimination epoch completed... 5 eqns remain in ideal basis Elimination epoch completed... 0 eqns remain in ideal basis - Hex elim step solved for 203 / 287 variables - Set up 994 reduced pentagons... - Elimination epoch completed... 699 eqns remain in ideal basis - Elimination epoch completed... 279 eqns remain in ideal basis - Elimination epoch completed... 9 eqns remain in ideal basis - Pent elim step solved for 270 / 287 variables - Partitioned 9 equations into 1 components of size: - [5] + Hex elim step solved for 173 / 238 variables + Set up 888 reduced pentagons... + Elimination epoch completed... 562 eqns remain in ideal basis + Elimination epoch completed... 209 eqns remain in ideal basis + Elimination epoch completed... 26 eqns remain in ideal basis + Elimination epoch completed... 8 eqns remain in ideal basis Elimination epoch completed... 0 eqns remain in ideal basis - Partitioned 16 equations into 16 components of size: - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] - Computing appropriate NumberField... + Pent elim step solved for 225 / 238 variables + Partitioned 0 equations into 0 components of size: + [] + Partitioned 13 equations into 13 components of size: + [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] """ if eqns is None: eqns = self.ideal_basis diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index 3030347ff8c..c2afc4b240c 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -495,7 +495,7 @@ cpdef executor(params): is the ``id`` of the :class:`FMatrix` object, and ``fn_args`` is a tuple containing all arguments to be passed to the function ``fn_name``. - .. NOTES:: + .. NOTE:: When the parent process is forked, each worker gets a copy of every global variable. The virtual memory address of object `X` in diff --git a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx index c02f49588d0..a40a5f136c1 100644 --- a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx @@ -341,7 +341,7 @@ cpdef executor(params): constructing a reference to the FMatrix object in the worker's memory adress space from its ``id``. - .. NOTES:: + .. NOTE:: When the parent process is forked, each worker gets a copy of every global variable. The virtual memory address of object `X` in diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 3a033f56d16..c402fd6b775 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -1079,37 +1079,6 @@ def is_multiplicity_free(self): ### Braid group representations ### ################################### - # def get_trees(self,top_row,root): - # """ - # Recursively enumerate all the admissible trees with given top row and root. - # - # INPUT: - # - # - ``top_row`` -- a list of basis elements of self - # - ``root`` -- a simple element of self - # - # Let `k` denote the length ``top_row``. This method returns - # Returns a list of tuples `(l_1,...,l_{k-2})` such that - # - # .. MATH:: - # - # \\begin{array}{l} - # root \\in l_{k-2} \otimes m_{k},\\ - # l_{k-2} \\in l_{k-3} \otimes m_{k-1},\\ - # \\cdots\\ - # l_2 \\in l_1\otimes m_3\\ - # l_1 \\in m_1\otimes m_2 - # \end{array} - # - # where `a \\to b\otimes c` means `N_{bc}^a\\neq 0`. - # """ - # if len(top_row) == 2: - # m1, m2 = top_row - # return [[]] if self.Nk_ij(m1,m2,root) else [] - # else: - # m1, m2 = top_row[:2] - # return [tuple([l,*b]) for l in self.basis() for b in self.get_trees([l]+top_row[2:],root) if self.Nk_ij(m1,m2,l)] - def get_computational_basis(self,a,b,n_strands): """ Return the so-called computational basis for `\\text{Hom}(b, a^n)`. diff --git a/src/sage/combinat/root_system/poly_tup_engine.pyx b/src/sage/combinat/root_system/poly_tup_engine.pyx index 2c7b034db1d..24baf59fd46 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pyx +++ b/src/sage/combinat/root_system/poly_tup_engine.pyx @@ -370,7 +370,7 @@ cdef dict subs_squares(dict eq_dict, dict known_sq): OUTPUT: - Returns a dictionary of ``(ETuple, coeff)`` pairs representing polynomial + A dictionary of ``(ETuple, coeff)`` pairs. """ cdef dict subbed, new_e cdef ETuple exp, lm @@ -396,7 +396,7 @@ cdef dict subs_squares(dict eq_dict, dict known_sq): cdef dict remove_gcf(dict eq_dict, ETuple nonz): r""" - Returns a dictionary of ``(ETuple, coeff)`` pairs describing the + Return a dictionary of ``(ETuple, coeff)`` pairs describing the polynomial ``eq / GCF(eq)``. The input ``nonz`` is an ``ETuple`` indicating the positions of From 082445ff85eb96f6e43919089d131deced3941e7 Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Wed, 7 Apr 2021 11:54:10 -0700 Subject: [PATCH 038/632] doctest tweaks in fusion_ring.py and f_matrix.py --- src/sage/combinat/root_system/f_matrix.py | 4 +--- src/sage/combinat/root_system/fusion_ring.py | 14 +++++++------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 48872ce5480..795e65b23c0 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -1489,9 +1489,7 @@ def _update_reduction_params(self,eqns=None,worker_pool=None,children_need_updat sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey sage: f.ideal_basis.sort(key=poly_tup_sortkey) sage: f.mp_thresh = 0 - sage: f._triangular_elim(worker_pool=pool) # indirect doctest - Elimination epoch completed... 10 eqns remain in ideal basis - Elimination epoch completed... 0 eqns remain in ideal basis + sage: f._triangular_elim(worker_pool=pool,verbose=False) # indirect doctest sage: f.ideal_basis [] """ diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index c402fd6b775..09ca6d348bf 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -393,8 +393,8 @@ def test_braid_representation(self, max_strands=6): EXAMPLES:: - sage: F41 = FusionRing("F4",1) - sage: F41.test_braid_representation(max_strands=4) + sage: A21 = FusionRing("A2",1) + sage: A21.test_braid_representation(max_strands=4) True sage: B22 = FusionRing("B2",2) # long time sage: B22.test_braid_representation() # long time @@ -550,10 +550,10 @@ def fvars_field(self): Cyclotomic Field of order 40 and degree 16 sage: a2**4 2*a0 + 3*a2 - sage: comp_basis, sig = A13.get_braid_generators(a2,a2,3,verbose=False) - sage: A13.fvars_field() + sage: comp_basis, sig = A13.get_braid_generators(a2,a2,3,verbose=False) # long time (<3s) + sage: A13.fvars_field() # long time Number Field in a with defining polynomial y^32 - 8*y^30 + 18*y^28 - 44*y^26 + 93*y^24 - 56*y^22 + 2132*y^20 - 1984*y^18 + 19738*y^16 - 28636*y^14 + 77038*y^12 - 109492*y^10 + 92136*y^8 - 32300*y^6 + 5640*y^4 - 500*y^2 + 25 - sage: a2.q_dimension().parent() + sage: a2.q_dimension().parent() # long time Number Field in a with defining polynomial y^32 - 8*y^30 + 18*y^28 - 44*y^26 + 93*y^24 - 56*y^22 + 2132*y^20 - 1984*y^18 + 19738*y^16 - 28636*y^14 + 77038*y^12 - 109492*y^10 + 92136*y^8 - 32300*y^6 + 5640*y^4 - 500*y^2 + 25 sage: A13.field() Cyclotomic Field of order 40 and degree 16 @@ -855,8 +855,8 @@ def s_ijconj(self, elt_i, elt_j, base_coercion=True): True sage: G22 = FusionRing("G2",2) sage: G22.fmats.find_orthogonal_solution(verbose=False) # long time (~11 s) - sage: b = G22.basis() - sage: all(G22.s_ijconj(x,y) == G22.fmats.field()(G22.s_ij(x,y,base_coercion=False).conjugate()) for x in b for y in b) + sage: b = G22.basis() # long time + sage: all(G22.s_ijconj(x,y) == G22.fmats.field()(G22.s_ij(x,y,base_coercion=False).conjugate()) for x in b for y in b) # long time True """ ret = self.s_ij(elt_i, elt_j, base_coercion=False).conjugate() From 4ef0bdf13ec00042572ceb7886393a9c5e17e3d1 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Wed, 7 Apr 2021 16:45:43 -0400 Subject: [PATCH 039/632] shorter doctest in _get_explicit_solution --- src/sage/combinat/root_system/f_matrix.py | 34 ++++++++++------------- 1 file changed, 15 insertions(+), 19 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 795e65b23c0..4b330d1a70e 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -1840,27 +1840,24 @@ def _get_explicit_solution(self,eqns=None,verbose=True): EXAMPLES:: - sage: f = FMatrix(FusionRing("A1",4)) # indirect doctest + sage: f = FMatrix(FusionRing("A1",3)) # indirect doctest sage: f.find_orthogonal_solution() # long time - Computing F-symbols for The Fusion Ring of Type A1 and level 4 with Integer Ring coefficients with 238 variables... - Set up 460 hex and orthogonality constraints... - Partitioned 460 equations into 41 components of size: - [24, 12, 12, 12, 12, 12, 12, 12, 12, 9, 6, 6, 6, 6, 6, 6, 6, 6, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1] - Elimination epoch completed... 63 eqns remain in ideal basis - Elimination epoch completed... 5 eqns remain in ideal basis + Set up 134 hex and orthogonality constraints... + Partitioned 134 equations into 17 components of size: + [12, 12, 6, 6, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1] + Elimination epoch completed... 10 eqns remain in ideal basis Elimination epoch completed... 0 eqns remain in ideal basis - Hex elim step solved for 173 / 238 variables - Set up 888 reduced pentagons... - Elimination epoch completed... 562 eqns remain in ideal basis - Elimination epoch completed... 209 eqns remain in ideal basis - Elimination epoch completed... 26 eqns remain in ideal basis - Elimination epoch completed... 8 eqns remain in ideal basis + Hex elim step solved for 51 / 71 variables + Set up 121 reduced pentagons... + Elimination epoch completed... 18 eqns remain in ideal basis + Elimination epoch completed... 5 eqns remain in ideal basis + Pent elim step solved for 64 / 71 variables + Partitioned 5 equations into 1 components of size: + [4] Elimination epoch completed... 0 eqns remain in ideal basis - Pent elim step solved for 225 / 238 variables - Partitioned 0 equations into 0 components of size: - [] - Partitioned 13 equations into 13 components of size: - [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] + Partitioned 6 equations into 6 components of size: + [1, 1, 1, 1, 1, 1] + Computing appropriate NumberField... """ if eqns is None: eqns = self.ideal_basis @@ -1939,7 +1936,6 @@ def _get_explicit_solution(self,eqns=None,verbose=True): self._fvars = {sextuple : apply_coeff_map(poly_to_tup(rhs),phi) for sextuple, rhs in self._fvars.items()} for fx, rhs in numeric_fvars.items(): self._fvars[self._idx_to_sextuple[fx]] = ((ETuple({},nvars),rhs),) - # self._backward_subs() _backward_subs(self) self._fvars = {sextuple : constant_coeff(rhs) for sextuple, rhs in self._fvars.items()} From 806ba0efc34705aeffc51edc98e2be266c426e7f Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 10 Apr 2021 18:11:45 +1000 Subject: [PATCH 040/632] Some formatting changes from initial reviewer pass. --- src/doc/en/reference/references/index.rst | 5 +- src/sage/combinat/root_system/f_matrix.py | 382 +++++++++--------- .../fast_parallel_fmats_methods.pxd | 1 + .../fast_parallel_fmats_methods.pyx | 110 ++--- .../fast_parallel_fusion_ring_braid_repn.pxd | 1 + .../fast_parallel_fusion_ring_braid_repn.pyx | 67 +-- src/sage/combinat/root_system/fusion_ring.py | 150 +++---- .../combinat/root_system/poly_tup_engine.pxd | 1 + .../combinat/root_system/poly_tup_engine.pyx | 133 +++--- 9 files changed, 444 insertions(+), 406 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 7aa2c0abb6f..0e4d35795e9 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1761,11 +1761,12 @@ REFERENCES: curves. preprint, 2005. .. [CHW2015] Shawn X.; Hong, Seung-Moon; Wang, Zhenghan Universal quantum computation - with weakly integral anyons. Quantum Inf. Process. 14 (2015), no. 8, 2687–2727. + with weakly integral anyons. Quantum Inf. Process. 14 (2015), + no. 8, 2687-2727. .. [CW2015] Cui, S. X. and Wang, Z. (2015). Universal quantum computation with metaplectic anyons. Journal of Mathematical Physics, 56(3), 032202. - doi:10.1063/1.4914941 + :doi:`10.1063/1.4914941` .. _ref-D: diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 4b330d1a70e..a14ed999c05 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -1,4 +1,4 @@ -""" +r""" F-Matrix Factory for FusionRings """ # **************************************************************************** @@ -17,6 +17,7 @@ import cPickle as pickle except: import pickle + from copy import deepcopy from itertools import product, zip_longest from multiprocessing import cpu_count, Pool, set_start_method @@ -47,23 +48,24 @@ class FMatrix(): def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variables=False): - r"""Return an F-Matrix factory for a :class:`FusionRing`. + r""" + Return an F-Matrix factory for a :class:`FusionRing`. INPUT: - - ``FR`` -- a :class:`FusionRing`. + - ``FR`` -- a :class:`FusionRing` - ``fusion_label`` -- (optional) a string used to label basis elements - of the :class:`FusionRing` associated to ``self``. + of the :class:`FusionRing` associated to ``self`` - See :meth:`FusionRing.fusion_labels`. + See :meth:`FusionRing.fusion_labels` - ``var_prefix`` -- (optional) a string indicating the desired prefix - for variables denoting F-symbols to be solved. + for variables denoting F-symbols to be solved - ``inject_variables`` -- (default: ``False``) a boolean indicating whether to inject variables (:class:`FusionRing` basis element - labels and F-symbols) into the global namespace. + labels and F-symbols) into the global namespace The :class:`FusionRing` or Verlinde algebra is the Grothendieck ring of a modular tensor category [BaKi2001]_. @@ -84,23 +86,23 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab the multiplicity-free cases are given by the following table. - +------------------------+----------+ - | Cartan Type | `k` | - +========================+==========+ - | `A_1` | any | - +------------------------+----------+ - | `A_r, r\geq 2` | `\leq 2` | - +------------------------+----------+ - | `B_r, r\geq 2` | `\leq 2` | - +------------------------+----------+ - | `C_2` | `\leq 2` | - +------------------------+----------+ - | `C_r, r\geq 3` | `\leq 1` | - +------------------------+----------+ - | `D_r, r\geq 4` | `\leq 2` | - +------------------------+----------+ - | `G_2,F_4,E_r` | `\leq 2` | - +------------------------+----------+ + +------------------------+----------+ + | Cartan Type | `k` | + +========================+==========+ + | `A_1` | any | + +------------------------+----------+ + | `A_r, r\geq 2` | `\leq 2` | + +------------------------+----------+ + | `B_r, r\geq 2` | `\leq 2` | + +------------------------+----------+ + | `C_2` | `\leq 2` | + +------------------------+----------+ + | `C_r, r\geq 3` | `\leq 1` | + +------------------------+----------+ + | `D_r, r\geq 4` | `\leq 2` | + +------------------------+----------+ + | `G_2,F_4,E_r` | `\leq 2` | + +------------------------+----------+ Beyond this limitation, computation of the F-matrix can involve very large systems of equations. A @@ -120,7 +122,8 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab .. MATH:: - \text{Hom}(D,(A\otimes B)\otimes C) \to \text{Hom}(D,A\otimes(B\otimes C)) + \text{Hom}(D,(A\otimes B)\otimes C) + \to \text{Hom}(D,A\otimes(B\otimes C)) by a matrix `F^{ABC}_D`. This depends on a pair of additional simple objects `X` and `Y`. Indeed, we can @@ -138,8 +141,8 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab [RoStWa2009]_ and [CHW2015]_. The F-matrix is only determined up to a *gauge*. This - is a family of embeddings `C\to A\otimes B` for - simple objects `A,B,C` such that `\text{Hom}(C,A\otimes B)` + is a family of embeddings `C \to A\otimes B` for + simple objects `A,B,C` such that `\text{Hom}(C, A\otimes B)` is nonzero. Changing the gauge changes the F-matrix though not in a very essential way. By varying the gauge it is possible to make the F-matrices unitary, or it is possible @@ -148,7 +151,6 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab Due to the large number of equations we may fail to find a Groebner basis if there are too many variables. - EXAMPLES:: sage: I = FusionRing("E8",2,conjugate=True) @@ -158,16 +160,16 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 F-Matrix factory for The Fusion Ring of Type E8 and level 2 with Integer Ring coefficients - We've exported two sets of variables to the global namespace. + We have injected two sets of variables to the global namespace. We created three variables ``i0, p, s`` to represent the - primary fields (simple elements) of the FusionRing. Creating - the FMatrix factory also created variables ``fx1,fx2, ... , fx14`` - in order to solve the hexagon and pentagon equations describing - the F-matrix. Since we called :class:`FMatrix` with the parameter - ``inject_variables=True``, these have been exported into the global - namespace. This is not necessary for the code to work but if you want - to run the code experimentally you may want access to these - variables. + primary fields (simple elements) of the :class:`FusionRing`. Creating + the :class:`FMatrix` factory also created variables + ``fx1, fx2, ..., fx14`` in order to solve the hexagon and pentagon + equations describing the F-matrix. Since we called :class:`FMatrix` + with the parameter ``inject_variables=True``, these have been injected + into the global namespace. This is not necessary for the code to work + but if you want to run the code experimentally you may want access + to these variables. EXAMPLES:: @@ -186,7 +188,7 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab for these are. In this example with `A=B=C=D=s` both `X` and `Y` are allowed to be `i_0` or `s`. - EXAMPLES:: + :: sage: f.f_from(s,s,s,s), f.f_to(s,s,s,s) ([i0, p], [i0, p]) @@ -195,15 +197,15 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab `X` and `Y` when `A=B=C=D=s` are `i_0` and `p`. The F-matrix is computed by solving the so-called - pentagon and hexagon equations. The *pentagon - equations* reflect the Mac Lane pentagon axiom in the - definition of a monoidal category. The hexagon relations + pentagon and hexagon equations. The *pentagon equations* + reflect the Mac Lane pentagon axiom in the definition + of a monoidal category. The hexagon relations reflect the axioms of a *braided monoidal category*, which are constraints on both the F-matrix and on the R-matrix. Optionally, orthogonality constraints may be imposed to obtain an orthogonal F-matrix. - EXAMPLES:: + :: sage: f.get_defining_equations("pentagons")[1:3] [fx9*fx12 - fx2*fx13, fx3*fx8 - fx4*fx9] @@ -221,12 +223,12 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab that should be kept in mind. :meth:`find_cyclotomic_solution` currently works only with - smaller examples. For example the :class:`FusionRing` for `G_2` at - level 2 is too large. When it is available, this method + smaller examples. For example the :class:`FusionRing` for `G_2` + at level 2 is too large. When it is available, this method produces an F-matrix whose entries are in the same cyclotomic field as the underlying :class:`FusionRing`. - EXAMPLES:: + :: sage: f.find_cyclotomic_solution() Setting up hexagons and pentagons... @@ -238,9 +240,7 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab Done! We now have access to the values of the F-matrix using - the methods :meth:`fmatrix` and :meth:`fmat`. - - EXAMPLES:: + the methods :meth:`fmatrix` and :meth:`fmat`:: sage: f.fmatrix(s,s,s,s) [(-1/2*zeta128^48 + 1/2*zeta128^16) 1] @@ -260,11 +260,11 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab containing the F-matrix. The field containing the F-matrix is available through :meth:`field`. - EXAMPLES:: + :: sage: f = FMatrix(FusionRing("B3",2)) - sage: f.find_orthogonal_solution(verbose=False,checkpoint=True) # long time (~100 s) - sage: all(v in CyclotomicField(56) for v in f.get_fvars().values()) # long time + sage: f.find_orthogonal_solution(verbose=False,checkpoint=True) # not tested (~100 s) + sage: all(v in CyclotomicField(56) for v in f.get_fvars().values()) # not tested True sage: f = FMatrix(FusionRing("G2",2)) @@ -281,17 +281,17 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab n_vars = self.findcases() self._poly_ring = PolynomialRing(self._FR.field(),n_vars,var_prefix) if inject_variables: - print ("creating variables %s%s..%s%s"%(var_prefix,1,var_prefix,n_vars)) + print("creating variables %s%s..%s%s"%(var_prefix,1,var_prefix,n_vars)) self._poly_ring.inject_variables(get_main_globals()) self._var_to_sextuple, self._fvars = self.findcases(output=True) - self._var_to_idx = {var : idx for idx, var in enumerate(self._poly_ring.gens())} - self._idx_to_sextuple = {i : self._var_to_sextuple[self._poly_ring.gen(i)] for i in range(self._poly_ring.ngens())} + self._var_to_idx = {var: idx for idx, var in enumerate(self._poly_ring.gens())} + self._idx_to_sextuple = {i: self._var_to_sextuple[self._poly_ring.gen(i)] for i in range(self._poly_ring.ngens())} self._singles = self.singletons() #Base field attributes self._field = self._FR.field() - r = self._field.defining_polynomial().roots(ring=QQbar,multiplicities=False)[0] - self._qqbar_embedding = self._field.hom([r],QQbar) + r = self._field.defining_polynomial().roots(ring=QQbar, multiplicities=False)[0] + self._qqbar_embedding = self._field.hom([r], QQbar) self._non_cyc_roots = list() #Useful solver state attributes @@ -318,7 +318,7 @@ def __repr__(self): return "F-Matrix factory for %s"%self._FR def clear_equations(self): - """ + r""" Clear the list of equations to be solved. EXAMPLES:: @@ -334,7 +334,7 @@ def clear_equations(self): self.ideal_basis = list() def clear_vars(self): - """ + r""" Reset the F-symbols. EXAMPLES:: @@ -355,7 +355,7 @@ def clear_vars(self): self._solved = set() def _reset_solver_state(self): - """ + r""" Reset solver state and clear relevant cache. Used to ensure state variables are the same for each orthogonal solver run. @@ -405,7 +405,7 @@ def _reset_solver_state(self): self._FR.s_ij.clear_cache() def _update_poly_ring_base_field(self,field): - """ + r""" Change base field of ``PolynomialRing`` and the corresponding index attributes @@ -423,13 +423,13 @@ def _update_poly_ring_base_field(self,field): new_poly_ring = self._poly_ring.change_ring(field) nvars = self._poly_ring.ngens() #Do some appropriate conversions - self._var_to_idx = {new_poly_ring.gen(i) : i for i in range(nvars)} - self._var_to_sextuple = {new_poly_ring.gen(i) : self._var_to_sextuple[self._poly_ring.gen(i)] for i in range(nvars)} + self._var_to_idx = {new_poly_ring.gen(i): i for i in range(nvars)} + self._var_to_sextuple = {new_poly_ring.gen(i): self._var_to_sextuple[self._poly_ring.gen(i)] for i in range(nvars)} self._poly_ring = new_poly_ring def fmat(self, a, b, c, d, x, y, data=True): - """ - Return the F-Matrix coefficient `(F^{a,b,c}_d)_{x,y}` + r""" + Return the F-Matrix coefficient `(F^{a,b,c}_d)_{x,y}`. EXAMPLES:: @@ -484,7 +484,7 @@ def fmat(self, a, b, c, d, x, y, data=True): return (a,b,c,d,x,y) def fmatrix(self,a,b,c,d): - """ + r""" Return the F-Matrix `F^{a,b,c}_d`. INPUT: @@ -508,7 +508,6 @@ def fmatrix(self,a,b,c,d): [ (1/2*zeta32^12 - 1/2*zeta32^4) (-1/2*zeta32^12 + 1/2*zeta32^4)] [ (1/2*zeta32^12 - 1/2*zeta32^4) (1/2*zeta32^12 - 1/2*zeta32^4)] """ - X = self.f_from(a,b,c,d) Y = self.f_to(a,b,c,d) return matrix([[self.fmat(a,b,c,d,x,y) for y in Y] for x in X]) @@ -546,7 +545,7 @@ def field(self): Cyclotomic Field of order 60 and degree 16 sage: f.find_orthogonal_solution(verbose=False) sage: f.field() - Number Field in a with defining polynomial y^32 - 6*y^30 - 7*y^28 + 62*y^26 - 52*y^24 - 308*y^22 + 831*y^20 + 7496*y^18 + 18003*y^16 - 2252*y^14 + 42259*y^12 - 65036*y^10 + 29368*y^8 - 3894*y^6 + 377*y^4 - 22*y^2 + 1 + Number Field in a with defining polynomial y^32 - ... - 22*y^2 + 1 sage: phi = f.get_qqbar_embedding() sage: [phi(r).n() for r in f.get_non_cyclotomic_roots()] [-0.786151377757423 - 8.92806368517581e-31*I] @@ -573,7 +572,7 @@ def FR(self): return self._FR def findcases(self,output=False): - """ + r""" Return unknown F-matrix entries. If run with ``output=True``, @@ -587,16 +586,15 @@ def findcases(self,output=False): 5 sage: f.findcases(output=True) ({fx4: (t, t, t, t, t, t), - fx3: (t, t, t, t, t, i0), - fx2: (t, t, t, t, i0, t), - fx1: (t, t, t, t, i0, i0), - fx0: (t, t, t, i0, t, t)}, - {(t, t, t, i0, t, t): fx0, - (t, t, t, t, i0, i0): fx1, - (t, t, t, t, i0, t): fx2, - (t, t, t, t, t, i0): fx3, - (t, t, t, t, t, t): fx4}) - + fx3: (t, t, t, t, t, i0), + fx2: (t, t, t, t, i0, t), + fx1: (t, t, t, t, i0, i0), + fx0: (t, t, t, i0, t, t)}, + {(t, t, t, i0, t, t): fx0, + (t, t, t, t, i0, i0): fx1, + (t, t, t, t, i0, t): fx2, + (t, t, t, t, t, i0): fx3, + (t, t, t, t, t, t): fx4}) """ i = 0 if output: @@ -630,8 +628,8 @@ def singletons(self): True """ ret = [] - for (a, b, c, d) in list(product(self._FR.basis(), repeat=4)): - (ff,ft) = (self.f_from(a,b,c,d),self.f_to(a,b,c,d)) + for (a, b, c, d) in product(self._FR.basis(), repeat=4): + (ff,ft) = (self.f_from(a,b,c,d), self.f_to(a,b,c,d)) if len(ff) == 1 and len(ft) == 1: v = self._fvars.get((a,b,c,d,ff[0],ft[0]), None) if v in self._poly_ring.gens(): @@ -641,11 +639,11 @@ def singletons(self): def f_from(self,a,b,c,d): r""" Return the possible `x` such that there are morphisms - `d\to x\otimes c\to (a\otimes b)\otimes c`. + `d \to x \otimes c \to (a\otimes b)\otimes c`. INPUT: - - ``a,b,c,d`` -- basis elements of the associated :class:`FusionRing`. + - ``a,b,c,d`` -- basis elements of the associated :class:`FusionRing` EXAMPLES:: @@ -659,8 +657,8 @@ def f_from(self,a,b,c,d): sage: f.f_to(a1,a1,a2,a2) [a1, a3] """ - - return [x for x in self._FR.basis() if self._FR.Nk_ij(a,b,x) != 0 and self._FR.Nk_ij(x,c,d) != 0] + return [x for x in self._FR.basis() + if self._FR.Nk_ij(a,b,x) != 0 and self._FR.Nk_ij(x,c,d) != 0] def f_to(self,a,b,c,d): r""" @@ -669,7 +667,7 @@ def f_to(self,a,b,c,d): INPUT: - - ``a,b,c,d`` -- basis elements of the associated :class:`FusionRing`. + - ``a,b,c,d`` -- basis elements of the associated :class:`FusionRing` EXAMPLES:: @@ -684,9 +682,9 @@ def f_to(self,a,b,c,d): [b1, b3, b5] sage: B.f_to(b2,b4,b2,b4) [b1, b3, b5] - """ - return [y for y in self._FR.basis() if self._FR.Nk_ij(b,c,y) != 0 and self._FR.Nk_ij(a,y,d) != 0] + return [y for y in self._FR.basis() + if self._FR.Nk_ij(b,c,y) != 0 and self._FR.Nk_ij(a,y,d) != 0] #################### ### Data getters ### @@ -723,7 +721,8 @@ def get_poly_ring(self): sage: f = FMatrix(FusionRing("B6",1)) sage: f.get_poly_ring() - Multivariate Polynomial Ring in fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 over Cyclotomic Field of order 96 and degree 32 + Multivariate Polynomial Ring in fx0, ..., fx13 over + Cyclotomic Field of order 96 and degree 32 """ return self._poly_ring @@ -767,8 +766,7 @@ def get_qqbar_embedding(self): r""" Return an embedding from the base field containing F-symbols (the associated :class:`FusionRing`'s :class:`CyclotomicField`, a - :class:`NumberField`, or ``QQbar``) - into ``QQbar``. + :class:`NumberField`, or ``QQbar``) into ``QQbar``. This embedding is useful for getting a better sense for the F-symbols, particularly when they are computed as elements of a @@ -825,7 +823,7 @@ def get_coerce_map_from_fr_cyclotomic_field(self): we have ``self.field() == self.FR().field()`` and this method returns the identity map on ``self.field()``. - EXAMPLES:: + :: sage: f = FMatrix(FusionRing("A2",1)) sage: f.find_orthogonal_solution(verbose=False) @@ -862,7 +860,7 @@ def get_fvars_in_alg_field(self): Defining fx0, fx1, fx2, fx3, fx4 sage: f.find_orthogonal_solution(verbose=False) sage: f.field() - Number Field in a with defining polynomial y^32 - 6*y^30 - 7*y^28 + 62*y^26 - 52*y^24 - 308*y^22 + 831*y^20 + 7496*y^18 + 18003*y^16 - 2252*y^14 + 42259*y^12 - 65036*y^10 + 29368*y^8 - 3894*y^6 + 377*y^4 - 22*y^2 + 1 + Number Field in a with defining polynomial y^32 - ... - 22*y^2 + 1 sage: f.get_fvars_in_alg_field() {(g1, g1, g1, g0, g1, g1): 1, (g1, g1, g1, g1, g0, g0): 0.61803399? + 0.?e-8*I, @@ -870,7 +868,7 @@ def get_fvars_in_alg_field(self): (g1, g1, g1, g1, g1, g0): -0.7861514? + 0.?e-8*I, (g1, g1, g1, g1, g1, g1): -0.61803399? + 0.?e-8*I} """ - return {sextuple : self._qqbar_embedding(fvar) for sextuple, fvar in self._fvars.items()} + return {sextuple: self._qqbar_embedding(fvar) for sextuple, fvar in self._fvars.items()} def get_radical_expression(self): """ @@ -885,7 +883,7 @@ def get_radical_expression(self): sage: radical_fvars[g1, g1, g1, g1, g1, g0] # long time -sqrt(1/2*sqrt(5) - 1/2) """ - return {sextuple : val.radical_expression() for sextuple, val in self.get_fvars_in_alg_field().items()} + return {sextuple: val.radical_expression() for sextuple, val in self.get_fvars_in_alg_field().items()} ####################### ### Private helpers ### @@ -893,8 +891,8 @@ def get_radical_expression(self): def _get_known_vals(self): r""" - Construct a dictionary of ``idx``, ``known_val`` pairs used for substituting - into remaining equations. + Construct a dictionary of ``idx``, ``known_val`` pairs used for + substituting into remaining equations. EXAMPLES:: @@ -905,7 +903,7 @@ def _get_known_vals(self): sage: len(f._get_known_vals()) == f._poly_ring.ngens() True """ - return {var_idx : self._fvars[self._idx_to_sextuple[var_idx]] for var_idx in self._solved} + return {var_idx: self._fvars[self._idx_to_sextuple[var_idx]] for var_idx in self._solved} def _get_known_sq(self,eqns=None): r""" @@ -952,14 +950,14 @@ def _get_known_nonz(self): .. NOTE:: MUST be called after ``self._ks = _get_known_sq()``. - This method is called by the constructor of ``self``. EXAMPLES:: sage: f = FMatrix(FusionRing("D5",1)) # indirect doctest sage: f._nnz - (100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100) + (100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100) """ nonz = {self._var_to_idx[var] : 100 for var in self._singles} for idx in self._ks: @@ -989,30 +987,30 @@ def get_fvars_by_size(self,n,indices=False): INPUT: - - `n` -- a positive integer + - `n` -- a positive integer - - ``indices`` -- (default: ``False``) a boolean. + - ``indices`` -- (default: ``False``) a boolean. - If ``indices`` is ``False`` (default), - this method returns a set of sextuples `(a,b,c,d,x,y)` identifying - the corresponding F-symbol. Each sextuple is a key in the - dictionary returned by :meth:`get_fvars`. + If ``indices`` is ``False`` (default), + this method returns a set of sextuples `(a,b,c,d,x,y)` identifying + the corresponding F-symbol. Each sextuple is a key in the + dictionary returned by :meth:`get_fvars`. - Otherwise the method returns a list of integer indices that - internally identify the F-symbols. The ``indices=True`` option is - meant for internal use. + Otherwise the method returns a list of integer indices that + internally identify the F-symbols. The ``indices=True`` option is + meant for internal use. EXAMPLES:: sage: f = FMatrix(FusionRing("A2",2), inject_variables=True) creating variables fx1..fx287 - Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19, fx20, fx21, fx22, fx23, fx24, fx25, fx26, fx27, fx28, fx29, fx30, fx31, fx32, fx33, fx34, fx35, fx36, fx37, fx38, fx39, fx40, fx41, fx42, fx43, fx44, fx45, fx46, fx47, fx48, fx49, fx50, fx51, fx52, fx53, fx54, fx55, fx56, fx57, fx58, fx59, fx60, fx61, fx62, fx63, fx64, fx65, fx66, fx67, fx68, fx69, fx70, fx71, fx72, fx73, fx74, fx75, fx76, fx77, fx78, fx79, fx80, fx81, fx82, fx83, fx84, fx85, fx86, fx87, fx88, fx89, fx90, fx91, fx92, fx93, fx94, fx95, fx96, fx97, fx98, fx99, fx100, fx101, fx102, fx103, fx104, fx105, fx106, fx107, fx108, fx109, fx110, fx111, fx112, fx113, fx114, fx115, fx116, fx117, fx118, fx119, fx120, fx121, fx122, fx123, fx124, fx125, fx126, fx127, fx128, fx129, fx130, fx131, fx132, fx133, fx134, fx135, fx136, fx137, fx138, fx139, fx140, fx141, fx142, fx143, fx144, fx145, fx146, fx147, fx148, fx149, fx150, fx151, fx152, fx153, fx154, fx155, fx156, fx157, fx158, fx159, fx160, fx161, fx162, fx163, fx164, fx165, fx166, fx167, fx168, fx169, fx170, fx171, fx172, fx173, fx174, fx175, fx176, fx177, fx178, fx179, fx180, fx181, fx182, fx183, fx184, fx185, fx186, fx187, fx188, fx189, fx190, fx191, fx192, fx193, fx194, fx195, fx196, fx197, fx198, fx199, fx200, fx201, fx202, fx203, fx204, fx205, fx206, fx207, fx208, fx209, fx210, fx211, fx212, fx213, fx214, fx215, fx216, fx217, fx218, fx219, fx220, fx221, fx222, fx223, fx224, fx225, fx226, fx227, fx228, fx229, fx230, fx231, fx232, fx233, fx234, fx235, fx236, fx237, fx238, fx239, fx240, fx241, fx242, fx243, fx244, fx245, fx246, fx247, fx248, fx249, fx250, fx251, fx252, fx253, fx254, fx255, fx256, fx257, fx258, fx259, fx260, fx261, fx262, fx263, fx264, fx265, fx266, fx267, fx268, fx269, fx270, fx271, fx272, fx273, fx274, fx275, fx276, fx277, fx278, fx279, fx280, fx281, fx282, fx283, fx284, fx285, fx286 + Defining fx0, ..., fx286 sage: f.largest_fmat_size() 2 sage: f.get_fvars_by_size(2) {(f2, f2, f2, f4, f1, f1), (f2, f2, f2, f4, f1, f5), - ... + ... (f4, f4, f4, f4, f4, f0), (f4, f4, f4, f4, f4, f4)} """ @@ -1042,7 +1040,7 @@ def save_fvars(self, filename): INPUT: - ``filename`` -- a string specifying the name of the pickle file - to be used. + to be used The current directory is used unless an absolute path to a file in a different directory is provided. @@ -1122,20 +1120,20 @@ def load_fvars(self, filename): self._field = self._qqbar_embedding.domain() def get_fr_str(self): - """ - Auto-generate an identifying key for saving results + r""" + Auto-generate an identifying key for saving results. EXAMPLES:: - sage: f = FMatrix(FusionRing("B3",1)) - sage: f.get_fr_str() - 'B31' + sage: f = FMatrix(FusionRing("B3",1)) + sage: f.get_fr_str() + 'B31' """ ct = self._FR.cartan_type() return ct.letter + str(ct.n) + str(self._FR.fusion_level()) - def _checkpoint(self,do_chkpt,status,verbose=True): - """ + def _checkpoint(self, do_chkpt, status, verbose=True): + r""" Pickle current solver state. EXAMPLES:: @@ -1276,8 +1274,8 @@ def _map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_t INPUT: - -``mapper`` -- string specifying the name of a function defined in - the ``fast_parallel_fmats_methods`` module. + -``mapper`` -- string specifying the name of a function defined in + the ``fast_parallel_fmats_methods`` module .. NOTE:: @@ -1338,7 +1336,7 @@ def get_orthogonality_constraints(self,output=True): INPUT: - - ``output`` -- a boolean. + - ``output`` -- a boolean OUTPUT: @@ -1352,7 +1350,7 @@ def get_orthogonality_constraints(self,output=True): EXAMPLES:: - sage: f = FMatrix(FusionRing("B4",1)) + sage: f = FMatrix(FusionRing("B4", 1)) sage: f.get_orthogonality_constraints() [fx0^2 - 1, fx1^2 - 1, @@ -1369,7 +1367,7 @@ def get_orthogonality_constraints(self,output=True): fx10*fx11 + fx12*fx13, fx11^2 + fx13^2 - 1] """ - eqns = list() + eqns = [] for tup in product(self._FR.basis(), repeat=4): mat = self.fmatrix(*tup) eqns.extend((mat.T * mat - matrix.identity(mat.nrows())).coefficients()) @@ -1384,29 +1382,24 @@ def get_defining_equations(self,option,worker_pool=None,output=True): INPUT: - - ``option`` -- a string determining equations to be set up. + - ``option`` -- a string determining equations to be set up: - Use ``option='hexagons'`` to get equations imposed on the F-matrix by - the hexagon relations in the definition of a braided category. + * ``'hexagons'`` - get equations imposed on the F-matrix by + the hexagon relations in the definition of a braided category - Use ``option='pentagons'`` to get equations imposed on the F-matrix by - the pentagon relations in the definition of a monoidal category. + * ``'pentagons'`` - get equations imposed on the F-matrix by + the pentagon relations in the definition of a monoidal category - ``worker_pool`` -- (default: ``None``) a ``Pool`` object of the - Python ``multiprocessing`` module. - - If a ``worker_pool`` object is passed, we distribute the work - amongst processes in the pool. + Python ``multiprocessing`` module - ``output`` -- (default: ``True``) a boolean indicating whether - results should be returned. - - If ``output=True``, equations are returned as polynomial objects. + results should be returned, where the equations will be polynomials. - Otherwise, the constraints are appended to ``self.ideal_basis``. - They are stored in the internal tuple representation. The - ``output=False`` option is meant mostly for internal use by the - F-matrix solver. + Otherwise, the constraints are appended to ``self.ideal_basis``. + They are stored in the internal tuple representation. The + ``output=False`` option is meant mostly for internal use by the + F-matrix solver. EXAMPLES:: @@ -1471,7 +1464,7 @@ def _tup_to_fpoly(self,eq_tup): return _tup_to_poly(eq_tup,parent=self._poly_ring) def _update_reduction_params(self,eqns=None,worker_pool=None,children_need_update=False): - """ + r""" Update reduction parameters that are solver state attributes. EXAMPLES:: @@ -1489,7 +1482,7 @@ def _update_reduction_params(self,eqns=None,worker_pool=None,children_need_updat sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey sage: f.ideal_basis.sort(key=poly_tup_sortkey) sage: f.mp_thresh = 0 - sage: f._triangular_elim(worker_pool=pool,verbose=False) # indirect doctest + sage: f._triangular_elim(worker_pool=pool,verbose=False) # indirect doctest sage: f.ideal_basis [] """ @@ -1505,14 +1498,14 @@ def _update_reduction_params(self,eqns=None,worker_pool=None,children_need_updat self._map_triv_reduce('update_child_fmats',new_data,worker_pool=worker_pool,chunksize=1,mp_thresh=0) def _triangular_elim(self,eqns=None,worker_pool=None,verbose=True): - """ + r""" Perform triangular elimination of linear terms in two-term equations until no such terms exist. .. NOTE:: - For optimal usage of TRIANGULAR elimination, pass in a - SORTED list of equations. + For optimal usage of triangular elimination, pass in a + *sorted* list of equations. EXAMPLES:: @@ -1536,7 +1529,8 @@ def _triangular_elim(self,eqns=None,worker_pool=None,verbose=True): while True: linear_terms_exist = _solve_for_linear_terms(self,eqns) - if not linear_terms_exist: break + if not linear_terms_exist: + break _backward_subs(self) #Compute new reduction params, send to child processes if any, and update eqns @@ -1557,7 +1551,7 @@ def _triangular_elim(self,eqns=None,worker_pool=None,verbose=True): ##################### def equations_graph(self,eqns=None): - """ + r""" Construct a graph corresponding to the given equations. Every node corresponds to a variable and nodes are connected when @@ -1565,7 +1559,7 @@ def equations_graph(self,eqns=None): INPUT: - - ``eqns`` -- a list of polynomials. + - ``eqns`` -- a list of polynomials Each polynomial is either an object in the ring returned by :meth:`get_poly_ring` or it is a tuple of pairs representing @@ -1626,7 +1620,7 @@ def equations_graph(self,eqns=None): return G def _partition_eqns(self,eqns=None,verbose=True): - """ + r""" Partition equations corresponding to edges in a disconnected graph. OUTPUT: @@ -1662,7 +1656,7 @@ def _partition_eqns(self,eqns=None,verbose=True): if eqns is None: eqns = self.ideal_basis graph = self.equations_graph(eqns) - partition = {tuple(c) : [] for c in graph.connected_components()} + partition = {tuple(c): [] for c in graph.connected_components()} for eq_tup in eqns: partition[tuple(graph.connected_component_containing_vertex(variables(eq_tup)[0]))].append(eq_tup) if verbose: @@ -1671,18 +1665,18 @@ def _partition_eqns(self,eqns=None,verbose=True): return partition def _par_graph_gb(self,worker_pool=None,eqns=None,term_order="degrevlex",verbose=True): - """ - Compute a Groebner basis for a list of equations partitioned according - to their corresponding graph. + r""" + Compute a Groebner basis for a list of equations partitioned + according to their corresponding graph. .. NOTE:: - If the graph has more than 50 components, this method computes the - Groebner basis in parallel when a ``worker_pool`` is provided. + If the graph has more than 50 components, this method computes the + Groebner basis in parallel when a ``worker_pool`` is provided. - This method will refuse to find a Groebner basis for a component - of size larger than 60, since such a calculation does not seem to - terminate. + This method will refuse to find a Groebner basis for a component + of size larger than 60, since such a calculation does not seem to + terminate. EXAMPLES:: @@ -1734,9 +1728,9 @@ def _par_graph_gb(self,worker_pool=None,eqns=None,term_order="degrevlex",verbose return ret def _get_component_variety(self,var,eqns): - """ - Translate equations in each connected component to smaller polynomial rings - so we can call built-in variety method. + r""" + Translate equations in each connected component to smaller polynomial + rings so we can call built-in variety method. INPUT: @@ -1755,7 +1749,8 @@ def _get_component_variety(self,var,eqns): sage: f.get_defining_equations('hexagons',worker_pool=Pool(),output=False) # long time sage: partition = f._partition_eqns() # long time Partitioned 327 equations into 35 components of size: - [27, 27, 27, 24, 24, 16, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 9, 9, 6, 6, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1] + [27, 27, 27, 24, 24, 16, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, + 9, 9, 6, 6, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1] sage: c = (216, 292, 319) sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup sage: eqns = partition[c] + [poly_to_tup(f._poly_ring.gen(216)-1)] # long time @@ -1763,25 +1758,25 @@ def _get_component_variety(self,var,eqns): [{216: -1, 292: -1, 319: 1}] """ #Define smaller poly ring in component vars - R = PolynomialRing(self._FR.field(),len(var),'a',order='lex') + R = PolynomialRing(self._FR.field(), len(var), 'a', order='lex') #Zip tuples into R and compute Groebner basis - idx_map = {old : new for new, old in enumerate(sorted(var))} + idx_map = {old: new for new, old in enumerate(sorted(var))} nvars = len(var) eqns = [_unflatten_coeffs(self._field,eq_tup) for eq_tup in eqns] polys = [_tup_to_poly(resize(eq_tup,idx_map,nvars),parent=R) for eq_tup in eqns] var_in_R = Ideal(sorted(polys)).variety(ring=AA) #Change back to fmats poly ring and append to temp_eqns - inv_idx_map = {v : k for k, v in idx_map.items()} - return [{inv_idx_map[i] : value for i, (key, value) in enumerate(sorted(soln.items()))} for soln in var_in_R] + inv_idx_map = {v: k for k, v in idx_map.items()} + return [{inv_idx_map[i]: value for i, (key, value) in enumerate(sorted(soln.items()))} for soln in var_in_R] ####################### ### Solution method ### ####################### def attempt_number_field_computation(self): - """ + r""" Based on the ``CartanType`` of ``self`` and data known on March 17, 2021, determine whether to attempt to find a :class:`NumberField` containing all the F-symbols. @@ -1790,8 +1785,8 @@ def attempt_number_field_computation(self): to determine a field containing all F-symbols. See :meth:`field` and :meth:`get_non_cyclotomic_roots`. - For certain :class:`FusionRing`'s, the number field computation does - not terminate in reasonable time. + For certain :class:`FusionRing `, the number field + computation does not terminate in reasonable time. In these cases, we report F-symbols as elements of the :class:`AlgebraicField` ``QQbar``. @@ -1832,11 +1827,11 @@ def attempt_number_field_computation(self): return True def _get_explicit_solution(self,eqns=None,verbose=True): - """ + r""" When this method is called, the solution is already found in terms of Groeber basis. A few degrees of freedom remain. - By specializing the free variables and back substituting, a solution in - the base field is now obtained. + By specializing the free variables and back substituting, a + solution in the base field is now obtained. EXAMPLES:: @@ -1943,7 +1938,7 @@ def _get_explicit_solution(self,eqns=None,verbose=True): self._FR._field = self.field() self._FR._basecoer = self.get_coerce_map_from_fr_cyclotomic_field() - def find_orthogonal_solution(self,checkpoint=False,save_results="",warm_start="",use_mp=True,verbose=True): + def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start="", use_mp=True, verbose=True): r""" Solve the the hexagon and pentagon relations, along with orthogonality constraints, to evaluate an orthogonal F-matrix. @@ -2037,13 +2032,15 @@ def find_orthogonal_solution(self,checkpoint=False,save_results="",warm_start="" to ``self``. See :meth:`attempt_number_field_computation` for details. """ self._reset_solver_state() - if self._poly_ring.ngens() == 0: return + if self._poly_ring.ngens() == 0: + return #Resume computation from checkpoint if warm_start: self._restore_state(warm_start) #Loading from a pickle with solved F-symbols - if self._chkpt_status > 5: return + if self._chkpt_status > 5: + return #Set multiprocessing parameters. Context can only be set once, so we try to set it try: @@ -2131,13 +2128,13 @@ def find_orthogonal_solution(self,checkpoint=False,save_results="",warm_start="" ######################### def _fix_gauge(self, algorithm=""): - """ + r""" Fix the gauge by forcing F-symbols not already fixed to equal 1. .. NOTE:: - This method should be used AFTER adding hexagon and pentagon - equations to ``self.ideal_basis``. + This method should be used *after* adding hexagon and pentagon + equations to ``self.ideal_basis``. EXAMPLES:: @@ -2175,7 +2172,7 @@ def _substitute_degree_one(self, eqns=None): sage: f = FMatrix(FusionRing("D3",1), inject_variables=True) creating variables fx1..fx27 - Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19, fx20, fx21, fx22, fx23, fx24, fx25, fx26 + Defining fx0, ..., fx26 sage: f.ideal_basis = [fx0 - 8, fx4**2 - 3, fx4 + fx10 + 3, fx4 + fx9] sage: _, _ = f._substitute_degree_one() sage: f._fvars[f._var_to_sextuple[fx0]] @@ -2204,14 +2201,14 @@ def _substitute_degree_one(self, eqns=None): return new_knowns, useless def _update_equations(self): - """ + r""" Perform backward substitution on equations in ``self.ideal_basis``. EXAMPLES:: sage: f = FMatrix(FusionRing("D3",1), inject_variables=True) creating variables fx1..fx27 - Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19, fx20, fx21, fx22, fx23, fx24, fx25, fx26 + Defining fx0, ..., fx26 sage: f.ideal_basis = [fx0 - 8, fx4 + fx9, fx4**2 + fx3 - fx9**2] sage: _, _ = f._substitute_degree_one() sage: f._update_equations() @@ -2222,8 +2219,8 @@ def _update_equations(self): self.ideal_basis = set(eq.subs(special_values) for eq in self.ideal_basis) self.ideal_basis.discard(0) - def find_cyclotomic_solution(self, equations=None, algorithm='', verbose=True, output=False): - """ + def find_cyclotomic_solution(self, equations=None, algorithm="", verbose=True, output=False): + r""" Solve the the hexagon and pentagon relations to evaluate the F-matrix. This method (omitting the orthogonality constraints) produces @@ -2235,10 +2232,10 @@ def find_cyclotomic_solution(self, equations=None, algorithm='', verbose=True, o INPUT: - ``equations`` -- (optional) a set of equations to be - solved. Defaults to the hexagon and pentagon equations. - - ``algorithm`` -- (optional). Algorithm to compute Groebner Basis. - - ``output`` -- (optional, default False). Output a dictionary of - F-matrix values. This may be useful to see but may be omitted + solved; defaults to the hexagon and pentagon equations + - ``algorithm`` -- (optional) algorithm to compute Groebner Basis + - ``output`` -- (default: ``False``) output a dictionary of + F-matrix values; this may be useful to see but may be omitted since this information will be available afterwards via the :meth:`fmatrix` and :meth:`fmat` methods. @@ -2275,10 +2272,10 @@ def find_cyclotomic_solution(self, equations=None, algorithm='', verbose=True, o [] sage: f.get_defining_equations("pentagons") [] - """ self._reset_solver_state() - if self._poly_ring.ngens() == 0: return + if self._poly_ring.ngens() == 0: + return if equations is None: if verbose: @@ -2303,7 +2300,7 @@ def find_cyclotomic_solution(self, equations=None, algorithm='', verbose=True, o ##################### def certify_pentagons(self,use_mp=True,verbose=False): - """ + r""" Obtain a certificate of satisfaction for the pentagon equations, up to floating-point error. @@ -2341,7 +2338,7 @@ def certify_pentagons(self,use_mp=True,verbose=False): Success!!! Found valid F-symbols for The Fusion Ring of Type C3 and level 1 with Integer Ring coefficients """ fvars_copy = deepcopy(self._fvars) - self._fvars = {sextuple : float(rhs) for sextuple, rhs in self.get_fvars_in_alg_field().items()} + self._fvars = {sextuple: float(rhs) for sextuple, rhs in self.get_fvars_in_alg_field().items()} if use_mp: pool = Pool(processes=cpu_count()) else: @@ -2359,7 +2356,7 @@ def certify_pentagons(self,use_mp=True,verbose=False): return pe def fmats_are_orthogonal(self): - """ + r""" Verify that all F-matrices are orthogonal. This method should always return ``True`` when called after running @@ -2379,7 +2376,7 @@ def fmats_are_orthogonal(self): return all(is_orthog) def fvars_are_real(self): - """ + r""" Test whether all F-symbols are real. EXAMPLES:: @@ -2396,3 +2393,4 @@ def fvars_are_real(self): print("The F-symbol {} (key {}) has a nonzero imaginary part!".format(v,k)) return False return True + diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd index 2092478205b..861504a79a3 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd @@ -6,3 +6,4 @@ cdef _fmat(fvars, Nk_ij, one, a, b, c, d, x, y) cpdef executor(params) cpdef collect_eqns(proc) + diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index c2afc4b240c..a66b1d0da97 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -1,5 +1,5 @@ """ -Fast FMatrix methods +Fast F-Matrix methods """ # **************************************************************************** # Copyright (C) 2021 Guillermo Aboumrad @@ -31,8 +31,8 @@ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing ### Fast class methods ### ########################## -cpdef _solve_for_linear_terms(factory,eqns=None): - """ +cpdef _solve_for_linear_terms(factory, eqns=None): + r""" Solve for a linear term occurring in a two-term equation, and for variables appearing in univariate single-term equations. @@ -40,7 +40,7 @@ cpdef _solve_for_linear_terms(factory,eqns=None): sage: f = FMatrix(FusionRing("D3",1), inject_variables=True) creating variables fx1..fx27 - Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19, fx20, fx21, fx22, fx23, fx24, fx25, fx26 + Defining fx0, ..., fx26 sage: f.ideal_basis = [fx0**3, fx0 + fx3**4, fx2**2 - fx3, fx2 - fx3**2, fx4 - fx2] sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup sage: f.ideal_basis = [poly_to_tup(p) for p in f.ideal_basis] @@ -60,11 +60,12 @@ cpdef _solve_for_linear_terms(factory,eqns=None): fx4 """ if eqns is None: - eqns = factory.ideal_basis + eqns = factory.ideal_basis - linear_terms_exist = False + cdef bint linear_terms_exist = False + cdef tuple eq_tup for eq_tup in eqns: - #Only unflatten relevant polynomials + # Only unflatten relevant polynomials if len(eq_tup) > 2: continue eq_tup = _unflatten_coeffs(factory._field, eq_tup) @@ -89,7 +90,7 @@ cpdef _solve_for_linear_terms(factory,eqns=None): return linear_terms_exist cpdef _backward_subs(factory): - """ + r""" Perform backward substitution on ``self.ideal_basis``, traversing variables in reverse lexicographical order. @@ -97,7 +98,7 @@ cpdef _backward_subs(factory): sage: f = FMatrix(FusionRing("D3",1), inject_variables=True) creating variables fx1..fx27 - Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19, fx20, fx21, fx22, fx23, fx24, fx25, fx26 + Defining fx0, ..., fx26 sage: f.ideal_basis = [fx0**3, fx0 + fx3**4, fx2**2 - fx3, fx2 - fx3**2, fx4 - fx2] sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup sage: f.ideal_basis = [poly_to_tup(p) for p in f.ideal_basis] @@ -134,10 +135,11 @@ cpdef _backward_subs(factory): for var in reversed(factory._poly_ring.gens()): sextuple = factory._var_to_sextuple[var] rhs = factory._fvars[sextuple] - d = {var_idx : factory._fvars[factory._idx_to_sextuple[var_idx]] for var_idx in variables(rhs) if var_idx in factory._solved} + d = {var_idx: factory._fvars[factory._idx_to_sextuple[var_idx]] + for var_idx in variables(rhs) if var_idx in factory._solved} if d: kp = compute_known_powers(get_variables_degrees([rhs]), d, one) - factory._fvars[sextuple] = tuple(subs_squares(subs(rhs,kp,one),factory._ks).items()) + factory._fvars[sextuple] = tuple(subs_squares(subs(rhs,kp,one), factory._ks).items()) ###################################### ### Fast fusion coefficients cache ### @@ -175,7 +177,7 @@ cdef _fmat(fvars, _Nk_ij, id_anyon, a, b, c, d, x, y): Cython version of fmat class method. Using cdef for fastest dispatch """ if _Nk_ij(a,b,x) == 0 or _Nk_ij(x,c,d) == 0 or _Nk_ij(b,c,y) == 0 or _Nk_ij(a,y,d) == 0: - return 0 + return 0 #Some known F-symbols if a == id_anyon: if x == b and y == d: @@ -196,15 +198,16 @@ cdef _fmat(fvars, _Nk_ij, id_anyon, a, b, c, d, x, y): cdef req_cy(tuple basis, r_matrix, dict fvars, Nk_ij, id_anyon, tuple sextuple): """ - Given an FMatrix factory and a sextuple, return a hexagon equation as a polynomial object + Given an FMatrix factory and a sextuple, return a hexagon equation + as a polynomial object. """ a, b, c, d, e, g = sextuple #To add typing we need to ensure all fmats.fmat are of the same type? #Return fmats._poly_ring.zero() and fmats._poly_ring.one() instead of 0 and 1? - lhs = r_matrix(a,c,e,base_coercion=False)*_fmat(fvars,Nk_ij,id_anyon,a,c,b,d,e,g)*r_matrix(b,c,g,base_coercion=False) + lhs = r_matrix(a,c,e,base_coercion=False) * _fmat(fvars,Nk_ij,id_anyon,a,c,b,d,e,g) * r_matrix(b,c,g,base_coercion=False) rhs = 0 for f in basis: - rhs += _fmat(fvars,Nk_ij,id_anyon,c,a,b,d,e,f)*r_matrix(f,c,d,base_coercion=False)*_fmat(fvars,Nk_ij,id_anyon,a,b,c,d,f,g) + rhs += _fmat(fvars,Nk_ij,id_anyon,c,a,b,d,e,f) * r_matrix(f,c,d,base_coercion=False) * _fmat(fvars,Nk_ij,id_anyon,a,b,c,d,f,g) return lhs-rhs @cython.wraparound(False) @@ -212,11 +215,11 @@ cdef req_cy(tuple basis, r_matrix, dict fvars, Nk_ij, id_anyon, tuple sextuple): @cython.cdivision(True) cdef get_reduced_hexagons(factory, tuple mp_params): """ - Set up and reduce the hexagon equations corresponding to this worker + Set up and reduce the hexagon equations corresponding to this worker. """ #Set up multiprocessing parameters global worker_results - cdef child_id, n_proc + cdef int child_id, n_proc cdef unsigned long i child_id, n_proc = mp_params cdef tuple sextuple, red @@ -232,7 +235,7 @@ cdef get_reduced_hexagons(factory, tuple mp_params): cdef dict _ks = factory._ks #Computation loop - for i, sextuple in enumerate(product(basis,repeat=6)): + for i, sextuple in enumerate(product(basis, repeat=6)): if i % n_proc == child_id: he = req_cy(basis,r_matrix,fvars,_Nk_ij,id_anyon,sextuple) if he: @@ -244,14 +247,15 @@ cdef get_reduced_hexagons(factory, tuple mp_params): worker_results.append(red) cdef MPolynomial_libsingular feq_cy(tuple basis, fvars, Nk_ij, id_anyon, zero, tuple nonuple, bint prune=False): - """ - Given an FMatrix factory and a nonuple, return a pentagon equation as a polynomial object + r""" + Given an FMatrix factory and a nonuple, return a pentagon equation + as a polynomial object. """ a, b, c, d, e, f, g, k, l = nonuple - cdef lhs = _fmat(fvars,Nk_ij,id_anyon,f,c,d,e,g,l)*_fmat(fvars,Nk_ij,id_anyon,a,b,l,e,f,k) + lhs = _fmat(fvars,Nk_ij,id_anyon,f,c,d,e,g,l) * _fmat(fvars,Nk_ij,id_anyon,a,b,l,e,f,k) if lhs == 0 and prune: # it is believed that if lhs=0, the equation carries no new information return zero - cdef rhs = zero + rhs = zero for h in basis: rhs += _fmat(fvars,Nk_ij,id_anyon,a,b,c,g,f,h)*_fmat(fvars,Nk_ij,id_anyon,a,h,d,e,g,k)*_fmat(fvars,Nk_ij,id_anyon,b,c,d,k,h,l) return lhs - rhs @@ -260,8 +264,8 @@ cdef MPolynomial_libsingular feq_cy(tuple basis, fvars, Nk_ij, id_anyon, zero, t @cython.nonecheck(False) @cython.cdivision(True) cdef get_reduced_pentagons(factory, tuple mp_params): - """ - Set up and reduce the pentagon equations corresponding to this worker + r""" + Set up and reduce the pentagon equations corresponding to this worker. """ #Set up multiprocessing parameters global worker_results @@ -286,9 +290,9 @@ cdef get_reduced_pentagons(factory, tuple mp_params): for i in range(len(basis)**9): nonuple = next(it) if i % n_proc == child_id: - pe = feq_cy(basis,fvars,_Nk_ij,id_anyon,zero,nonuple,prune=True) + pe = feq_cy(basis, fvars, _Nk_ij, id_anyon, zero, nonuple, prune=True) if pe: - red = reduce_poly_dict(pe.dict(),_nnz,_ks,one) + red = reduce_poly_dict(pe.dict(), _nnz, _ks, one) #Avoid pickling cyclotomic coefficients red = _flatten_coeffs(red) @@ -296,8 +300,8 @@ cdef get_reduced_pentagons(factory, tuple mp_params): worker_results.append(red) cpdef update_reduce(factory, tuple eq_tup): - """ - Substitute known values, known squares, and reduce! + r""" + Substitute known values, known squares, and reduce. EXAMPLES:: @@ -334,7 +338,7 @@ cpdef update_reduce(factory, tuple eq_tup): worker_results.append(red) cpdef compute_gb(factory, tuple args): - """ + r""" Compute the reduced Groebner basis for given equations iterable. EXAMPLES:: @@ -345,15 +349,17 @@ cpdef compute_gb(factory, tuple args): ....: set_start_method('fork') # context can be set only once ....: except RuntimeError: ....: pass - sage: f.get_defining_equations('hexagons',worker_pool=Pool(),output=False) - sage: partition = f._partition_eqns() + sage: f.get_defining_equations('hexagons',worker_pool=Pool(),output=False) # long time + sage: partition = f._partition_eqns() # long time Partitioned 261 equations into 57 components of size: - [24, 12, 12, 12, 12, 12, 12, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1] + [24, 12, 12, 12, 12, 12, 12, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 1, 1, 1, 1, 1] sage: from sage.combinat.root_system.fast_parallel_fmats_methods import compute_gb, collect_eqns - sage: args = (partition[(29, 40, 83, 111, 148, 154)], "degrevlex") - sage: compute_gb(f, args) + sage: args = (partition[(29, 40, 83, 111, 148, 154)], "degrevlex") # long time + sage: compute_gb(f, args) # long time sage: from sage.combinat.root_system.poly_tup_engine import _unflatten_coeffs - sage: [f._tup_to_fpoly(_unflatten_coeffs(f._field,t)) for t in collect_eqns(0)] + sage: [f._tup_to_fpoly(_unflatten_coeffs(f._field,t)) for t in collect_eqns(0)] # long time [fx83*fx154 - fx111, fx29^2*fx154 + fx29, fx29*fx148 - fx40, @@ -369,12 +375,12 @@ cpdef compute_gb(factory, tuple args): cdef list eqns, sorted_vars eqns, term_order = args #Define smaller poly ring in component vars - sorted_vars = list() + sorted_vars = [] cdef tuple eq_tup cdef int fx for eq_tup in eqns: - for fx in variables(eq_tup): - sorted_vars.append(fx) + for fx in variables(eq_tup): + sorted_vars.append(fx) sorted_vars = sorted(set(sorted_vars)) cdef MPolynomialRing_libsingular R = PolynomialRing(factory._FR.field(),len(sorted_vars),'a',order=term_order) @@ -401,10 +407,10 @@ cpdef compute_gb(factory, tuple args): worker_results.append(t) cpdef update_child_fmats(factory, tuple data_tup): - """ + r""" One-to-all communication used to update FMatrix object after each triangular elim step. We must update the algorithm's state values. These are: - _fvars, _solved, _ks, _var_degs, _nnz, and _kp. + ``_fvars``, ``_solved``, ``_ks``, ``_var_degs``, ``_nnz``, and ``_kp``. TESTS:: @@ -438,7 +444,7 @@ cpdef update_child_fmats(factory, tuple data_tup): ################ cpdef collect_eqns(proc): - """ + r""" Helper function for returning processed results back to parent process. Trivial reducer: simply collects objects with the same key in the worker. @@ -462,8 +468,8 @@ cpdef collect_eqns(proc): """ #Discard the zero polynomial global worker_results - reduced = set(worker_results)-set([tuple()]) - worker_results = list() + reduced = set(worker_results) - set([tuple()]) + worker_results = [] return list(reduced) ############################## @@ -503,7 +509,7 @@ cpdef executor(params): of object `X` in each worker, so we may construct references to forked copies of `X` using an ``id`` obtained in the parent process. - TESTS: + TESTS:: sage: from sage.combinat.root_system.fast_parallel_fmats_methods import executor sage: fmats = FMatrix(FusionRing("A1",3)) @@ -529,8 +535,8 @@ cpdef executor(params): #################### cdef feq_verif(factory, fvars, Nk_ij, id_anyon, tuple nonuple, float tol=5e-8): - """ - Check the pentagon equation corresponding to the given nonuple + r""" + Check the pentagon equation corresponding to the given nonuple. """ global worker_results a, b, c, d, e, f, g, k, l = nonuple @@ -548,8 +554,9 @@ cdef feq_verif(factory, fvars, Nk_ij, id_anyon, tuple nonuple, float tol=5e-8): @cython.nonecheck(False) @cython.cdivision(True) cdef pent_verify(factory, tuple mp_params): - """ - Generate all the pentagon equations assigned to this process, and reduce them + r""" + Generate all the pentagon equations assigned to this process, + and reduce them. """ child_id, n_proc, verbose = mp_params cdef float t0 @@ -560,8 +567,9 @@ cdef pent_verify(factory, tuple mp_params): Nk_ij = factory._FR.Nk_ij cdef dict fvars = factory._fvars id_anyon = factory._FR.one() - for i, nonuple in enumerate(product(factory._FR.basis(),repeat=9)): + for i, nonuple in enumerate(product(factory._FR.basis(), repeat=9)): if i % n_proc == child_id: - feq_verif(factory,fvars,Nk_ij,id_anyon,nonuple) + feq_verif(factory,fvars,Nk_ij,id_anyon,nonuple) if i % 50000000 == 0 and i and verbose: - print("{:5d}m equations checked... {} potential misses so far...".format(i // 1000000,len(worker_results))) + print("{:5d}m equations checked... {} potential misses so far...".format(i // 1000000,len(worker_results))) + diff --git a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pxd b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pxd index 4dd122a7fbf..a032a509637 100644 --- a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pxd +++ b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pxd @@ -1,3 +1,4 @@ cpdef _unflatten_entries(factory, list entries) cpdef executor(params) cpdef collect_results(proc) + diff --git a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx index a40a5f136c1..4fca46e377f 100644 --- a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx @@ -24,14 +24,14 @@ worker_results = list() ############### cpdef mid_sig_ij(fusion_ring,row,col,a,b): - """ - Compute the (xi,yi), (xj,yj) entry of generator braiding the middle two strands - in the tree b -> xi # yi -> (a # a) # (a # a), which results in a sum over j - of trees b -> xj # yj -> (a # a) # (a # a) + r""" + Compute the (xi,yi), (xj,yj) entry of generator braiding the middle two + strands in the tree b -> xi # yi -> (a # a) # (a # a), which results in + a sum over j of trees b -> xj # yj -> (a # a) # (a # a) .. WARNING:: - This method assumes F-matrices are orthogonal + This method assumes F-matrices are orthogonal. EXAMPLES:: @@ -60,7 +60,14 @@ cpdef mid_sig_ij(fusion_ring,row,col,a,b): sage: FR.get_computational_basis(a2,a2,4) # long time [(a2, a2), (a2, a0), (a0, a2)] sage: to_opt(mid_sig_ij(FR,(a2, a0),(a2, a2),a2,a2)) # long time - -2024728666370660589/10956322398441542221*a1^30 - 34142146914395596291/21912644796883084442*a1^28 - 21479437628091413631/21912644796883084442*a1^26 + 260131910217202103829/21912644796883084442*a1^24 + 69575612911670713183/10956322398441542221*a1^22 + 25621808994337724689/1992058617898462222*a1^20 - 1975139725303994650417/21912644796883084442*a1^18 - 1315664901396537703585/21912644796883084442*a1^16 - 2421451803369354765026/10956322398441542221*a1^14 - 5963323855935165859057/21912644796883084442*a1^12 - 4477124943233705460859/21912644796883084442*a1^10 - 2001454824483021618178/10956322398441542221*a1^8 - 2120319455379289595185/21912644796883084442*a1^6 - 15722612944437234961/755608441271830498*a1^4 - 39862668562651453480/10956322398441542221*a1^2 - 6967145776903524195/10956322398441542221 + -2024728666370660589/10956322398441542221*a1^30 - 34142146914395596291/21912644796883084442*a1^28 + - 21479437628091413631/21912644796883084442*a1^26 + 260131910217202103829/21912644796883084442*a1^24 + + 69575612911670713183/10956322398441542221*a1^22 + 25621808994337724689/1992058617898462222*a1^20 + - 1975139725303994650417/21912644796883084442*a1^18 - 1315664901396537703585/21912644796883084442*a1^16 + - 2421451803369354765026/10956322398441542221*a1^14 - 5963323855935165859057/21912644796883084442*a1^12 + - 4477124943233705460859/21912644796883084442*a1^10 - 2001454824483021618178/10956322398441542221*a1^8 + - 2120319455379289595185/21912644796883084442*a1^6 - 15722612944437234961/755608441271830498*a1^4 + - 39862668562651453480/10956322398441542221*a1^2 - 6967145776903524195/10956322398441542221 sage: FR = FusionRing("G2",2) # long time sage: FR.fusion_labels("g",inject_variables=True) # long time sage: FR.get_computational_basis(g1,g2,4) # long time @@ -96,7 +103,7 @@ cpdef odd_one_out_ij(fusion_ring,xi,xj,a,b): .. WARNING:: - This method assumes F-matrices are orthogonal + This method assumes F-matrices are orthogonal. EXAMPLES:: @@ -127,7 +134,14 @@ cpdef odd_one_out_ij(fusion_ring,xi,xj,a,b): sage: FR.get_computational_basis(a2,a2,3) # long time [(a2,), (a0,)] sage: to_opt(odd_one_out_ij(FR,a0,a2,a2,a2)) # long time - 6341990144855406911/21912644796883084442*a1^30 + 47313529044493641571/21912644796883084442*a1^28 - 6964289120109414595/10956322398441542221*a1^26 - 406719371329322780627/21912644796883084442*a1^24 + 87598732372849355687/10956322398441542221*a1^22 - 456724726845194775/19723352652460022*a1^20 + 3585892725441116840515/21912644796883084442*a1^18 - 645866255979227573282/10956322398441542221*a1^16 + 7958479159087829772639/21912644796883084442*a1^14 + 789748976956837633826/10956322398441542221*a1^12 + 3409710648897945752185/21912644796883084442*a1^10 + 903956381582048110980/10956322398441542221*a1^8 + 192973084151342020307/21912644796883084442*a1^6 - 9233312083438019435/755608441271830498*a1^4 + 667869266552877781/10956322398441542221*a1^2 + 17644302696056968099/21912644796883084442 + 6341990144855406911/21912644796883084442*a1^30 + 47313529044493641571/21912644796883084442*a1^28 + - 6964289120109414595/10956322398441542221*a1^26 - 406719371329322780627/21912644796883084442*a1^24 + + 87598732372849355687/10956322398441542221*a1^22 - 456724726845194775/19723352652460022*a1^20 + + 3585892725441116840515/21912644796883084442*a1^18 - 645866255979227573282/10956322398441542221*a1^16 + + 7958479159087829772639/21912644796883084442*a1^14 + 789748976956837633826/10956322398441542221*a1^12 + + 3409710648897945752185/21912644796883084442*a1^10 + 903956381582048110980/10956322398441542221*a1^8 + + 192973084151342020307/21912644796883084442*a1^6 - 9233312083438019435/755608441271830498*a1^4 + + 667869266552877781/10956322398441542221*a1^2 + 17644302696056968099/21912644796883084442 sage: FR = FusionRing("G2",2) # long time sage: FR.fusion_labels("g",inject_variables=True) # long time sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time (~11 s) @@ -155,7 +169,7 @@ odd_one_out_ij = cached_function(odd_one_out_ij, name='odd_one_out_ij') @cython.nonecheck(False) @cython.cdivision(True) cdef sig_2k(fusion_ring, tuple args): - """ + r""" Compute entries of the 2k-th braid generator """ #Pre-compute common parameters for efficiency @@ -163,6 +177,7 @@ cdef sig_2k(fusion_ring, tuple args): _Nk_ij = fusion_ring.Nk_ij one = fusion_ring.one() + cdef int child_id, n_proc child_id, n_proc, fn_args = args k, a, b, n_strands = fn_args cdef int ctr = -1 @@ -174,9 +189,9 @@ cdef sig_2k(fusion_ring, tuple args): cdef set coords = set() cdef int i #Avoid pickling cyclotomic field element objects - must_flatten_coeff = fusion_ring.fvars_field() != QQbar + cdef bint must_flatten_coeff = fusion_ring.fvars_field() != QQbar for i in range(dim): - for f,e,q in product(fusion_ring.basis(),repeat=3): + for f,e,q in product(fusion_ring.basis(), repeat=3): #Distribute work amongst processes ctr += 1 if ctr % n_proc != child_id: @@ -184,7 +199,8 @@ cdef sig_2k(fusion_ring, tuple args): #Compute appropriate possible nonzero row index nnz_pos = list(comp_basis[i]) - nnz_pos[k-1:k+1] = f,e + nnz_pos[k-1] = f + nnz_pos[k] = e #Handle the special case k = 1 if k > 1: nnz_pos[n_strands//2+k-2] = q @@ -223,14 +239,14 @@ cdef sig_2k(fusion_ring, tuple args): if must_flatten_coeff: entry = entry.list() - worker_results.append(((basis_dict[nnz_pos],i), entry)) + worker_results.append(((basis_dict[nnz_pos], i), entry)) @cython.nonecheck(False) @cython.cdivision(True) cdef odd_one_out(fusion_ring, tuple args): """ - Compute entries of the rightmost braid generator, in case we have an odd number - of strands + Compute entries of the rightmost braid generator, in case we have an + odd number of strands. """ #Pre-compute common parameters for efficiency _fvars = fusion_ring.fmats._fvars @@ -238,16 +254,17 @@ cdef odd_one_out(fusion_ring, tuple args): one = fusion_ring.one() global worker_results + cdef int child_id, n_proc child_id, n_proc, fn_args = args a, b, n_strands = fn_args - cdef ctr = -1 + cdef int ctr = -1 #Get computational basis - comp_basis = fusion_ring.get_computational_basis(a,b,n_strands) - basis_dict = { elt : i for i, elt in enumerate(comp_basis) } + comp_basis = fusion_ring.get_computational_basis(a, b, n_strands) + basis_dict = {elt: i for i, elt in enumerate(comp_basis)} dim = len(comp_basis) #Avoid pickling cyclotomic field element objects - must_flatten_coeff = fusion_ring.fvars_field() != QQbar + cdef bint must_flatten_coeff = fusion_ring.fvars_field() != QQbar for i in range(dim): for f, q in product(fusion_ring.basis(),repeat=2): #Distribute work amongst processes @@ -274,7 +291,7 @@ cdef odd_one_out(fusion_ring, tuple args): if must_flatten_coeff: entry = entry.list() - worker_results.append(((basis_dict[nnz_pos],i), entry)) + worker_results.append(((basis_dict[nnz_pos], i), entry)) continue top_left = m[0] if n_strands > 5: @@ -321,7 +338,7 @@ cpdef collect_results(proc): #Discard the zero polynomial global worker_results reduced = worker_results - worker_results = list() + worker_results = [] return reduced ############################## @@ -384,8 +401,8 @@ cpdef _unflatten_entries(fusion_ring, list entries): Restore cyclotomic coefficient object from its tuple of rational coefficients representation. - Used to circumvent pickling issue introduced by PARI settigs in trac - ticket #30537 + Used to circumvent pickling issue introduced by PARI settigs + in :trac:`30537`. EXAMPLES:: @@ -400,7 +417,7 @@ cpdef _unflatten_entries(fusion_ring, list entries): """ F = fusion_ring.fvars_field() fm = fusion_ring.fmats - must_unflatten = F != QQbar - if must_unflatten: + if F != QQbar: for i, (coord, entry) in enumerate(entries): entries[i] = (coord, F(entry)) + diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 09ca6d348bf..53a5a1438c2 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -14,7 +14,6 @@ from itertools import product, zip_longest from multiprocessing import Pool, set_start_method from sage.combinat.q_analogues import q_int -import sage.combinat.root_system.f_matrix as FMatrix from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import ( collect_results, executor, _unflatten_entries @@ -385,11 +384,12 @@ def test_braid_representation(self, max_strands=6): - ``max_strands`` -- (default: 6): maximum number of braid group strands Create a braid group representation using :meth:`get_braid_generators` - and confirms the braid relations. This test indirectly partially verifies - the correctness of the orthogonal F-matrix solver. If the code were - incorrect the method would not be deterministic because the fusing anyon - is chosen randomly. (A different choice is made for each number of strands tested.) - However the doctest is deterministic since it will always return True. + and confirms the braid relations. This test indirectly partially + verifies the correctness of the orthogonal F-matrix solver. If the + code were incorrect the method would not be deterministic because the + fusing anyon is chosen randomly. (A different choice is made for each + number of strands tested.) However the doctest is deterministic since + it will always return ``True``. EXAMPLES:: @@ -401,7 +401,7 @@ def test_braid_representation(self, max_strands=6): True """ if not self.is_multiplicity_free(): # Braid group representation is not available if self is not multiplicity free - return True + raise NotImplementedError("only implemented for multiplicity free fusion rings") b = self.basis() results = [] #Test with different numbers of strands @@ -552,9 +552,9 @@ def fvars_field(self): 2*a0 + 3*a2 sage: comp_basis, sig = A13.get_braid_generators(a2,a2,3,verbose=False) # long time (<3s) sage: A13.fvars_field() # long time - Number Field in a with defining polynomial y^32 - 8*y^30 + 18*y^28 - 44*y^26 + 93*y^24 - 56*y^22 + 2132*y^20 - 1984*y^18 + 19738*y^16 - 28636*y^14 + 77038*y^12 - 109492*y^10 + 92136*y^8 - 32300*y^6 + 5640*y^4 - 500*y^2 + 25 + Number Field in a with defining polynomial y^32 - ... - 500*y^2 + 25 sage: a2.q_dimension().parent() # long time - Number Field in a with defining polynomial y^32 - 8*y^30 + 18*y^28 - 44*y^26 + 93*y^24 - 56*y^22 + 2132*y^20 - 1984*y^18 + 19738*y^16 - 28636*y^14 + 77038*y^12 - 109492*y^10 + 92136*y^8 - 32300*y^6 + 5640*y^4 - 500*y^2 + 25 + Number Field in a with defining polynomial y^32 - ... - 500*y^2 + 25 sage: A13.field() Cyclotomic Field of order 40 and degree 16 @@ -1050,17 +1050,17 @@ def D_minus(self, base_coercion=True): return self._basecoer(ret) def is_multiplicity_free(self): - """ - Return True if the fusion multiplicities - :meth:`Nk_ij` are bounded by 1. The :class:`FMatrix` - is available only for multiplicity free instances of - :class:`FusionRing`. + r""" + Return ``True`` if the fusion multiplicities + :meth:`Nk_ij` are bounded by 1. + + The :class:`FMatrix` is available only for multiplicity free + instances of :class:`FusionRing`. EXAMPLES:: sage: [FusionRing(ct,k).is_multiplicity_free() for ct in ("A1","A2","B2","C3") for k in (1,2,3)] [True, True, True, True, True, False, True, True, False, True, False, False] - """ ct = self.cartan_type() k = self.fusion_level() @@ -1080,8 +1080,8 @@ def is_multiplicity_free(self): ################################### def get_computational_basis(self,a,b,n_strands): - """ - Return the so-called computational basis for `\\text{Hom}(b, a^n)`. + r""" + Return the so-called computational basis for `\text{Hom}(b, a^n)`. INPUT: @@ -1089,8 +1089,8 @@ def get_computational_basis(self,a,b,n_strands): - ``b`` -- another basis element - ``n_strands`` -- the number of strands for a braid group - Let `n=` ``n_strands`` and let `k` be the greatest integer `\leqslant n/2`. - The braid group acts on `\\text{Hom}(b,a^n)`. This action + Let `n=` ``n_strands`` and let `k` be the greatest integer `\leq n/2`. + The braid group acts on `\text{Hom}(b,a^n)`. This action is computed in :meth:`get_braid_generators`. This method returns the computational basis in the form of a list of fusion trees. Each tree is represented by an `(n-2)`-tuple @@ -1104,18 +1104,18 @@ def get_computational_basis(self,a,b,n_strands): .. MATH:: - \\begin{array}{l} - b \\in l_{k-2} \otimes m_{k},\\\\ - l_{k-2} \\in l_{k-3} \otimes m_{k-1},\\\\ - \\cdots,\\\\ - l_2 \\in l_1\otimes m_3,\\\\ - l_1 \\in m_1\otimes m_2. + \begin{array}{l} + b \in l_{k-2} \otimes m_{k},\\ + l_{k-2} \in l_{k-3} \otimes m_{k-1},\\ + \cdots,\\ + l_2 \in l_1 \otimes m_3,\\ + l_1 \in m_1 \otimes m_2. \end{array} - where `z \\in x\otimes y` means `N_{xy}^z\\neq 0`. + where `z \in x \otimes y` means `N_{xy}^z \neq 0`. As a computational device when ``n_strands`` is odd, we pad the - vector `(m_1,\ldots,m_k)` with an additional `m_{k+1}` equal to `a`. + vector `(m_1, \ldots, m_k)` with an additional `m_{k+1}` equal to `a`. However, this `m_{k+1}` does *not* appear in the output of this method. The following example appears in Section 3.1 of [CW2015]_. @@ -1148,9 +1148,9 @@ def _get_trees(fr,top_row,root): @lazy_attribute def fmats(self): - """ - Construct an FMatrix factory to solve the pentagon relations and - organize the resulting F-symbols. + r""" + Construct an :class:`FMatrix` factory to solve the pentagon relations + and organize the resulting F-symbols. We only need this attribute to compute braid group representations. @@ -1160,34 +1160,37 @@ def fmats(self): sage: A15.fmats F-Matrix factory for The Fusion Ring of Type A1 and level 5 with Integer Ring coefficients """ - return FMatrix.FMatrix(self) + from sage.combinat.root_system.f_matrix import FMatrix + return FMatrix(self) - def _emap(self,mapper,input_args,worker_pool=None): - """ - Apply the given mapper to each element of the given input iterable and - return the results (with no duplicates) in a list. This method applies the - mapper in parallel if a worker_pool is provided. + def _emap(self, mapper, input_args, worker_pool=None): + r""" + Apply the given mapper to each element of the given input iterable + and return the results (with no duplicates) in a list. INPUT: - - ``mapper`` -- a string specifying the name of a function defined in the - ``fast_parallel_fusion_ring_braid_repn`` module. + - ``mapper`` -- a string specifying the name of a function defined + in the ``fast_parallel_fusion_ring_braid_repn`` module - ``input_args`` -- a tuple of arguments to be passed to mapper + This method applies the mapper in parallel if a ``worker_pool`` + is provided. + .. NOTE:: - If ``worker_pool`` is not provided, function maps and reduces on a - single process. - If ``worker_pool`` is provided, the function attempts to determine - whether it should use multiprocessing based on the length of the - input iterable. If it can't determine the length of the input - iterable then it uses multiprocessing with the default chunksize of - `1` if chunksize is not explicitly provided. + If ``worker_pool`` is not provided, function maps and reduces on + a single process. If ``worker_pool`` is provided, the function + attempts to determine whether it should use multiprocessing + based on the length of the input iterable. If it cannot determine + the length of the input iterable then it uses multiprocessing + with the default chunksize of `1` if chunksize is not + explicitly provided. EXAMPLES:: sage: FR = FusionRing("A1",4) - sage: FR.fusion_labels(['idd','one','two','three','four'],inject_variables=True) + sage: FR.fusion_labels(['idd','one','two','three','four'], inject_variables=True) sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time sage: len(FR._emap('sig_2k',(1,one,one,5))) # long time 13 @@ -1211,7 +1214,7 @@ def _emap(self,mapper,input_args,worker_pool=None): if no_mp: results = collect_results(0) else: - results = list() + results = [] for worker_results in worker_pool.imap_unordered(collect_results,range(worker_pool._processes),chunksize=1): results.extend(worker_results) return results @@ -1225,28 +1228,31 @@ def get_braid_generators(self, warm_start="", use_mp=True, verbose=True): - """ - Compute generators of the Artin braid group on `n=` ``n_strands`` - strands. If `a` = ``fusing_anyon`` and `b` = ``total_charge_anyon`` - the generators are endomorphisms of `\\text{Hom}(b, a^n)`. + r""" + Compute generators of the Artin braid group on ``n_strands`` strands. + + If `a = ` ``fusing_anyon`` and `b = ` ``total_charge_anyon`` + the generators are endomorphisms of `\text{Hom}(b, a^n)`. INPUT: - - ``fusing_anyon`` -- a basis element of self - - ``total_charge_anyon`` -- a basis element of self + - ``fusing_anyon`` -- a basis element of ``self`` + - ``total_charge_anyon`` -- a basis element of ``self`` - ``n_strands`` -- a positive integer greater than 2 - - ``checkpoint`` -- (optional) a boolean indicating whether the - F-matrix solver should pickle checkpoints. + - ``checkpoint`` -- (default: ``False``) a boolean indicating + whether the F-matrix solver should pickle checkpoints - ``save_results`` -- (optional) a string indicating the name of - a file in which to pickle computed F-symbols for later use. + a file in which to pickle computed F-symbols for later use - ``warm_start`` -- (optional) a string indicating the name of a pickled checkpoint file to "warm" start the F-matrix solver. The pickle may be a checkpoint generated by the solver, or a file containing solver results. If all F-symbols are known, we don't run the solver again. - - ``use_mp`` -- (optional) a boolean indicating whether to use - multiprocessing to speed up the computation. This is highly - recommended. + - ``use_mp`` -- (default: ``True``) a boolean indicating whether + to use multiprocessing to speed up the computation; this is + highly recommended + - ``verbose`` -- (default: ``True``) boolean indicating whether + to be verbose with the computation For more information on the optional parameters, see :meth:`find_orthogonal_solution` of the :class:`FMatrix` module. @@ -1273,8 +1279,8 @@ def get_braid_generators(self, some cases these will be represented as sparse matrices. In the following example we compute a 5-dimensional braid group - representation on 5 strands associated to the spin representation in the - modular tensor category `SU(2)_4 \cong SO(3)_2`. + representation on 5 strands associated to the spin representation + in the modular tensor category `SU(2)_4 \cong SO(3)_2`. EXAMPLES:: @@ -1291,9 +1297,9 @@ def get_braid_generators(self, True sage: len(comp_basis) == 5 # long time True - """ - assert int(n_strands) > 2, "The number of strands must be an integer greater than 2" + if n_strands < 3: + raise ValueError("the number of strands must be an integer at least 3") #Construct associated FMatrix object and solve for F-symbols if self.fmats._chkpt_status < 7: self.fmats.find_orthogonal_solution(checkpoint=checkpoint, @@ -1315,18 +1321,17 @@ def get_braid_generators(self, comp_basis = self.get_computational_basis(a,b,n_strands) d = len(comp_basis) if verbose: - print("Computing an {}-dimensional representation of the Artin braid group on {} strands...".format(d,n_strands)) + print("Computing an {}-dimensional representation of the Artin braid group on {} strands...".format(d, n_strands)) #Compute diagonal odd-indexed generators using the 3j-symbols - gens = { 2*i+1 : diagonal_matrix(self.r_matrix(a,a,c[i]) for c in comp_basis) for i in range(n_strands//2) } + gens = {2*i+1: diagonal_matrix(self.r_matrix(a,a,c[i]) for c in comp_basis) for i in range(n_strands//2)} #Compute even-indexed generators using F-matrices - for k in range(1,n_strands//2): + for k in range(1, n_strands//2): entries = self._emap('sig_2k',(k,a,b,n_strands),pool) #Build cyclotomic field element objects from tuple of rationals repn _unflatten_entries(self, entries) - gens[2*k] = matrix(dict(entries)) #If n_strands is odd, we compute the final generator @@ -1335,15 +1340,16 @@ def get_braid_generators(self, #Build cyclotomic field element objects from tuple of rationals repn _unflatten_entries(self, entries) - gens[n_strands-1] = matrix(dict(entries)) return comp_basis, [gens[k] for k in sorted(gens)] - def gens_satisfy_braid_gp_rels(self,sig): - """ - Return True if the matrices in the list ``sig`` satisfy - the braid relations. This if `n` is the cardinality of ``sig``, this + def gens_satisfy_braid_gp_rels(self, sig): + r""" + Return ``True`` if the matrices in the list ``sig`` satisfy + the braid relations. + + This if `n` is the cardinality of ``sig``, this confirms that these matrices define a representation of the Artin braid group on `n+1` strands. Tests correctness of :meth:`get_braid_generators`. diff --git a/src/sage/combinat/root_system/poly_tup_engine.pxd b/src/sage/combinat/root_system/poly_tup_engine.pxd index 62f7401e7b0..7ca654259d3 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pxd +++ b/src/sage/combinat/root_system/poly_tup_engine.pxd @@ -19,3 +19,4 @@ cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq, NumberFiel cdef tuple _flatten_coeffs(tuple eq_tup) cpdef tuple _unflatten_coeffs(field, tuple eq_tup) cdef int has_appropriate_linear_term(tuple eq_tup) + diff --git a/src/sage/combinat/root_system/poly_tup_engine.pyx b/src/sage/combinat/root_system/poly_tup_engine.pyx index 24baf59fd46..5d662efc772 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pyx +++ b/src/sage/combinat/root_system/poly_tup_engine.pyx @@ -13,9 +13,9 @@ Arithmetic Engine for polynomials as tuples ########### cpdef inline tuple poly_to_tup(MPolynomial_libsingular poly): - """ + r""" Convert a polynomial object into the internal representation as tuple of - (ETuple exp, NumberFieldElement coeff) pairs + ``(ETuple exp, NumberFieldElement coeff)`` pairs. EXAMPLES:: @@ -34,14 +34,12 @@ cpdef inline MPolynomial_libsingular _tup_to_poly(tuple eq_tup, MPolynomialRing_ Inverse of :meth:`poly_to_tup`: - ``poly_to_tup(tup_to_poly(eq_tup, ring)) == eq_tup`` and - - ``tup_to_poly(poly_to_tup(eq), eq.parent()) == eq``. + - ``poly_to_tup(tup_to_poly(eq_tup, ring)) == eq_tup`` and + - ``tup_to_poly(poly_to_tup(eq), eq.parent()) == eq``. .. NOTE:: - Assumes ``parent.ngens() == len(exp_tup) for exp_tup, c in eq_tup``. - + Assumes ``all(parent.ngens() == len(exp_tup) for exp_tup, c in eq_tup)``. This method is meant for internal use. .. WARNING:: @@ -62,7 +60,7 @@ cpdef inline MPolynomial_libsingular _tup_to_poly(tuple eq_tup, MPolynomialRing_ sage: _tup_to_poly(poly_to_tup(poly), parent=R) == poly True - TESTS: + TESTS:: sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup, _tup_to_poly sage: R. = PolynomialRing(CyclotomicField(20)) @@ -77,27 +75,26 @@ cpdef inline MPolynomial_libsingular _tup_to_poly(tuple eq_tup, MPolynomialRing_ return parent._element_constructor_(dict(eq_tup), check=False) cdef inline tuple _flatten_coeffs(tuple eq_tup): - """ + r""" Flatten cyclotomic coefficients to a representation as a tuple of rational coefficients. This is used to avoid pickling cyclotomic coefficient objects, which fails with new PARI settings introduced in trac ticket #30537 """ - cdef list flat = list() - cdef ETuple exp + cdef list flat = [] cdef NumberFieldElement_absolute cyc_coeff for exp, cyc_coeff in eq_tup: flat.append((exp, tuple(cyc_coeff._coefficients()))) return tuple(flat) cpdef tuple _unflatten_coeffs(field, tuple eq_tup): - """ + r""" Restore cyclotomic coefficient object from its tuple of rational coefficients representation. - Used to circumvent pickling issue introduced by PARI settigs in trac - ticket #30537 + Used to circumvent pickling issue introduced by PARI settigs + in :trac:`30537`. EXAMPLES:: @@ -112,8 +109,7 @@ cpdef tuple _unflatten_coeffs(field, tuple eq_tup): sage: _unflatten_coeffs(fm.field(), flat_poly_tup) == poly_to_tup(p) True """ - cdef list unflat = list() - cdef ETuple exp + cdef list unflat = [] for exp, coeff_tup in eq_tup: unflat.append((exp, field(list(coeff_tup)))) return tuple(unflat) @@ -123,7 +119,7 @@ cpdef tuple _unflatten_coeffs(field, tuple eq_tup): ################################# cdef inline int has_appropriate_linear_term(tuple eq_tup): - """ + r""" Determine whether the given tuple of pairs (of length 2) contains an *appropriate* linear term. @@ -140,8 +136,9 @@ cdef inline int has_appropriate_linear_term(tuple eq_tup): Otherwise, the method returns -1. """ - max_var = variables(eq_tup)[0] + max_var = degrees(eq_tup).nonzero_positions()[0] cdef ETuple m + cdef int i for i in range(2): m = eq_tup[i][0] if m._nonzero == 1 and m._data[1] == 1 and m._data[0] == max_var and eq_tup[(i+1) % 2][0][max_var] == 0: @@ -196,7 +193,7 @@ cpdef inline tuple resize(tuple eq_tup, dict idx_map, int nvars): sage: from sage.rings.polynomial.polydict import ETuple sage: K = CyclotomicField(56) sage: poly_tup = ((ETuple([0,3,0,2]),K(2)), (ETuple([0,1,0,1]),K(-1)), (ETuple([0,0,0,0]),K(-2/3))) - sage: idx_map = { 1 : 0, 3 : 1 } + sage: idx_map = {1: 0, 3: 1} sage: resize(poly_tup,idx_map,2) (((3, 2), 2), ((1, 1), -1), ((0, 0), -2/3)) @@ -213,8 +210,8 @@ cpdef inline tuple resize(tuple eq_tup, dict idx_map, int nvars): cdef NumberFieldElement_absolute c cdef list resized = list() for exp, c in eq_tup: - new_e = ETuple({ idx_map[pos] : d for pos, d in exp.sparse_iter() }, nvars) - resized.append((new_e,c)) + new_e = ETuple({idx_map[pos]: d for pos, d in exp.sparse_iter()}, nvars) + resized.append((new_e, c)) return tuple(resized) ########################### @@ -222,20 +219,22 @@ cpdef inline tuple resize(tuple eq_tup, dict idx_map, int nvars): ########################### cdef inline ETuple degrees(tuple poly_tup): - """ - Return the maximal degree of each variable in the polynomial + r""" + Return the maximal degree of each variable in the polynomial. """ #Deal with the empty tuple, representing the zero polynomial - if not poly_tup: return ETuple() + if not poly_tup: + return ETuple() cdef ETuple max_degs, exp - max_degs = poly_tup[0][0] - for exp, c in poly_tup[1:]: - max_degs = max_degs.emax(exp) + cdef int i + max_degs = ( poly_tup[0])[0] + for i in range(1, len(poly_tup)): + max_degs = max_degs.emax( ( poly_tup[i])[0]) return max_degs cpdef ETuple get_variables_degrees(list eqns): - """ - Find maximum degrees for each variable in equations + r""" + Find maximum degrees for each variable in equations. EXAMPLES:: @@ -246,12 +245,13 @@ cpdef ETuple get_variables_degrees(list eqns): sage: get_variables_degrees([poly_to_tup(p) for p in polys]) (2, 1, 3) """ - if not eqns: return ETuple([]) - cdef tuple eq_tup + if not eqns: + return ETuple([]) cdef ETuple max_deg + cdef int i max_deg = degrees(eqns[0]) - for eq_tup in eqns[1:]: - max_deg = max_deg.emax(degrees(eq_tup)) + for i in range(1, len(eqns)): + max_deg = max_deg.emax(degrees( (eqns[i]) )) return max_deg cpdef list variables(tuple eq_tup): @@ -275,7 +275,7 @@ cpdef list variables(tuple eq_tup): return degrees(eq_tup).nonzero_positions() cpdef constant_coeff(tuple eq_tup): - """ + r""" Return the constant coefficient of the polynomial represented by given tuple. @@ -293,8 +293,8 @@ cpdef constant_coeff(tuple eq_tup): """ cdef ETuple exp for exp, coeff in eq_tup: - if exp.is_constant(): - return coeff + if exp.is_constant(): + return coeff return 0 cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map): @@ -311,7 +311,7 @@ cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map): x + 4*y + 9*z """ cdef ETuple exp - cdef list new_tup = list() + cdef list new_tup = [] for exp, coeff in eq_tup: new_tup.append((exp, coeff_map(coeff))) return tuple(new_tup) @@ -409,7 +409,7 @@ cdef dict remove_gcf(dict eq_dict, ETuple nonz): common_powers = nonz for exp, c in eq_dict.items(): common_powers = common_powers.emin(exp) - cdef dict ret = dict() + cdef dict ret = {} for exp, c in eq_dict.items(): ret[exp.esub(common_powers)] = c return ret @@ -422,11 +422,13 @@ cdef tuple to_monic(dict eq_dict, one): Here, the leading coefficient is chosen according to the degree reverse lexicographic ordering (default for multivariate polynomial rings). """ - if not eq_dict: return tuple() + if not eq_dict: + return () cdef list ord_monoms = sorted(eq_dict, key=monom_sortkey) cdef ETuple lm = ord_monoms[-1] cdef NumberFieldElement_absolute lc = eq_dict[lm] - if not lc: return tuple() + if not lc: + return () cdef list ret = [(lm, one)] inv_lc = lc.inverse_of_unit() cdef int i, n @@ -440,7 +442,8 @@ cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq, NumberFiel Return a tuple describing a monic polynomial with no known nonzero gcf and no known squares. """ - if not eq_dict: return tuple() + if not eq_dict: + return () cdef dict sq_rmvd = subs_squares(eq_dict, known_sq) cdef dict gcf_rmvd = remove_gcf(sq_rmvd, nonz) return to_monic(gcf_rmvd, one) @@ -454,14 +457,14 @@ cpdef dict compute_known_powers(ETuple max_deg, dict val_dict, one): Pre-compute powers of known values for efficiency when preparing to substitute into a list of polynomials. - INPUTS: + INPUT: - - ``max_deg`` -- an ``ETuple`` indicating the maximal degree of - each variable. - - ``val_dict`` -- a dictionary of ``(var_idx, poly_tup)`` key-value - pairs. - - ``poly_tup`` -- a tuple of (ETuple, coeff) pairs reperesenting a - multivariate polynomial. + - ``max_deg`` -- an ``ETuple`` indicating the maximal degree of + each variable + - ``val_dict`` -- a dictionary of ``(var_idx, poly_tup)`` key-value + pairs + - ``poly_tup`` -- a tuple of ``(ETuple, coeff)`` pairs reperesenting a + multivariate polynomial EXAMPLES:: @@ -482,22 +485,23 @@ cpdef dict compute_known_powers(ETuple max_deg, dict val_dict, one): (((0, 4, 0), 1),), (((0, 6, 0), 1),)]} """ - if not max_deg: return dict() + if not max_deg: + return {} assert max(max_deg.nonzero_values(sort=False)) <= 100, "NotImplementedError: Cannot substitute for degree larger than 100" - max_deg = max_deg.emin(ETuple({ idx : 100 for idx in val_dict }, len(max_deg))) + max_deg = max_deg.emin(ETuple({idx: 100 for idx in val_dict}, len(max_deg))) cdef dict known_powers #Get polynomial unit as tuple to initialize list elements cdef tuple one_tup = ((max_deg._new(), one),) cdef int d, power, var_idx - known_powers = { var_idx : [one_tup]*(d+1) for var_idx, d in max_deg.sparse_iter() } + known_powers = {var_idx: [one_tup]*(d+1) for var_idx, d in max_deg.sparse_iter()} for var_idx, d in max_deg.sparse_iter(): for power in range(d): - known_powers[var_idx][power+1] = tup_mul(known_powers[var_idx][power],val_dict[var_idx]) + known_powers[var_idx][power+1] = tup_mul(known_powers[var_idx][power], val_dict[var_idx]) return known_powers cdef dict subs(tuple poly_tup, dict known_powers, one): """ - Substitute given variables into a polynomial tuple + Substitute given variables into a polynomial tuple. """ cdef dict subbed = {} cdef ETuple exp, m, shifted_exp @@ -513,24 +517,24 @@ cdef dict subs(tuple poly_tup, dict known_powers, one): for m, c in temp: shifted_exp = exp.eadd(m) if shifted_exp in subbed: - subbed[shifted_exp] += coeff*c + subbed[shifted_exp] += coeff * c else: - subbed[shifted_exp] = coeff*c + subbed[shifted_exp] = coeff * c return subbed cdef tuple tup_mul(tuple p1, tuple p2): + r""" + Multiplication of two polynomial tuples using schoolbook multiplication. """ - Multiplication of two tuples... may have to make this faster - """ - cdef dict prod = dict() + cdef dict prod = {} cdef ETuple xi, yj, shifted_exp for xi, ai in p1: for yj, bj in p2: shifted_exp = xi.eadd(yj) if shifted_exp in prod: - prod[shifted_exp] += ai*bj + prod[shifted_exp] += ai * bj else: - prod[shifted_exp] = ai*bj + prod[shifted_exp] = ai * bj return tuple(prod.items()) ############### @@ -538,7 +542,7 @@ cdef tuple tup_mul(tuple p1, tuple p2): ############### cdef tuple monom_sortkey(ETuple exp): - """ + r""" Produce a sortkey for a monomial exponent w.r.t. degree reversed lexicographic ordering. """ @@ -549,7 +553,7 @@ cdef tuple monom_sortkey(ETuple exp): return (deg, rev) cpdef tuple poly_tup_sortkey(tuple eq_tup): - """ + r""" Return the sortkey of a polynomial represented as a tuple of ``(ETuple, coeff)`` pairs with respect to the degree lexicographical term order. @@ -576,7 +580,7 @@ cpdef tuple poly_tup_sortkey(tuple eq_tup): sage: (p1 < p2) == (poly_tup_sortkey(poly_to_tup(p1)) < poly_tup_sortkey(poly_to_tup(p2))) True - TESTS: + TESTS:: sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey, poly_to_tup sage: R. = PolynomialRing(CyclotomicField(20)) @@ -591,7 +595,7 @@ cpdef tuple poly_tup_sortkey(tuple eq_tup): """ cdef ETuple exp cdef int i, l, nnz - cdef list key = list() + cdef list key = [] for exp, c in eq_tup: #Compare by term degree key.append(exp.unweighted_degree()) @@ -602,3 +606,4 @@ cpdef tuple poly_tup_sortkey(tuple eq_tup): key.append(-exp._data[2*i]) key.append(exp._data[2*i+1]) return tuple(key) + From d5e2b0b0d95e17d4d708523af511f2f9f8b2f2db Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Sat, 10 Apr 2021 14:54:56 -0700 Subject: [PATCH 041/632] fix one doctest in f_matrix.py --- src/sage/combinat/root_system/f_matrix.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index a14ed999c05..109ec44cafe 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -1837,6 +1837,7 @@ def _get_explicit_solution(self,eqns=None,verbose=True): sage: f = FMatrix(FusionRing("A1",3)) # indirect doctest sage: f.find_orthogonal_solution() # long time + Computing F-symbols for The Fusion Ring of Type A1 and level 3 with Integer Ring coefficients with 71 variables... Set up 134 hex and orthogonality constraints... Partitioned 134 equations into 17 components of size: [12, 12, 6, 6, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1] From f90eb4c63b0921206bf32a7440e27dc013145df3 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Mon, 12 Apr 2021 23:03:22 +0200 Subject: [PATCH 042/632] manual cache for fast fusion ring cdef methods, removed long doctests --- src/sage/combinat/root_system/f_matrix.py | 5 +- .../fast_parallel_fmats_methods.pxd | 5 +- .../fast_parallel_fmats_methods.pyx | 13 +- .../fast_parallel_fusion_ring_braid_repn.pxd | 5 +- .../fast_parallel_fusion_ring_braid_repn.pyx | 147 +++++------------- 5 files changed, 49 insertions(+), 126 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 109ec44cafe..876735e53d7 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -268,7 +268,7 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab True sage: f = FMatrix(FusionRing("G2",2)) - sage: f.find_orthogonal_solution(verbose=False) # long time (~18 s) + sage: f.find_orthogonal_solution(verbose=False) # long time (~11 s) sage: f.field() # long time Algebraic Field """ @@ -2335,7 +2335,7 @@ def certify_pentagons(self,use_mp=True,verbose=False): Partitioned 6 equations into 6 components of size: [1, 1, 1, 1, 1, 1] Computing appropriate NumberField... - sage: f.certify_pentagons() # long time + sage: f.certify_pentagons() # long time (~1.5s) Success!!! Found valid F-symbols for The Fusion Ring of Type C3 and level 1 with Integer Ring coefficients """ fvars_copy = deepcopy(self._fvars) @@ -2394,4 +2394,3 @@ def fvars_are_real(self): print("The F-symbol {} (key {}) has a nonzero imaginary part!".format(v,k)) return False return True - diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd index 861504a79a3..9c5370130c0 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd @@ -4,6 +4,5 @@ cpdef update_child_fmats(factory, tuple data_tup) cdef _fmat(fvars, Nk_ij, one, a, b, c, d, x, y) -cpdef executor(params) -cpdef collect_eqns(proc) - +cpdef executor(tuple params) +cpdef list collect_eqns(int proc) diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index a66b1d0da97..a359c238fc8 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -443,7 +443,7 @@ cpdef update_child_fmats(factory, tuple data_tup): ### Reducers ### ################ -cpdef collect_eqns(proc): +cpdef list collect_eqns(int proc): r""" Helper function for returning processed results back to parent process. @@ -460,10 +460,10 @@ cpdef collect_eqns(proc): sage: from sage.combinat.root_system.fast_parallel_fmats_methods import collect_eqns sage: len(collect_eqns(0)) == 63 True - sage: fmats = FMatrix(FusionRing("G2",2)) + sage: fmats = FMatrix(FusionRing("C3",1)) sage: params = (('get_reduced_pentagons', id(fmats)), (0,1)) sage: executor(params) - sage: len(collect_eqns(0)) == 4911 + sage: len(collect_eqns(0)) == 374 True """ #Discard the zero polynomial @@ -486,7 +486,7 @@ cdef dict mappers = { "pent_verify": pent_verify } -cpdef executor(params): +cpdef executor(tuple params): r""" Execute a function defined in this module (``sage.combinat.root_system.fast_parallel_fmats_methods``) in a worker @@ -518,10 +518,10 @@ cpdef executor(params): sage: from sage.combinat.root_system.fast_parallel_fmats_methods import collect_eqns sage: len(collect_eqns(0)) == 63 True - sage: fmats = FMatrix(FusionRing("E8",2)) + sage: fmats = FMatrix(FusionRing("E6",1)) sage: params = (('get_reduced_hexagons', id(fmats)), (0,1)) sage: executor(params) - sage: len(collect_eqns(0)) == 11 + sage: len(collect_eqns(0)) == 6 True """ (fn_name, fmats_id), args = params @@ -572,4 +572,3 @@ cdef pent_verify(factory, tuple mp_params): feq_verif(factory,fvars,Nk_ij,id_anyon,nonuple) if i % 50000000 == 0 and i and verbose: print("{:5d}m equations checked... {} potential misses so far...".format(i // 1000000,len(worker_results))) - diff --git a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pxd b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pxd index a032a509637..9fcaddb56e0 100644 --- a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pxd +++ b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pxd @@ -1,4 +1,3 @@ cpdef _unflatten_entries(factory, list entries) -cpdef executor(params) -cpdef collect_results(proc) - +cpdef executor(tuple params) +cpdef list collect_results(int proc) diff --git a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx index 4fca46e377f..436809c2518 100644 --- a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx @@ -23,7 +23,7 @@ worker_results = list() ### Mappers ### ############### -cpdef mid_sig_ij(fusion_ring,row,col,a,b): +cdef mid_sig_ij(fusion_ring,row,col,a,b): r""" Compute the (xi,yi), (xj,yj) entry of generator braiding the middle two strands in the tree b -> xi # yi -> (a # a) # (a # a), which results in @@ -32,49 +32,6 @@ cpdef mid_sig_ij(fusion_ring,row,col,a,b): .. WARNING:: This method assumes F-matrices are orthogonal. - - EXAMPLES:: - - sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import mid_sig_ij - sage: FR = FusionRing("A1",4) # long time - sage: FR.fusion_labels(['idd','one','two','three','four'],inject_variables=True) # long time - sage: one.weight() # long time - (1/2, -1/2) - sage: FR.get_computational_basis(one,two,4) # long time - [(two, two), (two, idd), (idd, two)] - sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time (~7s) - sage: mid_sig_ij(FR, (two, two), (two, idd), one, two) # long time - 1/3*zeta48^10 - 2/3*zeta48^2 - - This method works for all possible types of fields returned by - ``self.fmats.field()``. - - TESTS:: - - sage: FR = FusionRing("A1",3) # long time - sage: FR.fusion_labels("a",inject_variables=True) # long time - sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time - sage: _, _, to_opt = FR.fmats.field().optimized_representation() # long time - sage: a2**4 # long time - 2*a0 + 3*a2 - sage: FR.get_computational_basis(a2,a2,4) # long time - [(a2, a2), (a2, a0), (a0, a2)] - sage: to_opt(mid_sig_ij(FR,(a2, a0),(a2, a2),a2,a2)) # long time - -2024728666370660589/10956322398441542221*a1^30 - 34142146914395596291/21912644796883084442*a1^28 - - 21479437628091413631/21912644796883084442*a1^26 + 260131910217202103829/21912644796883084442*a1^24 - + 69575612911670713183/10956322398441542221*a1^22 + 25621808994337724689/1992058617898462222*a1^20 - - 1975139725303994650417/21912644796883084442*a1^18 - 1315664901396537703585/21912644796883084442*a1^16 - - 2421451803369354765026/10956322398441542221*a1^14 - 5963323855935165859057/21912644796883084442*a1^12 - - 4477124943233705460859/21912644796883084442*a1^10 - 2001454824483021618178/10956322398441542221*a1^8 - - 2120319455379289595185/21912644796883084442*a1^6 - 15722612944437234961/755608441271830498*a1^4 - - 39862668562651453480/10956322398441542221*a1^2 - 6967145776903524195/10956322398441542221 - sage: FR = FusionRing("G2",2) # long time - sage: FR.fusion_labels("g",inject_variables=True) # long time - sage: FR.get_computational_basis(g1,g2,4) # long time - [(g3, g2), (g3, g1), (g2, g3), (g2, g0), (g1, g3), (g1, g1), (g0, g2)] - sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time (~11 s) - sage: mid_sig_ij(FR,(g2, g3),(g1, g1),g1,g2) # long time - -0.4566723195695565? + 0.0805236512828312?*I """ #Pre-compute common parameters for efficiency _fvars = fusion_ring.fmats._fvars @@ -95,7 +52,7 @@ cpdef mid_sig_ij(fusion_ring,row,col,a,b): entry += f1 * f2 * r * f3 * f4 return entry -cpdef odd_one_out_ij(fusion_ring,xi,xj,a,b): +cdef odd_one_out_ij(fusion_ring,xi,xj,a,b): r""" Compute the `xi`, `xj` entry of the braid generator on the two right-most strands, corresponding to the tree b -> (xi # a) -> (a # a) # a, which @@ -104,49 +61,6 @@ cpdef odd_one_out_ij(fusion_ring,xi,xj,a,b): .. WARNING:: This method assumes F-matrices are orthogonal. - - EXAMPLES:: - - sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import odd_one_out_ij - sage: FR = FusionRing("A1",4) # long time - sage: FR.fusion_labels(["one","two","three","four","five"],inject_variables=True) # long time - sage: FR.get_computational_basis(two,two,5) # long time - [(three, three, one), - (three, three, three), - (three, one, three), - (one, three, three), - (one, one, one)] - sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time - sage: odd_one_out_ij(FR,one,three,two,two) # long time - 2/3*zeta48^12 - 1/3*zeta48^8 - 1/3*zeta48^4 - 1/3 - - This method works for all possible types of fields returned by - ``self.fmats.field()``. - - TESTS:: - - sage: FR = FusionRing("A1",3) # long time - sage: FR.fusion_labels("a",inject_variables=True) # long time - sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time - sage: _, _, to_opt = FR.fmats.field().optimized_representation() # long time - sage: a2**3 # long time - a0 + 2*a2 - sage: FR.get_computational_basis(a2,a2,3) # long time - [(a2,), (a0,)] - sage: to_opt(odd_one_out_ij(FR,a0,a2,a2,a2)) # long time - 6341990144855406911/21912644796883084442*a1^30 + 47313529044493641571/21912644796883084442*a1^28 - - 6964289120109414595/10956322398441542221*a1^26 - 406719371329322780627/21912644796883084442*a1^24 - + 87598732372849355687/10956322398441542221*a1^22 - 456724726845194775/19723352652460022*a1^20 - + 3585892725441116840515/21912644796883084442*a1^18 - 645866255979227573282/10956322398441542221*a1^16 - + 7958479159087829772639/21912644796883084442*a1^14 + 789748976956837633826/10956322398441542221*a1^12 - + 3409710648897945752185/21912644796883084442*a1^10 + 903956381582048110980/10956322398441542221*a1^8 - + 192973084151342020307/21912644796883084442*a1^6 - 9233312083438019435/755608441271830498*a1^4 - + 667869266552877781/10956322398441542221*a1^2 + 17644302696056968099/21912644796883084442 - sage: FR = FusionRing("G2",2) # long time - sage: FR.fusion_labels("g",inject_variables=True) # long time - sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time (~11 s) - sage: odd_one_out_ij(FR,g1,g2,g1,g1) # long time - -0.2636598866349343? + 0.4566723195695565?*I """ #Pre-compute common parameters for efficiency _fvars = fusion_ring.fmats._fvars @@ -162,9 +76,23 @@ cpdef odd_one_out_ij(fusion_ring,xi,xj,a,b): entry += f1 * r * f2 return entry -#Cache methods -mid_sig_ij = cached_function(mid_sig_ij, name='mid_sig_ij') -odd_one_out_ij = cached_function(odd_one_out_ij, name='odd_one_out_ij') +#Cache methods (manually for cdef methods) +cdef odd_one_out_ij_cache = dict() +cdef mid_sig_ij_cache = dict() + +cdef cached_mid_sig_ij(fusion_ring,row,col,a,b): + if (row,col,a,b) in mid_sig_ij_cache: + return mid_sig_ij_cache[row,col,a,b] + entry = mid_sig_ij(fusion_ring,row,col,a,b) + mid_sig_ij_cache[row,col,a,b] = entry + return entry + +cdef cached_odd_one_out_ij(fusion_ring,xi,xj,a,b): + if (xi,xj,a,b) in odd_one_out_ij_cache: + return odd_one_out_ij_cache[xi,xj,a,b] + entry = odd_one_out_ij(fusion_ring,xi,xj,a,b) + odd_one_out_ij_cache[xi,xj,a,b] = entry + return entry @cython.nonecheck(False) @cython.cdivision(True) @@ -219,7 +147,7 @@ cdef sig_2k(fusion_ring, tuple args): #Handle the special case k = 1 if k == 1: - entry = mid_sig_ij(fusion_ring,m[:2],(f,e),a,root) + entry = cached_mid_sig_ij(fusion_ring,m[:2],(f,e),a,root) #Avoid pickling cyclotomic field element objects if must_flatten_coeff: @@ -233,7 +161,7 @@ cdef sig_2k(fusion_ring, tuple args): for p in fusion_ring.basis(): f1 = _fmat(_fvars,_Nk_ij,one,top_left,m[k-1],m[k],root,l[k-2],p) f2 = _fmat(_fvars,_Nk_ij,one,top_left,f,e,root,q,p) - entry += f1 * mid_sig_ij(fusion_ring,(m[k-1],m[k]),(f,e),a,p) * f2 + entry += f1 * cached_mid_sig_ij(fusion_ring,(m[k-1],m[k]),(f,e),a,p) * f2 #Avoid pickling cyclotomic field element objects if must_flatten_coeff: @@ -285,7 +213,7 @@ cdef odd_one_out(fusion_ring, tuple args): #Handle a couple of small special cases if n_strands == 3: - entry = odd_one_out_ij(fusion_ring,m[-1],f,a,b) + entry = cached_odd_one_out_ij(fusion_ring,m[-1],f,a,b) #Avoid pickling cyclotomic field element objects if must_flatten_coeff: @@ -303,7 +231,7 @@ cdef odd_one_out(fusion_ring, tuple args): for p in fusion_ring.basis(): f1 = _fmat(_fvars,_Nk_ij,one,top_left,m[-1],a,root,l[-1],p) f2 = _fmat(_fvars,_Nk_ij,one,top_left,f,a,root,q,p) - entry += f1 * odd_one_out_ij(fusion_ring,m[-1],f,a,p) * f2 + entry += f1 * cached_odd_one_out_ij(fusion_ring,m[-1],f,a,p) * f2 #Avoid pickling cyclotomic field element objects if must_flatten_coeff: @@ -315,7 +243,7 @@ cdef odd_one_out(fusion_ring, tuple args): ### Reducers ### ################ -cpdef collect_results(proc): +cpdef list collect_results(int proc): """ Helper function for returning processed results back to parent process. @@ -326,13 +254,13 @@ cpdef collect_results(proc): EXAMPLES:: sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import executor - sage: FR = FusionRing("A1",4) # long time - sage: FR.fusion_labels(['idd','one','two','three','four'],inject_variables=True) # long time - sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time - sage: params = (('sig_2k',id(FR)),(0,1,(2,one,one,9))) # long time - sage: executor(params) # long time + sage: FR = FusionRing("A2",1) + sage: FR.fusion_labels("a",inject_variables=True) + sage: FR.fmats.find_orthogonal_solution(verbose=False) + sage: params = (('sig_2k',id(FR)),(0,1,(2,a2,a0,9))) + sage: executor(params) sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import collect_results - sage: len(collect_results(0)) == 171 # long time + sage: len(collect_results(0)) == 1 True """ #Discard the zero polynomial @@ -351,7 +279,7 @@ cdef dict mappers = { "odd_one_out": odd_one_out } -cpdef executor(params): +cpdef executor(tuple params): r""" Execute a function registered in this module's ``mappers`` in a worker process, and supply the ``FusionRing`` parameter by @@ -378,12 +306,12 @@ cpdef executor(params): sage: len(collect_results(0)) == 13 # long time True sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import executor, collect_results - sage: FR = FusionRing("B2",2) # long time - sage: FR.fusion_labels(['I0','Y1','X','Z','Xp','Y2'],inject_variables=True) # long time - sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time (~23 s) - sage: params = (('odd_one_out',id(FR)),(0,1,(X,Xp,5))) # long time - sage: executor(params) # long time - sage: len(collect_results(0)) == 54 # long time + sage: FR = FusionRing("A1",2) + sage: FR.fusion_labels("a",inject_variables=True) + sage: FR.fmats.find_orthogonal_solution(verbose=False) + sage: params = (('odd_one_out',id(FR)),(0,1,(a2,a2,5))) + sage: executor(params) + sage: len(collect_results(0)) == 1 True """ (fn_name, fr_id), args = params @@ -420,4 +348,3 @@ cpdef _unflatten_entries(fusion_ring, list entries): if F != QQbar: for i, (coord, entry) in enumerate(entries): entries[i] = (coord, F(entry)) - From 9109bbf28c2aab26a10c8fd1a61b6c967d955bf3 Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Mon, 12 Apr 2021 14:59:40 -0700 Subject: [PATCH 043/632] give more information in total_q_order docstring --- src/sage/combinat/root_system/fusion_ring.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 53a5a1438c2..25ff5433ccf 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -981,7 +981,8 @@ def global_q_dimension(self, base_coercion=True): def total_q_order(self, base_coercion=True): r""" Return the positive square root of ``self.global_q_dimension()`` - as an element of ``self.field()``. + as an element of ``self.field()``. Implemented as `D_{+}e^{-i\pi c/4}` + where `D_+` is ``self.D_plus()`` and `c` is ``self.virasoro_central_charge()`` EXAMPLES:: From 14f7b5cb0e56048aef98357f45953e91e8a9a1c6 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Thu, 15 Apr 2021 13:53:17 -0700 Subject: [PATCH 044/632] initialize self._kp in f_matrix --- .../reference/combinat/media/fusiontree.png | Bin 37425 -> 37424 bytes src/sage/combinat/root_system/f_matrix.py | 1 + 2 files changed, 1 insertion(+) diff --git a/src/doc/en/reference/combinat/media/fusiontree.png b/src/doc/en/reference/combinat/media/fusiontree.png index 1fde2a94c81f02f7bd8d9752d5b46c1469436d48..ecad6ab87eae6f8f1870de6963e6cb9db617560f 100644 GIT binary patch delta 15 WcmdnEglWSPCYH_sKlhESI+Fk}a0QeA delta 16 Xcmdn6glXdvCf3dXKX=}ZEIN|_G}Q&6 diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 876735e53d7..ec849e1bafd 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -298,6 +298,7 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab self.ideal_basis = list() self._solved = set() self._ks = dict() + self._kp = dict() self._nnz = self._get_known_nonz() self._chkpt_status = -1 From eb488914e9c893c754bea88e7c34da786ad0627e Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Thu, 15 Apr 2021 14:40:30 -0700 Subject: [PATCH 045/632] multiprocessing not the default after this commit. Doctests pass on OSX and Ubuntu, but run slowly --- src/sage/combinat/root_system/f_matrix.py | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index ec849e1bafd..39dbdd3c51f 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -1294,8 +1294,8 @@ def _map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_t sage: len(f._map_triv_reduce('get_reduced_hexagons',[(0,1)])) 11 sage: from multiprocessing import Pool - sage: pool = Pool() - sage: mp_params = [(i,pool._processes) for i in range(pool._processes)] + sage: pool = None + sage: mp_params = [(0,1)] sage: len(f._map_triv_reduce('get_reduced_pentagons',mp_params,worker_pool=pool,chunksize=1,mp_thresh=0)) 33 """ @@ -1457,7 +1457,7 @@ def _tup_to_fpoly(self,eq_tup): ....: set_start_method('fork') ....: except: ....: pass - sage: pool = Pool() + sage: pool = None sage: he = f.get_defining_equations('hexagons',pool) sage: all(f._tup_to_fpoly(poly_to_tup(h)) for h in he) True @@ -1477,7 +1477,7 @@ def _update_reduction_params(self,eqns=None,worker_pool=None,children_need_updat ....: set_start_method('fork') ....: except: ....: pass - sage: pool = Pool() + sage: pool = None sage: f.get_defining_equations('hexagons',worker_pool=pool,output=False) sage: f.ideal_basis = f._par_graph_gb(worker_pool=pool,verbose=False) sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey @@ -1688,18 +1688,19 @@ def _par_graph_gb(self,worker_pool=None,eqns=None,term_order="degrevlex",verbose ....: set_start_method('fork') # context can be set only once ....: except RuntimeError: ....: pass - sage: pool = Pool() + sage: pool = None sage: f.get_defining_equations('hexagons',worker_pool=pool,output=False) sage: gb = f._par_graph_gb(worker_pool=pool) Partitioned 10 equations into 2 components of size: [4, 1] sage: from sage.combinat.root_system.poly_tup_engine import _unflatten_coeffs - sage: [f._tup_to_fpoly(_unflatten_coeffs(f.field(), t)) for t in gb] - [fx0 - 1, + sage: ret = [f._tup_to_fpoly(_unflatten_coeffs(f.field(), t)) for t in gb] + sage: ret.sort(); ret + [fx4 + (-zeta80^24 + zeta80^16), fx2 - fx3, - fx3^2 + (zeta80^24 - zeta80^16), - fx4 + (-zeta80^24 + zeta80^16), - fx1 + (zeta80^24 - zeta80^16)] + fx1 + (zeta80^24 - zeta80^16), + fx0 - 1, + fx3^2 + (zeta80^24 - zeta80^16)] """ if eqns is None: eqns = self.ideal_basis small_comps = list() @@ -1940,7 +1941,7 @@ def _get_explicit_solution(self,eqns=None,verbose=True): self._FR._field = self.field() self._FR._basecoer = self.get_coerce_map_from_fr_cyclotomic_field() - def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start="", use_mp=True, verbose=True): + def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start="", use_mp=False, verbose=True): r""" Solve the the hexagon and pentagon relations, along with orthogonality constraints, to evaluate an orthogonal F-matrix. From 4a57f9652e109d551f45d38a9d88c621d703a771 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Fri, 16 Apr 2021 13:31:39 -0400 Subject: [PATCH 046/632] no default multiprocessing for braid representations --- src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx | 4 ++-- src/sage/combinat/root_system/fusion_ring.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index a359c238fc8..5b08eb2b5e6 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -349,7 +349,7 @@ cpdef compute_gb(factory, tuple args): ....: set_start_method('fork') # context can be set only once ....: except RuntimeError: ....: pass - sage: f.get_defining_equations('hexagons',worker_pool=Pool(),output=False) # long time + sage: f.get_defining_equations('hexagons',worker_pool=None,output=False) # long time sage: partition = f._partition_eqns() # long time Partitioned 261 equations into 57 components of size: [24, 12, 12, 12, 12, 12, 12, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, @@ -421,7 +421,7 @@ cpdef update_child_fmats(factory, tuple data_tup): ....: set_start_method('fork') # context can be set only once ....: except RuntimeError: ....: pass - sage: pool = Pool() + sage: pool = None sage: f.get_defining_equations('hexagons',worker_pool=pool,output=False) sage: f.ideal_basis = f._par_graph_gb(worker_pool=pool,verbose=False) sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 25ff5433ccf..218989cbfae 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -1184,7 +1184,7 @@ def _emap(self, mapper, input_args, worker_pool=None): a single process. If ``worker_pool`` is provided, the function attempts to determine whether it should use multiprocessing based on the length of the input iterable. If it cannot determine - the length of the input iterable then it uses multiprocessing + the length of the input iterable then it uses multiprocessing with the default chunksize of `1` if chunksize is not explicitly provided. @@ -1227,7 +1227,7 @@ def get_braid_generators(self, checkpoint=False, save_results="", warm_start="", - use_mp=True, + use_mp=False, verbose=True): r""" Compute generators of the Artin braid group on ``n_strands`` strands. From 6e392b07ba660e2f0651fc5e86dd01975575bf4f Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Wed, 21 Apr 2021 17:36:46 -0400 Subject: [PATCH 047/632] fixed assumed one-to-one collect_eqns to process map bug, made use_mp default again --- src/sage/combinat/root_system/f_matrix.py | 39 ++-- .../fast_parallel_fmats_methods.pxd | 6 - .../fast_parallel_fmats_methods.pyx | 195 +++++------------- .../fast_parallel_fusion_ring_braid_repn.pxd | 1 - .../fast_parallel_fusion_ring_braid_repn.pyx | 50 +---- src/sage/combinat/root_system/fusion_ring.py | 20 +- 6 files changed, 89 insertions(+), 222 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 39dbdd3c51f..ac262a0b644 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -20,13 +20,13 @@ from copy import deepcopy from itertools import product, zip_longest -from multiprocessing import cpu_count, Pool, set_start_method +from multiprocessing import cpu_count, Pool, Queue, set_start_method import numpy as np import os from sage.combinat.root_system.fast_parallel_fmats_methods import ( _backward_subs, _solve_for_linear_terms, - collect_eqns, executor + executor ) from sage.combinat.root_system.poly_tup_engine import ( apply_coeff_map, constant_coeff, @@ -1294,8 +1294,8 @@ def _map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_t sage: len(f._map_triv_reduce('get_reduced_hexagons',[(0,1)])) 11 sage: from multiprocessing import Pool - sage: pool = None - sage: mp_params = [(0,1)] + sage: pool = Pool() + sage: mp_params = [(i,pool._processes) for i in range(pool._processes)] sage: len(f._map_triv_reduce('get_reduced_pentagons',mp_params,worker_pool=pool,chunksize=1,mp_thresh=0)) 33 """ @@ -1310,21 +1310,18 @@ def _map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_t if chunksize is None: chunksize = n // (worker_pool._processes**2) + 1 no_mp = worker_pool is None or n < mp_thresh - #Map phase. Casting Async Object blocks execution... Each process holds results - #in its copy of fmats.temp_eqns + #Map phase input_iter = zip_longest([],input_iter,fillvalue=(mapper,id(self))) if no_mp: - list(map(executor,input_iter)) + mapped = map(executor,input_iter) else: - list(worker_pool.imap_unordered(executor,input_iter,chunksize=chunksize)) + mapped = worker_pool.imap_unordered(executor,input_iter,chunksize=chunksize) #Reduce phase - if no_mp: - results = collect_eqns(0) - else: - results = set() - for child_eqns in worker_pool.imap_unordered(collect_eqns,range(worker_pool._processes)): + results = set() + for child_eqns in mapped: + if child_eqns is not None: results.update(child_eqns) - results = list(results) + results = list(results) return results ######################## @@ -1457,7 +1454,7 @@ def _tup_to_fpoly(self,eq_tup): ....: set_start_method('fork') ....: except: ....: pass - sage: pool = None + sage: pool = Pool() sage: he = f.get_defining_equations('hexagons',pool) sage: all(f._tup_to_fpoly(poly_to_tup(h)) for h in he) True @@ -1477,7 +1474,7 @@ def _update_reduction_params(self,eqns=None,worker_pool=None,children_need_updat ....: set_start_method('fork') ....: except: ....: pass - sage: pool = None + sage: pool = Pool() sage: f.get_defining_equations('hexagons',worker_pool=pool,output=False) sage: f.ideal_basis = f._par_graph_gb(worker_pool=pool,verbose=False) sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey @@ -1536,6 +1533,8 @@ def _triangular_elim(self,eqns=None,worker_pool=None,verbose=True): #Compute new reduction params, send to child processes if any, and update eqns self._update_reduction_params(eqns=eqns,worker_pool=worker_pool,children_need_update=len(eqns)>self.mp_thresh) + n = len(eqns) // worker_pool._processes ** 2 + 1 if worker_pool is not None else len(eqns) + eqns = [eqns[i:i+n] for i in range(0,len(eqns),n)] eqns = self._map_triv_reduce('update_reduce',eqns,worker_pool=worker_pool) eqns.sort(key=poly_tup_sortkey) if verbose: @@ -1688,7 +1687,7 @@ def _par_graph_gb(self,worker_pool=None,eqns=None,term_order="degrevlex",verbose ....: set_start_method('fork') # context can be set only once ....: except RuntimeError: ....: pass - sage: pool = None + sage: pool = Pool() sage: f.get_defining_equations('hexagons',worker_pool=pool,output=False) sage: gb = f._par_graph_gb(worker_pool=pool) Partitioned 10 equations into 2 components of size: @@ -1861,7 +1860,6 @@ def _get_explicit_solution(self,eqns=None,verbose=True): eqns = self.ideal_basis #Don't add square fixers when warm starting from a late-stage checkpoint if self._chkpt_status < 5: - # self._add_square_fixers() n = self._poly_ring.ngens() one = self._field.one() for fx, rhs in self._ks.items(): @@ -1894,7 +1892,6 @@ def _get_explicit_solution(self,eqns=None,verbose=True): #Otherwise, compute the component variety and select a point to obtain a numerical solution else: sols = self._get_component_variety(comp,part) - # assert len(sols) > 1, "No real solution exists... component with variables {} has no real points".format(comp) for fx, rhs in sols[0].items(): non_cyclotomic_roots.append((fx,rhs)) must_change_base_field = True @@ -1941,7 +1938,7 @@ def _get_explicit_solution(self,eqns=None,verbose=True): self._FR._field = self.field() self._FR._basecoer = self.get_coerce_map_from_fr_cyclotomic_field() - def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start="", use_mp=False, verbose=True): + def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start="", use_mp=True, verbose=True): r""" Solve the the hexagon and pentagon relations, along with orthogonality constraints, to evaluate an orthogonal F-matrix. @@ -2343,7 +2340,7 @@ def certify_pentagons(self,use_mp=True,verbose=False): fvars_copy = deepcopy(self._fvars) self._fvars = {sextuple: float(rhs) for sextuple, rhs in self.get_fvars_in_alg_field().items()} if use_mp: - pool = Pool(processes=cpu_count()) + pool = Pool() else: pool = None n_proc = pool._processes if pool is not None else 1 diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd index 9c5370130c0..f54fa2ff174 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd @@ -1,8 +1,2 @@ -cpdef update_reduce(factory, tuple eq_tup) -cpdef compute_gb(factory, tuple args) -cpdef update_child_fmats(factory, tuple data_tup) - cdef _fmat(fvars, Nk_ij, one, a, b, c, d, x, y) - cpdef executor(tuple params) -cpdef list collect_eqns(int proc) diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index 5b08eb2b5e6..c9c09294c4c 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -141,6 +141,30 @@ cpdef _backward_subs(factory): kp = compute_known_powers(get_variables_degrees([rhs]), d, one) factory._fvars[sextuple] = tuple(subs_squares(subs(rhs,kp,one), factory._ks).items()) +cdef _fmat(fvars, _Nk_ij, id_anyon, a, b, c, d, x, y): + """ + Cython version of fmat class method. Using cdef for fastest dispatch + """ + if _Nk_ij(a,b,x) == 0 or _Nk_ij(x,c,d) == 0 or _Nk_ij(b,c,y) == 0 or _Nk_ij(a,y,d) == 0: + return 0 + #Some known F-symbols + if a == id_anyon: + if x == b and y == d: + return 1 + else: + return 0 + if b == id_anyon: + if x == a and y == c: + return 1 + else: + return 0 + if c == id_anyon: + if x == d and y == b: + return 1 + else: + return 0 + return fvars[a,b,c,d,x,y] + ###################################### ### Fast fusion coefficients cache ### ###################################### @@ -169,33 +193,6 @@ cpdef _backward_subs(factory): ### Mappers ### ############### -#Define a global temporary worker results repository -cdef list worker_results = list() - -cdef _fmat(fvars, _Nk_ij, id_anyon, a, b, c, d, x, y): - """ - Cython version of fmat class method. Using cdef for fastest dispatch - """ - if _Nk_ij(a,b,x) == 0 or _Nk_ij(x,c,d) == 0 or _Nk_ij(b,c,y) == 0 or _Nk_ij(a,y,d) == 0: - return 0 - #Some known F-symbols - if a == id_anyon: - if x == b and y == d: - return 1 - else: - return 0 - if b == id_anyon: - if x == a and y == c: - return 1 - else: - return 0 - if c == id_anyon: - if x == d and y == b: - return 1 - else: - return 0 - return fvars[a,b,c,d,x,y] - cdef req_cy(tuple basis, r_matrix, dict fvars, Nk_ij, id_anyon, tuple sextuple): """ Given an FMatrix factory and a sextuple, return a hexagon equation @@ -218,7 +215,7 @@ cdef get_reduced_hexagons(factory, tuple mp_params): Set up and reduce the hexagon equations corresponding to this worker. """ #Set up multiprocessing parameters - global worker_results + cdef list worker_results = list() cdef int child_id, n_proc cdef unsigned long i child_id, n_proc = mp_params @@ -246,6 +243,8 @@ cdef get_reduced_hexagons(factory, tuple mp_params): worker_results.append(red) + return collect_eqns(worker_results) + cdef MPolynomial_libsingular feq_cy(tuple basis, fvars, Nk_ij, id_anyon, zero, tuple nonuple, bint prune=False): r""" Given an FMatrix factory and a nonuple, return a pentagon equation @@ -268,7 +267,7 @@ cdef get_reduced_pentagons(factory, tuple mp_params): Set up and reduce the pentagon equations corresponding to this worker. """ #Set up multiprocessing parameters - global worker_results + cdef list worker_results = list() cdef int child_id, n_proc child_id, n_proc = mp_params cdef unsigned long i @@ -298,80 +297,36 @@ cdef get_reduced_pentagons(factory, tuple mp_params): red = _flatten_coeffs(red) worker_results.append(red) + return collect_eqns(worker_results) -cpdef update_reduce(factory, tuple eq_tup): +cdef list update_reduce(factory, list eqns): r""" Substitute known values, known squares, and reduce. - - EXAMPLES:: - - sage: f = FMatrix(FusionRing("B4",1), fusion_label="b", inject_variables=True) - creating variables fx1..fx14 - Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 - sage: p = 2*fx0**2 - 3/7*fx0*fx1*fx3**3 + 5*fx0*fx4 - sage: from sage.combinat.root_system.poly_tup_engine import * - sage: f.ideal_basis = [poly_to_tup(p)] - sage: f._ks = {3: 2} - sage: f._var_degs = get_variables_degrees(f.ideal_basis) - sage: known_vals = {4: poly_to_tup(fx11**2)} - sage: f._nnz - (100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 0, 0, 0, 0) - sage: f._kp = compute_known_powers(f._var_degs,known_vals,f._field.one()) - sage: from sage.combinat.root_system.fast_parallel_fmats_methods import update_reduce, collect_eqns - sage: update_reduce(f, f.ideal_basis[0]) - sage: from sage.combinat.root_system.poly_tup_engine import _unflatten_coeffs - sage: [f._tup_to_fpoly(_unflatten_coeffs(f._field,p)) for p in collect_eqns(0)] - [fx1*fx3 - 35/6*fx11^2 - 7/3*fx0] """ - global worker_results + cdef list res = list() cdef NumberFieldElement_absolute one = factory._field.one() + cdef tuple eq_tup, red, unflat + cdef dict eq_dict - #Construct cyclotomic field elts from list repn - cdef tuple unflat = _unflatten_coeffs(factory._field, eq_tup) + for i in range(len(eqns)): + eq_tup = eqns[i] + #Construct cyclotomic field elts from list repn + unflat = _unflatten_coeffs(factory._field, eq_tup) - cdef dict eq_dict = subs(unflat,factory._kp,one) - cdef tuple red = reduce_poly_dict(eq_dict,factory._nnz,factory._ks,one) + eq_dict = subs(unflat,factory._kp,one) + red = reduce_poly_dict(eq_dict,factory._nnz,factory._ks,one) - #Avoid pickling cyclotomic coefficients - red = _flatten_coeffs(red) + #Avoid pickling cyclotomic coefficients + red = _flatten_coeffs(red) - worker_results.append(red) + res.append(red) + return collect_eqns(res) -cpdef compute_gb(factory, tuple args): +cdef list compute_gb(factory, tuple args): r""" Compute the reduced Groebner basis for given equations iterable. - - EXAMPLES:: - - sage: f = FMatrix(FusionRing("A2",2)) - sage: from multiprocessing import Pool, set_start_method - sage: try: - ....: set_start_method('fork') # context can be set only once - ....: except RuntimeError: - ....: pass - sage: f.get_defining_equations('hexagons',worker_pool=None,output=False) # long time - sage: partition = f._partition_eqns() # long time - Partitioned 261 equations into 57 components of size: - [24, 12, 12, 12, 12, 12, 12, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, - 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 1, 1, 1, 1, 1] - sage: from sage.combinat.root_system.fast_parallel_fmats_methods import compute_gb, collect_eqns - sage: args = (partition[(29, 40, 83, 111, 148, 154)], "degrevlex") # long time - sage: compute_gb(f, args) # long time - sage: from sage.combinat.root_system.poly_tup_engine import _unflatten_coeffs - sage: [f._tup_to_fpoly(_unflatten_coeffs(f._field,t)) for t in collect_eqns(0)] # long time - [fx83*fx154 - fx111, - fx29^2*fx154 + fx29, - fx29*fx148 - fx40, - fx29*fx154^2 + fx154, - fx83*fx148 + fx29*fx154, - fx40*fx83 - fx29, - fx111*fx148 - fx154, - fx40*fx154 + fx148, - fx40*fx111 - fx29*fx154, - fx29*fx111 + fx83] """ - global worker_results + cdef list res = list() cdef list eqns, sorted_vars eqns, term_order = args #Define smaller poly ring in component vars @@ -404,34 +359,14 @@ cpdef compute_gb(factory, tuple args): #Avoid pickling cyclotomic coefficients t = _flatten_coeffs(t) - worker_results.append(t) + res.append(t) + return collect_eqns(res) -cpdef update_child_fmats(factory, tuple data_tup): +cdef update_child_fmats(factory, tuple data_tup): r""" One-to-all communication used to update FMatrix object after each triangular elim step. We must update the algorithm's state values. These are: ``_fvars``, ``_solved``, ``_ks``, ``_var_degs``, ``_nnz``, and ``_kp``. - - TESTS:: - - sage: f = FMatrix(FusionRing("A1",3)) - sage: f.get_orthogonality_constraints(output=False) - sage: from multiprocessing import Pool, set_start_method - sage: try: - ....: set_start_method('fork') # context can be set only once - ....: except RuntimeError: - ....: pass - sage: pool = None - sage: f.get_defining_equations('hexagons',worker_pool=pool,output=False) - sage: f.ideal_basis = f._par_graph_gb(worker_pool=pool,verbose=False) - sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey - sage: f.ideal_basis.sort(key=poly_tup_sortkey) - sage: f.mp_thresh = 0 - sage: f._triangular_elim(worker_pool=pool) # indirect doctest - Elimination epoch completed... 10 eqns remain in ideal basis - Elimination epoch completed... 0 eqns remain in ideal basis - sage: f.ideal_basis - [] """ #factory object is assumed global before forking used to create the Pool object, #so each child has a global fmats variable. So it's enough to update that object @@ -443,33 +378,16 @@ cpdef update_child_fmats(factory, tuple data_tup): ### Reducers ### ################ -cpdef list collect_eqns(int proc): +cdef inline list collect_eqns(list eqns): r""" Helper function for returning processed results back to parent process. Trivial reducer: simply collects objects with the same key in the worker. This method is only useful when called after :meth:`executor`, whose function argument appends output to the ``worker_results`` list. - - EXAMPLES:: - - sage: from sage.combinat.root_system.fast_parallel_fmats_methods import executor - sage: fmats = FMatrix(FusionRing("A1",3)) - sage: params = (('get_reduced_hexagons', id(fmats)), (0,1)) - sage: executor(params) - sage: from sage.combinat.root_system.fast_parallel_fmats_methods import collect_eqns - sage: len(collect_eqns(0)) == 63 - True - sage: fmats = FMatrix(FusionRing("C3",1)) - sage: params = (('get_reduced_pentagons', id(fmats)), (0,1)) - sage: executor(params) - sage: len(collect_eqns(0)) == 374 - True """ #Discard the zero polynomial - global worker_results - reduced = set(worker_results) - set([tuple()]) - worker_results = [] + reduced = set(eqns) - set([tuple()]) return list(reduced) ############################## @@ -514,14 +432,11 @@ cpdef executor(tuple params): sage: from sage.combinat.root_system.fast_parallel_fmats_methods import executor sage: fmats = FMatrix(FusionRing("A1",3)) sage: params = (('get_reduced_hexagons', id(fmats)), (0,1)) - sage: executor(params) - sage: from sage.combinat.root_system.fast_parallel_fmats_methods import collect_eqns - sage: len(collect_eqns(0)) == 63 + sage: len(executor(params)) == 63 True sage: fmats = FMatrix(FusionRing("E6",1)) sage: params = (('get_reduced_hexagons', id(fmats)), (0,1)) - sage: executor(params) - sage: len(collect_eqns(0)) == 6 + sage: len(executor(params)) == 6 True """ (fn_name, fmats_id), args = params @@ -534,11 +449,10 @@ cpdef executor(tuple params): ### Verification ### #################### -cdef feq_verif(factory, fvars, Nk_ij, id_anyon, tuple nonuple, float tol=5e-8): +cdef feq_verif(factory, worker_results, fvars, Nk_ij, id_anyon, tuple nonuple, float tol=5e-8): r""" Check the pentagon equation corresponding to the given nonuple. """ - global worker_results a, b, c, d, e, f, g, k, l = nonuple cdef float diff, lhs, rhs @@ -562,6 +476,7 @@ cdef pent_verify(factory, tuple mp_params): cdef float t0 cdef tuple nonuple cdef unsigned long long i + cdef list worker_results = list() #Pre-compute common parameters for speed Nk_ij = factory._FR.Nk_ij @@ -569,6 +484,6 @@ cdef pent_verify(factory, tuple mp_params): id_anyon = factory._FR.one() for i, nonuple in enumerate(product(factory._FR.basis(), repeat=9)): if i % n_proc == child_id: - feq_verif(factory,fvars,Nk_ij,id_anyon,nonuple) + feq_verif(factory,worker_results,fvars,Nk_ij,id_anyon,nonuple) if i % 50000000 == 0 and i and verbose: print("{:5d}m equations checked... {} potential misses so far...".format(i // 1000000,len(worker_results))) diff --git a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pxd b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pxd index 9fcaddb56e0..a992f0339a4 100644 --- a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pxd +++ b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pxd @@ -1,3 +1,2 @@ cpdef _unflatten_entries(factory, list entries) cpdef executor(tuple params) -cpdef list collect_results(int proc) diff --git a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx index 436809c2518..9be0bf567fe 100644 --- a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx @@ -16,9 +16,6 @@ from itertools import product from sage.misc.cachefunc import cached_function from sage.rings.qqbar import QQbar -#Define a global temporary worker results repository -worker_results = list() - ############### ### Mappers ### ############### @@ -109,7 +106,7 @@ cdef sig_2k(fusion_ring, tuple args): child_id, n_proc, fn_args = args k, a, b, n_strands = fn_args cdef int ctr = -1 - global worker_results + cdef list worker_results = list() #Get computational basis cdef list comp_basis = fusion_ring.get_computational_basis(a,b,n_strands) cdef dict basis_dict = { elt : i for i, elt in enumerate(comp_basis) } @@ -168,6 +165,7 @@ cdef sig_2k(fusion_ring, tuple args): entry = entry.list() worker_results.append(((basis_dict[nnz_pos], i), entry)) + return worker_results @cython.nonecheck(False) @cython.cdivision(True) @@ -181,7 +179,7 @@ cdef odd_one_out(fusion_ring, tuple args): _Nk_ij = fusion_ring.Nk_ij one = fusion_ring.one() - global worker_results + cdef list worker_results = list() cdef int child_id, n_proc child_id, n_proc, fn_args = args a, b, n_strands = fn_args @@ -238,36 +236,7 @@ cdef odd_one_out(fusion_ring, tuple args): entry = entry.list() worker_results.append(((basis_dict[nnz_pos],i), entry)) - -################ -### Reducers ### -################ - -cpdef list collect_results(int proc): - """ - Helper function for returning processed results back to parent process. - - Trivial reducer: simply collects objects with the same key in the worker. - This method is only useful when called after :meth:`executor`, whose - function argument appends output to the ``worker_results`` list. - - EXAMPLES:: - - sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import executor - sage: FR = FusionRing("A2",1) - sage: FR.fusion_labels("a",inject_variables=True) - sage: FR.fmats.find_orthogonal_solution(verbose=False) - sage: params = (('sig_2k',id(FR)),(0,1,(2,a2,a0,9))) - sage: executor(params) - sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import collect_results - sage: len(collect_results(0)) == 1 - True - """ - #Discard the zero polynomial - global worker_results - reduced = worker_results - worker_results = [] - return reduced + return worker_results ############################## ### Parallel code executor ### @@ -301,24 +270,21 @@ cpdef executor(tuple params): sage: FR.fusion_labels(['idd','one','two','three','four'],inject_variables=True) sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time sage: params = (('sig_2k',id(FR)),(0,1,(1,one,one,5))) # long time - sage: executor(params) # long time - sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import collect_results - sage: len(collect_results(0)) == 13 # long time + sage: len(executor(params)) == 13 # long time True - sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import executor, collect_results + sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import executor sage: FR = FusionRing("A1",2) sage: FR.fusion_labels("a",inject_variables=True) sage: FR.fmats.find_orthogonal_solution(verbose=False) sage: params = (('odd_one_out',id(FR)),(0,1,(a2,a2,5))) - sage: executor(params) - sage: len(collect_results(0)) == 1 + sage: len(executor(params)) == 1 True """ (fn_name, fr_id), args = params #Construct a reference to global FMatrix object in this worker's memory fusion_ring_obj = ctypes.cast(fr_id, ctypes.py_object).value #Bind module method to FMatrix object in worker process, and call the method - mappers[fn_name](fusion_ring_obj,args) + return mappers[fn_name](fusion_ring_obj,args) ###################################### ### Pickling circumvention helpers ### diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 218989cbfae..b602904d1aa 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -15,7 +15,7 @@ from multiprocessing import Pool, set_start_method from sage.combinat.q_analogues import q_int from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import ( - collect_results, executor, + executor, _unflatten_entries ) from sage.combinat.root_system.weyl_characters import WeylCharacterRing @@ -1204,20 +1204,16 @@ def _emap(self, mapper, input_args, worker_pool=None): n_proc = worker_pool._processes if worker_pool is not None else 1 input_iter = [(child_id, n_proc, input_args) for child_id in range(n_proc)] no_mp = worker_pool is None - #Map phase. Casting Async Object blocks execution... Each process holds results - #in its copy of fmats.temp_eqns + #Map phase input_iter = zip_longest([],input_iter,fillvalue=(mapper,id(self))) + results = list() if no_mp: - list(map(executor,input_iter)) + mapped = map(executor,input_iter) else: - list(worker_pool.imap_unordered(executor,input_iter,chunksize=1)) + mapped = worker_pool.imap_unordered(executor,input_iter,chunksize=1) #Reduce phase - if no_mp: - results = collect_results(0) - else: - results = [] - for worker_results in worker_pool.imap_unordered(collect_results,range(worker_pool._processes),chunksize=1): - results.extend(worker_results) + for worker_results in mapped: + results.extend(worker_results) return results def get_braid_generators(self, @@ -1227,7 +1223,7 @@ def get_braid_generators(self, checkpoint=False, save_results="", warm_start="", - use_mp=False, + use_mp=True, verbose=True): r""" Compute generators of the Artin braid group on ``n_strands`` strands. From 6c6b76b52498e012feb2de7e6e0e609a7f0d244b Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Wed, 21 Apr 2021 17:53:06 -0400 Subject: [PATCH 048/632] update_child_fmats cannot be cdef --- .../fast_parallel_fmats_methods.pxd | 1 + .../fast_parallel_fmats_methods.pyx | 23 ++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd index f54fa2ff174..faed5a56f5e 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd @@ -1,2 +1,3 @@ +cpdef update_child_fmats(factory, tuple data_tup) cdef _fmat(fvars, Nk_ij, one, a, b, c, d, x, y) cpdef executor(tuple params) diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index c9c09294c4c..21033456c1a 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -362,11 +362,32 @@ cdef list compute_gb(factory, tuple args): res.append(t) return collect_eqns(res) -cdef update_child_fmats(factory, tuple data_tup): +cpdef update_child_fmats(factory, tuple data_tup): r""" One-to-all communication used to update FMatrix object after each triangular elim step. We must update the algorithm's state values. These are: ``_fvars``, ``_solved``, ``_ks``, ``_var_degs``, ``_nnz``, and ``_kp``. + + TESTS:: + + sage: f = FMatrix(FusionRing("A1",3)) + sage: f.get_orthogonality_constraints(output=False) + sage: from multiprocessing import Pool, set_start_method + sage: try: + ....: set_start_method('fork') # context can be set only once + ....: except RuntimeError: + ....: pass + sage: pool = Pool() + sage: f.get_defining_equations('hexagons',worker_pool=pool,output=False) + sage: f.ideal_basis = f._par_graph_gb(worker_pool=pool,verbose=False) + sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey + sage: f.ideal_basis.sort(key=poly_tup_sortkey) + sage: f.mp_thresh = 0 + sage: f._triangular_elim(worker_pool=pool) # indirect doctest + Elimination epoch completed... 10 eqns remain in ideal basis + Elimination epoch completed... 0 eqns remain in ideal basis + sage: f.ideal_basis + [] """ #factory object is assumed global before forking used to create the Pool object, #so each child has a global fmats variable. So it's enough to update that object From 3eb09def29915f8630ebe37c8ed39197863fd480 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Wed, 21 Apr 2021 18:59:38 -0400 Subject: [PATCH 049/632] quick sleep fix for _update_child_fmats bug (same process takes two tasks off the queue) --- .../combinat/root_system/fast_parallel_fmats_methods.pyx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index 21033456c1a..2e8145fb03c 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -27,6 +27,8 @@ from itertools import product from sage.rings.ideal import Ideal from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from time import sleep + ########################## ### Fast class methods ### ########################## @@ -395,6 +397,9 @@ cpdef update_child_fmats(factory, tuple data_tup): factory._nnz = factory._get_known_nonz() factory._kp = compute_known_powers(factory._var_degs,factory._get_known_vals(),factory._field.one()) + #Wait this process isn't used again + sleep(0.4) + ################ ### Reducers ### ################ From 9a1949ef38743d6c2218f9aefffa88cc5723aafe Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Thu, 22 Apr 2021 14:20:45 -0400 Subject: [PATCH 050/632] get_variables_degrees returns dictionary and factory._ks uses flattened coefficients --- src/sage/combinat/root_system/f_matrix.py | 11 ++--- .../fast_parallel_fmats_methods.pyx | 43 +++++++++++++------ .../fast_parallel_fusion_ring_braid_repn.pyx | 1 - .../combinat/root_system/poly_tup_engine.pxd | 9 ++-- .../combinat/root_system/poly_tup_engine.pyx | 26 ++++++----- 5 files changed, 57 insertions(+), 33 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index ac262a0b644..231e36c0c0b 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -20,7 +20,7 @@ from copy import deepcopy from itertools import product, zip_longest -from multiprocessing import cpu_count, Pool, Queue, set_start_method +from multiprocessing import cpu_count, Pool, set_start_method import numpy as np import os @@ -941,7 +941,7 @@ def _get_known_sq(self,eqns=None): F = self._field for eq_tup in eqns: if tup_fixes_sq(eq_tup): - ks[variables(eq_tup)[0]] = -F(list(eq_tup[-1][1])) + ks[variables(eq_tup)[0]] = tuple(-v for v in eq_tup[-1][1]) return ks def _get_known_nonz(self): @@ -1486,9 +1486,10 @@ def _update_reduction_params(self,eqns=None,worker_pool=None,children_need_updat """ if eqns is None: eqns = self.ideal_basis - self._ks, self._var_degs = self._get_known_sq(eqns), get_variables_degrees(eqns) + self._ks = self._get_known_sq(eqns) + self._var_degs = ETuple(get_variables_degrees(eqns),self._poly_ring.ngens()) self._nnz = self._get_known_nonz() - self._kp = compute_known_powers(self._var_degs,self._get_known_vals(), self._field.one()) + self._kp = compute_known_powers(self._var_degs,self._get_known_vals(),self._field.one()) if worker_pool is not None and children_need_update: #self._nnz and self._kp are computed in child processes to reduce IPC overhead n_proc = worker_pool._processes @@ -1865,7 +1866,7 @@ def _get_explicit_solution(self,eqns=None,verbose=True): for fx, rhs in self._ks.items(): if fx not in self._solved: lt = (ETuple({fx : 2},n), one) - eqns.append((lt, (ETuple({},n), -rhs))) + eqns.append((lt, (ETuple({},n), -self._field(list(rhs))))) eqns_partition = self._partition_eqns(verbose=verbose) F = self._field diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index 2e8145fb03c..a81599c8e85 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -134,14 +134,18 @@ cpdef _backward_subs(factory): 1 """ one = factory._field.one() - for var in reversed(factory._poly_ring.gens()): + _ks = {k : factory._FR.field()(list(v)) for k, v in factory._ks.items()} + vars = factory._poly_ring.gens() + n = len(vars) + for var in reversed(vars): sextuple = factory._var_to_sextuple[var] rhs = factory._fvars[sextuple] d = {var_idx: factory._fvars[factory._idx_to_sextuple[var_idx]] for var_idx in variables(rhs) if var_idx in factory._solved} if d: - kp = compute_known_powers(get_variables_degrees([rhs]), d, one) - factory._fvars[sextuple] = tuple(subs_squares(subs(rhs,kp,one), factory._ks).items()) + degs = ETuple(get_variables_degrees([rhs]),n) + kp = compute_known_powers(degs, d, one) + factory._fvars[sextuple] = tuple(subs_squares(subs(rhs,kp,one), _ks).items()) cdef _fmat(fvars, _Nk_ij, id_anyon, a, b, c, d, x, y): """ @@ -229,9 +233,11 @@ cdef get_reduced_hexagons(factory, tuple mp_params): r_matrix = factory._FR.r_matrix _Nk_ij = factory._FR.Nk_ij id_anyon = factory._FR.one() - cdef NumberFieldElement_absolute one = factory._field.one() + _field = factory._field + cdef NumberFieldElement_absolute one = _field.one() cdef ETuple _nnz = factory._nnz - cdef dict _ks = factory._ks + # cdef dict _ks = factory._ks + _ks = {k : _field(list(v)) for k, v in factory._ks.items()} #Computation loop for i, sextuple in enumerate(product(basis, repeat=6)): @@ -281,9 +287,11 @@ cdef get_reduced_pentagons(factory, tuple mp_params): cdef dict fvars = factory._fvars _Nk_ij = factory._FR.Nk_ij id_anyon = factory._FR.one() - cdef NumberFieldElement_absolute one = factory._field.one() + _field = factory._field + cdef NumberFieldElement_absolute one = _field.one() cdef ETuple _nnz = factory._nnz - cdef dict _ks = factory._ks + # cdef dict _ks = factory._ks + _ks = {k : _field(list(v)) for k, v in factory._ks.items()} cdef MPolynomial_libsingular zero = factory._poly_ring.zero() #Computation loop @@ -306,17 +314,23 @@ cdef list update_reduce(factory, list eqns): Substitute known values, known squares, and reduce. """ cdef list res = list() - cdef NumberFieldElement_absolute one = factory._field.one() cdef tuple eq_tup, red, unflat cdef dict eq_dict + #Pre-compute common parameters for speed + _field = factory._field + one = _field.one() + _ks = {k : _field(list(v)) for k, v in factory._ks.items()} + cdef dict _kp = factory._kp + cdef ETuple _nnz = factory._nnz + for i in range(len(eqns)): eq_tup = eqns[i] #Construct cyclotomic field elts from list repn - unflat = _unflatten_coeffs(factory._field, eq_tup) + unflat = _unflatten_coeffs(_field, eq_tup) - eq_dict = subs(unflat,factory._kp,one) - red = reduce_poly_dict(eq_dict,factory._nnz,factory._ks,one) + eq_dict = subs(unflat,_kp,one) + red = reduce_poly_dict(eq_dict,_nnz,_ks,one) #Avoid pickling cyclotomic coefficients red = _flatten_coeffs(red) @@ -394,11 +408,12 @@ cpdef update_child_fmats(factory, tuple data_tup): #factory object is assumed global before forking used to create the Pool object, #so each child has a global fmats variable. So it's enough to update that object factory._fvars, factory._solved, factory._ks, factory._var_degs = data_tup + # factory._fvars, factory._solved, factory._var_degs = data_tup factory._nnz = factory._get_known_nonz() factory._kp = compute_known_powers(factory._var_degs,factory._get_known_vals(),factory._field.one()) #Wait this process isn't used again - sleep(0.4) + sleep(0.25) ################ ### Reducers ### @@ -420,6 +435,10 @@ cdef inline list collect_eqns(list eqns): ### Parallel code executor ### ############################## +def init(fmats_id, ks_proxy): + fmats_obj = ctypes.cast(fmats_id, ctypes.py_object).value + fmats_obj._ks = ks_proxy + #Hard-coded module __dict__-style attribute with visible cdef methods cdef dict mappers = { "get_reduced_hexagons": get_reduced_hexagons, diff --git a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx index 9be0bf567fe..a685dc1d48f 100644 --- a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx @@ -13,7 +13,6 @@ cimport cython from sage.combinat.root_system.fast_parallel_fmats_methods cimport _fmat from itertools import product -from sage.misc.cachefunc import cached_function from sage.rings.qqbar import QQbar ############### diff --git a/src/sage/combinat/root_system/poly_tup_engine.pxd b/src/sage/combinat/root_system/poly_tup_engine.pxd index 7ca654259d3..7b58bd39b2a 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pxd +++ b/src/sage/combinat/root_system/poly_tup_engine.pxd @@ -5,18 +5,19 @@ from sage.rings.polynomial.polydict cimport ETuple cpdef tuple poly_to_tup(MPolynomial_libsingular poly) cpdef MPolynomial_libsingular _tup_to_poly(tuple eq_tup, MPolynomialRing_libsingular parent) cpdef tuple resize(tuple eq_tup, dict idx_map, int nvars) -cpdef ETuple get_variables_degrees(list eqns) +cpdef dict get_variables_degrees(list eqns) cpdef list variables(tuple eq_tup) cpdef constant_coeff(tuple eq_tup) cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map) cpdef bint tup_fixes_sq(tuple eq_tup) -cdef dict subs_squares(dict eq_dict, dict known_sq) +# cdef dict subs_squares(dict eq_dict, dict known_sq) +cdef dict subs_squares(dict eq_dict, known_sq) cpdef dict compute_known_powers(ETuple max_deg, dict val_dict, one) cdef dict subs(tuple poly_tup, dict known_powers, one) cpdef tup_to_univ_poly(tuple eq_tup, univ_poly_ring) cpdef tuple poly_tup_sortkey(tuple eq_tup) -cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq, NumberFieldElement_absolute one) +# cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq, NumberFieldElement_absolute one) +cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, known_sq, NumberFieldElement_absolute one) cdef tuple _flatten_coeffs(tuple eq_tup) cpdef tuple _unflatten_coeffs(field, tuple eq_tup) cdef int has_appropriate_linear_term(tuple eq_tup) - diff --git a/src/sage/combinat/root_system/poly_tup_engine.pyx b/src/sage/combinat/root_system/poly_tup_engine.pyx index 5d662efc772..a812a9d5f28 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pyx +++ b/src/sage/combinat/root_system/poly_tup_engine.pyx @@ -80,7 +80,7 @@ cdef inline tuple _flatten_coeffs(tuple eq_tup): coefficients. This is used to avoid pickling cyclotomic coefficient objects, which fails - with new PARI settings introduced in trac ticket #30537 + with new PARI settings introduced in :trac:`30537`. """ cdef list flat = [] cdef NumberFieldElement_absolute cyc_coeff @@ -232,7 +232,7 @@ cdef inline ETuple degrees(tuple poly_tup): max_degs = max_degs.emax( ( poly_tup[i])[0]) return max_degs -cpdef ETuple get_variables_degrees(list eqns): +cpdef dict get_variables_degrees(list eqns): r""" Find maximum degrees for each variable in equations. @@ -243,16 +243,18 @@ cpdef ETuple get_variables_degrees(list eqns): sage: polys = [x**2 + 1, x*y*z**2 - 4*x*y, x*z**3 - 4/3*y + 1] sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup sage: get_variables_degrees([poly_to_tup(p) for p in polys]) - (2, 1, 3) + {0: 2, 1: 1, 2: 3} """ if not eqns: - return ETuple([]) + # return ETuple([]) + return dict() cdef ETuple max_deg cdef int i max_deg = degrees(eqns[0]) for i in range(1, len(eqns)): max_deg = max_deg.emax(degrees( (eqns[i]) )) - return max_deg + # return max_deg + return { max_deg._data[2*i] : max_deg._data[2*i+1] for i in range(max_deg._nonzero)} cpdef list variables(tuple eq_tup): """ @@ -356,7 +358,8 @@ cpdef inline bint tup_fixes_sq(tuple eq_tup): ### Simplification ### ###################### -cdef dict subs_squares(dict eq_dict, dict known_sq): +# cdef dict subs_squares(dict eq_dict, dict known_sq): +cdef dict subs_squares(dict eq_dict, known_sq): r""" Substitute for known squares into a given polynomial. @@ -437,7 +440,7 @@ cdef tuple to_monic(dict eq_dict, one): ret.append((ord_monoms[n-2-i], inv_lc * eq_dict[ord_monoms[n-2-i]])) return tuple(ret) -cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq, NumberFieldElement_absolute one): +cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, known_sq, NumberFieldElement_absolute one): """ Return a tuple describing a monic polynomial with no known nonzero gcf and no known squares. @@ -475,6 +478,8 @@ cpdef dict compute_known_powers(ETuple max_deg, dict val_dict, one): sage: known_val = { 0 : poly_to_tup(R(-1)), 2 : poly_to_tup(y**2) } sage: from sage.combinat.root_system.poly_tup_engine import get_variables_degrees sage: max_deg = get_variables_degrees([poly_to_tup(p) for p in polys]) + sage: from sage.rings.polynomial.polydict import ETuple + sage: max_deg = ETuple(max_deg,R.ngens()) sage: compute_known_powers(max_deg, known_val, R.base_ring().one()) {0: [(((0, 0, 0), 1),), (((0, 0, 0), -1),), @@ -485,9 +490,9 @@ cpdef dict compute_known_powers(ETuple max_deg, dict val_dict, one): (((0, 4, 0), 1),), (((0, 6, 0), 1),)]} """ - if not max_deg: - return {} - assert max(max_deg.nonzero_values(sort=False)) <= 100, "NotImplementedError: Cannot substitute for degree larger than 100" + # if not max_deg: + # return {} + assert max_deg._nonzero and max(max_deg.nonzero_values(sort=False)) <= 100 or True, "NotImplementedError: Cannot substitute for degree larger than 100" max_deg = max_deg.emin(ETuple({idx: 100 for idx in val_dict}, len(max_deg))) cdef dict known_powers #Get polynomial unit as tuple to initialize list elements @@ -606,4 +611,3 @@ cpdef tuple poly_tup_sortkey(tuple eq_tup): key.append(-exp._data[2*i]) key.append(exp._data[2*i+1]) return tuple(key) - From 2f101022a28be8c480e1260e3f2a8a466aafdcbc Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Thu, 22 Apr 2021 15:06:41 -0400 Subject: [PATCH 051/632] changed factory._solved from set -> indicator list --- src/sage/combinat/root_system/f_matrix.py | 54 ++++++++++++------- .../fast_parallel_fmats_methods.pyx | 15 ++++-- 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 231e36c0c0b..a6f68f93e59 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -296,7 +296,7 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab #Useful solver state attributes self.ideal_basis = list() - self._solved = set() + self._solved = list(False for fx in self._fvars) self._ks = dict() self._kp = dict() self._nnz = self._get_known_nonz() @@ -353,7 +353,8 @@ def clear_vars(self): fx0 """ self._fvars = {self._var_to_sextuple[key] : key for key in self._var_to_sextuple} - self._solved = set() + # self._solved = set() + self._solved = list(False for fx in self._fvars) def _reset_solver_state(self): r""" @@ -376,7 +377,7 @@ def _reset_solver_state(self): True sage: f._poly_ring.base_ring() == K True - sage: len(f._solved) == 0 + sage: sum(f._solved) == 0 True sage: len(f.ideal_basis) == 0 True @@ -904,7 +905,8 @@ def _get_known_vals(self): sage: len(f._get_known_vals()) == f._poly_ring.ngens() True """ - return {var_idx: self._fvars[self._idx_to_sextuple[var_idx]] for var_idx in self._solved} + # return {var_idx: self._fvars[self._idx_to_sextuple[var_idx]] for var_idx in self._solved} + return {var_idx: self._fvars[self._idx_to_sextuple[var_idx]] for var_idx, v in enumerate(self._solved) if v} def _get_known_sq(self,eqns=None): r""" @@ -1117,7 +1119,8 @@ def load_fvars(self, filename): self._fvars, self._non_cyc_roots, self._coerce_map_from_cyc_field, self._qqbar_embedding = pickle.load(f) #Update state attributes self._chkpt_status = 7 - self._solved = set(range(self._poly_ring.ngens())) + # self._solved = set(range(self._poly_ring.ngens())) + self._solved = list(True for v in self._fvars) self._field = self._qqbar_embedding.domain() def get_fr_str(self): @@ -1166,7 +1169,7 @@ def _checkpoint(self, do_chkpt, status, verbose=True): Computing appropriate NumberField... sage: f._chkpt_status == 7 True - sage: len(f._solved) == f._poly_ring.ngens() + sage: sum(f._solved) == f._poly_ring.ngens() True sage: os.remove("fmatrix_solver_checkpoint_A13.pickle") sage: f = FMatrix(FusionRing("A1",2)) @@ -1193,7 +1196,7 @@ def _checkpoint(self, do_chkpt, status, verbose=True): [1, 1] sage: f._chkpt_status == 7 True - sage: len(f._solved) == f._poly_ring.ngens() + sage: sum(f._solved) == f._poly_ring.ngens() True sage: os.remove("fmatrix_solver_checkpoint_A12.pickle") """ @@ -1864,7 +1867,8 @@ def _get_explicit_solution(self,eqns=None,verbose=True): n = self._poly_ring.ngens() one = self._field.one() for fx, rhs in self._ks.items(): - if fx not in self._solved: + # if fx not in self._solved: + if not self._solved[fx]: lt = (ETuple({fx : 2},n), one) eqns.append((lt, (ETuple({},n), -self._field(list(rhs))))) eqns_partition = self._partition_eqns(verbose=verbose) @@ -1924,9 +1928,11 @@ def _get_explicit_solution(self,eqns=None,verbose=True): self._update_poly_ring_base_field(self._field) #Ensure all F-symbols are known - self._solved.update(numeric_fvars) + # self._solved.update(numeric_fvars) + for fx in numeric_fvars: + self._solved[fx] = True nvars = self._poly_ring.ngens() - assert len(self._solved) == nvars, "Some F-symbols are still missing...{}".format([self._poly_ring.gen(fx) for fx in set(range(nvars)).difference(self._solved)]) + assert sum(self._solved) == nvars, "Some F-symbols are still missing...{}".format([self._poly_ring.gen(fx) for fx in range(nvars) if not self._solved[fx]]) #Backward substitution step. Traverse variables in reverse lexicographical order. (System is in triangular form) self._fvars = {sextuple : apply_coeff_map(poly_to_tup(rhs),phi) for sextuple, rhs in self._fvars.items()} @@ -2074,7 +2080,8 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start #Report progress if verbose: - print("Hex elim step solved for {} / {} variables".format(len(self._solved), len(self._poly_ring.gens()))) + # print("Hex elim step solved for {} / {} variables".format(len(self._solved), len(self._poly_ring.gens()))) + print("Hex elim step solved for {} / {} variables".format(sum(self._solved), len(self._poly_ring.gens()))) self._checkpoint(checkpoint,2,verbose=verbose) @@ -2095,7 +2102,8 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start #Report progress if verbose: - print("Pent elim step solved for {} / {} variables".format(len(self._solved), len(self._poly_ring.gens()))) + # print("Pent elim step solved for {} / {} variables".format(len(self._solved), len(self._poly_ring.gens()))) + print("Pent elim step solved for {} / {} variables".format(sum(self._solved), len(self._poly_ring.gens()))) self._checkpoint(checkpoint,4,verbose=verbose) @@ -2149,11 +2157,14 @@ def _fix_gauge(self, algorithm=""): adding equation... fx18 - 1 adding equation... fx21 - 1 """ - while len(self._solved) < len(self._poly_ring.gens()): + # while len(self._solved) < len(self._poly_ring.gens()): + while sum(1 for v in self._solved if not v) > 0: #Get a variable that has not been fixed #In ascending index order, for consistent results - for var in self._poly_ring.gens(): - if var not in self._solved: + # for var in self._poly_ring.gens(): + # if var not in self._solved: + for i, var in enumerate(self._poly_ring.gens()): + if not self._solved[i]: break #Fix var = 1, substitute, and solve equations @@ -2187,16 +2198,20 @@ def _substitute_degree_one(self, eqns=None): new_knowns = set() useless = set() for eq in eqns: - if eq.degree() == 1 and sum(eq.degrees()) <= 2 and eq.lm() not in self._solved: + # if eq.degree() == 1 and sum(eq.degrees()) <= 2 and eq.lm() not in self._solved: + if eq.degree() == 1 and sum(eq.degrees()) <= 2 and not self._solved[self._var_to_idx[eq.lm()]]: self._fvars[self._var_to_sextuple[eq.lm()]] = -sum(c * m for c, m in zip(eq.coefficients()[1:], eq.monomials()[1:])) / eq.lc() #Add variable to set of known values and remove this equation new_knowns.add(eq.lm()) useless.add(eq) #Update fvars depending on other variables - self._solved.update(new_knowns) + # self._solved.update(new_knowns) + for fx in new_knowns: + self._solved[self._var_to_idx[fx]] = fx for sextuple, rhs in self._fvars.items(): - d = {var : self._fvars[self._var_to_sextuple[var]] for var in rhs.variables() if var in self._solved} + # d = {var : self._fvars[self._var_to_sextuple[var]] for var in rhs.variables() if var in self._solved} + d = {var: self._fvars[self._var_to_sextuple[var]] for var in rhs.variables() if self._solved[self._var_to_idx[var]]} if d: self._fvars[sextuple] = rhs.subs(d) return new_knowns, useless @@ -2216,7 +2231,8 @@ def _update_equations(self): sage: f.ideal_basis {fx3} """ - special_values = {known : self._fvars[self._var_to_sextuple[known]] for known in self._solved} + # special_values = {known : self._fvars[self._var_to_sextuple[known]] for known in self._solved} + special_values = {known : self._fvars[self._var_to_sextuple[known]] for known in self._solved if known} self.ideal_basis = set(eq.subs(special_values) for eq in self.ideal_basis) self.ideal_basis.discard(0) diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index a81599c8e85..c1f4c96ae07 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -74,20 +74,24 @@ cpdef _solve_for_linear_terms(factory, eqns=None): if len(eq_tup) == 1: vars = variables(eq_tup) - if len(vars) == 1 and vars[0] not in factory._solved: + # if len(vars) == 1 and vars[0] not in factory._solved: + if len(vars) == 1 and not factory._solved[vars[0]]: factory._fvars[factory._idx_to_sextuple[vars[0]]] = tuple() - factory._solved.add(vars[0]) + # factory._solved.add(vars[0]) + factory._solved[vars[0]] = True linear_terms_exist = True if len(eq_tup) == 2: idx = has_appropriate_linear_term(eq_tup) if idx < 0: continue #The chosen term is guaranteed to be univariate in the largest variable max_var = eq_tup[idx][0].nonzero_positions()[0] - if max_var not in factory._solved: + # if max_var not in factory._solved: + if not factory._solved[max_var]: rhs_exp = eq_tup[(idx+1) % 2][0] rhs_coeff = -eq_tup[(idx+1) % 2][1] / eq_tup[idx][1] factory._fvars[factory._idx_to_sextuple[max_var]] = ((rhs_exp,rhs_coeff),) - factory._solved.add(max_var) + # factory._solved.add(max_var) + factory._solved[max_var] = True linear_terms_exist = True return linear_terms_exist @@ -141,7 +145,8 @@ cpdef _backward_subs(factory): sextuple = factory._var_to_sextuple[var] rhs = factory._fvars[sextuple] d = {var_idx: factory._fvars[factory._idx_to_sextuple[var_idx]] - for var_idx in variables(rhs) if var_idx in factory._solved} + # for var_idx in variables(rhs) if var_idx in factory._solved} + for var_idx in variables(rhs) if factory._solved[var_idx]} if d: degs = ETuple(get_variables_degrees([rhs]),n) kp = compute_known_powers(degs, d, one) From a68b93708e9d9a2260fb9febc0300c3fc5f1a0b2 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Thu, 22 Apr 2021 15:54:01 -0400 Subject: [PATCH 052/632] _var_degs changed from dict to dense list --- src/sage/combinat/root_system/f_matrix.py | 3 +-- .../fast_parallel_fmats_methods.pyx | 3 +-- .../combinat/root_system/poly_tup_engine.pxd | 4 ++-- .../combinat/root_system/poly_tup_engine.pyx | 20 +++++++++++-------- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index a6f68f93e59..54ff59cb699 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -1489,8 +1489,7 @@ def _update_reduction_params(self,eqns=None,worker_pool=None,children_need_updat """ if eqns is None: eqns = self.ideal_basis - self._ks = self._get_known_sq(eqns) - self._var_degs = ETuple(get_variables_degrees(eqns),self._poly_ring.ngens()) + self._ks, self._var_degs = self._get_known_sq(eqns), get_variables_degrees(eqns) self._nnz = self._get_known_nonz() self._kp = compute_known_powers(self._var_degs,self._get_known_vals(),self._field.one()) if worker_pool is not None and children_need_update: diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index c1f4c96ae07..a777313b5a1 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -148,8 +148,7 @@ cpdef _backward_subs(factory): # for var_idx in variables(rhs) if var_idx in factory._solved} for var_idx in variables(rhs) if factory._solved[var_idx]} if d: - degs = ETuple(get_variables_degrees([rhs]),n) - kp = compute_known_powers(degs, d, one) + kp = compute_known_powers(get_variables_degrees([rhs]), d, one) factory._fvars[sextuple] = tuple(subs_squares(subs(rhs,kp,one), _ks).items()) cdef _fmat(fvars, _Nk_ij, id_anyon, a, b, c, d, x, y): diff --git a/src/sage/combinat/root_system/poly_tup_engine.pxd b/src/sage/combinat/root_system/poly_tup_engine.pxd index 7b58bd39b2a..07781084c36 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pxd +++ b/src/sage/combinat/root_system/poly_tup_engine.pxd @@ -5,14 +5,14 @@ from sage.rings.polynomial.polydict cimport ETuple cpdef tuple poly_to_tup(MPolynomial_libsingular poly) cpdef MPolynomial_libsingular _tup_to_poly(tuple eq_tup, MPolynomialRing_libsingular parent) cpdef tuple resize(tuple eq_tup, dict idx_map, int nvars) -cpdef dict get_variables_degrees(list eqns) +cpdef list get_variables_degrees(list eqns) cpdef list variables(tuple eq_tup) cpdef constant_coeff(tuple eq_tup) cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map) cpdef bint tup_fixes_sq(tuple eq_tup) # cdef dict subs_squares(dict eq_dict, dict known_sq) cdef dict subs_squares(dict eq_dict, known_sq) -cpdef dict compute_known_powers(ETuple max_deg, dict val_dict, one) +cpdef dict compute_known_powers(list max_degs, dict val_dict, one) cdef dict subs(tuple poly_tup, dict known_powers, one) cpdef tup_to_univ_poly(tuple eq_tup, univ_poly_ring) cpdef tuple poly_tup_sortkey(tuple eq_tup) diff --git a/src/sage/combinat/root_system/poly_tup_engine.pyx b/src/sage/combinat/root_system/poly_tup_engine.pyx index a812a9d5f28..ee2357b2468 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pyx +++ b/src/sage/combinat/root_system/poly_tup_engine.pyx @@ -232,7 +232,7 @@ cdef inline ETuple degrees(tuple poly_tup): max_degs = max_degs.emax( ( poly_tup[i])[0]) return max_degs -cpdef dict get_variables_degrees(list eqns): +cpdef list get_variables_degrees(list eqns): r""" Find maximum degrees for each variable in equations. @@ -243,18 +243,22 @@ cpdef dict get_variables_degrees(list eqns): sage: polys = [x**2 + 1, x*y*z**2 - 4*x*y, x*z**3 - 4/3*y + 1] sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup sage: get_variables_degrees([poly_to_tup(p) for p in polys]) - {0: 2, 1: 1, 2: 3} + [2, 1, 3] """ if not eqns: # return ETuple([]) - return dict() + return list() cdef ETuple max_deg cdef int i max_deg = degrees(eqns[0]) for i in range(1, len(eqns)): max_deg = max_deg.emax(degrees( (eqns[i]) )) # return max_deg - return { max_deg._data[2*i] : max_deg._data[2*i+1] for i in range(max_deg._nonzero)} + # return { max_deg._data[2*i] : max_deg._data[2*i+1] for i in range(max_deg._nonzero)} + cdef list dense = [0]*len(max_deg) + for i in range(max_deg._nonzero): + dense[max_deg._data[2*i]] = max_deg._data[2*i+1] + return dense cpdef list variables(tuple eq_tup): """ @@ -455,7 +459,7 @@ cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, known_sq, NumberFieldElem ### Substitution ### #################### -cpdef dict compute_known_powers(ETuple max_deg, dict val_dict, one): +cpdef dict compute_known_powers(list max_degs, dict val_dict, one): """ Pre-compute powers of known values for efficiency when preparing to substitute into a list of polynomials. @@ -478,8 +482,6 @@ cpdef dict compute_known_powers(ETuple max_deg, dict val_dict, one): sage: known_val = { 0 : poly_to_tup(R(-1)), 2 : poly_to_tup(y**2) } sage: from sage.combinat.root_system.poly_tup_engine import get_variables_degrees sage: max_deg = get_variables_degrees([poly_to_tup(p) for p in polys]) - sage: from sage.rings.polynomial.polydict import ETuple - sage: max_deg = ETuple(max_deg,R.ngens()) sage: compute_known_powers(max_deg, known_val, R.base_ring().one()) {0: [(((0, 0, 0), 1),), (((0, 0, 0), -1),), @@ -492,7 +494,9 @@ cpdef dict compute_known_powers(ETuple max_deg, dict val_dict, one): """ # if not max_deg: # return {} - assert max_deg._nonzero and max(max_deg.nonzero_values(sort=False)) <= 100 or True, "NotImplementedError: Cannot substitute for degree larger than 100" + # assert max_deg._nonzero and max(max_deg.nonzero_values(sort=False)) <= 100 or True, "NotImplementedError: Cannot substitute for degree larger than 100" + assert (max_degs and max(max_degs) <= 100) or True, "Cannot substitute for degree larger than 100" + cdef ETuple max_deg = ETuple(max_degs) max_deg = max_deg.emin(ETuple({idx: 100 for idx in val_dict}, len(max_deg))) cdef dict known_powers #Get polynomial unit as tuple to initialize list elements From 78da266deeb0acd48856bed262ce825d5b113a81 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Fri, 23 Apr 2021 14:17:35 -0400 Subject: [PATCH 053/632] _solved and _var_degs using shared_memory... FileNotFoundError in _checkpoint remains --- src/sage/combinat/root_system/f_matrix.py | 51 ++++++++++++++++--- .../fast_parallel_fmats_methods.pyx | 22 +++++--- .../combinat/root_system/poly_tup_engine.pxd | 2 +- .../combinat/root_system/poly_tup_engine.pyx | 4 +- 4 files changed, 63 insertions(+), 16 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 54ff59cb699..0ec222fe63c 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -26,7 +26,7 @@ from sage.combinat.root_system.fast_parallel_fmats_methods import ( _backward_subs, _solve_for_linear_terms, - executor + executor, init ) from sage.combinat.root_system.poly_tup_engine import ( apply_coeff_map, constant_coeff, @@ -46,6 +46,8 @@ from sage.rings.polynomial.polydict import ETuple from sage.rings.qqbar import AA, QQbar, number_field_elements_from_algebraics +from multiprocessing import shared_memory + class FMatrix(): def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variables=False): r""" @@ -297,6 +299,7 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab #Useful solver state attributes self.ideal_basis = list() self._solved = list(False for fx in self._fvars) + self._var_degs = [0]*len(self._fvars) self._ks = dict() self._kp = dict() self._nnz = self._get_known_nonz() @@ -1204,7 +1207,7 @@ def _checkpoint(self, do_chkpt, status, verbose=True): return filename = "fmatrix_solver_checkpoint_" + self.get_fr_str() + ".pickle" with open(filename, 'wb') as f: - pickle.dump([self._fvars, self._solved, self._ks, self.ideal_basis, status], f) + pickle.dump([self._fvars, list(self._solved), self._ks, self.ideal_basis, status], f) if verbose: print(f"Checkpoint {status} reached!") @@ -1472,12 +1475,19 @@ def _update_reduction_params(self,eqns=None,worker_pool=None,children_need_updat sage: f = FMatrix(FusionRing("A1",3)) sage: f.get_orthogonality_constraints(output=False) - sage: from multiprocessing import Pool, set_start_method + sage: from multiprocessing import cpu_count, Pool, set_start_method, shared_memory sage: try: ....: set_start_method('fork') ....: except: ....: pass - sage: pool = Pool() + sage: n = max(cpu_count()-1,1) + sage: f._solved = shared_memory.ShareableList(f._solved) + sage: s_name = f._solved.shm.name + sage: f._var_degs = shared_memory.ShareableList(f._var_degs) + sage: vd_name = f._var_degs.shm.name + sage: args = (id(f), s_name, vd_name) + sage: from sage.combinat.root_system.fast_parallel_fmats_methods import init + sage: pool = Pool(processes=n,initializer=init,initargs=args) sage: f.get_defining_equations('hexagons',worker_pool=pool,output=False) sage: f.ideal_basis = f._par_graph_gb(worker_pool=pool,verbose=False) sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey @@ -1489,13 +1499,25 @@ def _update_reduction_params(self,eqns=None,worker_pool=None,children_need_updat """ if eqns is None: eqns = self.ideal_basis - self._ks, self._var_degs = self._get_known_sq(eqns), get_variables_degrees(eqns) + self._ks = self._get_known_sq(eqns) + # print("res",get_variables_degrees(eqns)) + degs = get_variables_degrees(eqns) + if degs: + for i, d in enumerate(degs): + # print(i, d) + self._var_degs[i] = d + else: + for i in range(len(self._fvars)): + self._var_degs[i] = 0 + # print("vd, var_degs") + # self._var_degs = get_variables_degrees(eqns) self._nnz = self._get_known_nonz() self._kp = compute_known_powers(self._var_degs,self._get_known_vals(),self._field.one()) if worker_pool is not None and children_need_update: #self._nnz and self._kp are computed in child processes to reduce IPC overhead n_proc = worker_pool._processes - new_data = [(self._fvars,self._solved,self._ks,self._var_degs)]*n_proc + # new_data = [(self._fvars,self._solved,self._ks,self._var_degs)]*n_proc + new_data = [(self._fvars,self._ks)]*n_proc self._map_triv_reduce('update_child_fmats',new_data,worker_pool=worker_pool,chunksize=1,mp_thresh=0) def _triangular_elim(self,eqns=None,worker_pool=None,verbose=True): @@ -2053,7 +2075,18 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start set_start_method('fork') except RuntimeError: pass - pool = Pool(processes=max(cpu_count()-1,1)) if use_mp else None + # pool = Pool(processes=max(cpu_count()-1,1)) if use_mp else None + if use_mp: + n = max(cpu_count()-1,1) + self._solved = shared_memory.ShareableList(self._solved) + print(self._solved) + s_name = self._solved.shm.name + self._var_degs = shared_memory.ShareableList(self._var_degs) + vd_name = self._var_degs.shm.name + args = (id(self), s_name, vd_name) + pool = Pool(processes=n,initializer=init,initargs=args) + else: + pool = None if verbose: print("Computing F-symbols for {} with {} variables...".format(self._FR, len(self._fvars))) @@ -2111,6 +2144,9 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start #Close worker pool to free resources if pool is not None: pool.close() + #Destroy shared resources + self._solved.shm.unlink() + self._var_degs.shm.unlink() #Set up new equations graph and compute variety for each component if self._chkpt_status < 5: @@ -2131,6 +2167,7 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start if save_results: self.save_fvars(save_results) + ######################### ### Cyclotomic method ### ######################### diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index a777313b5a1..c2e7316b4cf 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -29,6 +29,8 @@ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from time import sleep +from multiprocessing import shared_memory + ########################## ### Fast class methods ### ########################## @@ -392,12 +394,19 @@ cpdef update_child_fmats(factory, tuple data_tup): sage: f = FMatrix(FusionRing("A1",3)) sage: f.get_orthogonality_constraints(output=False) - sage: from multiprocessing import Pool, set_start_method + sage: from multiprocessing import cpu_count, Pool, set_start_method, shared_memory sage: try: ....: set_start_method('fork') # context can be set only once ....: except RuntimeError: ....: pass - sage: pool = Pool() + sage: n = max(cpu_count()-1,1) + sage: f._solved = shared_memory.ShareableList(f._solved) + sage: s_name = f._solved.shm.name + sage: f._var_degs = shared_memory.ShareableList(f._var_degs) + sage: vd_name = f._var_degs.shm.name + sage: args = (id(f), s_name, vd_name) + sage: from sage.combinat.root_system.fast_parallel_fmats_methods import init + sage: pool = Pool(processes=n,initializer=init,initargs=args) sage: f.get_defining_equations('hexagons',worker_pool=pool,output=False) sage: f.ideal_basis = f._par_graph_gb(worker_pool=pool,verbose=False) sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey @@ -411,8 +420,8 @@ cpdef update_child_fmats(factory, tuple data_tup): """ #factory object is assumed global before forking used to create the Pool object, #so each child has a global fmats variable. So it's enough to update that object - factory._fvars, factory._solved, factory._ks, factory._var_degs = data_tup - # factory._fvars, factory._solved, factory._var_degs = data_tup + # factory._fvars, factory._solved, factory._ks, factory._var_degs = data_tup + factory._fvars, factory._ks = data_tup factory._nnz = factory._get_known_nonz() factory._kp = compute_known_powers(factory._var_degs,factory._get_known_vals(),factory._field.one()) @@ -439,9 +448,10 @@ cdef inline list collect_eqns(list eqns): ### Parallel code executor ### ############################## -def init(fmats_id, ks_proxy): +def init(fmats_id, solved_name, vd_name): fmats_obj = ctypes.cast(fmats_id, ctypes.py_object).value - fmats_obj._ks = ks_proxy + fmats_obj._solved = shared_memory.ShareableList(name=solved_name) + fmats_obj._var_degs = shared_memory.ShareableList(name=vd_name) #Hard-coded module __dict__-style attribute with visible cdef methods cdef dict mappers = { diff --git a/src/sage/combinat/root_system/poly_tup_engine.pxd b/src/sage/combinat/root_system/poly_tup_engine.pxd index 07781084c36..0a768b40851 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pxd +++ b/src/sage/combinat/root_system/poly_tup_engine.pxd @@ -12,7 +12,7 @@ cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map) cpdef bint tup_fixes_sq(tuple eq_tup) # cdef dict subs_squares(dict eq_dict, dict known_sq) cdef dict subs_squares(dict eq_dict, known_sq) -cpdef dict compute_known_powers(list max_degs, dict val_dict, one) +cpdef dict compute_known_powers(max_degs, dict val_dict, one) cdef dict subs(tuple poly_tup, dict known_powers, one) cpdef tup_to_univ_poly(tuple eq_tup, univ_poly_ring) cpdef tuple poly_tup_sortkey(tuple eq_tup) diff --git a/src/sage/combinat/root_system/poly_tup_engine.pyx b/src/sage/combinat/root_system/poly_tup_engine.pyx index ee2357b2468..42ee9217782 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pyx +++ b/src/sage/combinat/root_system/poly_tup_engine.pyx @@ -459,7 +459,7 @@ cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, known_sq, NumberFieldElem ### Substitution ### #################### -cpdef dict compute_known_powers(list max_degs, dict val_dict, one): +cpdef dict compute_known_powers(max_degs, dict val_dict, one): """ Pre-compute powers of known values for efficiency when preparing to substitute into a list of polynomials. @@ -496,7 +496,7 @@ cpdef dict compute_known_powers(list max_degs, dict val_dict, one): # return {} # assert max_deg._nonzero and max(max_deg.nonzero_values(sort=False)) <= 100 or True, "NotImplementedError: Cannot substitute for degree larger than 100" assert (max_degs and max(max_degs) <= 100) or True, "Cannot substitute for degree larger than 100" - cdef ETuple max_deg = ETuple(max_degs) + cdef ETuple max_deg = ETuple(list(max_degs)) max_deg = max_deg.emin(ETuple({idx: 100 for idx in val_dict}, len(max_deg))) cdef dict known_powers #Get polynomial unit as tuple to initialize list elements From 2ff7cc36e86d060fe3f4f547bc13a48c14a6c387 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Fri, 23 Apr 2021 17:49:48 -0400 Subject: [PATCH 054/632] _ks now in shared_memory. must re-implement using single numpy array due to too many open files issue. _checkpoint bug remains --- src/sage/combinat/root_system/f_matrix.py | 29 ++++--- .../fast_parallel_fmats_methods.pyx | 19 +++-- src/sage/combinat/root_system/shm_managers.py | 76 +++++++++++++++++++ 3 files changed, 108 insertions(+), 16 deletions(-) create mode 100644 src/sage/combinat/root_system/shm_managers.py diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 0ec222fe63c..5e8df07744d 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -47,6 +47,7 @@ from sage.rings.qqbar import AA, QQbar, number_field_elements_from_algebraics from multiprocessing import shared_memory +from sage.combinat.root_system.shm_managers import KSHandler class FMatrix(): def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variables=False): @@ -300,7 +301,8 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab self.ideal_basis = list() self._solved = list(False for fx in self._fvars) self._var_degs = [0]*len(self._fvars) - self._ks = dict() + # self._ks = dict() + self._ks = KSHandler(self) self._kp = dict() self._nnz = self._get_known_nonz() self._chkpt_status = -1 @@ -401,7 +403,8 @@ def _reset_solver_state(self): self._chkpt_status = -1 self.clear_vars() self.clear_equations() - self._ks = dict() + # self._ks = dict() + self._ks.reset() self._nnz = self._get_known_nonz() #Clear relevant caches @@ -942,12 +945,13 @@ def _get_known_sq(self,eqns=None): """ if eqns is None: eqns = self.ideal_basis - ks = deepcopy(self._ks) + # ks = deepcopy(self._ks) F = self._field for eq_tup in eqns: if tup_fixes_sq(eq_tup): - ks[variables(eq_tup)[0]] = tuple(-v for v in eq_tup[-1][1]) - return ks + # ks[variables(eq_tup)[0]] = tuple(-v for v in eq_tup[-1][1]) + self._ks[variables(eq_tup)[0]] = -self._field(list(eq_tup[-1][1])) + # return ks def _get_known_nonz(self): r""" @@ -966,7 +970,8 @@ def _get_known_nonz(self): 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100) """ nonz = {self._var_to_idx[var] : 100 for var in self._singles} - for idx in self._ks: + # for idx in self._ks: + for idx, v in self._ks.items(): nonz[idx] = 100 return ETuple(nonz, self._poly_ring.ngens()) @@ -1499,7 +1504,8 @@ def _update_reduction_params(self,eqns=None,worker_pool=None,children_need_updat """ if eqns is None: eqns = self.ideal_basis - self._ks = self._get_known_sq(eqns) + # self._ks = self._get_known_sq(eqns) + self._get_known_sq(eqns) # print("res",get_variables_degrees(eqns)) degs = get_variables_degrees(eqns) if degs: @@ -1517,7 +1523,8 @@ def _update_reduction_params(self,eqns=None,worker_pool=None,children_need_updat #self._nnz and self._kp are computed in child processes to reduce IPC overhead n_proc = worker_pool._processes # new_data = [(self._fvars,self._solved,self._ks,self._var_degs)]*n_proc - new_data = [(self._fvars,self._ks)]*n_proc + # new_data = [(self._fvars,self._ks)]*n_proc + new_data = [(self._fvars,)]*n_proc self._map_triv_reduce('update_child_fmats',new_data,worker_pool=worker_pool,chunksize=1,mp_thresh=0) def _triangular_elim(self,eqns=None,worker_pool=None,verbose=True): @@ -2079,11 +2086,12 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start if use_mp: n = max(cpu_count()-1,1) self._solved = shared_memory.ShareableList(self._solved) - print(self._solved) + # print(self._solved) s_name = self._solved.shm.name self._var_degs = shared_memory.ShareableList(self._var_degs) vd_name = self._var_degs.shm.name - args = (id(self), s_name, vd_name) + ks_names = self._ks.get_names() + args = (id(self), s_name, vd_name, ks_names) pool = Pool(processes=n,initializer=init,initargs=args) else: pool = None @@ -2147,6 +2155,7 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start #Destroy shared resources self._solved.shm.unlink() self._var_degs.shm.unlink() + self._ks.unlink() #Set up new equations graph and compute variety for each component if self._chkpt_status < 5: diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index c2e7316b4cf..634b2d21eaf 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -30,6 +30,7 @@ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from time import sleep from multiprocessing import shared_memory +from sage.combinat.root_system.shm_managers import KSHandler ########################## ### Fast class methods ### @@ -140,7 +141,8 @@ cpdef _backward_subs(factory): 1 """ one = factory._field.one() - _ks = {k : factory._FR.field()(list(v)) for k, v in factory._ks.items()} + # _ks = {k : factory._FR.field()(list(v)) for k, v in factory._ks.items()} + _ks = factory._ks vars = factory._poly_ring.gens() n = len(vars) for var in reversed(vars): @@ -243,7 +245,8 @@ cdef get_reduced_hexagons(factory, tuple mp_params): cdef NumberFieldElement_absolute one = _field.one() cdef ETuple _nnz = factory._nnz # cdef dict _ks = factory._ks - _ks = {k : _field(list(v)) for k, v in factory._ks.items()} + # _ks = {k : _field(list(v)) for k, v in factory._ks.items()} + _ks = factory._ks #Computation loop for i, sextuple in enumerate(product(basis, repeat=6)): @@ -297,7 +300,8 @@ cdef get_reduced_pentagons(factory, tuple mp_params): cdef NumberFieldElement_absolute one = _field.one() cdef ETuple _nnz = factory._nnz # cdef dict _ks = factory._ks - _ks = {k : _field(list(v)) for k, v in factory._ks.items()} + # _ks = {k : _field(list(v)) for k, v in factory._ks.items()} + _ks = factory._ks cdef MPolynomial_libsingular zero = factory._poly_ring.zero() #Computation loop @@ -326,7 +330,8 @@ cdef list update_reduce(factory, list eqns): #Pre-compute common parameters for speed _field = factory._field one = _field.one() - _ks = {k : _field(list(v)) for k, v in factory._ks.items()} + # _ks = {k : _field(list(v)) for k, v in factory._ks.items()} + _ks = factory._ks cdef dict _kp = factory._kp cdef ETuple _nnz = factory._nnz @@ -421,7 +426,8 @@ cpdef update_child_fmats(factory, tuple data_tup): #factory object is assumed global before forking used to create the Pool object, #so each child has a global fmats variable. So it's enough to update that object # factory._fvars, factory._solved, factory._ks, factory._var_degs = data_tup - factory._fvars, factory._ks = data_tup + # factory._fvars, factory._ks = data_tup + factory._fvars = data_tup[0] factory._nnz = factory._get_known_nonz() factory._kp = compute_known_powers(factory._var_degs,factory._get_known_vals(),factory._field.one()) @@ -448,10 +454,11 @@ cdef inline list collect_eqns(list eqns): ### Parallel code executor ### ############################## -def init(fmats_id, solved_name, vd_name): +def init(fmats_id, solved_name, vd_name, ks_names): fmats_obj = ctypes.cast(fmats_id, ctypes.py_object).value fmats_obj._solved = shared_memory.ShareableList(name=solved_name) fmats_obj._var_degs = shared_memory.ShareableList(name=vd_name) + fmats_obj._ks = KSHandler(fmats_obj,ks_names) #Hard-coded module __dict__-style attribute with visible cdef methods cdef dict mappers = { diff --git a/src/sage/combinat/root_system/shm_managers.py b/src/sage/combinat/root_system/shm_managers.py new file mode 100644 index 00000000000..83f1dfdb27e --- /dev/null +++ b/src/sage/combinat/root_system/shm_managers.py @@ -0,0 +1,76 @@ +from multiprocessing import shared_memory + +class KSHandler(): + def __init__(self, factory, names=None): + n = len(factory._fvars) + self.field = factory._field + self.index = 0 + if names is None: + self.ks = shared_memory.ShareableList([False]*n) + self.nums = [shared_memory.ShareableList([int(0)]*self.field.degree()) for i in range(n)] + self.denoms = shared_memory.ShareableList([int(0)]*n) + else: + self.ks = shared_memory.ShareableList(name=names[0]) + self.nums = [shared_memory.ShareableList(name=names[1][i]) for i in range(n)] + self.denoms = shared_memory.ShareableList(name=names[2]) + + def __getitem__(self,idx): + if not self.ks[idx]: + raise KeyError('Index {} does not correspond to a known square'.format(idx)) + denom = self.denoms[idx] + return self.field([num / denom for num in self.nums[idx]]) + + def __setitem__(self,idx,rhs): + self.ks[idx] = True + self.denoms[idx] = int(rhs.denominator()) + for i, c in enumerate(rhs._coefficients()): + self.nums[idx][i] = int(c * self.denoms[idx]) + + def __iter__(self): + self.index = 0 + return self + + def __next__(self): + if self.index == len(self.ks): + raise StopIteration + + #Skip indices that are not known + while not self.ks[self.index]: + self.index += 1 + if self.index == len(self.ks): + raise StopIteration + + denom = self.denoms[self.index] + ret = self.field([num / denom for num in self.nums[self.index]]) + self.index += 1 + return ret + + def __contains__(self,idx): + return self.ks[idx] + + def __len__(self): + return sum(self.ks) + + def items(self): + for v in self: + yield self.index-1, v + + def reset(self): + n = len(self.ks) + for i in range(n): + self.ks[i] = False + self.denoms[i] = 0 + for j in range(len(self.nums[i])): + self.nums[i][j] = 0 + + def unlink(self): + self.ks.shm.unlink() + for num_list in self.nums: + num_list.shm.unlink() + self.denoms.shm.unlink() + + def get_names(self): + ks_name = self.ks.shm.name + nums_names = [num_list.shm.name for num_list in self.nums] + denom_name = self.denoms.shm.name + return (ks_name,nums_names,denom_name) From c92392fbf70f2bf4c7e6ce2078164001a8e505d7 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Tue, 27 Apr 2021 16:49:25 -0400 Subject: [PATCH 055/632] _ks, _solved, _var_degs in shared_memory. all doctests passing --- src/sage/combinat/root_system/f_matrix.py | 57 ++++++--------- .../fast_parallel_fmats_methods.pyx | 17 +---- .../combinat/root_system/poly_tup_engine.pyx | 6 -- src/sage/combinat/root_system/shm_managers.py | 70 +++++++++++-------- 4 files changed, 63 insertions(+), 87 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 5e8df07744d..c647f94c9fb 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -300,8 +300,8 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab #Useful solver state attributes self.ideal_basis = list() self._solved = list(False for fx in self._fvars) + # self._solved = np.zeros((len(self._fvars),), dtype='bool') self._var_degs = [0]*len(self._fvars) - # self._ks = dict() self._ks = KSHandler(self) self._kp = dict() self._nnz = self._get_known_nonz() @@ -358,8 +358,8 @@ def clear_vars(self): fx0 """ self._fvars = {self._var_to_sextuple[key] : key for key in self._var_to_sextuple} - # self._solved = set() self._solved = list(False for fx in self._fvars) + # self._solved[:] = np.zeros((len(self._fvars),), dtype='bool') def _reset_solver_state(self): r""" @@ -403,7 +403,6 @@ def _reset_solver_state(self): self._chkpt_status = -1 self.clear_vars() self.clear_equations() - # self._ks = dict() self._ks.reset() self._nnz = self._get_known_nonz() @@ -911,7 +910,6 @@ def _get_known_vals(self): sage: len(f._get_known_vals()) == f._poly_ring.ngens() True """ - # return {var_idx: self._fvars[self._idx_to_sextuple[var_idx]] for var_idx in self._solved} return {var_idx: self._fvars[self._idx_to_sextuple[var_idx]] for var_idx, v in enumerate(self._solved) if v} def _get_known_sq(self,eqns=None): @@ -940,18 +938,16 @@ def _get_known_sq(self,eqns=None): fx10*fx11 + fx12*fx13, fx11^2 + fx13^2 - 1] sage: f.get_orthogonality_constraints(output=False) - sage: len(f._get_known_sq()) == 10 + sage: f._get_known_sq() + sage: len(f._ks) == 10 True """ if eqns is None: eqns = self.ideal_basis - # ks = deepcopy(self._ks) F = self._field for eq_tup in eqns: if tup_fixes_sq(eq_tup): - # ks[variables(eq_tup)[0]] = tuple(-v for v in eq_tup[-1][1]) - self._ks[variables(eq_tup)[0]] = -self._field(list(eq_tup[-1][1])) - # return ks + self._ks[variables(eq_tup)[0]] = eq_tup[-1][1] def _get_known_nonz(self): r""" @@ -970,7 +966,6 @@ def _get_known_nonz(self): 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100) """ nonz = {self._var_to_idx[var] : 100 for var in self._singles} - # for idx in self._ks: for idx, v in self._ks.items(): nonz[idx] = 100 return ETuple(nonz, self._poly_ring.ngens()) @@ -1127,8 +1122,8 @@ def load_fvars(self, filename): self._fvars, self._non_cyc_roots, self._coerce_map_from_cyc_field, self._qqbar_embedding = pickle.load(f) #Update state attributes self._chkpt_status = 7 - # self._solved = set(range(self._poly_ring.ngens())) self._solved = list(True for v in self._fvars) + # self._solved = np.zeros((len(self._fvars),), dtype='bool') self._field = self._qqbar_embedding.domain() def get_fr_str(self): @@ -1212,7 +1207,7 @@ def _checkpoint(self, do_chkpt, status, verbose=True): return filename = "fmatrix_solver_checkpoint_" + self.get_fr_str() + ".pickle" with open(filename, 'wb') as f: - pickle.dump([self._fvars, list(self._solved), self._ks, self.ideal_basis, status], f) + pickle.dump([self._fvars, self._solved, self._ks, self.ideal_basis, status], f) if verbose: print(f"Checkpoint {status} reached!") @@ -1490,7 +1485,7 @@ def _update_reduction_params(self,eqns=None,worker_pool=None,children_need_updat sage: s_name = f._solved.shm.name sage: f._var_degs = shared_memory.ShareableList(f._var_degs) sage: vd_name = f._var_degs.shm.name - sage: args = (id(f), s_name, vd_name) + sage: args = (id(f), s_name, vd_name, f._ks.get_names()) sage: from sage.combinat.root_system.fast_parallel_fmats_methods import init sage: pool = Pool(processes=n,initializer=init,initargs=args) sage: f.get_defining_equations('hexagons',worker_pool=pool,output=False) @@ -1504,26 +1499,19 @@ def _update_reduction_params(self,eqns=None,worker_pool=None,children_need_updat """ if eqns is None: eqns = self.ideal_basis - # self._ks = self._get_known_sq(eqns) self._get_known_sq(eqns) - # print("res",get_variables_degrees(eqns)) degs = get_variables_degrees(eqns) if degs: for i, d in enumerate(degs): - # print(i, d) self._var_degs[i] = d else: for i in range(len(self._fvars)): self._var_degs[i] = 0 - # print("vd, var_degs") - # self._var_degs = get_variables_degrees(eqns) self._nnz = self._get_known_nonz() self._kp = compute_known_powers(self._var_degs,self._get_known_vals(),self._field.one()) if worker_pool is not None and children_need_update: #self._nnz and self._kp are computed in child processes to reduce IPC overhead n_proc = worker_pool._processes - # new_data = [(self._fvars,self._solved,self._ks,self._var_degs)]*n_proc - # new_data = [(self._fvars,self._ks)]*n_proc new_data = [(self._fvars,)]*n_proc self._map_triv_reduce('update_child_fmats',new_data,worker_pool=worker_pool,chunksize=1,mp_thresh=0) @@ -1956,7 +1944,6 @@ def _get_explicit_solution(self,eqns=None,verbose=True): self._update_poly_ring_base_field(self._field) #Ensure all F-symbols are known - # self._solved.update(numeric_fvars) for fx in numeric_fvars: self._solved[fx] = True nvars = self._poly_ring.ngens() @@ -2086,8 +2073,11 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start if use_mp: n = max(cpu_count()-1,1) self._solved = shared_memory.ShareableList(self._solved) - # print(self._solved) + # self._solved_shm = shared_memory.SharedMemory(create=True,size=len(self._fvars)) + # self._solved = np.ndarray((len(self._fvars),), dtype='bool', buffer=self._solved_shm.buf) + # self._solved[:] = np.zeros((len(self._fvars),), dtype='bool') s_name = self._solved.shm.name + # s_name = self._solved_shm.name self._var_degs = shared_memory.ShareableList(self._var_degs) vd_name = self._var_degs.shm.name ks_names = self._ks.get_names() @@ -2149,14 +2139,6 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start #Try adding degrevlex gb -> elim loop until len(ideal_basis) does not change - #Close worker pool to free resources - if pool is not None: - pool.close() - #Destroy shared resources - self._solved.shm.unlink() - self._var_degs.shm.unlink() - self._ks.unlink() - #Set up new equations graph and compute variety for each component if self._chkpt_status < 5: self.ideal_basis = self._par_graph_gb(term_order="lex",verbose=verbose) @@ -2176,6 +2158,14 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start if save_results: self.save_fvars(save_results) + #Close worker pool to free resources + if pool is not None: + pool.close() + #Destroy shared resources + self._solved.shm.unlink() + # self._solved_shm.unlink() + self._var_degs.shm.unlink() + self._ks.unlink() ######################### ### Cyclotomic method ### @@ -2204,10 +2194,9 @@ def _fix_gauge(self, algorithm=""): """ # while len(self._solved) < len(self._poly_ring.gens()): while sum(1 for v in self._solved if not v) > 0: + # while self._solved.sum() < self._solved.size: #Get a variable that has not been fixed #In ascending index order, for consistent results - # for var in self._poly_ring.gens(): - # if var not in self._solved: for i, var in enumerate(self._poly_ring.gens()): if not self._solved[i]: break @@ -2243,7 +2232,6 @@ def _substitute_degree_one(self, eqns=None): new_knowns = set() useless = set() for eq in eqns: - # if eq.degree() == 1 and sum(eq.degrees()) <= 2 and eq.lm() not in self._solved: if eq.degree() == 1 and sum(eq.degrees()) <= 2 and not self._solved[self._var_to_idx[eq.lm()]]: self._fvars[self._var_to_sextuple[eq.lm()]] = -sum(c * m for c, m in zip(eq.coefficients()[1:], eq.monomials()[1:])) / eq.lc() #Add variable to set of known values and remove this equation @@ -2251,11 +2239,9 @@ def _substitute_degree_one(self, eqns=None): useless.add(eq) #Update fvars depending on other variables - # self._solved.update(new_knowns) for fx in new_knowns: self._solved[self._var_to_idx[fx]] = fx for sextuple, rhs in self._fvars.items(): - # d = {var : self._fvars[self._var_to_sextuple[var]] for var in rhs.variables() if var in self._solved} d = {var: self._fvars[self._var_to_sextuple[var]] for var in rhs.variables() if self._solved[self._var_to_idx[var]]} if d: self._fvars[sextuple] = rhs.subs(d) @@ -2276,7 +2262,6 @@ def _update_equations(self): sage: f.ideal_basis {fx3} """ - # special_values = {known : self._fvars[self._var_to_sextuple[known]] for known in self._solved} special_values = {known : self._fvars[self._var_to_sextuple[known]] for known in self._solved if known} self.ideal_basis = set(eq.subs(special_values) for eq in self.ideal_basis) self.ideal_basis.discard(0) diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index 634b2d21eaf..7a070724c6c 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -77,10 +77,8 @@ cpdef _solve_for_linear_terms(factory, eqns=None): if len(eq_tup) == 1: vars = variables(eq_tup) - # if len(vars) == 1 and vars[0] not in factory._solved: if len(vars) == 1 and not factory._solved[vars[0]]: factory._fvars[factory._idx_to_sextuple[vars[0]]] = tuple() - # factory._solved.add(vars[0]) factory._solved[vars[0]] = True linear_terms_exist = True if len(eq_tup) == 2: @@ -88,12 +86,10 @@ cpdef _solve_for_linear_terms(factory, eqns=None): if idx < 0: continue #The chosen term is guaranteed to be univariate in the largest variable max_var = eq_tup[idx][0].nonzero_positions()[0] - # if max_var not in factory._solved: if not factory._solved[max_var]: rhs_exp = eq_tup[(idx+1) % 2][0] rhs_coeff = -eq_tup[(idx+1) % 2][1] / eq_tup[idx][1] factory._fvars[factory._idx_to_sextuple[max_var]] = ((rhs_exp,rhs_coeff),) - # factory._solved.add(max_var) factory._solved[max_var] = True linear_terms_exist = True return linear_terms_exist @@ -141,7 +137,6 @@ cpdef _backward_subs(factory): 1 """ one = factory._field.one() - # _ks = {k : factory._FR.field()(list(v)) for k, v in factory._ks.items()} _ks = factory._ks vars = factory._poly_ring.gens() n = len(vars) @@ -149,7 +144,6 @@ cpdef _backward_subs(factory): sextuple = factory._var_to_sextuple[var] rhs = factory._fvars[sextuple] d = {var_idx: factory._fvars[factory._idx_to_sextuple[var_idx]] - # for var_idx in variables(rhs) if var_idx in factory._solved} for var_idx in variables(rhs) if factory._solved[var_idx]} if d: kp = compute_known_powers(get_variables_degrees([rhs]), d, one) @@ -244,8 +238,6 @@ cdef get_reduced_hexagons(factory, tuple mp_params): _field = factory._field cdef NumberFieldElement_absolute one = _field.one() cdef ETuple _nnz = factory._nnz - # cdef dict _ks = factory._ks - # _ks = {k : _field(list(v)) for k, v in factory._ks.items()} _ks = factory._ks #Computation loop @@ -299,8 +291,6 @@ cdef get_reduced_pentagons(factory, tuple mp_params): _field = factory._field cdef NumberFieldElement_absolute one = _field.one() cdef ETuple _nnz = factory._nnz - # cdef dict _ks = factory._ks - # _ks = {k : _field(list(v)) for k, v in factory._ks.items()} _ks = factory._ks cdef MPolynomial_libsingular zero = factory._poly_ring.zero() @@ -330,7 +320,6 @@ cdef list update_reduce(factory, list eqns): #Pre-compute common parameters for speed _field = factory._field one = _field.one() - # _ks = {k : _field(list(v)) for k, v in factory._ks.items()} _ks = factory._ks cdef dict _kp = factory._kp cdef ETuple _nnz = factory._nnz @@ -409,7 +398,9 @@ cpdef update_child_fmats(factory, tuple data_tup): sage: s_name = f._solved.shm.name sage: f._var_degs = shared_memory.ShareableList(f._var_degs) sage: vd_name = f._var_degs.shm.name - sage: args = (id(f), s_name, vd_name) + sage: from sage.combinat.root_system.shm_managers import KSHandler + sage: f._ks = KSHandler(f) + sage: args = (id(f), s_name, vd_name, f._ks.get_names()) sage: from sage.combinat.root_system.fast_parallel_fmats_methods import init sage: pool = Pool(processes=n,initializer=init,initargs=args) sage: f.get_defining_equations('hexagons',worker_pool=pool,output=False) @@ -425,8 +416,6 @@ cpdef update_child_fmats(factory, tuple data_tup): """ #factory object is assumed global before forking used to create the Pool object, #so each child has a global fmats variable. So it's enough to update that object - # factory._fvars, factory._solved, factory._ks, factory._var_degs = data_tup - # factory._fvars, factory._ks = data_tup factory._fvars = data_tup[0] factory._nnz = factory._get_known_nonz() factory._kp = compute_known_powers(factory._var_degs,factory._get_known_vals(),factory._field.one()) diff --git a/src/sage/combinat/root_system/poly_tup_engine.pyx b/src/sage/combinat/root_system/poly_tup_engine.pyx index 42ee9217782..519b5023e94 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pyx +++ b/src/sage/combinat/root_system/poly_tup_engine.pyx @@ -246,15 +246,12 @@ cpdef list get_variables_degrees(list eqns): [2, 1, 3] """ if not eqns: - # return ETuple([]) return list() cdef ETuple max_deg cdef int i max_deg = degrees(eqns[0]) for i in range(1, len(eqns)): max_deg = max_deg.emax(degrees( (eqns[i]) )) - # return max_deg - # return { max_deg._data[2*i] : max_deg._data[2*i+1] for i in range(max_deg._nonzero)} cdef list dense = [0]*len(max_deg) for i in range(max_deg._nonzero): dense[max_deg._data[2*i]] = max_deg._data[2*i+1] @@ -492,9 +489,6 @@ cpdef dict compute_known_powers(max_degs, dict val_dict, one): (((0, 4, 0), 1),), (((0, 6, 0), 1),)]} """ - # if not max_deg: - # return {} - # assert max_deg._nonzero and max(max_deg.nonzero_values(sort=False)) <= 100 or True, "NotImplementedError: Cannot substitute for degree larger than 100" assert (max_degs and max(max_degs) <= 100) or True, "Cannot substitute for degree larger than 100" cdef ETuple max_deg = ETuple(list(max_degs)) max_deg = max_deg.emin(ETuple({idx: 100 for idx in val_dict}, len(max_deg))) diff --git a/src/sage/combinat/root_system/shm_managers.py b/src/sage/combinat/root_system/shm_managers.py index 83f1dfdb27e..bdbd08cae41 100644 --- a/src/sage/combinat/root_system/shm_managers.py +++ b/src/sage/combinat/root_system/shm_managers.py @@ -1,30 +1,42 @@ from multiprocessing import shared_memory +import numpy as np +from sage.misc.cachefunc import cached_method class KSHandler(): def __init__(self, factory, names=None): - n = len(factory._fvars) self.field = factory._field self.index = 0 + n = len(factory._fvars) + d = self.field.degree() + coeff_dtype = np.dtype([('nums','i4',(d,)), ('denoms','u4',(d,))]) if names is None: - self.ks = shared_memory.ShareableList([False]*n) - self.nums = [shared_memory.ShareableList([int(0)]*self.field.degree()) for i in range(n)] - self.denoms = shared_memory.ShareableList([int(0)]*n) + self.ks_shm = shared_memory.SharedMemory(create=True,size=n) + self.ks = np.ndarray((n,),dtype='bool',buffer=self.ks_shm.buf) + self.ks[:] = np.zeros((n,),dtype='bool') + self.coeff_shm = shared_memory.SharedMemory(create=True,size=2*n*d*4) + self.coeffs = np.ndarray((n,),dtype=coeff_dtype,buffer=self.coeff_shm.buf) + self.coeffs['nums'][:] = np.zeros((n,d),dtype='i4') + self.coeffs['denoms'][:] = np.ones((n,d),dtype='u4') else: - self.ks = shared_memory.ShareableList(name=names[0]) - self.nums = [shared_memory.ShareableList(name=names[1][i]) for i in range(n)] - self.denoms = shared_memory.ShareableList(name=names[2]) + self.ks_shm = shared_memory.SharedMemory(name=names[0]) + self.ks = np.ndarray((n,),dtype='bool',buffer=self.ks_shm.buf) + self.coeff_shm = shared_memory.SharedMemory(name=names[1]) + self.coeffs = np.ndarray((n,),dtype=coeff_dtype,buffer=self.coeff_shm.buf) - def __getitem__(self,idx): + @cached_method + def __getitem__(self, idx): if not self.ks[idx]: raise KeyError('Index {} does not correspond to a known square'.format(idx)) - denom = self.denoms[idx] - return self.field([num / denom for num in self.nums[idx]]) + rat = list() + for i in range(self.field.degree()): + rat.append(self.coeffs['nums'][idx][i] / self.coeffs['denoms'][idx][i]) + return self.field(rat) - def __setitem__(self,idx,rhs): + def __setitem__(self, idx, rhs): self.ks[idx] = True - self.denoms[idx] = int(rhs.denominator()) - for i, c in enumerate(rhs._coefficients()): - self.nums[idx][i] = int(c * self.denoms[idx]) + for i, c in enumerate(rhs): + self.coeffs['nums'][idx][i] = -c.numerator() + self.coeffs['denoms'][idx][i] = c.denominator() def __iter__(self): self.index = 0 @@ -40,37 +52,33 @@ def __next__(self): if self.index == len(self.ks): raise StopIteration - denom = self.denoms[self.index] - ret = self.field([num / denom for num in self.nums[self.index]]) self.index += 1 - return ret + return self[self.index-1] - def __contains__(self,idx): + def __contains__(self, idx): return self.ks[idx] def __len__(self): return sum(self.ks) + def __eq__(self, other): + return np.all(self.ks == other.ks) and np.all(self.coeffs == other.coeffs) + def items(self): for v in self: yield self.index-1, v def reset(self): n = len(self.ks) - for i in range(n): - self.ks[i] = False - self.denoms[i] = 0 - for j in range(len(self.nums[i])): - self.nums[i][j] = 0 + d = self.field.degree() + self.ks[:] = np.zeros((n,),dtype='bool') + self.__getitem__.clear_cache() + self.coeffs['nums'][:] = np.zeros((n,d),dtype='i4') + self.coeffs['denoms'][:] = np.ones((n,d),dtype='u4') def unlink(self): - self.ks.shm.unlink() - for num_list in self.nums: - num_list.shm.unlink() - self.denoms.shm.unlink() + self.ks_shm.unlink() + self.coeff_shm.unlink() def get_names(self): - ks_name = self.ks.shm.name - nums_names = [num_list.shm.name for num_list in self.nums] - denom_name = self.denoms.shm.name - return (ks_name,nums_names,denom_name) + return (self.ks_shm.name,self.coeff_shm.name) From 7ec815b141c0fea9641d6fa88984e43f5ecd1994 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Tue, 27 Apr 2021 17:12:11 -0400 Subject: [PATCH 056/632] some deprecated code removed --- src/sage/combinat/root_system/f_matrix.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index c647f94c9fb..a485f3fbc48 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -1883,7 +1883,6 @@ def _get_explicit_solution(self,eqns=None,verbose=True): n = self._poly_ring.ngens() one = self._field.one() for fx, rhs in self._ks.items(): - # if fx not in self._solved: if not self._solved[fx]: lt = (ETuple({fx : 2},n), one) eqns.append((lt, (ETuple({},n), -self._field(list(rhs))))) @@ -2069,7 +2068,6 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start set_start_method('fork') except RuntimeError: pass - # pool = Pool(processes=max(cpu_count()-1,1)) if use_mp else None if use_mp: n = max(cpu_count()-1,1) self._solved = shared_memory.ShareableList(self._solved) @@ -2110,7 +2108,6 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start #Report progress if verbose: - # print("Hex elim step solved for {} / {} variables".format(len(self._solved), len(self._poly_ring.gens()))) print("Hex elim step solved for {} / {} variables".format(sum(self._solved), len(self._poly_ring.gens()))) self._checkpoint(checkpoint,2,verbose=verbose) @@ -2132,7 +2129,6 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start #Report progress if verbose: - # print("Pent elim step solved for {} / {} variables".format(len(self._solved), len(self._poly_ring.gens()))) print("Pent elim step solved for {} / {} variables".format(sum(self._solved), len(self._poly_ring.gens()))) self._checkpoint(checkpoint,4,verbose=verbose) @@ -2158,10 +2154,9 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start if save_results: self.save_fvars(save_results) - #Close worker pool to free resources + #Close worker pool and destroy shared resources if pool is not None: pool.close() - #Destroy shared resources self._solved.shm.unlink() # self._solved_shm.unlink() self._var_degs.shm.unlink() From ba0b572b0731d0737c019b426a15c38d7780f72d Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Wed, 28 Apr 2021 02:12:56 -0400 Subject: [PATCH 057/632] reduced zipping/unzipping of fvars, moved sovler state attributes out of constructor. leaked memory issue remains --- src/sage/combinat/root_system/f_matrix.py | 177 ++++++++++-------- .../fast_parallel_fmats_methods.pyx | 35 ++-- 2 files changed, 117 insertions(+), 95 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index a485f3fbc48..0aee09da311 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -297,14 +297,7 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab self._qqbar_embedding = self._field.hom([r], QQbar) self._non_cyc_roots = list() - #Useful solver state attributes - self.ideal_basis = list() - self._solved = list(False for fx in self._fvars) - # self._solved = np.zeros((len(self._fvars),), dtype='bool') - self._var_degs = [0]*len(self._fvars) - self._ks = KSHandler(self) - self._kp = dict() - self._nnz = self._get_known_nonz() + #Warm starting self._chkpt_status = -1 #Multiprocessing attributes @@ -369,6 +362,7 @@ def _reset_solver_state(self): EXAMPLES:: sage: f = FMatrix(FusionRing("G2",1)) + sage: f._reset_solver_state() sage: K = f.field() sage: len(f._nnz.nonzero_positions()) 1 @@ -403,7 +397,9 @@ def _reset_solver_state(self): self._chkpt_status = -1 self.clear_vars() self.clear_equations() - self._ks.reset() + self._var_degs = [0]*len(self._fvars) + self._kp = dict() + self._ks = KSHandler(self) self._nnz = self._get_known_nonz() #Clear relevant caches @@ -904,6 +900,7 @@ def _get_known_vals(self): EXAMPLES:: sage: f = FMatrix(FusionRing("D4",1)) + sage: f._reset_solver_state() sage: len(f._get_known_vals()) == 0 True sage: f.find_orthogonal_solution(verbose=False) @@ -920,6 +917,7 @@ def _get_known_sq(self,eqns=None): EXAMPLES:: sage: f = FMatrix(FusionRing("B5",1)) + sage: f._reset_solver_state() sage: len(f._ks) == 0 True sage: f.get_orthogonality_constraints() @@ -961,6 +959,7 @@ def _get_known_nonz(self): EXAMPLES:: sage: f = FMatrix(FusionRing("D5",1)) # indirect doctest + sage: f._reset_solver_state() sage: f._nnz (100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100) @@ -1020,20 +1019,24 @@ def get_fvars_by_size(self,n,indices=False): (f4, f4, f4, f4, f4, f0), (f4, f4, f4, f4, f4, f4)} """ - fvars_copy = deepcopy(self._fvars) - solved_copy = deepcopy(self._solved) - self.clear_vars() var_set = set() - for quadruple in product(self._FR.basis(),repeat=4): - F = self.fmatrix(*quadruple) - #Discard trivial 1x1 F-matrix, if applicable - if F.nrows() == n and F.coefficients() != [1]: - var_set.update(F.coefficients()) - self._fvars = fvars_copy - self._solved = solved_copy + one = self._FR.one() + for a,b,c,d in product(self._FR.basis(),repeat=4): + X = self.f_from(a,b,c,d) + Y = self.f_to(a,b,c,d) + if len(X) == n and len(Y) == n: + for x in X: + for y in Y: + #Discard trivial 1x1 F-matrix + trivial = a == one and x == b and y == d + trivial |= b == one and x == a and y == c + trivial |= c == one and x == d and y == b + if not trivial: + var_set.add((a,b,c,d,x,y)) if indices: - return {self._var_to_idx[fx] for fx in var_set} - return {self._var_to_sextuple[fx] for fx in var_set} + sext_to_idx = {v: k for k, v in self._idx_to_sextuple.items()} + return {sext_to_idx[fx] for fx in var_set} + return var_set ############################ ### Checkpoint utilities ### @@ -1150,8 +1153,9 @@ def _checkpoint(self, do_chkpt, status, verbose=True): sage: f.get_orthogonality_constraints(output=False) sage: f.get_defining_equations('hexagons',output=False) sage: f.ideal_basis = f._par_graph_gb(verbose=False) - sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey + sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey, poly_to_tup sage: f.ideal_basis.sort(key=poly_tup_sortkey) + sage: f._fvars = {sextuple : poly_to_tup(rhs) for sextuple, rhs in f._fvars.items()} sage: f._triangular_elim(verbose=False) sage: f._update_reduction_params(children_need_update=False) sage: f._checkpoint(do_chkpt=True,status=2) @@ -1182,6 +1186,7 @@ def _checkpoint(self, do_chkpt, status, verbose=True): sage: f.ideal_basis = f._par_graph_gb(verbose=False) sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey sage: f.ideal_basis.sort(key=poly_tup_sortkey) + sage: f._fvars = {sextuple : poly_to_tup(rhs) for sextuple, rhs in f._fvars.items()} sage: f._triangular_elim(verbose=False) sage: f._update_reduction_params(children_need_update=False) sage: f.get_defining_equations('pentagons',output=False) @@ -1223,8 +1228,9 @@ def _restore_state(self,filename): sage: f.get_orthogonality_constraints(output=False) sage: f.get_defining_equations('hexagons',output=False) sage: f.ideal_basis = f._par_graph_gb(verbose=False) - sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey + sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey, poly_to_tup sage: f.ideal_basis.sort(key=poly_tup_sortkey) + sage: f._fvars = {sextuple : poly_to_tup(rhs) for sextuple, rhs in f._fvars.items()} sage: f._triangular_elim(verbose=False) sage: f._update_reduction_params(children_need_update=False) sage: fvars = f._fvars @@ -1236,6 +1242,7 @@ def _restore_state(self,filename): Checkpoint 2 reached! sage: del f sage: f = FMatrix(FusionRing("A1",2)) + sage: f._reset_solver_state() sage: f._restore_state("fmatrix_solver_checkpoint_A12.pickle") sage: fvars == f._fvars True @@ -1274,6 +1281,50 @@ def _restore_state(self,filename): ### MapReduce ### ################# + def get_worker_pool(self,processes=None): + """ + Get a worker pool for parallel processing, which may be used e.g. + to set up defining equations using :meth:`get_defining_equations`. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("G2",1)) + sage: pool = f.get_worker_pool() + sage: he = f.get_defining_equations('hexagons',worker_pool=pool) + sage: sorted(he) + [fx0 - 1, + fx2*fx3 + (zeta60^14 + zeta60^12 - zeta60^6 - zeta60^4 + 1)*fx4^2 + (zeta60^6)*fx4, + fx1*fx3 + (zeta60^14 + zeta60^12 - zeta60^6 - zeta60^4 + 1)*fx3*fx4 + (zeta60^14 - zeta60^4)*fx3, + fx1*fx2 + (zeta60^14 + zeta60^12 - zeta60^6 - zeta60^4 + 1)*fx2*fx4 + (zeta60^14 - zeta60^4)*fx2, + fx1^2 + (zeta60^14 + zeta60^12 - zeta60^6 - zeta60^4 + 1)*fx2*fx3 + (-zeta60^12)*fx1] + + .. WARNING:: + + This method is needed to initialize the worker pool using the + necessary shared memory resources. Simply using the + ``multiprocessing.Pool`` constructor will not work with our class + methods. + """ + try: + set_start_method('fork') + except RuntimeError: + pass + if not hasattr(self, '_nnz'): + self._reset_solver_state() + self._solved = shared_memory.ShareableList(self._solved) + # self._solved_shm = shared_memory.SharedMemory(create=True,size=len(self._fvars)) + # self._solved = np.ndarray((len(self._fvars),), dtype='bool', buffer=self._solved_shm.buf) + # self._solved[:] = np.zeros((len(self._fvars),), dtype='bool') + s_name = self._solved.shm.name + # s_name = self._solved_shm.name + self._var_degs = shared_memory.ShareableList(self._var_degs) + vd_name = self._var_degs.shm.name + ks_names = self._ks.get_names() + args = (id(self), s_name, vd_name, ks_names) + n = cpu_count() if processes is None else processes + pool = Pool(processes=n,initializer=init,initargs=args) + return pool + def _map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_thresh=None): r""" Apply the given mapper to each element of the given input iterable and @@ -1297,6 +1348,7 @@ def _map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_t EXAMPLES:: sage: f = FMatrix(FusionRing("A1",2)) + sage: f._reset_solver_state() sage: len(f._map_triv_reduce('get_reduced_hexagons',[(0,1)])) 11 sage: from multiprocessing import Pool @@ -1424,6 +1476,8 @@ def get_defining_equations(self,option,worker_pool=None,output=True): sage: len(pe) 33 """ + if not hasattr(self, '_nnz'): + self._reset_solver_state() n_proc = worker_pool._processes if worker_pool is not None else 1 params = [(child_id, n_proc) for child_id in range(n_proc)] eqns = self._map_triv_reduce('get_reduced_'+option,params,worker_pool=worker_pool,chunksize=1,mp_thresh=0) @@ -1455,12 +1509,7 @@ def _tup_to_fpoly(self,eq_tup): sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup sage: f = FMatrix(FusionRing("C3",1)) - sage: from multiprocessing import Pool, set_start_method - sage: try: - ....: set_start_method('fork') - ....: except: - ....: pass - sage: pool = Pool() + sage: pool = f.get_worker_pool() sage: he = f.get_defining_equations('hexagons',pool) sage: all(f._tup_to_fpoly(poly_to_tup(h)) for h in he) True @@ -1474,25 +1523,15 @@ def _update_reduction_params(self,eqns=None,worker_pool=None,children_need_updat EXAMPLES:: sage: f = FMatrix(FusionRing("A1",3)) + sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) - sage: from multiprocessing import cpu_count, Pool, set_start_method, shared_memory - sage: try: - ....: set_start_method('fork') - ....: except: - ....: pass - sage: n = max(cpu_count()-1,1) - sage: f._solved = shared_memory.ShareableList(f._solved) - sage: s_name = f._solved.shm.name - sage: f._var_degs = shared_memory.ShareableList(f._var_degs) - sage: vd_name = f._var_degs.shm.name - sage: args = (id(f), s_name, vd_name, f._ks.get_names()) - sage: from sage.combinat.root_system.fast_parallel_fmats_methods import init - sage: pool = Pool(processes=n,initializer=init,initargs=args) + sage: pool = f.get_worker_pool() sage: f.get_defining_equations('hexagons',worker_pool=pool,output=False) sage: f.ideal_basis = f._par_graph_gb(worker_pool=pool,verbose=False) - sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey + sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey, poly_to_tup sage: f.ideal_basis.sort(key=poly_tup_sortkey) sage: f.mp_thresh = 0 + sage: f._fvars = {sextuple : poly_to_tup(rhs) for sextuple, rhs in f._fvars.items()} sage: f._triangular_elim(worker_pool=pool,verbose=False) # indirect doctest sage: f.ideal_basis [] @@ -1531,8 +1570,9 @@ def _triangular_elim(self,eqns=None,worker_pool=None,verbose=True): sage: f.get_defining_equations('hexagons',output=False) sage: f.get_orthogonality_constraints(output=False) sage: gb = f._par_graph_gb(verbose=False) - sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey + sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey, poly_to_tup sage: f.ideal_basis = sorted(gb, key=poly_tup_sortkey) + sage: f._fvars = {sextuple : poly_to_tup(rhs) for sextuple, rhs in f._fvars.items()} sage: f._triangular_elim() Elimination epoch completed... 0 eqns remain in ideal basis sage: f.ideal_basis @@ -1543,7 +1583,7 @@ def _triangular_elim(self,eqns=None,worker_pool=None,verbose=True): eqns = self.ideal_basis ret = False #Unzip polynomials - self._fvars = {sextuple : poly_to_tup(rhs) for sextuple, rhs in self._fvars.items()} + # self._fvars = {sextuple : poly_to_tup(rhs) for sextuple, rhs in self._fvars.items()} while True: linear_terms_exist = _solve_for_linear_terms(self,eqns) @@ -1561,7 +1601,7 @@ def _triangular_elim(self,eqns=None,worker_pool=None,verbose=True): print("Elimination epoch completed... {} eqns remain in ideal basis".format(len(eqns))) #Zip up _fvars before exiting - self._fvars = {sextuple : self._tup_to_fpoly(rhs_tup) for sextuple, rhs_tup in self._fvars.items()} + # self._fvars = {sextuple : self._tup_to_fpoly(rhs_tup) for sextuple, rhs_tup in self._fvars.items()} if ret: return eqns self.ideal_basis = eqns @@ -1701,13 +1741,9 @@ def _par_graph_gb(self,worker_pool=None,eqns=None,term_order="degrevlex",verbose EXAMPLES:: sage: f = FMatrix(FusionRing("F4",1)) + sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) - sage: from multiprocessing import Pool, set_start_method - sage: try: - ....: set_start_method('fork') # context can be set only once - ....: except RuntimeError: - ....: pass - sage: pool = Pool() + sage: pool = f.get_worker_pool() sage: f.get_defining_equations('hexagons',worker_pool=pool,output=False) sage: gb = f._par_graph_gb(worker_pool=pool) Partitioned 10 equations into 2 components of size: @@ -1762,12 +1798,8 @@ def _get_component_variety(self,var,eqns): EXAMPLES:: sage: f = FMatrix(FusionRing("G2",2)) - sage: from multiprocessing import Pool, set_start_method - sage: try: - ....: set_start_method('fork') # context can be set only once - ....: except RuntimeError: - ....: pass - sage: f.get_defining_equations('hexagons',worker_pool=Pool(),output=False) # long time + sage: pool = f.get_worker_pool() + sage: f.get_defining_equations('hexagons',worker_pool=pool,output=False) # long time sage: partition = f._partition_eqns() # long time Partitioned 327 equations into 35 components of size: [27, 27, 27, 24, 24, 16, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, @@ -1949,7 +1981,8 @@ def _get_explicit_solution(self,eqns=None,verbose=True): assert sum(self._solved) == nvars, "Some F-symbols are still missing...{}".format([self._poly_ring.gen(fx) for fx in range(nvars) if not self._solved[fx]]) #Backward substitution step. Traverse variables in reverse lexicographical order. (System is in triangular form) - self._fvars = {sextuple : apply_coeff_map(poly_to_tup(rhs),phi) for sextuple, rhs in self._fvars.items()} + # self._fvars = {sextuple : apply_coeff_map(poly_to_tup(rhs),phi) for sextuple, rhs in self._fvars.items()} + self._fvars = {sextuple : apply_coeff_map(rhs,phi) for sextuple, rhs in self._fvars.items()} for fx, rhs in numeric_fvars.items(): self._fvars[self._idx_to_sextuple[fx]] = ((ETuple({},nvars),rhs),) _backward_subs(self) @@ -2063,26 +2096,7 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start if self._chkpt_status > 5: return - #Set multiprocessing parameters. Context can only be set once, so we try to set it - try: - set_start_method('fork') - except RuntimeError: - pass - if use_mp: - n = max(cpu_count()-1,1) - self._solved = shared_memory.ShareableList(self._solved) - # self._solved_shm = shared_memory.SharedMemory(create=True,size=len(self._fvars)) - # self._solved = np.ndarray((len(self._fvars),), dtype='bool', buffer=self._solved_shm.buf) - # self._solved[:] = np.zeros((len(self._fvars),), dtype='bool') - s_name = self._solved.shm.name - # s_name = self._solved_shm.name - self._var_degs = shared_memory.ShareableList(self._var_degs) - vd_name = self._var_degs.shm.name - ks_names = self._ks.get_names() - args = (id(self), s_name, vd_name, ks_names) - pool = Pool(processes=n,initializer=init,initargs=args) - else: - pool = None + pool = self.get_worker_pool(max(cpu_count()-1,1)) if use_mp else None if verbose: print("Computing F-symbols for {} with {} variables...".format(self._FR, len(self._fvars))) @@ -2090,6 +2104,8 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start #Set up hexagon equations and orthogonality constraints self.get_orthogonality_constraints(output=False) self.get_defining_equations('hexagons',worker_pool=pool,output=False) + #Unzip fvars + self._fvars = {sextuple : poly_to_tup(rhs) for sextuple, rhs in self._fvars.items()} #Report progress if verbose: @@ -2214,6 +2230,7 @@ def _substitute_degree_one(self, eqns=None): sage: f = FMatrix(FusionRing("D3",1), inject_variables=True) creating variables fx1..fx27 Defining fx0, ..., fx26 + sage: f._reset_solver_state() sage: f.ideal_basis = [fx0 - 8, fx4**2 - 3, fx4 + fx10 + 3, fx4 + fx9] sage: _, _ = f._substitute_degree_one() sage: f._fvars[f._var_to_sextuple[fx0]] @@ -2251,6 +2268,7 @@ def _update_equations(self): sage: f = FMatrix(FusionRing("D3",1), inject_variables=True) creating variables fx1..fx27 Defining fx0, ..., fx26 + sage: f._reset_solver_state() sage: f.ideal_basis = [fx0 - 8, fx4 + fx9, fx4**2 + fx3 - fx9**2] sage: _, _ = f._substitute_degree_one() sage: f._update_equations() @@ -2336,6 +2354,7 @@ def find_cyclotomic_solution(self, equations=None, algorithm="", verbose=True, o print("Done!") if output: return self._fvars + self._ks.unlink() ##################### ### Verifications ### diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index 7a070724c6c..4687472dc14 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -46,6 +46,7 @@ cpdef _solve_for_linear_terms(factory, eqns=None): sage: f = FMatrix(FusionRing("D3",1), inject_variables=True) creating variables fx1..fx27 Defining fx0, ..., fx26 + sage: f._reset_solver_state() sage: f.ideal_basis = [fx0**3, fx0 + fx3**4, fx2**2 - fx3, fx2 - fx3**2, fx4 - fx2] sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup sage: f.ideal_basis = [poly_to_tup(p) for p in f.ideal_basis] @@ -104,6 +105,7 @@ cpdef _backward_subs(factory): sage: f = FMatrix(FusionRing("D3",1), inject_variables=True) creating variables fx1..fx27 Defining fx0, ..., fx26 + sage: f._reset_solver_state() sage: f.ideal_basis = [fx0**3, fx0 + fx3**4, fx2**2 - fx3, fx2 - fx3**2, fx4 - fx2] sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup sage: f.ideal_basis = [poly_to_tup(p) for p in f.ideal_basis] @@ -285,7 +287,17 @@ cdef get_reduced_pentagons(factory, tuple mp_params): #Pre-compute common parameters for speed cdef tuple basis = tuple(factory._FR.basis()) - cdef dict fvars = factory._fvars + # cdef dict fvars = factory._fvars + #Handle both cyclotomic and orthogonal solution method + cdef bint must_zip_up + for k in factory._fvars: + must_zip_up = isinstance(factory._fvars[k], tuple) + break + cdef dict fvars + if must_zip_up: + fvars = {k: _tup_to_poly(v,parent=factory._poly_ring) for k, v in factory._fvars.items()} + else: + fvars = factory._fvars _Nk_ij = factory._FR.Nk_ij id_anyon = factory._FR.one() _field = factory._field @@ -387,27 +399,16 @@ cpdef update_child_fmats(factory, tuple data_tup): TESTS:: sage: f = FMatrix(FusionRing("A1",3)) + sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) sage: from multiprocessing import cpu_count, Pool, set_start_method, shared_memory - sage: try: - ....: set_start_method('fork') # context can be set only once - ....: except RuntimeError: - ....: pass - sage: n = max(cpu_count()-1,1) - sage: f._solved = shared_memory.ShareableList(f._solved) - sage: s_name = f._solved.shm.name - sage: f._var_degs = shared_memory.ShareableList(f._var_degs) - sage: vd_name = f._var_degs.shm.name - sage: from sage.combinat.root_system.shm_managers import KSHandler - sage: f._ks = KSHandler(f) - sage: args = (id(f), s_name, vd_name, f._ks.get_names()) - sage: from sage.combinat.root_system.fast_parallel_fmats_methods import init - sage: pool = Pool(processes=n,initializer=init,initargs=args) + sage: pool = f.get_worker_pool() sage: f.get_defining_equations('hexagons',worker_pool=pool,output=False) sage: f.ideal_basis = f._par_graph_gb(worker_pool=pool,verbose=False) - sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey + sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey, poly_to_tup sage: f.ideal_basis.sort(key=poly_tup_sortkey) sage: f.mp_thresh = 0 + sage: f._fvars = {sextuple : poly_to_tup(rhs) for sextuple, rhs in f._fvars.items()} sage: f._triangular_elim(worker_pool=pool) # indirect doctest Elimination epoch completed... 10 eqns remain in ideal basis Elimination epoch completed... 0 eqns remain in ideal basis @@ -486,10 +487,12 @@ cpdef executor(tuple params): sage: from sage.combinat.root_system.fast_parallel_fmats_methods import executor sage: fmats = FMatrix(FusionRing("A1",3)) + sage: fmats._reset_solver_state() sage: params = (('get_reduced_hexagons', id(fmats)), (0,1)) sage: len(executor(params)) == 63 True sage: fmats = FMatrix(FusionRing("E6",1)) + sage: fmats._reset_solver_state() sage: params = (('get_reduced_hexagons', id(fmats)), (0,1)) sage: len(executor(params)) == 6 True From 54d6473154834466604e7ce5a11ef19e5e59e044 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Thu, 29 Apr 2021 14:03:42 -0400 Subject: [PATCH 058/632] improved _get_known_vals method --- src/sage/combinat/root_system/f_matrix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 0aee09da311..85a51af6862 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -907,7 +907,7 @@ def _get_known_vals(self): sage: len(f._get_known_vals()) == f._poly_ring.ngens() True """ - return {var_idx: self._fvars[self._idx_to_sextuple[var_idx]] for var_idx, v in enumerate(self._solved) if v} + return {i: self._fvars[s] for i, s in self._idx_to_sextuple.items() if self._solved[i]} def _get_known_sq(self,eqns=None): r""" From d7bc58d3ebafd59d5b9a4453f2de455975fb28f7 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Thu, 29 Apr 2021 15:03:16 -0400 Subject: [PATCH 059/632] _fvars in shared_memory, both solvers working. some doctests failing, fvars_handler in separate pyx --- src/sage/combinat/root_system/f_matrix.py | 14 ++++++++++++-- .../root_system/fast_parallel_fmats_methods.pyx | 15 +++++++++------ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 85a51af6862..59c458ecc77 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -48,6 +48,7 @@ from multiprocessing import shared_memory from sage.combinat.root_system.shm_managers import KSHandler +from sage.combinat.root_system.fvars_handler import FvarsHandler class FMatrix(): def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variables=False): @@ -1320,7 +1321,9 @@ def get_worker_pool(self,processes=None): self._var_degs = shared_memory.ShareableList(self._var_degs) vd_name = self._var_degs.shm.name ks_names = self._ks.get_names() - args = (id(self), s_name, vd_name, ks_names) + self._shared_fvars = FvarsHandler(self) + fvar_names = self._shared_fvars.get_names() + args = (id(self), s_name, vd_name, ks_names, fvar_names) n = cpu_count() if processes is None else processes pool = Pool(processes=n,initializer=init,initargs=args) return pool @@ -1551,7 +1554,8 @@ def _update_reduction_params(self,eqns=None,worker_pool=None,children_need_updat if worker_pool is not None and children_need_update: #self._nnz and self._kp are computed in child processes to reduce IPC overhead n_proc = worker_pool._processes - new_data = [(self._fvars,)]*n_proc + # new_data = [(self._fvars,)]*n_proc + new_data = [(1,)]*n_proc self._map_triv_reduce('update_child_fmats',new_data,worker_pool=worker_pool,chunksize=1,mp_thresh=0) def _triangular_elim(self,eqns=None,worker_pool=None,verbose=True): @@ -2107,6 +2111,11 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start #Unzip fvars self._fvars = {sextuple : poly_to_tup(rhs) for sextuple, rhs in self._fvars.items()} + #Initialize shared fvars handler + for sextuple, fvar in self._fvars.items(): + self._shared_fvars[sextuple] = fvar + self._fvars = self._shared_fvars + #Report progress if verbose: print("Set up {} hex and orthogonality constraints...".format(len(self.ideal_basis))) @@ -2177,6 +2186,7 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start # self._solved_shm.unlink() self._var_degs.shm.unlink() self._ks.unlink() + self._shared_fvars.unlink() ######################### ### Cyclotomic method ### diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index 4687472dc14..9e490d5debe 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -31,6 +31,7 @@ from time import sleep from multiprocessing import shared_memory from sage.combinat.root_system.shm_managers import KSHandler +from sage.combinat.root_system.fvars_handler import FvarsHandler ########################## ### Fast class methods ### @@ -233,7 +234,8 @@ cdef get_reduced_hexagons(factory, tuple mp_params): #Pre-compute common parameters for speed cdef tuple basis = tuple(factory._FR.basis()) - cdef dict fvars = factory._fvars + # cdef dict fvars = factory._fvars + cdef dict fvars = {v: k for k, v in factory._var_to_sextuple.items()} r_matrix = factory._FR.r_matrix _Nk_ij = factory._FR.Nk_ij id_anyon = factory._FR.one() @@ -290,8 +292,8 @@ cdef get_reduced_pentagons(factory, tuple mp_params): # cdef dict fvars = factory._fvars #Handle both cyclotomic and orthogonal solution method cdef bint must_zip_up - for k in factory._fvars: - must_zip_up = isinstance(factory._fvars[k], tuple) + for k, v in factory._fvars.items(): + must_zip_up = isinstance(v, tuple) break cdef dict fvars if must_zip_up: @@ -417,12 +419,12 @@ cpdef update_child_fmats(factory, tuple data_tup): """ #factory object is assumed global before forking used to create the Pool object, #so each child has a global fmats variable. So it's enough to update that object - factory._fvars = data_tup[0] + # factory._fvars = data_tup[0] factory._nnz = factory._get_known_nonz() factory._kp = compute_known_powers(factory._var_degs,factory._get_known_vals(),factory._field.one()) #Wait this process isn't used again - sleep(0.25) + sleep(0.5) ################ ### Reducers ### @@ -444,11 +446,12 @@ cdef inline list collect_eqns(list eqns): ### Parallel code executor ### ############################## -def init(fmats_id, solved_name, vd_name, ks_names): +def init(fmats_id, solved_name, vd_name, ks_names, fvar_names): fmats_obj = ctypes.cast(fmats_id, ctypes.py_object).value fmats_obj._solved = shared_memory.ShareableList(name=solved_name) fmats_obj._var_degs = shared_memory.ShareableList(name=vd_name) fmats_obj._ks = KSHandler(fmats_obj,ks_names) + fmats_obj._fvars = FvarsHandler(fmats_obj,name=fvar_names) #Hard-coded module __dict__-style attribute with visible cdef methods cdef dict mappers = { From fa984df990eb22294ba4a06771437a1ce4133f3d Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Thu, 29 Apr 2021 17:18:49 -0400 Subject: [PATCH 060/632] both solvers working w fvars_handler in shm_managers. memory leak fixed --- src/sage/combinat/root_system/f_matrix.py | 122 ++++++++---- .../fast_parallel_fmats_methods.pyx | 29 +-- src/sage/combinat/root_system/shm_managers.py | 84 -------- .../combinat/root_system/shm_managers.pyx | 179 ++++++++++++++++++ 4 files changed, 277 insertions(+), 137 deletions(-) delete mode 100644 src/sage/combinat/root_system/shm_managers.py create mode 100644 src/sage/combinat/root_system/shm_managers.pyx diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 59c458ecc77..e54c1c71ee2 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -19,6 +19,7 @@ import pickle from copy import deepcopy +from ctypes import cast, py_object from itertools import product, zip_longest from multiprocessing import cpu_count, Pool, set_start_method import numpy as np @@ -26,7 +27,7 @@ from sage.combinat.root_system.fast_parallel_fmats_methods import ( _backward_subs, _solve_for_linear_terms, - executor, init + executor ) from sage.combinat.root_system.poly_tup_engine import ( apply_coeff_map, constant_coeff, @@ -47,8 +48,8 @@ from sage.rings.qqbar import AA, QQbar, number_field_elements_from_algebraics from multiprocessing import shared_memory -from sage.combinat.root_system.shm_managers import KSHandler -from sage.combinat.root_system.fvars_handler import FvarsHandler +from sage.combinat.root_system.shm_managers import KSHandler, FvarsHandler +# from sage.combinat.root_system.fvars_handler import FvarsHandler class FMatrix(): def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variables=False): @@ -353,7 +354,6 @@ def clear_vars(self): """ self._fvars = {self._var_to_sextuple[key] : key for key in self._var_to_sextuple} self._solved = list(False for fx in self._fvars) - # self._solved[:] = np.zeros((len(self._fvars),), dtype='bool') def _reset_solver_state(self): r""" @@ -400,7 +400,8 @@ def _reset_solver_state(self): self.clear_equations() self._var_degs = [0]*len(self._fvars) self._kp = dict() - self._ks = KSHandler(self) + # self._ks = KSHandler(self) + self._ks = dict() self._nnz = self._get_known_nonz() #Clear relevant caches @@ -1127,7 +1128,6 @@ def load_fvars(self, filename): #Update state attributes self._chkpt_status = 7 self._solved = list(True for v in self._fvars) - # self._solved = np.zeros((len(self._fvars),), dtype='bool') self._field = self._qqbar_embedding.domain() def get_fr_str(self): @@ -1213,7 +1213,7 @@ def _checkpoint(self, do_chkpt, status, verbose=True): return filename = "fmatrix_solver_checkpoint_" + self.get_fr_str() + ".pickle" with open(filename, 'wb') as f: - pickle.dump([self._fvars, self._solved, self._ks, self.ideal_basis, status], f) + pickle.dump([self._fvars, list(self._solved), self._ks, self.ideal_basis, status], f) if verbose: print(f"Checkpoint {status} reached!") @@ -1284,8 +1284,15 @@ def _restore_state(self,filename): def get_worker_pool(self,processes=None): """ - Get a worker pool for parallel processing, which may be used e.g. - to set up defining equations using :meth:`get_defining_equations`. + Get an initialized worker pool for parallel processing, + which may be used e.g. to set up defining equations using + :meth:`get_defining_equations`. + + More than one task may be submitted to the same worker pool. + + When you are done using the worker pool, use + :meth:`shutdown_worker_pool` to close the pool and properly dispose + of shared memory resources. EXAMPLES:: @@ -1298,6 +1305,8 @@ def get_worker_pool(self,processes=None): fx1*fx3 + (zeta60^14 + zeta60^12 - zeta60^6 - zeta60^4 + 1)*fx3*fx4 + (zeta60^14 - zeta60^4)*fx3, fx1*fx2 + (zeta60^14 + zeta60^12 - zeta60^6 - zeta60^4 + 1)*fx2*fx4 + (zeta60^14 - zeta60^4)*fx2, fx1^2 + (zeta60^14 + zeta60^12 - zeta60^6 - zeta60^4 + 1)*fx2*fx3 + (-zeta60^12)*fx1] + sage: pe = f.get_defining_equations('pentaongs',worker_pool=pool) + sage: f.shutdown_worker_pool(pool) .. WARNING:: @@ -1305,6 +1314,12 @@ def get_worker_pool(self,processes=None): necessary shared memory resources. Simply using the ``multiprocessing.Pool`` constructor will not work with our class methods. + + .. WARNING:: + + Failure to call :meth:`shutdown_worker_pool` may result in a memory + leak, since shared memory resources outlive the process that created + them. """ try: set_start_method('fork') @@ -1312,22 +1327,65 @@ def get_worker_pool(self,processes=None): pass if not hasattr(self, '_nnz'): self._reset_solver_state() + #Set up shared memory resource handlers self._solved = shared_memory.ShareableList(self._solved) - # self._solved_shm = shared_memory.SharedMemory(create=True,size=len(self._fvars)) - # self._solved = np.ndarray((len(self._fvars),), dtype='bool', buffer=self._solved_shm.buf) - # self._solved[:] = np.zeros((len(self._fvars),), dtype='bool') s_name = self._solved.shm.name - # s_name = self._solved_shm.name self._var_degs = shared_memory.ShareableList(self._var_degs) vd_name = self._var_degs.shm.name - ks_names = self._ks.get_names() + ks = KSHandler(self) + for idx, val in self._ks.items(): + ks[idx] = val + self._ks = ks + ks_names = self._ks.shm.name self._shared_fvars = FvarsHandler(self) - fvar_names = self._shared_fvars.get_names() + for sextuple, fvar in self._fvars.items(): + if self._chkpt_status < 0: + self._shared_fvars[sextuple] = poly_to_tup(fvar) + else: + self._shared_fvars[sextuple] = fvar + fvar_names = self._shared_fvars.shm.name + #Initialize worker pool processes args = (id(self), s_name, vd_name, ks_names, fvar_names) n = cpu_count() if processes is None else processes + def init(fmats_id, solved_name, vd_name, ks_names, fvar_names): + """ + Connect worker process to shared memory resources + """ + fmats_obj = cast(fmats_id, py_object).value + fmats_obj._solved = shared_memory.ShareableList(name=solved_name) + fmats_obj._var_degs = shared_memory.ShareableList(name=vd_name) + fmats_obj._ks = KSHandler(fmats_obj,name=ks_names) + fmats_obj._fvars = FvarsHandler(fmats_obj,name=fvar_names) + pool = Pool(processes=n,initializer=init,initargs=args) return pool + def shutdown_worker_pool(self,pool): + """ + Shutdown the given worker pool and dispose of shared memory resources + created when the pool was set up using :meth:`get_worker_pool`. + + .. WARNING:: + + Failure to call this method after using :meth:`get_worker_pool` + to create a process pool may result in a memory + leak, since shared memory resources outlive the process that created + them. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("A1",3)) + sage: pool = f.get_worker_pool() + sage: he = f.get_defining_equations('hexagons', worker_pool=pool) + sage: f.shutdown_worker_pool(pool) + """ + pool.close() + self._solved.shm.unlink() + self._var_degs.shm.unlink() + self._ks.shm.unlink() + self._shared_fvars.shm.unlink() + del self.__dict__['_shared_fvars'] + def _map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_thresh=None): r""" Apply the given mapper to each element of the given input iterable and @@ -1457,8 +1515,9 @@ def get_defining_equations(self,option,worker_pool=None,output=True): Otherwise, the constraints are appended to ``self.ideal_basis``. They are stored in the internal tuple representation. The - ``output=False`` option is meant mostly for internal use by the - F-matrix solver. + ``output=False`` option is meant only for internal use by the + F-matrix solver. When computing the hexagon equations with the + ``output=False`` option, the initial state of the F-symbols is used. EXAMPLES:: @@ -1482,7 +1541,7 @@ def get_defining_equations(self,option,worker_pool=None,output=True): if not hasattr(self, '_nnz'): self._reset_solver_state() n_proc = worker_pool._processes if worker_pool is not None else 1 - params = [(child_id, n_proc) for child_id in range(n_proc)] + params = [(child_id, n_proc, output) for child_id in range(n_proc)] eqns = self._map_triv_reduce('get_reduced_'+option,params,worker_pool=worker_pool,chunksize=1,mp_thresh=0) if output: F = self._field @@ -1586,9 +1645,6 @@ def _triangular_elim(self,eqns=None,worker_pool=None,verbose=True): if eqns is None: eqns = self.ideal_basis ret = False - #Unzip polynomials - # self._fvars = {sextuple : poly_to_tup(rhs) for sextuple, rhs in self._fvars.items()} - while True: linear_terms_exist = _solve_for_linear_terms(self,eqns) if not linear_terms_exist: @@ -1603,9 +1659,6 @@ def _triangular_elim(self,eqns=None,worker_pool=None,verbose=True): eqns.sort(key=poly_tup_sortkey) if verbose: print("Elimination epoch completed... {} eqns remain in ideal basis".format(len(eqns))) - - #Zip up _fvars before exiting - # self._fvars = {sextuple : self._tup_to_fpoly(rhs_tup) for sextuple, rhs_tup in self._fvars.items()} if ret: return eqns self.ideal_basis = eqns @@ -1985,7 +2038,6 @@ def _get_explicit_solution(self,eqns=None,verbose=True): assert sum(self._solved) == nvars, "Some F-symbols are still missing...{}".format([self._poly_ring.gen(fx) for fx in range(nvars) if not self._solved[fx]]) #Backward substitution step. Traverse variables in reverse lexicographical order. (System is in triangular form) - # self._fvars = {sextuple : apply_coeff_map(poly_to_tup(rhs),phi) for sextuple, rhs in self._fvars.items()} self._fvars = {sextuple : apply_coeff_map(rhs,phi) for sextuple, rhs in self._fvars.items()} for fx, rhs in numeric_fvars.items(): self._fvars[self._idx_to_sextuple[fx]] = ((ETuple({},nvars),rhs),) @@ -2108,12 +2160,6 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start #Set up hexagon equations and orthogonality constraints self.get_orthogonality_constraints(output=False) self.get_defining_equations('hexagons',worker_pool=pool,output=False) - #Unzip fvars - self._fvars = {sextuple : poly_to_tup(rhs) for sextuple, rhs in self._fvars.items()} - - #Initialize shared fvars handler - for sextuple, fvar in self._fvars.items(): - self._shared_fvars[sextuple] = fvar self._fvars = self._shared_fvars #Report progress @@ -2168,6 +2214,10 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start self._checkpoint(checkpoint,5,verbose=verbose) + #Close worker pool and destroy shared resources + if use_mp: + self.shutdown_worker_pool(pool) + #Find numeric values for each F-symbol self._get_explicit_solution(verbose=verbose) @@ -2179,15 +2229,6 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start if save_results: self.save_fvars(save_results) - #Close worker pool and destroy shared resources - if pool is not None: - pool.close() - self._solved.shm.unlink() - # self._solved_shm.unlink() - self._var_degs.shm.unlink() - self._ks.unlink() - self._shared_fvars.unlink() - ######################### ### Cyclotomic method ### ######################### @@ -2364,7 +2405,6 @@ def find_cyclotomic_solution(self, equations=None, algorithm="", verbose=True, o print("Done!") if output: return self._fvars - self._ks.unlink() ##################### ### Verifications ### diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index 9e490d5debe..98bd95357dd 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -30,8 +30,8 @@ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from time import sleep from multiprocessing import shared_memory -from sage.combinat.root_system.shm_managers import KSHandler -from sage.combinat.root_system.fvars_handler import FvarsHandler +from sage.combinat.root_system.shm_managers import KSHandler, FvarsHandler +# from sage.combinat.root_system.fvars_handler import FvarsHandler ########################## ### Fast class methods ### @@ -229,13 +229,25 @@ cdef get_reduced_hexagons(factory, tuple mp_params): cdef list worker_results = list() cdef int child_id, n_proc cdef unsigned long i - child_id, n_proc = mp_params + child_id, n_proc, output = mp_params cdef tuple sextuple, red #Pre-compute common parameters for speed cdef tuple basis = tuple(factory._FR.basis()) # cdef dict fvars = factory._fvars - cdef dict fvars = {v: k for k, v in factory._var_to_sextuple.items()} + cdef dict fvars + cdef bint must_zip_up + if not output: + fvars = {v: k for k, v in factory._var_to_sextuple.items()} + else: + #Handle both cyclotomic and orthogonal solution method + for k, v in factory._fvars.items(): + must_zip_up = isinstance(v, tuple) + break + if must_zip_up: + fvars = {k: _tup_to_poly(v,parent=factory._poly_ring) for k, v in factory._fvars.items()} + else: + fvars = factory._fvars r_matrix = factory._FR.r_matrix _Nk_ij = factory._FR.Nk_ij id_anyon = factory._FR.one() @@ -282,7 +294,7 @@ cdef get_reduced_pentagons(factory, tuple mp_params): #Set up multiprocessing parameters cdef list worker_results = list() cdef int child_id, n_proc - child_id, n_proc = mp_params + child_id, n_proc, output = mp_params cdef unsigned long i cdef tuple nonuple, red cdef MPolynomial_libsingular pe @@ -446,13 +458,6 @@ cdef inline list collect_eqns(list eqns): ### Parallel code executor ### ############################## -def init(fmats_id, solved_name, vd_name, ks_names, fvar_names): - fmats_obj = ctypes.cast(fmats_id, ctypes.py_object).value - fmats_obj._solved = shared_memory.ShareableList(name=solved_name) - fmats_obj._var_degs = shared_memory.ShareableList(name=vd_name) - fmats_obj._ks = KSHandler(fmats_obj,ks_names) - fmats_obj._fvars = FvarsHandler(fmats_obj,name=fvar_names) - #Hard-coded module __dict__-style attribute with visible cdef methods cdef dict mappers = { "get_reduced_hexagons": get_reduced_hexagons, diff --git a/src/sage/combinat/root_system/shm_managers.py b/src/sage/combinat/root_system/shm_managers.py deleted file mode 100644 index bdbd08cae41..00000000000 --- a/src/sage/combinat/root_system/shm_managers.py +++ /dev/null @@ -1,84 +0,0 @@ -from multiprocessing import shared_memory -import numpy as np -from sage.misc.cachefunc import cached_method - -class KSHandler(): - def __init__(self, factory, names=None): - self.field = factory._field - self.index = 0 - n = len(factory._fvars) - d = self.field.degree() - coeff_dtype = np.dtype([('nums','i4',(d,)), ('denoms','u4',(d,))]) - if names is None: - self.ks_shm = shared_memory.SharedMemory(create=True,size=n) - self.ks = np.ndarray((n,),dtype='bool',buffer=self.ks_shm.buf) - self.ks[:] = np.zeros((n,),dtype='bool') - self.coeff_shm = shared_memory.SharedMemory(create=True,size=2*n*d*4) - self.coeffs = np.ndarray((n,),dtype=coeff_dtype,buffer=self.coeff_shm.buf) - self.coeffs['nums'][:] = np.zeros((n,d),dtype='i4') - self.coeffs['denoms'][:] = np.ones((n,d),dtype='u4') - else: - self.ks_shm = shared_memory.SharedMemory(name=names[0]) - self.ks = np.ndarray((n,),dtype='bool',buffer=self.ks_shm.buf) - self.coeff_shm = shared_memory.SharedMemory(name=names[1]) - self.coeffs = np.ndarray((n,),dtype=coeff_dtype,buffer=self.coeff_shm.buf) - - @cached_method - def __getitem__(self, idx): - if not self.ks[idx]: - raise KeyError('Index {} does not correspond to a known square'.format(idx)) - rat = list() - for i in range(self.field.degree()): - rat.append(self.coeffs['nums'][idx][i] / self.coeffs['denoms'][idx][i]) - return self.field(rat) - - def __setitem__(self, idx, rhs): - self.ks[idx] = True - for i, c in enumerate(rhs): - self.coeffs['nums'][idx][i] = -c.numerator() - self.coeffs['denoms'][idx][i] = c.denominator() - - def __iter__(self): - self.index = 0 - return self - - def __next__(self): - if self.index == len(self.ks): - raise StopIteration - - #Skip indices that are not known - while not self.ks[self.index]: - self.index += 1 - if self.index == len(self.ks): - raise StopIteration - - self.index += 1 - return self[self.index-1] - - def __contains__(self, idx): - return self.ks[idx] - - def __len__(self): - return sum(self.ks) - - def __eq__(self, other): - return np.all(self.ks == other.ks) and np.all(self.coeffs == other.coeffs) - - def items(self): - for v in self: - yield self.index-1, v - - def reset(self): - n = len(self.ks) - d = self.field.degree() - self.ks[:] = np.zeros((n,),dtype='bool') - self.__getitem__.clear_cache() - self.coeffs['nums'][:] = np.zeros((n,d),dtype='i4') - self.coeffs['denoms'][:] = np.ones((n,d),dtype='u4') - - def unlink(self): - self.ks_shm.unlink() - self.coeff_shm.unlink() - - def get_names(self): - return (self.ks_shm.name,self.coeff_shm.name) diff --git a/src/sage/combinat/root_system/shm_managers.pyx b/src/sage/combinat/root_system/shm_managers.pyx new file mode 100644 index 00000000000..52efb52a8a4 --- /dev/null +++ b/src/sage/combinat/root_system/shm_managers.pyx @@ -0,0 +1,179 @@ +from cysignals.memory cimport sig_malloc +from sage.rings.polynomial.polydict cimport ETuple + +from multiprocessing import shared_memory +import numpy as np +from sage.misc.cachefunc import cached_method + +class KSHandler(): + def __init__(self, factory, name=None): + self.field = factory._field + self.index = 0 + n = len(factory._fvars) + d = self.field.degree() + ks_t = np.dtype([ + ('known', 'bool', (1,)), + ('nums','i4',(d,)), + ('denoms','u4',(d,)) + ]) + if name is None: + self.shm = shared_memory.SharedMemory(create=True,size=n*ks_t.itemsize) + self.ks_dat = np.ndarray((n,),dtype=ks_t,buffer=self.shm.buf) + self.reset() + else: + self.shm = shared_memory.SharedMemory(name=name) + self.ks_dat = np.ndarray((n,),dtype=ks_t,buffer=self.shm.buf) + + @cached_method + def __getitem__(self, idx): + if not self.ks_dat['known'][idx]: + raise KeyError('Index {} does not correspond to a known square'.format(idx)) + cdef list rat = list() + for i in range(self.field.degree()): + rat.append(self.ks_dat['nums'][idx][i] / self.ks_dat['denoms'][idx][i]) + return self.field(rat) + + def __setitem__(self, idx, rhs): + self.ks_dat['known'][idx] = True + cdef int i + for i in range(len(rhs)): + self.ks_dat['nums'][idx][i] = -rhs[i].numerator() + self.ks_dat['denoms'][idx][i] = rhs[i].denominator() + + def __iter__(self): + self.index = 0 + return self + + def __next__(self): + if self.index == self.ks_dat.size: + raise StopIteration + + #Skip indices that are not known + while not self.ks_dat['known'][self.index]: + self.index += 1 + if self.index == self.ks_dat.size: + raise StopIteration + + self.index += 1 + return self[self.index-1] + + def __contains__(self, idx): + return self.ks_dat['known'][idx] + + def __len__(self): + return sum(self.ks_dat['known']) + + def __eq__(self, other): + return np.all(self.ks_dat == other.ks_dat) + + def items(self): + for v in self: + yield self.index-1, v + + def reset(self): + n = self.ks_dat.size + d = self.field.degree() + self.__getitem__.clear_cache() + self.ks_dat['known'] = np.zeros((n,1),dtype='bool') + self.ks_dat['nums'] = np.zeros((n,d),dtype='i4') + self.ks_dat['denoms'] = np.ones((n,d),dtype='u4') + +class FvarsHandler(): + # cdef dict sext_to_idx + # cdef int ngens + # cdef field, fvars, fvars_t, shm + + def __init__(self, factory, name=None, max_terms=20): + n = len(factory._fvars) + d = factory._field.degree() + self.obj_cache = dict() + self.fvars_t = np.dtype([ + ('modified','bool',(1,)), + ('ticks', 'u1', (max_terms,)), + ('exp_data', 'u2', (max_terms*50,)), + ('coeff_nums','i4',(max_terms,d)), + ('coeff_denom','u4',(max_terms,)) + ]) + self.sext_to_idx = {s: i for i, s in factory._idx_to_sextuple.items()} + self.ngens = factory._poly_ring.ngens() + self.field = factory._field + if name is None: + self.shm = shared_memory.SharedMemory(create=True,size=n*self.fvars_t.itemsize) + self.fvars = np.ndarray((n,),dtype=self.fvars_t,buffer=self.shm.buf) + else: + self.shm = shared_memory.SharedMemory(name=name) + self.fvars = np.ndarray((n,),dtype=self.fvars_t,buffer=self.shm.buf) + + #Given a tuple of labels and a tuple of (ETuple, cyc_coeff) pairs, insert into + #shared dictionary + #WARNING: current data structure supports up to 2**16-1 entries, + #each with each monomial in each entry having at most 254 nonzero terms. + def __setitem__(self, sextuple, fvar): + cdef int cum, denom, i, j, k, idx + cdef ETuple exp + idx = self.sext_to_idx[sextuple] + #Clear entry before inserting + self.fvars[idx] = np.zeros((1,), dtype=self.fvars_t) + cum = 0 + i = 0 + for exp, coeff in fvar: + #Handle constant coefficient + if exp._nonzero > 0: + self.fvars['ticks'][idx][i] = exp._nonzero + else: + self.fvars['ticks'][idx][i] = -1 + for j in range(2*exp._nonzero): + self.fvars['exp_data'][idx][cum] = exp._data[j] + cum += 1 + denom = coeff.denominator() + self.fvars['coeff_denom'][idx][i] = denom + for k, c in enumerate(coeff._coefficients()): + self.fvars['coeff_nums'][idx][i,k] = c * denom + i += 1 + self.fvars['modified'][idx] = True + + #Retrieve a record by unflattening and constructing relevant Python objects + #Only the parent is allowed to modify the shared _fvars object, so to implement caching + #we must tag modified objects and delete cache if present before retrieval + def __getitem__(self, sextuple): + if not sextuple in self.sext_to_idx: + raise KeyError('Invalid sextuple {}'.format(sextuple)) + cdef int idx = self.sext_to_idx[sextuple] + if idx in self.obj_cache: + if self.fvars['modified'][idx]: + del self.obj_cache[idx] + else: + return self.obj_cache[idx] + cdef ETuple e = ETuple({}, self.ngens) + cdef int cum, d, i, j + cdef list poly_tup = list() + cum = 0 + for i in range(np.count_nonzero(self.fvars['ticks'][idx])): + #Construct new ETuple for each monomial + exp = e._new() + #Handle constant coeff + nnz = self.fvars['ticks'][idx][i] if self.fvars['ticks'][idx][i] < 255 else 0 + exp._nonzero = nnz + exp._data = sig_malloc(sizeof(int)*nnz*2) + for j in range(2*nnz): + exp._data[j] = self.fvars['exp_data'][idx][cum] + cum += 1 + + #Construct cyclotomic field coefficient + d = self.fvars['coeff_denom'][idx][i] + cyc_coeff = self.field([num / d for num in self.fvars['coeff_nums'][idx][i]]) + + poly_tup.append((exp, cyc_coeff)) + cdef tuple ret = tuple(poly_tup) + self.obj_cache[idx] = ret + return ret + + def __len__(self): + return self.fvars.size + + def __repr__(self): + return str({sextuple: self[sextuple] for sextuple in self.sext_to_idx}) + + def items(self): + for sextuple in self.sext_to_idx: + yield sextuple, self[sextuple] From 8b70e802d4c85b7a8e69fc2e612df4b09f59fcf6 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Fri, 30 Apr 2021 16:34:18 -0400 Subject: [PATCH 061/632] removed update_child_fmats method --- src/sage/combinat/root_system/f_matrix.py | 90 +++---- .../fast_parallel_fmats_methods.pxd | 1 - .../fast_parallel_fmats_methods.pyx | 55 +--- .../combinat/root_system/poly_tup_engine.pxd | 2 - .../combinat/root_system/poly_tup_engine.pyx | 1 - .../combinat/root_system/shm_managers.pyx | 239 ++++++++++++++---- 6 files changed, 240 insertions(+), 148 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index e54c1c71ee2..ca61dab5793 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -17,13 +17,12 @@ import cPickle as pickle except: import pickle - from copy import deepcopy from ctypes import cast, py_object from itertools import product, zip_longest -from multiprocessing import cpu_count, Pool, set_start_method +from multiprocessing import cpu_count, Pool, set_start_method, shared_memory import numpy as np -import os +from os import remove from sage.combinat.root_system.fast_parallel_fmats_methods import ( _backward_subs, _solve_for_linear_terms, @@ -39,6 +38,7 @@ tup_fixes_sq, resize, ) +from sage.combinat.root_system.shm_managers import KSHandler, FvarsHandler from sage.graphs.graph import Graph from sage.matrix.constructor import matrix from sage.misc.misc import get_main_globals @@ -47,10 +47,6 @@ from sage.rings.polynomial.polydict import ETuple from sage.rings.qqbar import AA, QQbar, number_field_elements_from_algebraics -from multiprocessing import shared_memory -from sage.combinat.root_system.shm_managers import KSHandler, FvarsHandler -# from sage.combinat.root_system.fvars_handler import FvarsHandler - class FMatrix(): def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variables=False): r""" @@ -398,9 +394,8 @@ def _reset_solver_state(self): self._chkpt_status = -1 self.clear_vars() self.clear_equations() - self._var_degs = [0]*len(self._fvars) + self._var_degs = [0]*self._poly_ring.ngens() self._kp = dict() - # self._ks = KSHandler(self) self._ks = dict() self._nnz = self._get_known_nonz() @@ -944,10 +939,15 @@ def _get_known_sq(self,eqns=None): """ if eqns is None: eqns = self.ideal_basis - F = self._field for eq_tup in eqns: if tup_fixes_sq(eq_tup): - self._ks[variables(eq_tup)[0]] = eq_tup[-1][1] + #Handle mp and single process cases + if isinstance(self._ks, dict): + rhs = -self._field(list(eq_tup[-1][1])) + else: + rhs = [-v for v in eq_tup[-1][1]] + self._ks[variables(eq_tup)[0]] = rhs + def _get_known_nonz(self): r""" @@ -1288,7 +1288,7 @@ def get_worker_pool(self,processes=None): which may be used e.g. to set up defining equations using :meth:`get_defining_equations`. - More than one task may be submitted to the same worker pool. + This pool may be reused time and again. When you are done using the worker pool, use :meth:`shutdown_worker_pool` to close the pool and properly dispose @@ -1305,7 +1305,7 @@ def get_worker_pool(self,processes=None): fx1*fx3 + (zeta60^14 + zeta60^12 - zeta60^6 - zeta60^4 + 1)*fx3*fx4 + (zeta60^14 - zeta60^4)*fx3, fx1*fx2 + (zeta60^14 + zeta60^12 - zeta60^6 - zeta60^4 + 1)*fx2*fx4 + (zeta60^14 - zeta60^4)*fx2, fx1^2 + (zeta60^14 + zeta60^12 - zeta60^6 - zeta60^4 + 1)*fx2*fx3 + (-zeta60^12)*fx1] - sage: pe = f.get_defining_equations('pentaongs',worker_pool=pool) + sage: pe = f.get_defining_equations('pentagons',worker_pool=pool) sage: f.shutdown_worker_pool(pool) .. WARNING:: @@ -1334,6 +1334,8 @@ def get_worker_pool(self,processes=None): vd_name = self._var_degs.shm.name ks = KSHandler(self) for idx, val in self._ks.items(): + if not isinstance(val, tuple): + val = val._coefficients() ks[idx] = val self._ks = ks ks_names = self._ks.shm.name @@ -1410,13 +1412,13 @@ def _map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_t sage: f = FMatrix(FusionRing("A1",2)) sage: f._reset_solver_state() - sage: len(f._map_triv_reduce('get_reduced_hexagons',[(0,1)])) + sage: len(f._map_triv_reduce('get_reduced_hexagons',[(0,1,False)])) 11 - sage: from multiprocessing import Pool - sage: pool = Pool() - sage: mp_params = [(i,pool._processes) for i in range(pool._processes)] + sage: pool = f.get_worker_pool() + sage: mp_params = [(i,pool._processes,True) for i in range(pool._processes)] sage: len(f._map_triv_reduce('get_reduced_pentagons',mp_params,worker_pool=pool,chunksize=1,mp_thresh=0)) 33 + sage: f.shutdown_worker_pool(pool) """ if mp_thresh is None: mp_thresh = self.mp_thresh @@ -1593,10 +1595,11 @@ def _update_reduction_params(self,eqns=None,worker_pool=None,children_need_updat sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey, poly_to_tup sage: f.ideal_basis.sort(key=poly_tup_sortkey) sage: f.mp_thresh = 0 - sage: f._fvars = {sextuple : poly_to_tup(rhs) for sextuple, rhs in f._fvars.items()} + sage: f._fvars = f._shared_fvars sage: f._triangular_elim(worker_pool=pool,verbose=False) # indirect doctest sage: f.ideal_basis [] + sage: f.shutdown_worker_pool(pool) """ if eqns is None: eqns = self.ideal_basis @@ -1606,16 +1609,10 @@ def _update_reduction_params(self,eqns=None,worker_pool=None,children_need_updat for i, d in enumerate(degs): self._var_degs[i] = d else: - for i in range(len(self._fvars)): + for i in range(self._poly_ring.ngens()): self._var_degs[i] = 0 self._nnz = self._get_known_nonz() self._kp = compute_known_powers(self._var_degs,self._get_known_vals(),self._field.one()) - if worker_pool is not None and children_need_update: - #self._nnz and self._kp are computed in child processes to reduce IPC overhead - n_proc = worker_pool._processes - # new_data = [(self._fvars,)]*n_proc - new_data = [(1,)]*n_proc - self._map_triv_reduce('update_child_fmats',new_data,worker_pool=worker_pool,chunksize=1,mp_thresh=0) def _triangular_elim(self,eqns=None,worker_pool=None,verbose=True): r""" @@ -1646,6 +1643,9 @@ def _triangular_elim(self,eqns=None,worker_pool=None,verbose=True): eqns = self.ideal_basis ret = False while True: + #Reset modification cache + if worker_pool is not None: + self._fvars.fvars['modified'][:] = False linear_terms_exist = _solve_for_linear_terms(self,eqns) if not linear_terms_exist: break @@ -1653,8 +1653,16 @@ def _triangular_elim(self,eqns=None,worker_pool=None,verbose=True): #Compute new reduction params, send to child processes if any, and update eqns self._update_reduction_params(eqns=eqns,worker_pool=worker_pool,children_need_update=len(eqns)>self.mp_thresh) - n = len(eqns) // worker_pool._processes ** 2 + 1 if worker_pool is not None else len(eqns) - eqns = [eqns[i:i+n] for i in range(0,len(eqns),n)] + # n = len(eqns) // worker_pool._processes ** 2 + 1 if worker_pool is not None else len(eqns) + # eqns = [eqns[i:i+n] for i in range(0,len(eqns),n)] + if worker_pool is not None and len(eqns) > self.mp_thresh: + n = worker_pool._processes + chunks = [[] for i in range(n)] + for i, eq_tup in enumerate(eqns): + chunks[i%n].append(eq_tup) + eqns = chunks + else: + eqns = [eqns] eqns = self._map_triv_reduce('update_reduce',eqns,worker_pool=worker_pool) eqns.sort(key=poly_tup_sortkey) if verbose: @@ -2151,21 +2159,22 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start #Loading from a pickle with solved F-symbols if self._chkpt_status > 5: return - - pool = self.get_worker_pool(max(cpu_count()-1,1)) if use_mp else None + #max(cpu_count()-1,1) + pool = self.get_worker_pool() if use_mp else None if verbose: - print("Computing F-symbols for {} with {} variables...".format(self._FR, len(self._fvars))) + print("Computing F-symbols for {} with {} variables...".format(self._FR, self._poly_ring.ngens())) if self._chkpt_status < 1: #Set up hexagon equations and orthogonality constraints self.get_orthogonality_constraints(output=False) self.get_defining_equations('hexagons',worker_pool=pool,output=False) - self._fvars = self._shared_fvars - #Report progress if verbose: print("Set up {} hex and orthogonality constraints...".format(len(self.ideal_basis))) - + #Unzip _fvars and link to shared_memory structure if using multiprocessing + self._fvars = {sextuple: poly_to_tup(fvar) for sextuple, fvar in self._fvars.items()} + if use_mp: + self._fvars = self._shared_fvars self._checkpoint(checkpoint,1,verbose=verbose) if self._chkpt_status < 2: @@ -2173,10 +2182,6 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start self.ideal_basis = self._par_graph_gb(worker_pool=pool,verbose=verbose) self.ideal_basis.sort(key=poly_tup_sortkey) self._triangular_elim(worker_pool=pool,verbose=verbose) - - #Update reduction parameters, also in children if any - self._update_reduction_params(worker_pool=pool,children_need_update=True) - #Report progress if verbose: print("Hex elim step solved for {} / {} variables".format(sum(self._solved), len(self._poly_ring.gens()))) @@ -2187,7 +2192,6 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start #Set up pentagon equations in parallel self.get_defining_equations('pentagons',worker_pool=pool,output=False) self.ideal_basis.sort(key=poly_tup_sortkey) - #Report progress if verbose: print("Set up {} reduced pentagons...".format(len(self.ideal_basis))) @@ -2197,7 +2201,6 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start #Simplify and eliminate variables if self._chkpt_status < 4: self._triangular_elim(worker_pool=pool,verbose=verbose) - #Report progress if verbose: print("Pent elim step solved for {} / {} variables".format(sum(self._solved), len(self._poly_ring.gens()))) @@ -2214,18 +2217,17 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start self._checkpoint(checkpoint,5,verbose=verbose) - #Close worker pool and destroy shared resources - if use_mp: - self.shutdown_worker_pool(pool) - #Find numeric values for each F-symbol self._get_explicit_solution(verbose=verbose) #The calculation was successful, so we may delete checkpoints self._chkpt_status = 7 self.clear_equations() + #Close worker pool and destroy shared resources + if use_mp: + self.shutdown_worker_pool(pool) if checkpoint: - os.remove("fmatrix_solver_checkpoint_"+self.get_fr_str()+".pickle") + remove("fmatrix_solver_checkpoint_"+self.get_fr_str()+".pickle") if save_results: self.save_fvars(save_results) diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd index faed5a56f5e..f54fa2ff174 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd @@ -1,3 +1,2 @@ -cpdef update_child_fmats(factory, tuple data_tup) cdef _fmat(fvars, Nk_ij, one, a, b, c, d, x, y) cpdef executor(tuple params) diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index 98bd95357dd..02d2ebc799f 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -22,16 +22,12 @@ from sage.rings.number_field.number_field_element cimport NumberFieldElement_abs from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular, MPolynomialRing_libsingular from sage.rings.polynomial.polydict cimport ETuple -import ctypes +from ctypes import cast, py_object from itertools import product -from sage.rings.ideal import Ideal -from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - -from time import sleep - from multiprocessing import shared_memory +from sage.rings.ideal import Ideal from sage.combinat.root_system.shm_managers import KSHandler, FvarsHandler -# from sage.combinat.root_system.fvars_handler import FvarsHandler +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing ########################## ### Fast class methods ### @@ -316,6 +312,7 @@ cdef get_reduced_pentagons(factory, tuple mp_params): id_anyon = factory._FR.one() _field = factory._field cdef NumberFieldElement_absolute one = _field.one() + factory._nnz = factory._get_known_nonz() cdef ETuple _nnz = factory._nnz _ks = factory._ks cdef MPolynomial_libsingular zero = factory._poly_ring.zero() @@ -347,6 +344,9 @@ cdef list update_reduce(factory, list eqns): _field = factory._field one = _field.one() _ks = factory._ks + #Update reduction params + factory._nnz = factory._get_known_nonz() + factory._kp = compute_known_powers(factory._var_degs,factory._get_known_vals(),factory._field.one()) cdef dict _kp = factory._kp cdef ETuple _nnz = factory._nnz @@ -404,40 +404,6 @@ cdef list compute_gb(factory, tuple args): res.append(t) return collect_eqns(res) -cpdef update_child_fmats(factory, tuple data_tup): - r""" - One-to-all communication used to update FMatrix object after each triangular - elim step. We must update the algorithm's state values. These are: - ``_fvars``, ``_solved``, ``_ks``, ``_var_degs``, ``_nnz``, and ``_kp``. - - TESTS:: - - sage: f = FMatrix(FusionRing("A1",3)) - sage: f._reset_solver_state() - sage: f.get_orthogonality_constraints(output=False) - sage: from multiprocessing import cpu_count, Pool, set_start_method, shared_memory - sage: pool = f.get_worker_pool() - sage: f.get_defining_equations('hexagons',worker_pool=pool,output=False) - sage: f.ideal_basis = f._par_graph_gb(worker_pool=pool,verbose=False) - sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey, poly_to_tup - sage: f.ideal_basis.sort(key=poly_tup_sortkey) - sage: f.mp_thresh = 0 - sage: f._fvars = {sextuple : poly_to_tup(rhs) for sextuple, rhs in f._fvars.items()} - sage: f._triangular_elim(worker_pool=pool) # indirect doctest - Elimination epoch completed... 10 eqns remain in ideal basis - Elimination epoch completed... 0 eqns remain in ideal basis - sage: f.ideal_basis - [] - """ - #factory object is assumed global before forking used to create the Pool object, - #so each child has a global fmats variable. So it's enough to update that object - # factory._fvars = data_tup[0] - factory._nnz = factory._get_known_nonz() - factory._kp = compute_known_powers(factory._var_degs,factory._get_known_vals(),factory._field.one()) - - #Wait this process isn't used again - sleep(0.5) - ################ ### Reducers ### ################ @@ -464,7 +430,6 @@ cdef dict mappers = { "get_reduced_pentagons": get_reduced_pentagons, "update_reduce": update_reduce, "compute_gb": compute_gb, - "update_child_fmats": update_child_fmats, "pent_verify": pent_verify } @@ -496,18 +461,18 @@ cpdef executor(tuple params): sage: from sage.combinat.root_system.fast_parallel_fmats_methods import executor sage: fmats = FMatrix(FusionRing("A1",3)) sage: fmats._reset_solver_state() - sage: params = (('get_reduced_hexagons', id(fmats)), (0,1)) + sage: params = (('get_reduced_hexagons', id(fmats)), (0,1,True)) sage: len(executor(params)) == 63 True sage: fmats = FMatrix(FusionRing("E6",1)) sage: fmats._reset_solver_state() - sage: params = (('get_reduced_hexagons', id(fmats)), (0,1)) + sage: params = (('get_reduced_hexagons', id(fmats)), (0,1,False)) sage: len(executor(params)) == 6 True """ (fn_name, fmats_id), args = params #Construct a reference to global FMatrix object in this worker's memory - fmats_obj = ctypes.cast(fmats_id, ctypes.py_object).value + fmats_obj = cast(fmats_id, py_object).value #Bind module method to FMatrix object in worker process, and call the method return mappers[fn_name](fmats_obj,args) diff --git a/src/sage/combinat/root_system/poly_tup_engine.pxd b/src/sage/combinat/root_system/poly_tup_engine.pxd index 0a768b40851..5d9987f5a9c 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pxd +++ b/src/sage/combinat/root_system/poly_tup_engine.pxd @@ -10,13 +10,11 @@ cpdef list variables(tuple eq_tup) cpdef constant_coeff(tuple eq_tup) cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map) cpdef bint tup_fixes_sq(tuple eq_tup) -# cdef dict subs_squares(dict eq_dict, dict known_sq) cdef dict subs_squares(dict eq_dict, known_sq) cpdef dict compute_known_powers(max_degs, dict val_dict, one) cdef dict subs(tuple poly_tup, dict known_powers, one) cpdef tup_to_univ_poly(tuple eq_tup, univ_poly_ring) cpdef tuple poly_tup_sortkey(tuple eq_tup) -# cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, dict known_sq, NumberFieldElement_absolute one) cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, known_sq, NumberFieldElement_absolute one) cdef tuple _flatten_coeffs(tuple eq_tup) cpdef tuple _unflatten_coeffs(field, tuple eq_tup) diff --git a/src/sage/combinat/root_system/poly_tup_engine.pyx b/src/sage/combinat/root_system/poly_tup_engine.pyx index 519b5023e94..e4144eb5bfb 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pyx +++ b/src/sage/combinat/root_system/poly_tup_engine.pyx @@ -359,7 +359,6 @@ cpdef inline bint tup_fixes_sq(tuple eq_tup): ### Simplification ### ###################### -# cdef dict subs_squares(dict eq_dict, dict known_sq): cdef dict subs_squares(dict eq_dict, known_sq): r""" Substitute for known squares into a given polynomial. diff --git a/src/sage/combinat/root_system/shm_managers.pyx b/src/sage/combinat/root_system/shm_managers.pyx index 52efb52a8a4..f72b963c0c9 100644 --- a/src/sage/combinat/root_system/shm_managers.pyx +++ b/src/sage/combinat/root_system/shm_managers.pyx @@ -1,25 +1,32 @@ from cysignals.memory cimport sig_malloc +cimport numpy as np from sage.rings.polynomial.polydict cimport ETuple +from sage.rings.number_field.number_field_base cimport NumberField from multiprocessing import shared_memory -import numpy as np from sage.misc.cachefunc import cached_method +import numpy as np class KSHandler(): + # cdef field + # cdef np.ndarray ks_dat + # cdef public shm + def __init__(self, factory, name=None): self.field = factory._field - self.index = 0 - n = len(factory._fvars) + n = factory._poly_ring.ngens() d = self.field.degree() ks_t = np.dtype([ ('known', 'bool', (1,)), - ('nums','i4',(d,)), - ('denoms','u4',(d,)) + ('nums','i8',(d,)), + ('denoms','u8',(d,)) ]) if name is None: self.shm = shared_memory.SharedMemory(create=True,size=n*ks_t.itemsize) self.ks_dat = np.ndarray((n,),dtype=ks_t,buffer=self.shm.buf) - self.reset() + self.ks_dat['known'] = np.zeros((n,1),dtype='bool') + self.ks_dat['nums'] = np.zeros((n,d),dtype='i4') + self.ks_dat['denoms'] = np.ones((n,d),dtype='u4') else: self.shm = shared_memory.SharedMemory(name=name) self.ks_dat = np.ndarray((n,),dtype=ks_t,buffer=self.shm.buf) @@ -28,62 +35,116 @@ class KSHandler(): def __getitem__(self, idx): if not self.ks_dat['known'][idx]: raise KeyError('Index {} does not correspond to a known square'.format(idx)) + cdef unsigned int i, d cdef list rat = list() - for i in range(self.field.degree()): + d = self.field.degree() + for i in range(d): rat.append(self.ks_dat['nums'][idx][i] / self.ks_dat['denoms'][idx][i]) return self.field(rat) def __setitem__(self, idx, rhs): self.ks_dat['known'][idx] = True - cdef int i + cdef unsigned int i for i in range(len(rhs)): - self.ks_dat['nums'][idx][i] = -rhs[i].numerator() + self.ks_dat['nums'][idx][i] = rhs[i].numerator() self.ks_dat['denoms'][idx][i] = rhs[i].denominator() - def __iter__(self): - self.index = 0 - return self - - def __next__(self): - if self.index == self.ks_dat.size: - raise StopIteration - - #Skip indices that are not known - while not self.ks_dat['known'][self.index]: - self.index += 1 - if self.index == self.ks_dat.size: - raise StopIteration - - self.index += 1 - return self[self.index-1] - def __contains__(self, idx): return self.ks_dat['known'][idx] def __len__(self): - return sum(self.ks_dat['known']) + """ + Compute the number of known squares. + + """ + return self.ks_dat['known'].sum() def __eq__(self, other): + """ + Test for equality. + """ return np.all(self.ks_dat == other.ks_dat) def items(self): - for v in self: - yield self.index-1, v - - def reset(self): - n = self.ks_dat.size - d = self.field.degree() - self.__getitem__.clear_cache() - self.ks_dat['known'] = np.zeros((n,1),dtype='bool') - self.ks_dat['nums'] = np.zeros((n,d),dtype='i4') - self.ks_dat['denoms'] = np.ones((n,d),dtype='u4') + cdef unsigned int i + for i in range(self.ks_dat.size): + if self.ks_dat['known'][i]: + yield i, self[i] -class FvarsHandler(): - # cdef dict sext_to_idx - # cdef int ngens - # cdef field, fvars, fvars_t, shm +cdef class FvarsHandler(): + cdef dict sext_to_idx, obj_cache + cdef unsigned int ngens + cdef fvars_t + cdef NumberField field + cdef public np.ndarray fvars + cdef public shm def __init__(self, factory, name=None, max_terms=20): + """ + Return a shared memory backed dict-like structure to manage the + ``_fvars`` attribute of an F-matrix factory object. + + This structure implements a representation of the F-symbols dictionary + using a structured NumPy array backed by a contiguous shared memory + object. + + The monomial data is stored in the ``exp_data`` structure. Monomial + exponent data is stored contiguously and ``ticks`` are used to + indicate different monomials. + + Coefficient data is stored in the ``coeff_nums`` and ``coeff_denom`` + arrays. The ``coeff_denom`` array stores the value + ``d = coeff.denominator()`` for each cyclotomic coefficient. The + ``coeff_nums`` array stores the values + ``c.numerator() * d for c in coeff._coefficients()``, the abridged + list representation of the cyclotomic coefficient ``coeff``. + + Each entry also has a boolean ``modified`` attribute, indicating + whether it has been modified by the parent process. Entry retrieval + is cached in each process, so each process must check whether + entries have been modified before attempting retrieval. + + The parent process should construct this object without a + ``name`` attribute. Children processes use the ``name`` attribute, + accessed via ``self.shm.name`` to attach to the shared memory block. + + INPUT: + + - ``factory`` -- an F-matrix factory object. + - ``name`` -- the name of a shared memory object + (used by child processes for attaching). + - ``max_terms`` -- maximum number of terms in each entry. Since + we use contiguous C-style memory blocks, the size of the block + must be known in advance. + + .. NOTE:: + + To properly dispose of shared memory resources, + ``self.shm.unlink()`` must be called before exiting. + + .. WARNING:: + + The current data structure supports up to 2**16-1 entries, + each with each monomial in each entry having at most 254 + nonzero terms. On average, each of the ``max_terms`` monomials + can have at most 50 terms. + + EXAMPLES:: + + sage: from sage.combinat.root_system.shm_managers import FvarsHandler + sage: #Create shared data structure + sage: f = FMatrix(FusionRing("A2",1), inject_variables=True) + creating variables fx1..fx8 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 + sage: fvars = FvarsHandler(f) + sage: #In the same shell or in a different shell, attach to fvars + sage: fvars2 = FvarsHandler(f, name=fvars.shm.name) + sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: fvars[f2, f1, f2, f2, f0, f0] = poly_to_tup(fx5**5) + sage: f._tup_to_fpoly(fvars2[f2, f1, f2, f2, f0, f0]) + fx5^5 + sage: fvars.shm.unlink() + """ n = len(factory._fvars) d = factory._field.degree() self.obj_cache = dict() @@ -104,11 +165,32 @@ class FvarsHandler(): self.shm = shared_memory.SharedMemory(name=name) self.fvars = np.ndarray((n,),dtype=self.fvars_t,buffer=self.shm.buf) - #Given a tuple of labels and a tuple of (ETuple, cyc_coeff) pairs, insert into - #shared dictionary - #WARNING: current data structure supports up to 2**16-1 entries, - #each with each monomial in each entry having at most 254 nonzero terms. def __setitem__(self, sextuple, fvar): + """ + Given a sextuple of labels and a tuple of ``(ETuple, cyc_coeff)`` pairs, + create or overwrite an entry in the shared data structure + corresponding to the given sextuple. + + EXAMPLES:: + + sage: from sage.combinat.root_system.shm_managers import FvarsHandler + sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: f = FMatrix(FusionRing("A3", 1), inject_variables=True) + creating variables fx1..fx27 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19, fx20, fx21, fx22, fx23, fx24, fx25, fx26 + sage: fvars = FvarsHandler(f) + sage: fvars[(f3, f2, f1, f2, f1, f3)] = poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx21**3 - 799/2881*fx1*fx2**5*fx10) + sage: fvars[f3, f2, f3, f0, f1, f1] = poly_to_tup(f._poly_ring.zero()) + sage: fvars[f3, f3, f3, f1, f2, f2] = poly_to_tup(-1/19*f._poly_ring.one()) + sage: s, t, r = (f3, f2, f1, f2, f1, f3), (f3, f2, f3, f0, f1, f1), (f3, f3, f3, f1, f2, f2) + sage: f._tup_to_fpoly(fvars[s]) == 1/8*fx0**15 - 23/79*fx2*fx21**3 - 799/2881*fx1*fx2**5*fx10 + True + sage: f._tup_to_fpoly(fvars[t]) == 0 + True + sage: f._tup_to_fpoly(fvars[r]) == -1/19 + True + sage: fvars.shm.unlink() + """ cdef int cum, denom, i, j, k, idx cdef ETuple exp idx = self.sext_to_idx[sextuple] @@ -132,10 +214,42 @@ class FvarsHandler(): i += 1 self.fvars['modified'][idx] = True - #Retrieve a record by unflattening and constructing relevant Python objects - #Only the parent is allowed to modify the shared _fvars object, so to implement caching - #we must tag modified objects and delete cache if present before retrieval def __getitem__(self, sextuple): + """ + Retrieve a record from the shared memory data structure by + unflattening its representation and constructing relevant Python + objects. + + This method returns a tuple of ``(ETuple, coeff)`` pairs, + where ``coeff`` is an element of ``self.field``. + + EXAMPLES:: + + sage: from sage.combinat.root_system.shm_managers import FvarsHandler + sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: f = FMatrix(FusionRing("A3", 1), inject_variables=True) + creating variables fx1..fx27 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19, fx20, fx21, fx22, fx23, fx24, fx25, fx26 + sage: fvars = FvarsHandler(f) + sage: fvars[(f3, f2, f1, f2, f1, f3)] = poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx21**3 - 799/2881*fx1*fx2**5*fx10) + sage: fvars[f3, f2, f3, f0, f1, f1] = poly_to_tup(f._poly_ring.zero()) + sage: fvars[f3, f3, f3, f1, f2, f2] = poly_to_tup(-1/19*f._poly_ring.one()) + sage: s, t, r = (f3, f2, f1, f2, f1, f3), (f3, f2, f3, f0, f1, f1), (f3, f3, f3, f1, f2, f2) + sage: f._tup_to_fpoly(fvars[s]) == 1/8*fx0**15 - 23/79*fx2*fx21**3 - 799/2881*fx1*fx2**5*fx10 + True + sage: f._tup_to_fpoly(fvars[t]) == 0 + True + sage: f._tup_to_fpoly(fvars[r]) == -1/19 + True + sage: fvars.shm.unlink() + + .. NOTE:: + + This method implements caching. Only the parent process is allowed + to modify the shared fvars structure. Each process builds its own + cache, so each process must update its cache before retrieving a + modified entry, tagged via its ``modified`` property. + """ if not sextuple in self.sext_to_idx: raise KeyError('Invalid sextuple {}'.format(sextuple)) cdef int idx = self.sext_to_idx[sextuple] @@ -145,8 +259,9 @@ class FvarsHandler(): else: return self.obj_cache[idx] cdef ETuple e = ETuple({}, self.ngens) - cdef int cum, d, i, j + cdef unsigned int cum, d, i, j, nnz cdef list poly_tup = list() + cdef tuple ret cum = 0 for i in range(np.count_nonzero(self.fvars['ticks'][idx])): #Construct new ETuple for each monomial @@ -164,16 +279,30 @@ class FvarsHandler(): cyc_coeff = self.field([num / d for num in self.fvars['coeff_nums'][idx][i]]) poly_tup.append((exp, cyc_coeff)) - cdef tuple ret = tuple(poly_tup) + ret = tuple(poly_tup) self.obj_cache[idx] = ret return ret - def __len__(self): - return self.fvars.size + def items(self): + """ + Iterates through key-value pairs in the data structure as if it + were a Python dict. - def __repr__(self): - return str({sextuple: self[sextuple] for sextuple in self.sext_to_idx}) + As in a Python dict, the key-value pairs are yielded in no particular + order. - def items(self): + EXAMPLES:: + + sage: f = FMatrix(FusionRing("G2", 1), inject_variables=True) + creating variables fx1..fx5 + Defining fx0, fx1, fx2, fx3, fx4 + sage: p = f.get_worker_pool() + sage: for sextuple, fvar in f._shared_fvars.items(): + ....: if sextuple == (f1, f1, f1, f1, f1, f1): + ....: f._tup_to_fpoly(fvar) + ....: + fx4 + sage: f.shutdown_worker_pool(p) + """ for sextuple in self.sext_to_idx: yield sextuple, self[sextuple] From b430001d6ff5b91619c9f6534ad7115697b33db8 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Fri, 30 Apr 2021 16:53:01 -0400 Subject: [PATCH 062/632] added copyright message to shm_managers and fixed doctest --- .../combinat/root_system/shm_managers.pyx | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/sage/combinat/root_system/shm_managers.pyx b/src/sage/combinat/root_system/shm_managers.pyx index f72b963c0c9..253dc8e466a 100644 --- a/src/sage/combinat/root_system/shm_managers.pyx +++ b/src/sage/combinat/root_system/shm_managers.pyx @@ -1,3 +1,13 @@ +""" +Shared memory managers for F-symbol attributes +""" +# **************************************************************************** +# Copyright (C) 2021 Guillermo Aboumrad +# +# Distributed under the terms of the GNU General Public License (GPL) +# https://www.gnu.org/licenses/ +# **************************************************************************** + from cysignals.memory cimport sig_malloc cimport numpy as np from sage.rings.polynomial.polydict cimport ETuple @@ -227,15 +237,15 @@ cdef class FvarsHandler(): sage: from sage.combinat.root_system.shm_managers import FvarsHandler sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup - sage: f = FMatrix(FusionRing("A3", 1), inject_variables=True) - creating variables fx1..fx27 - Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19, fx20, fx21, fx22, fx23, fx24, fx25, fx26 + sage: f = FMatrix(FusionRing("B7", 1), inject_variables=True) + creating variables fx1..fx14 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 sage: fvars = FvarsHandler(f) - sage: fvars[(f3, f2, f1, f2, f1, f3)] = poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx21**3 - 799/2881*fx1*fx2**5*fx10) - sage: fvars[f3, f2, f3, f0, f1, f1] = poly_to_tup(f._poly_ring.zero()) - sage: fvars[f3, f3, f3, f1, f2, f2] = poly_to_tup(-1/19*f._poly_ring.one()) - sage: s, t, r = (f3, f2, f1, f2, f1, f3), (f3, f2, f3, f0, f1, f1), (f3, f3, f3, f1, f2, f2) - sage: f._tup_to_fpoly(fvars[s]) == 1/8*fx0**15 - 23/79*fx2*fx21**3 - 799/2881*fx1*fx2**5*fx10 + sage: fvars[(f1, f2, f1, f2, f2, f2)] = poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx13**3 - 799/2881*fx1*fx2**5*fx10) + sage: fvars[f2, f2, f2, f2, f0, f0] = poly_to_tup(f._poly_ring.zero()) + sage: fvars[f2, f1, f2, f1, f2, f2] = poly_to_tup(-1/19*f._poly_ring.one()) + sage: s, t, r = (f1, f2, f1, f2, f2, f2), (f2, f2, f2, f2, f0, f0), (f2, f1, f2, f1, f2, f2) + sage: f._tup_to_fpoly(fvars[s]) == 1/8*fx0**15 - 23/79*fx2*fx13**3 - 799/2881*fx1*fx2**5*fx10 True sage: f._tup_to_fpoly(fvars[t]) == 0 True From 34cb4f63a391dbefc319ba529a06f34d567cb8f9 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Fri, 30 Apr 2021 15:29:12 -0700 Subject: [PATCH 063/632] minor doctest tweaks --- src/sage/combinat/root_system/f_matrix.py | 12 ++++++------ src/sage/combinat/root_system/fusion_ring.py | 6 ++++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index ca61dab5793..efa16b5abb8 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -748,13 +748,13 @@ def get_non_cyclotomic_roots(self): True sage: f.get_non_cyclotomic_roots() [] - sage: f = FMatrix(FusionRing("F4",1)) + sage: f = FMatrix(FusionRing("G2",1)) sage: f.find_orthogonal_solution(verbose=False) sage: f.field() == f.FR().field() False sage: phi = f.get_qqbar_embedding() sage: [phi(r).n() for r in f.get_non_cyclotomic_roots()] - [-0.786151377757423 + 1.73579267033929e-59*I] + [-0.786151377757423 - 8.92806368517581e-31*I] When ``self.field()`` is a ``NumberField``, one may use :meth:`get_qqbar_embedding` to embed the resulting values into @@ -774,11 +774,11 @@ def get_qqbar_embedding(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("F4",1), fusion_label="f", inject_variables=True) + sage: f = FMatrix(FusionRing("G2",1), fusion_label="g", inject_variables=True) creating variables fx1..fx5 Defining fx0, fx1, fx2, fx3, fx4 sage: f.find_orthogonal_solution() - Computing F-symbols for The Fusion Ring of Type F4 and level 1 with Integer Ring coefficients with 5 variables... + Computing F-symbols for The Fusion Ring of Type G2 and level 1 with Integer Ring coefficients with 5 variables... Set up 10 hex and orthogonality constraints... Partitioned 10 equations into 2 components of size: [4, 1] @@ -792,8 +792,8 @@ def get_qqbar_embedding(self): [1] Computing appropriate NumberField... sage: phi = f.get_qqbar_embedding() - sage: phi(f.fmat(f1,f1,f1,f1,f1,f1)).n() - -0.618033988749895 + 3.63089268571980e-21*I + sage: phi(f.fmat(g1,g1,g1,g1,g1,g1)).n() + -0.618033988749895 + 1.46674215951686e-29*I """ return self._qqbar_embedding diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index b602904d1aa..db546190cb7 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -216,8 +216,10 @@ class FusionRing(WeylCharacterRing): sage: I.fusion_labels(["i0","p","s"],inject_variables=True) sage: b = I.basis().list(); b [i0, p, s] - sage: [[x*y for x in b] for y in b] - [[i0, p, s], [p, i0, s], [s, s, i0 + p]] + sage: Matrix([[x*y for x in b] for y in b]) # long time (.93s) + [ i0 p s] + [ p i0 s] + [ s s i0 + p] sage: [x.twist() for x in b] [0, 1, 1/8] sage: [x.ribbon() for x in b] From c67dfc20af0a6b396e76d46e117ba2951b289645 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Fri, 30 Apr 2021 18:38:02 -0400 Subject: [PATCH 064/632] indentation warning block --- src/sage/combinat/root_system/f_matrix.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index efa16b5abb8..10335cd49b3 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -1310,16 +1310,16 @@ def get_worker_pool(self,processes=None): .. WARNING:: - This method is needed to initialize the worker pool using the - necessary shared memory resources. Simply using the - ``multiprocessing.Pool`` constructor will not work with our class - methods. + This method is needed to initialize the worker pool using the + necessary shared memory resources. Simply using the + ``multiprocessing.Pool`` constructor will not work with our class + methods. .. WARNING:: - Failure to call :meth:`shutdown_worker_pool` may result in a memory - leak, since shared memory resources outlive the process that created - them. + Failure to call :meth:`shutdown_worker_pool` may result in a memory + leak, since shared memory resources outlive the process that created + them. """ try: set_start_method('fork') From 4959ff7c7f0be31d023de48eeb2f86eef854a63d Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Sat, 1 May 2021 16:49:16 -0400 Subject: [PATCH 065/632] KSHandler now a cdef class, some minor touch ups --- src/sage/combinat/root_system/f_matrix.py | 10 +-- .../fast_parallel_fusion_ring_braid_repn.pyx | 6 ++ .../combinat/root_system/poly_tup_engine.pyx | 3 +- .../combinat/root_system/shm_managers.pyx | 79 ++++++++++++------- 4 files changed, 64 insertions(+), 34 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 10335cd49b3..f48901a8190 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -1158,7 +1158,7 @@ def _checkpoint(self, do_chkpt, status, verbose=True): sage: f.ideal_basis.sort(key=poly_tup_sortkey) sage: f._fvars = {sextuple : poly_to_tup(rhs) for sextuple, rhs in f._fvars.items()} sage: f._triangular_elim(verbose=False) - sage: f._update_reduction_params(children_need_update=False) + sage: f._update_reduction_params() sage: f._checkpoint(do_chkpt=True,status=2) Checkpoint 2 reached! sage: del f @@ -1189,7 +1189,7 @@ def _checkpoint(self, do_chkpt, status, verbose=True): sage: f.ideal_basis.sort(key=poly_tup_sortkey) sage: f._fvars = {sextuple : poly_to_tup(rhs) for sextuple, rhs in f._fvars.items()} sage: f._triangular_elim(verbose=False) - sage: f._update_reduction_params(children_need_update=False) + sage: f._update_reduction_params() sage: f.get_defining_equations('pentagons',output=False) sage: f.ideal_basis.sort(key=poly_tup_sortkey) sage: f._triangular_elim(verbose=False) @@ -1233,7 +1233,7 @@ def _restore_state(self,filename): sage: f.ideal_basis.sort(key=poly_tup_sortkey) sage: f._fvars = {sextuple : poly_to_tup(rhs) for sextuple, rhs in f._fvars.items()} sage: f._triangular_elim(verbose=False) - sage: f._update_reduction_params(children_need_update=False) + sage: f._update_reduction_params() sage: fvars = f._fvars sage: ib = f.ideal_basis sage: solved = f._solved @@ -1580,7 +1580,7 @@ def _tup_to_fpoly(self,eq_tup): """ return _tup_to_poly(eq_tup,parent=self._poly_ring) - def _update_reduction_params(self,eqns=None,worker_pool=None,children_need_update=False): + def _update_reduction_params(self,eqns=None): r""" Update reduction parameters that are solver state attributes. @@ -1652,7 +1652,7 @@ def _triangular_elim(self,eqns=None,worker_pool=None,verbose=True): _backward_subs(self) #Compute new reduction params, send to child processes if any, and update eqns - self._update_reduction_params(eqns=eqns,worker_pool=worker_pool,children_need_update=len(eqns)>self.mp_thresh) + self._update_reduction_params(eqns=eqns) # n = len(eqns) // worker_pool._processes ** 2 + 1 if worker_pool is not None else len(eqns) # eqns = [eqns[i:i+n] for i in range(0,len(eqns),n)] if worker_pool is not None and len(eqns) > self.mp_thresh: diff --git a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx index a685dc1d48f..a9664c79139 100644 --- a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx @@ -77,6 +77,9 @@ cdef odd_one_out_ij_cache = dict() cdef mid_sig_ij_cache = dict() cdef cached_mid_sig_ij(fusion_ring,row,col,a,b): + """ + Cached version of :meth:`mid_sig_ij`. + """ if (row,col,a,b) in mid_sig_ij_cache: return mid_sig_ij_cache[row,col,a,b] entry = mid_sig_ij(fusion_ring,row,col,a,b) @@ -84,6 +87,9 @@ cdef cached_mid_sig_ij(fusion_ring,row,col,a,b): return entry cdef cached_odd_one_out_ij(fusion_ring,xi,xj,a,b): + """ + Cached version of :meth:`odd_one_out_ij`. + """ if (xi,xj,a,b) in odd_one_out_ij_cache: return odd_one_out_ij_cache[xi,xj,a,b] entry = odd_one_out_ij(fusion_ring,xi,xj,a,b) diff --git a/src/sage/combinat/root_system/poly_tup_engine.pyx b/src/sage/combinat/root_system/poly_tup_engine.pyx index e4144eb5bfb..ad3245a287f 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pyx +++ b/src/sage/combinat/root_system/poly_tup_engine.pyx @@ -383,7 +383,8 @@ cdef dict subs_squares(dict eq_dict, known_sq): new_e = dict() for idx, power in exp.sparse_iter(): if idx in known_sq: - coeff *= known_sq[idx] ** (power // 2) + # coeff *= known_sq[idx] ** (power // 2) + coeff *= pow(known_sq[idx], power // 2) #New power is 1 if power is odd if power & True: new_e[idx] = 1 diff --git a/src/sage/combinat/root_system/shm_managers.pyx b/src/sage/combinat/root_system/shm_managers.pyx index 253dc8e466a..e617881b02b 100644 --- a/src/sage/combinat/root_system/shm_managers.pyx +++ b/src/sage/combinat/root_system/shm_managers.pyx @@ -8,21 +8,25 @@ Shared memory managers for F-symbol attributes # https://www.gnu.org/licenses/ # **************************************************************************** +cimport cython from cysignals.memory cimport sig_malloc cimport numpy as np from sage.rings.polynomial.polydict cimport ETuple from sage.rings.number_field.number_field_base cimport NumberField +# from sage.rings.number_field.number_field_element cimport NumberFieldElement_absolute from multiprocessing import shared_memory from sage.misc.cachefunc import cached_method import numpy as np -class KSHandler(): - # cdef field - # cdef np.ndarray ks_dat - # cdef public shm +cdef class KSHandler(): + cdef list obj_cache + cdef np.ndarray ks_dat + cdef NumberField field + cdef public shm def __init__(self, factory, name=None): + cdef int n, d self.field = factory._field n = factory._poly_ring.ngens() d = self.field.degree() @@ -31,33 +35,45 @@ class KSHandler(): ('nums','i8',(d,)), ('denoms','u8',(d,)) ]) + self.obj_cache = [None]*n if name is None: self.shm = shared_memory.SharedMemory(create=True,size=n*ks_t.itemsize) self.ks_dat = np.ndarray((n,),dtype=ks_t,buffer=self.shm.buf) self.ks_dat['known'] = np.zeros((n,1),dtype='bool') - self.ks_dat['nums'] = np.zeros((n,d),dtype='i4') - self.ks_dat['denoms'] = np.ones((n,d),dtype='u4') + self.ks_dat['nums'] = np.zeros((n,d),dtype='i8') + self.ks_dat['denoms'] = np.ones((n,d),dtype='u8') else: self.shm = shared_memory.SharedMemory(name=name) self.ks_dat = np.ndarray((n,),dtype=ks_t,buffer=self.shm.buf) - @cached_method + @cython.nonecheck(False) + @cython.wraparound(False) def __getitem__(self, idx): if not self.ks_dat['known'][idx]: raise KeyError('Index {} does not correspond to a known square'.format(idx)) + if self.obj_cache[idx] is not None: + return self.obj_cache[idx] cdef unsigned int i, d cdef list rat = list() d = self.field.degree() for i in range(d): - rat.append(self.ks_dat['nums'][idx][i] / self.ks_dat['denoms'][idx][i]) - return self.field(rat) + rat.append(self.ks_dat['nums'][idx,i] / self.ks_dat['denoms'][idx,i]) + cyc_coeff = self.field(rat) + self.obj_cache[idx] = cyc_coeff + return cyc_coeff + @cython.nonecheck(False) + @cython.wraparound(False) def __setitem__(self, idx, rhs): self.ks_dat['known'][idx] = True cdef unsigned int i + cdef long long num + cdef unsigned long long denom for i in range(len(rhs)): - self.ks_dat['nums'][idx][i] = rhs[i].numerator() - self.ks_dat['denoms'][idx][i] = rhs[i].denominator() + num = (rhs[i].numerator()) + denom = (rhs[i].denominator()) + self.ks_dat['nums'][idx,i] = num + self.ks_dat['denoms'][idx,i] = denom def __contains__(self, idx): return self.ks_dat['known'][idx] @@ -155,8 +171,7 @@ cdef class FvarsHandler(): fx5^5 sage: fvars.shm.unlink() """ - n = len(factory._fvars) - d = factory._field.degree() + cdef int d = factory._field.degree() self.obj_cache = dict() self.fvars_t = np.dtype([ ('modified','bool',(1,)), @@ -169,12 +184,14 @@ cdef class FvarsHandler(): self.ngens = factory._poly_ring.ngens() self.field = factory._field if name is None: - self.shm = shared_memory.SharedMemory(create=True,size=n*self.fvars_t.itemsize) - self.fvars = np.ndarray((n,),dtype=self.fvars_t,buffer=self.shm.buf) + self.shm = shared_memory.SharedMemory(create=True,size=self.ngens*self.fvars_t.itemsize) + self.fvars = np.ndarray((self.ngens,),dtype=self.fvars_t,buffer=self.shm.buf) else: self.shm = shared_memory.SharedMemory(name=name) - self.fvars = np.ndarray((n,),dtype=self.fvars_t,buffer=self.shm.buf) + self.fvars = np.ndarray((self.ngens,),dtype=self.fvars_t,buffer=self.shm.buf) + @cython.nonecheck(False) + @cython.wraparound(False) def __setitem__(self, sextuple, fvar): """ Given a sextuple of labels and a tuple of ``(ETuple, cyc_coeff)`` pairs, @@ -201,7 +218,9 @@ cdef class FvarsHandler(): True sage: fvars.shm.unlink() """ - cdef int cum, denom, i, j, k, idx + cdef unsigned int cum, i, j, k, idx + cdef unsigned long denom + cdef long c cdef ETuple exp idx = self.sext_to_idx[sextuple] #Clear entry before inserting @@ -211,19 +230,22 @@ cdef class FvarsHandler(): for exp, coeff in fvar: #Handle constant coefficient if exp._nonzero > 0: - self.fvars['ticks'][idx][i] = exp._nonzero + self.fvars['ticks'][idx,i] = exp._nonzero else: - self.fvars['ticks'][idx][i] = -1 + self.fvars['ticks'][idx,i] = -1 for j in range(2*exp._nonzero): - self.fvars['exp_data'][idx][cum] = exp._data[j] + self.fvars['exp_data'][idx,cum] = exp._data[j] cum += 1 denom = coeff.denominator() - self.fvars['coeff_denom'][idx][i] = denom - for k, c in enumerate(coeff._coefficients()): - self.fvars['coeff_nums'][idx][i,k] = c * denom + self.fvars['coeff_denom'][idx,i] = denom + for k, r in enumerate(coeff._coefficients()): + c = r * denom + self.fvars['coeff_nums'][idx,i,k] = c i += 1 self.fvars['modified'][idx] = True + @cython.nonecheck(False) + @cython.wraparound(False) def __getitem__(self, sextuple): """ Retrieve a record from the shared memory data structure by @@ -269,7 +291,8 @@ cdef class FvarsHandler(): else: return self.obj_cache[idx] cdef ETuple e = ETuple({}, self.ngens) - cdef unsigned int cum, d, i, j, nnz + cdef unsigned int cum, i, j, nnz + cdef unsigned long d cdef list poly_tup = list() cdef tuple ret cum = 0 @@ -277,16 +300,16 @@ cdef class FvarsHandler(): #Construct new ETuple for each monomial exp = e._new() #Handle constant coeff - nnz = self.fvars['ticks'][idx][i] if self.fvars['ticks'][idx][i] < 255 else 0 + nnz = self.fvars['ticks'][idx,i] if self.fvars['ticks'][idx,i] < 255 else 0 exp._nonzero = nnz exp._data = sig_malloc(sizeof(int)*nnz*2) for j in range(2*nnz): - exp._data[j] = self.fvars['exp_data'][idx][cum] + exp._data[j] = self.fvars['exp_data'][idx,cum] cum += 1 #Construct cyclotomic field coefficient - d = self.fvars['coeff_denom'][idx][i] - cyc_coeff = self.field([num / d for num in self.fvars['coeff_nums'][idx][i]]) + d = self.fvars['coeff_denom'][idx,i] + cyc_coeff = self.field([num / d for num in self.fvars['coeff_nums'][idx,i]]) poly_tup.append((exp, cyc_coeff)) ret = tuple(poly_tup) From f5ae50158920e83d7d54aeac1aaa5c94dc0acbeb Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Sun, 2 May 2021 11:59:34 -0400 Subject: [PATCH 066/632] new constructor and pickling capability for KSHandler --- src/sage/combinat/root_system/f_matrix.py | 22 ++++--- .../combinat/root_system/poly_tup_engine.pxd | 2 + .../combinat/root_system/shm_managers.pyx | 57 +++++++++++++++++-- 3 files changed, 66 insertions(+), 15 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index f48901a8190..0101471d3d7 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -394,9 +394,11 @@ def _reset_solver_state(self): self._chkpt_status = -1 self.clear_vars() self.clear_equations() - self._var_degs = [0]*self._poly_ring.ngens() + n = self._poly_ring.ngens() + self._var_degs = [0]*n self._kp = dict() - self._ks = dict() + # self._ks = dict() + self._ks = KSHandler(n,self._field) self._nnz = self._get_known_nonz() #Clear relevant caches @@ -1332,12 +1334,13 @@ def get_worker_pool(self,processes=None): s_name = self._solved.shm.name self._var_degs = shared_memory.ShareableList(self._var_degs) vd_name = self._var_degs.shm.name - ks = KSHandler(self) - for idx, val in self._ks.items(): - if not isinstance(val, tuple): - val = val._coefficients() - ks[idx] = val - self._ks = ks + n = self._poly_ring.ngens() + self._ks = KSHandler(n,self._field,use_mp=True,init_data=self._ks) + # for idx, val in self._ks.items(): + # if not isinstance(val, tuple): + # val = val._coefficients() + # ks[idx] = val + # self._ks = ks ks_names = self._ks.shm.name self._shared_fvars = FvarsHandler(self) for sextuple, fvar in self._fvars.items(): @@ -1356,8 +1359,9 @@ def init(fmats_id, solved_name, vd_name, ks_names, fvar_names): fmats_obj = cast(fmats_id, py_object).value fmats_obj._solved = shared_memory.ShareableList(name=solved_name) fmats_obj._var_degs = shared_memory.ShareableList(name=vd_name) - fmats_obj._ks = KSHandler(fmats_obj,name=ks_names) fmats_obj._fvars = FvarsHandler(fmats_obj,name=fvar_names) + n = fmats_obj._poly_ring.ngens() + fmats_obj._ks = KSHandler(n,fmats_obj._field,name=ks_names) pool = Pool(processes=n,initializer=init,initargs=args) return pool diff --git a/src/sage/combinat/root_system/poly_tup_engine.pxd b/src/sage/combinat/root_system/poly_tup_engine.pxd index 5d9987f5a9c..349f9e7be43 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pxd +++ b/src/sage/combinat/root_system/poly_tup_engine.pxd @@ -2,6 +2,8 @@ from sage.rings.number_field.number_field_element cimport NumberFieldElement_abs from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular, MPolynomialRing_libsingular from sage.rings.polynomial.polydict cimport ETuple +# from sage.combinat.root_system.shm_managers cimport KSHandler + cpdef tuple poly_to_tup(MPolynomial_libsingular poly) cpdef MPolynomial_libsingular _tup_to_poly(tuple eq_tup, MPolynomialRing_libsingular parent) cpdef tuple resize(tuple eq_tup, dict idx_map, int nvars) diff --git a/src/sage/combinat/root_system/shm_managers.pyx b/src/sage/combinat/root_system/shm_managers.pyx index e617881b02b..06829a46d83 100644 --- a/src/sage/combinat/root_system/shm_managers.pyx +++ b/src/sage/combinat/root_system/shm_managers.pyx @@ -25,10 +25,10 @@ cdef class KSHandler(): cdef NumberField field cdef public shm - def __init__(self, factory, name=None): + def __init__(self, n_slots, field, use_mp=False, init_data={}, name=None): cdef int n, d - self.field = factory._field - n = factory._poly_ring.ngens() + self.field = field + n = n_slots d = self.field.degree() ks_t = np.dtype([ ('known', 'bool', (1,)), @@ -37,14 +37,20 @@ cdef class KSHandler(): ]) self.obj_cache = [None]*n if name is None: - self.shm = shared_memory.SharedMemory(create=True,size=n*ks_t.itemsize) - self.ks_dat = np.ndarray((n,),dtype=ks_t,buffer=self.shm.buf) + if use_mp: + self.shm = shared_memory.SharedMemory(create=True,size=n*ks_t.itemsize) + self.ks_dat = np.ndarray((n,),dtype=ks_t,buffer=self.shm.buf) + else: + self.ks_dat = np.ndarray((n,),dtype=ks_t) self.ks_dat['known'] = np.zeros((n,1),dtype='bool') self.ks_dat['nums'] = np.zeros((n,d),dtype='i8') self.ks_dat['denoms'] = np.ones((n,d),dtype='u8') else: self.shm = shared_memory.SharedMemory(name=name) self.ks_dat = np.ndarray((n,),dtype=ks_t,buffer=self.shm.buf) + #Populate initializer data + for idx, sq in init_data.items(): + self[idx] = sq @cython.nonecheck(False) @cython.wraparound(False) @@ -69,6 +75,8 @@ cdef class KSHandler(): cdef unsigned int i cdef long long num cdef unsigned long long denom + if not isinstance(rhs, list): + rhs = rhs._coefficients() for i in range(len(rhs)): num = (rhs[i].numerator()) denom = (rhs[i].denominator()) @@ -89,7 +97,28 @@ cdef class KSHandler(): """ Test for equality. """ - return np.all(self.ks_dat == other.ks_dat) + ret = True + for idx, sq in self.items(): + ret &= other[idx] == sq + # return np.all(self.ks_dat == other.ks_dat) + return ret + + def __reduce__(self): + """ + Provide pickling / unpickling support for ``self.`` + + TESTS:: + + sage: f = FMatrix(FusionRing("A3",1)) + sage: f._reset_solver_state() + sage: loads(dumps(f._ks)) == f._ks + True + sage: f.find_orthogonal_solution(verbose=False) #long time + sage: loads(dumps(f._ks)) == f._ks + True + """ + d = {i: sq for i, sq in self.items()} + return make_KSHandler, (self.ks_dat.size,self.field,d) def items(self): cdef unsigned int i @@ -97,6 +126,22 @@ cdef class KSHandler(): if self.ks_dat['known'][i]: yield i, self[i] +def make_KSHandler(n_slots,field,init_data): + """ + Provide pickling / unpickling support for ``self.`` + + TESTS:: + + sage: f = FMatrix(FusionRing("B4",1)) + sage: f._reset_solver_state() + sage: loads(dumps(f._ks)) == f._ks + True + sage: f.find_orthogonal_solution(verbose=False) #long time + sage: loads(dumps(f._ks)) == f._ks + True + """ + return KSHandler(n_slots,field,init_data=init_data) + cdef class FvarsHandler(): cdef dict sext_to_idx, obj_cache cdef unsigned int ngens From c27b3d703ff07771b847dd4ffd01227be9865cbd Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Sun, 2 May 2021 13:37:14 -0400 Subject: [PATCH 067/632] removed _get_known_sq method, cdef attributes for KSHandler --- src/sage/combinat/root_system/f_matrix.py | 52 +----------- .../combinat/root_system/poly_tup_engine.pxd | 9 +- .../combinat/root_system/poly_tup_engine.pyx | 31 ++----- .../combinat/root_system/shm_managers.pxd | 22 +++++ .../combinat/root_system/shm_managers.pyx | 85 +++++++++++++------ 5 files changed, 94 insertions(+), 105 deletions(-) create mode 100644 src/sage/combinat/root_system/shm_managers.pxd diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 0101471d3d7..ca2bd9323c3 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -35,7 +35,6 @@ poly_to_tup, _tup_to_poly, tup_to_univ_poly, _unflatten_coeffs, poly_tup_sortkey, - tup_fixes_sq, resize, ) from sage.combinat.root_system.shm_managers import KSHandler, FvarsHandler @@ -397,7 +396,6 @@ def _reset_solver_state(self): n = self._poly_ring.ngens() self._var_degs = [0]*n self._kp = dict() - # self._ks = dict() self._ks = KSHandler(n,self._field) self._nnz = self._get_known_nonz() @@ -908,49 +906,6 @@ def _get_known_vals(self): """ return {i: self._fvars[s] for i, s in self._idx_to_sextuple.items() if self._solved[i]} - def _get_known_sq(self,eqns=None): - r""" - Update ```self``'s dictionary of known squares. Keys are variable - indices and corresponding values are the squares. - - EXAMPLES:: - - sage: f = FMatrix(FusionRing("B5",1)) - sage: f._reset_solver_state() - sage: len(f._ks) == 0 - True - sage: f.get_orthogonality_constraints() - [fx0^2 - 1, - fx1^2 - 1, - fx2^2 - 1, - fx3^2 - 1, - fx4^2 - 1, - fx5^2 - 1, - fx6^2 - 1, - fx7^2 - 1, - fx8^2 - 1, - fx9^2 - 1, - fx10^2 + fx12^2 - 1, - fx10*fx11 + fx12*fx13, - fx10*fx11 + fx12*fx13, - fx11^2 + fx13^2 - 1] - sage: f.get_orthogonality_constraints(output=False) - sage: f._get_known_sq() - sage: len(f._ks) == 10 - True - """ - if eqns is None: - eqns = self.ideal_basis - for eq_tup in eqns: - if tup_fixes_sq(eq_tup): - #Handle mp and single process cases - if isinstance(self._ks, dict): - rhs = -self._field(list(eq_tup[-1][1])) - else: - rhs = [-v for v in eq_tup[-1][1]] - self._ks[variables(eq_tup)[0]] = rhs - - def _get_known_nonz(self): r""" Construct an ETuple indicating positions of known nonzero variables. @@ -1336,11 +1291,6 @@ def get_worker_pool(self,processes=None): vd_name = self._var_degs.shm.name n = self._poly_ring.ngens() self._ks = KSHandler(n,self._field,use_mp=True,init_data=self._ks) - # for idx, val in self._ks.items(): - # if not isinstance(val, tuple): - # val = val._coefficients() - # ks[idx] = val - # self._ks = ks ks_names = self._ks.shm.name self._shared_fvars = FvarsHandler(self) for sextuple, fvar in self._fvars.items(): @@ -1607,7 +1557,7 @@ def _update_reduction_params(self,eqns=None): """ if eqns is None: eqns = self.ideal_basis - self._get_known_sq(eqns) + self._ks.update(eqns) degs = get_variables_degrees(eqns) if degs: for i, d in enumerate(degs): diff --git a/src/sage/combinat/root_system/poly_tup_engine.pxd b/src/sage/combinat/root_system/poly_tup_engine.pxd index 349f9e7be43..5526a71ff22 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pxd +++ b/src/sage/combinat/root_system/poly_tup_engine.pxd @@ -1,9 +1,8 @@ +from sage.combinat.root_system.shm_managers cimport KSHandler from sage.rings.number_field.number_field_element cimport NumberFieldElement_absolute from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular, MPolynomialRing_libsingular from sage.rings.polynomial.polydict cimport ETuple -# from sage.combinat.root_system.shm_managers cimport KSHandler - cpdef tuple poly_to_tup(MPolynomial_libsingular poly) cpdef MPolynomial_libsingular _tup_to_poly(tuple eq_tup, MPolynomialRing_libsingular parent) cpdef tuple resize(tuple eq_tup, dict idx_map, int nvars) @@ -11,8 +10,10 @@ cpdef list get_variables_degrees(list eqns) cpdef list variables(tuple eq_tup) cpdef constant_coeff(tuple eq_tup) cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map) -cpdef bint tup_fixes_sq(tuple eq_tup) -cdef dict subs_squares(dict eq_dict, known_sq) +# cpdef bint tup_fixes_sq(tuple eq_tup) +cdef bint tup_fixes_sq(tuple eq_tup) +# cdef dict subs_squares(dict eq_dict, known_sq) +cdef dict subs_squares(dict eq_dict, KSHandler known_sq) cpdef dict compute_known_powers(max_degs, dict val_dict, one) cdef dict subs(tuple poly_tup, dict known_powers, one) cpdef tup_to_univ_poly(tuple eq_tup, univ_poly_ring) diff --git a/src/sage/combinat/root_system/poly_tup_engine.pyx b/src/sage/combinat/root_system/poly_tup_engine.pyx index ad3245a287f..8db9e9f017e 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pyx +++ b/src/sage/combinat/root_system/poly_tup_engine.pyx @@ -319,34 +319,16 @@ cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map): new_tup.append((exp, coeff_map(coeff))) return tuple(new_tup) -cpdef inline bint tup_fixes_sq(tuple eq_tup): +cdef inline bint tup_fixes_sq(tuple eq_tup): r""" Determine if given equation fixes the square of a variable. An equation fixes the sq of a variable if it is of the form `a*x^2 + c` for *nonzero* constants `a`, `c`. - - EXAMPLES:: - - sage: from sage.combinat.root_system.poly_tup_engine import tup_fixes_sq - sage: R. = PolynomialRing(QQ) - sage: sq_fixer = 3*z**2 - 1/8 - sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup - sage: tup_fixes_sq(poly_to_tup(sq_fixer)) - True - sage: tup_fixes_sq(poly_to_tup(y**3 + 2)) - False - sage: tup_fixes_sq(poly_to_tup(x*y + 2)) - False - sage: tup_fixes_sq(poly_to_tup(x**2 + y**2 + 2)) - False """ - #Make this faster by combining two conditions into one... don't create temp variables - # return len(eq_tup) == 2 and len(variables(eq_tup)) == 1 and eq_tup[0][0].nonzero_values() == [2] - # return len(eq_tup) == 2 and eq_tup[0][0].emax(eq_tup[1][0]).nonzero_values() == [2] if len(eq_tup) != 2: return False - #In order to access _attributes, we must cdef ETuple + #To access _attributes, we must cdef ETuple cdef ETuple lm = eq_tup[0][0] if lm._nonzero != 1 or lm._data[1] != 2: return False @@ -359,7 +341,8 @@ cpdef inline bint tup_fixes_sq(tuple eq_tup): ### Simplification ### ###################### -cdef dict subs_squares(dict eq_dict, known_sq): +# cdef dict subs_squares(dict eq_dict, known_sq): +cdef dict subs_squares(dict eq_dict, KSHandler known_sq): r""" Substitute for known squares into a given polynomial. @@ -382,9 +365,11 @@ cdef dict subs_squares(dict eq_dict, known_sq): for exp, coeff in eq_dict.items(): new_e = dict() for idx, power in exp.sparse_iter(): - if idx in known_sq: + # if idx in known_sq: + if known_sq.contains(idx): # coeff *= known_sq[idx] ** (power // 2) - coeff *= pow(known_sq[idx], power // 2) + # coeff *= pow(known_sq[idx], power // 2) + coeff *= pow(known_sq.get(idx), power // 2) #New power is 1 if power is odd if power & True: new_e[idx] = 1 diff --git a/src/sage/combinat/root_system/shm_managers.pxd b/src/sage/combinat/root_system/shm_managers.pxd new file mode 100644 index 00000000000..e3d4f44ad4a --- /dev/null +++ b/src/sage/combinat/root_system/shm_managers.pxd @@ -0,0 +1,22 @@ +cimport numpy as np +import numpy as np +from sage.rings.number_field.number_field_base cimport NumberField + +cdef class KSHandler: + cdef list obj_cache + cdef np.ndarray ks_dat + cdef NumberField field + cdef public shm + + cdef bint contains(self, int idx) + cdef get(self, int idx) + cdef setitem(self, int idx, rhs) + cpdef update(self, list eqns) + +cdef class FvarsHandler: + cdef dict sext_to_idx, obj_cache + cdef unsigned int ngens + cdef fvars_t + cdef NumberField field + cdef public np.ndarray fvars + cdef public shm diff --git a/src/sage/combinat/root_system/shm_managers.pyx b/src/sage/combinat/root_system/shm_managers.pyx index 06829a46d83..e0eebf3f50d 100644 --- a/src/sage/combinat/root_system/shm_managers.pyx +++ b/src/sage/combinat/root_system/shm_managers.pyx @@ -11,20 +11,14 @@ Shared memory managers for F-symbol attributes cimport cython from cysignals.memory cimport sig_malloc cimport numpy as np +from sage.combinat.root_system.poly_tup_engine cimport tup_fixes_sq, variables from sage.rings.polynomial.polydict cimport ETuple -from sage.rings.number_field.number_field_base cimport NumberField # from sage.rings.number_field.number_field_element cimport NumberFieldElement_absolute from multiprocessing import shared_memory -from sage.misc.cachefunc import cached_method import numpy as np -cdef class KSHandler(): - cdef list obj_cache - cdef np.ndarray ks_dat - cdef NumberField field - cdef public shm - +cdef class KSHandler: def __init__(self, n_slots, field, use_mp=False, init_data={}, name=None): cdef int n, d self.field = field @@ -50,11 +44,11 @@ cdef class KSHandler(): self.ks_dat = np.ndarray((n,),dtype=ks_t,buffer=self.shm.buf) #Populate initializer data for idx, sq in init_data.items(): - self[idx] = sq + self.setitem(idx,sq) @cython.nonecheck(False) @cython.wraparound(False) - def __getitem__(self, idx): + cdef get(self, int idx): if not self.ks_dat['known'][idx]: raise KeyError('Index {} does not correspond to a known square'.format(idx)) if self.obj_cache[idx] is not None: @@ -68,13 +62,58 @@ cdef class KSHandler(): self.obj_cache[idx] = cyc_coeff return cyc_coeff + cpdef update(self, list eqns): + r""" + Update ```self``'s ``shared_memory``-backed dictionary of known + squares. Keys are variable indices and corresponding values + are the squares. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("B5",1)) + sage: f._reset_solver_state() + sage: len(f._ks) == 0 + True + sage: f.get_orthogonality_constraints() + [fx0^2 - 1, + fx1^2 - 1, + fx2^2 - 1, + fx3^2 - 1, + fx4^2 - 1, + fx5^2 - 1, + fx6^2 - 1, + fx7^2 - 1, + fx8^2 - 1, + fx9^2 - 1, + fx10^2 + fx12^2 - 1, + fx10*fx11 + fx12*fx13, + fx10*fx11 + fx12*fx13, + fx11^2 + fx13^2 - 1] + sage: f.get_orthogonality_constraints(output=False) + sage: f._ks.update(f.ideal_basis) + sage: len(f._ks) == 10 + True + """ + cdef unsigned int i, idx + cdef ETuple lm + cdef list rhs + cdef tuple eq_tup + for i in range(len(eqns)): + eq_tup = eqns[i] + if tup_fixes_sq(eq_tup): + rhs = [-v for v in eq_tup[-1][1]] + #eq_tup is guaranteed univariate, so we extract variable idx from lm + lm = eq_tup[0][0] + idx = lm._data[0] + self.setitem(idx, rhs) + @cython.nonecheck(False) @cython.wraparound(False) - def __setitem__(self, idx, rhs): - self.ks_dat['known'][idx] = True + cdef setitem(self, int idx, rhs): cdef unsigned int i cdef long long num cdef unsigned long long denom + self.ks_dat['known'][idx] = True if not isinstance(rhs, list): rhs = rhs._coefficients() for i in range(len(rhs)): @@ -83,8 +122,8 @@ cdef class KSHandler(): self.ks_dat['nums'][idx,i] = num self.ks_dat['denoms'][idx,i] = denom - def __contains__(self, idx): - return self.ks_dat['known'][idx] + cdef bint contains(self, int idx): + return self.ks_dat[idx]['known'] def __len__(self): """ @@ -93,14 +132,13 @@ cdef class KSHandler(): """ return self.ks_dat['known'].sum() - def __eq__(self, other): + def __eq__(self, KSHandler other): """ Test for equality. """ ret = True for idx, sq in self.items(): - ret &= other[idx] == sq - # return np.all(self.ks_dat == other.ks_dat) + ret &= other.get(idx) == sq return ret def __reduce__(self): @@ -124,11 +162,11 @@ cdef class KSHandler(): cdef unsigned int i for i in range(self.ks_dat.size): if self.ks_dat['known'][i]: - yield i, self[i] + yield i, self.get(i) def make_KSHandler(n_slots,field,init_data): """ - Provide pickling / unpickling support for ``self.`` + Provide pickling / unpickling support for :class:`KSHandler`. TESTS:: @@ -142,14 +180,7 @@ def make_KSHandler(n_slots,field,init_data): """ return KSHandler(n_slots,field,init_data=init_data) -cdef class FvarsHandler(): - cdef dict sext_to_idx, obj_cache - cdef unsigned int ngens - cdef fvars_t - cdef NumberField field - cdef public np.ndarray fvars - cdef public shm - +cdef class FvarsHandler: def __init__(self, factory, name=None, max_terms=20): """ Return a shared memory backed dict-like structure to manage the From f65d76595f49dd8f0ae7e6f9f12a4ff2fa0146a4 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Sun, 2 May 2021 15:38:54 -0400 Subject: [PATCH 068/632] documented KSHandler --- src/sage/combinat/root_system/f_matrix.py | 13 +- .../combinat/root_system/shm_managers.pxd | 3 +- .../combinat/root_system/shm_managers.pyx | 152 ++++++++++++++++-- 3 files changed, 146 insertions(+), 22 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index ca2bd9323c3..1920e53db76 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -376,8 +376,9 @@ def _reset_solver_state(self): True sage: len(f.ideal_basis) == 0 True - sage: len(f._ks) == 0 - True + sage: for k, v in f._ks.items(): + ....: k + ....: sage: len(f._nnz.nonzero_positions()) == 1 True sage: all(len(x.q_dimension.cache) == 0 for x in f.FR().basis()) @@ -1323,10 +1324,10 @@ def shutdown_worker_pool(self,pool): .. WARNING:: - Failure to call this method after using :meth:`get_worker_pool` - to create a process pool may result in a memory - leak, since shared memory resources outlive the process that created - them. + Failure to call this method after using :meth:`get_worker_pool` + to create a process pool may result in a memory + leak, since shared memory resources outlive the process that created + them. EXAMPLES:: diff --git a/src/sage/combinat/root_system/shm_managers.pxd b/src/sage/combinat/root_system/shm_managers.pxd index e3d4f44ad4a..be13623646f 100644 --- a/src/sage/combinat/root_system/shm_managers.pxd +++ b/src/sage/combinat/root_system/shm_managers.pxd @@ -1,6 +1,7 @@ cimport numpy as np import numpy as np from sage.rings.number_field.number_field_base cimport NumberField +from sage.rings.number_field.number_field_element cimport NumberFieldElement_absolute cdef class KSHandler: cdef list obj_cache @@ -9,7 +10,7 @@ cdef class KSHandler: cdef public shm cdef bint contains(self, int idx) - cdef get(self, int idx) + cdef NumberFieldElement_absolute get(self, int idx) cdef setitem(self, int idx, rhs) cpdef update(self, list eqns) diff --git a/src/sage/combinat/root_system/shm_managers.pyx b/src/sage/combinat/root_system/shm_managers.pyx index e0eebf3f50d..35fd8cf3f73 100644 --- a/src/sage/combinat/root_system/shm_managers.pyx +++ b/src/sage/combinat/root_system/shm_managers.pyx @@ -11,14 +11,76 @@ Shared memory managers for F-symbol attributes cimport cython from cysignals.memory cimport sig_malloc cimport numpy as np -from sage.combinat.root_system.poly_tup_engine cimport tup_fixes_sq, variables +from sage.combinat.root_system.poly_tup_engine cimport tup_fixes_sq from sage.rings.polynomial.polydict cimport ETuple -# from sage.rings.number_field.number_field_element cimport NumberFieldElement_absolute from multiprocessing import shared_memory import numpy as np cdef class KSHandler: + """ + Return a shared memory backed dict-like structure to manage the + ``_ks`` attribute of an F-matrix factory object. + + This structure implements a representation of the known squares dictionary + using a structured NumPy array backed by a contiguous shared memory + object. + + The structure mimics a dictionary of ``(idx, known_sq)`` pairs. Each + integer index corresponds to a variable and each ``known_sq`` is an + element of the F-matrix factory's base cyclotomic field. + + Each cyclotomic coefficient is stored as a list of numerators and a + list of denominators representing the rational coefficients. The + structured array also maintains ``known`` attribute that indicates + whether the structure contains an entry corresponding to the given index. + + The parent process should construct this object without a + ``name`` attribute. Children processes use the ``name`` attribute, + accessed via ``self.shm.name`` to attach to the shared memory block. + + INPUT: + + - ``n_slots`` -- The total number of F-symbols. + - ``field`` -- F-matrix factory's base cyclotomic field. + - ``use_mp`` -- a boolean indicating whether to construct a shared + memory block to back ``self``. + - ``name`` -- the name of a shared memory object + (used by child processes for attaching). + - ``init_data`` -- a dictionary or :class:`KSHandler` object containing + known squares for initialization, e.g. from a solver checkpoint. + + .. NOTE:: + + To properly dispose of shared memory resources, + ``self.shm.unlink()`` must be called before exiting. + + .. WARNING:: + + This structure does *not* cannot modify an entry that + has already been set. + + EXAMPLES:: + + sage: from sage.combinat.root_system.shm_managers import KSHandler + sage: #Create shared data structure + sage: f = FMatrix(FusionRing("A1",2), inject_variables=True) + creating variables fx1..fx14 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 + sage: n = f._poly_ring.ngens() + sage: ks = KSHandler(n,f._field,use_mp=True) + sage: #In the same shell or in a different shell, attach to fvars + sage: ks2 = KSHandler(n,f._field,name=ks.shm.name) + sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: eqns = [fx1**2 - 4, fx3**2 + f._field.gen()**4 - 1/19*f._field.gen()**2] + sage: ks.update([poly_to_tup(p) for p in eqns]) + sage: for idx, sq in ks.items(): + ....: print("Index: {}, square: {}".format(idx, sq)) + ....: + Index: 1, square: 4 + Index: 3, square: -zeta32^4 + 1/19*zeta32^2 + sage: ks.shm.unlink() + """ def __init__(self, n_slots, field, use_mp=False, init_data={}, name=None): cdef int n, d self.field = field @@ -48,13 +110,18 @@ cdef class KSHandler: @cython.nonecheck(False) @cython.wraparound(False) - cdef get(self, int idx): + cdef NumberFieldElement_absolute get(self, int idx): + """ + Retrieve the known square corresponding to the given index, + if it exists. + """ if not self.ks_dat['known'][idx]: raise KeyError('Index {} does not correspond to a known square'.format(idx)) if self.obj_cache[idx] is not None: return self.obj_cache[idx] cdef unsigned int i, d cdef list rat = list() + cdef NumberFieldElement_absolute cyc_coeff d = self.field.degree() for i in range(d): rat.append(self.ks_dat['nums'][idx,i] / self.ks_dat['denoms'][idx,i]) @@ -72,8 +139,9 @@ cdef class KSHandler: sage: f = FMatrix(FusionRing("B5",1)) sage: f._reset_solver_state() - sage: len(f._ks) == 0 - True + sage: for idx, sq in f._ks.items(): + ....: k + ....: sage: f.get_orthogonality_constraints() [fx0^2 - 1, fx1^2 - 1, @@ -91,8 +159,23 @@ cdef class KSHandler: fx11^2 + fx13^2 - 1] sage: f.get_orthogonality_constraints(output=False) sage: f._ks.update(f.ideal_basis) - sage: len(f._ks) == 10 - True + sage: for idx, sq in f._ks.items(): + ....: print(idx, "-->", sq) + ....: + 0 --> 1 + 1 --> 1 + 2 --> 1 + 3 --> 1 + 4 --> 1 + 5 --> 1 + 6 --> 1 + 7 --> 1 + 8 --> 1 + 9 --> 1 + + .. WARNING:: + + This method assumes every polynomial in ``eqns`` is *monic*. """ cdef unsigned int i, idx cdef ETuple lm @@ -110,6 +193,12 @@ cdef class KSHandler: @cython.nonecheck(False) @cython.wraparound(False) cdef setitem(self, int idx, rhs): + """ + Create an entry corresponding to the given index. + + The ``rhs`` parameter may be a cyclotomic coefficient or its + list/tuple representation. + """ cdef unsigned int i cdef long long num cdef unsigned long long denom @@ -123,18 +212,29 @@ cdef class KSHandler: self.ks_dat['denoms'][idx,i] = denom cdef bint contains(self, int idx): - return self.ks_dat[idx]['known'] - - def __len__(self): """ - Compute the number of known squares. - + Determine whether ``self`` contains entry corresponding to given + ``idx``. """ - return self.ks_dat['known'].sum() + return self.ks_dat[idx]['known'] def __eq__(self, KSHandler other): """ Test for equality. + + TESTS:: + + sage: f = FMatrix(FusionRing("C2",2)) + sage: f._reset_solver_state() + sage: f.get_orthogonality_constraints(output=False) + sage: from sage.combinat.root_system.shm_managers import KSHandler + sage: n = f._poly_ring.ngens() + sage: ks = KSHandler(n,f._field,use_mp=True,init_data=f._ks) + sage: #In the same shell or in a different one, attach to shared memory handler + sage: k2 = KSHandler(n,f._field,name=ks.shm.name) + sage: ks == k2 + True + sage: ks.shm.unlink() """ ret = True for idx, sq in self.items(): @@ -159,6 +259,27 @@ cdef class KSHandler: return make_KSHandler, (self.ks_dat.size,self.field,d) def items(self): + """ + Iterate through existing entries using Python dict-style syntax. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("A3",1)) + sage: f._reset_solver_state() + sage: f.get_orthogonality_constraints(output=False) + sage: f._ks.update(f.ideal_basis) + sage: for idx, sq in f._ks.items(): + ....: print("Index: {}, sq: {}".format(idx,sq)) + ....: + Index: 0, sq: 1 + Index: 1, sq: 1 + Index: 2, sq: 1 + Index: 3, sq: 1 + Index: 4, sq: 1 + ... + Index: 25, sq: 1 + Index: 26, sq: 1 + """ cdef unsigned int i for i in range(self.ks_dat.size): if self.ks_dat['known'][i]: @@ -172,10 +293,10 @@ def make_KSHandler(n_slots,field,init_data): sage: f = FMatrix(FusionRing("B4",1)) sage: f._reset_solver_state() - sage: loads(dumps(f._ks)) == f._ks + sage: loads(dumps(f._ks)) == f._ks #indirect doctest True sage: f.find_orthogonal_solution(verbose=False) #long time - sage: loads(dumps(f._ks)) == f._ks + sage: loads(dumps(f._ks)) == f._ks #indirect doctest True """ return KSHandler(n_slots,field,init_data=init_data) @@ -298,6 +419,7 @@ cdef class FvarsHandler: cdef unsigned long denom cdef long c cdef ETuple exp + cdef NumberFieldElement_absolute coeff idx = self.sext_to_idx[sextuple] #Clear entry before inserting self.fvars[idx] = np.zeros((1,), dtype=self.fvars_t) From 651fe2a7633f9c92dd0a1eaa12811f2a3389f174 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Sun, 2 May 2021 16:50:54 -0400 Subject: [PATCH 069/632] pickling support for FvarsHandler... required for checkpointing --- src/sage/combinat/root_system/f_matrix.py | 14 ++--- .../combinat/root_system/poly_tup_engine.pxd | 2 - .../combinat/root_system/shm_managers.pyx | 62 ++++++++++++++++--- 3 files changed, 59 insertions(+), 19 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 1920e53db76..0b4ee2f5d3f 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -1293,12 +1293,12 @@ def get_worker_pool(self,processes=None): n = self._poly_ring.ngens() self._ks = KSHandler(n,self._field,use_mp=True,init_data=self._ks) ks_names = self._ks.shm.name - self._shared_fvars = FvarsHandler(self) - for sextuple, fvar in self._fvars.items(): - if self._chkpt_status < 0: - self._shared_fvars[sextuple] = poly_to_tup(fvar) - else: - self._shared_fvars[sextuple] = fvar + self._shared_fvars = FvarsHandler(n,self._field,self._idx_to_sextuple,init_data=self._fvars) + # for sextuple, fvar in self._fvars.items(): + # if self._chkpt_status < 0: + # self._shared_fvars[sextuple] = poly_to_tup(fvar) + # else: + # self._shared_fvars[sextuple] = fvar fvar_names = self._shared_fvars.shm.name #Initialize worker pool processes args = (id(self), s_name, vd_name, ks_names, fvar_names) @@ -1310,8 +1310,8 @@ def init(fmats_id, solved_name, vd_name, ks_names, fvar_names): fmats_obj = cast(fmats_id, py_object).value fmats_obj._solved = shared_memory.ShareableList(name=solved_name) fmats_obj._var_degs = shared_memory.ShareableList(name=vd_name) - fmats_obj._fvars = FvarsHandler(fmats_obj,name=fvar_names) n = fmats_obj._poly_ring.ngens() + fmats_obj._fvars = FvarsHandler(n,fmats_obj._field,fmats_obj._idx_to_sextuple,name=fvar_names) fmats_obj._ks = KSHandler(n,fmats_obj._field,name=ks_names) pool = Pool(processes=n,initializer=init,initargs=args) diff --git a/src/sage/combinat/root_system/poly_tup_engine.pxd b/src/sage/combinat/root_system/poly_tup_engine.pxd index 5526a71ff22..7c889bce661 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pxd +++ b/src/sage/combinat/root_system/poly_tup_engine.pxd @@ -10,9 +10,7 @@ cpdef list get_variables_degrees(list eqns) cpdef list variables(tuple eq_tup) cpdef constant_coeff(tuple eq_tup) cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map) -# cpdef bint tup_fixes_sq(tuple eq_tup) cdef bint tup_fixes_sq(tuple eq_tup) -# cdef dict subs_squares(dict eq_dict, known_sq) cdef dict subs_squares(dict eq_dict, KSHandler known_sq) cpdef dict compute_known_powers(max_degs, dict val_dict, one) cdef dict subs(tuple poly_tup, dict known_powers, one) diff --git a/src/sage/combinat/root_system/shm_managers.pyx b/src/sage/combinat/root_system/shm_managers.pyx index 35fd8cf3f73..e1c8c553b2d 100644 --- a/src/sage/combinat/root_system/shm_managers.pyx +++ b/src/sage/combinat/root_system/shm_managers.pyx @@ -11,7 +11,7 @@ Shared memory managers for F-symbol attributes cimport cython from cysignals.memory cimport sig_malloc cimport numpy as np -from sage.combinat.root_system.poly_tup_engine cimport tup_fixes_sq +from sage.combinat.root_system.poly_tup_engine cimport poly_to_tup, tup_fixes_sq from sage.rings.polynomial.polydict cimport ETuple from multiprocessing import shared_memory @@ -302,7 +302,7 @@ def make_KSHandler(n_slots,field,init_data): return KSHandler(n_slots,field,init_data=init_data) cdef class FvarsHandler: - def __init__(self, factory, name=None, max_terms=20): + def __init__(self,n_slots,field,idx_to_sextuple,init_data={},name=None,max_terms=20): """ Return a shared memory backed dict-like structure to manage the ``_fvars`` attribute of an F-matrix factory object. @@ -359,16 +359,17 @@ cdef class FvarsHandler: sage: f = FMatrix(FusionRing("A2",1), inject_variables=True) creating variables fx1..fx8 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 - sage: fvars = FvarsHandler(f) + sage: fvars = FvarsHandler(8,f._field,f._idx_to_sextuple) sage: #In the same shell or in a different shell, attach to fvars - sage: fvars2 = FvarsHandler(f, name=fvars.shm.name) + sage: fvars2 = FvarsHandler(8,f._field,f._idx_to_sextuple,name=fvars.shm.name) sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup sage: fvars[f2, f1, f2, f2, f0, f0] = poly_to_tup(fx5**5) sage: f._tup_to_fpoly(fvars2[f2, f1, f2, f2, f0, f0]) fx5^5 sage: fvars.shm.unlink() """ - cdef int d = factory._field.degree() + self.field = field + cdef int d = self.field.degree() self.obj_cache = dict() self.fvars_t = np.dtype([ ('modified','bool',(1,)), @@ -377,15 +378,19 @@ cdef class FvarsHandler: ('coeff_nums','i4',(max_terms,d)), ('coeff_denom','u4',(max_terms,)) ]) - self.sext_to_idx = {s: i for i, s in factory._idx_to_sextuple.items()} - self.ngens = factory._poly_ring.ngens() - self.field = factory._field + self.sext_to_idx = {s: i for i, s in idx_to_sextuple.items()} + self.ngens = n_slots if name is None: self.shm = shared_memory.SharedMemory(create=True,size=self.ngens*self.fvars_t.itemsize) self.fvars = np.ndarray((self.ngens,),dtype=self.fvars_t,buffer=self.shm.buf) else: self.shm = shared_memory.SharedMemory(name=name) self.fvars = np.ndarray((self.ngens,),dtype=self.fvars_t,buffer=self.shm.buf) + #Populate with initialziation data + for sextuple, fvar in init_data.items(): + if not isinstance(fvar, tuple): + fvar = poly_to_tup(fvar) + self[sextuple] = fvar @cython.nonecheck(False) @cython.wraparound(False) @@ -402,7 +407,7 @@ cdef class FvarsHandler: sage: f = FMatrix(FusionRing("A3", 1), inject_variables=True) creating variables fx1..fx27 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19, fx20, fx21, fx22, fx23, fx24, fx25, fx26 - sage: fvars = FvarsHandler(f) + sage: fvars = FvarsHandler(27,f._field,f._idx_to_sextuple) sage: fvars[(f3, f2, f1, f2, f1, f3)] = poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx21**3 - 799/2881*fx1*fx2**5*fx10) sage: fvars[f3, f2, f3, f0, f1, f1] = poly_to_tup(f._poly_ring.zero()) sage: fvars[f3, f3, f3, f1, f2, f2] = poly_to_tup(-1/19*f._poly_ring.one()) @@ -460,7 +465,7 @@ cdef class FvarsHandler: sage: f = FMatrix(FusionRing("B7", 1), inject_variables=True) creating variables fx1..fx14 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 - sage: fvars = FvarsHandler(f) + sage: fvars = FvarsHandler(14,f._field,f._idx_to_sextuple) sage: fvars[(f1, f2, f1, f2, f2, f2)] = poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx13**3 - 799/2881*fx1*fx2**5*fx10) sage: fvars[f2, f2, f2, f2, f0, f0] = poly_to_tup(f._poly_ring.zero()) sage: fvars[f2, f1, f2, f1, f2, f2] = poly_to_tup(-1/19*f._poly_ring.one()) @@ -514,6 +519,26 @@ cdef class FvarsHandler: self.obj_cache[idx] = ret return ret + def __reduce__(self): + """ + Provide pickling / unpickling support for ``self.`` + + TESTS:: + + sage: f = FMatrix(FusionRing("F4",1)) + sage: from sage.combinat.root_system.shm_managers import FvarsHandler + sage: n = f._poly_ring.ngens() + sage: fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars) + sage: for s, fvar in loads(dumps(fvars)).items(): + ....: assert f._fvars[s] == f._tup_to_fpoly(fvar) + ....: + sage: fvars.shm.unlink() + """ + n = self.fvars.size + idx_map = {i: s for s, i in self.sext_to_idx.items()} + d = {s: fvar for s, fvar in self.items()} + return make_FvarsHandler, (n,self.field,idx_map,d) + def items(self): """ Iterates through key-value pairs in the data structure as if it @@ -537,3 +562,20 @@ cdef class FvarsHandler: """ for sextuple in self.sext_to_idx: yield sextuple, self[sextuple] + +def make_FvarsHandler(n,field,idx_map,init_data): + """ + Provide pickling / unpickling support for :class:`FvarsHandler`. + + TESTS:: + + sage: f = FMatrix(FusionRing("G2",1)) + sage: from sage.combinat.root_system.shm_managers import FvarsHandler + sage: n = f._poly_ring.ngens() + sage: fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars) + sage: for s, fvar in loads(dumps(fvars)).items(): + ....: assert f._fvars[s] == f._tup_to_fpoly(fvar) + ....: + sage: fvars.shm.unlink() + """ + return FvarsHandler(n,field,idx_map,init_data=init_data) From ba0d435a354e19035ed95cf8f77cbc68c92473b6 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Sun, 2 May 2021 20:58:14 -0400 Subject: [PATCH 070/632] new pool usage syntax, added pool attribute to FMatrix --- src/sage/combinat/root_system/f_matrix.py | 148 +++++++++--------- .../fast_parallel_fmats_methods.pxd | 2 + .../fast_parallel_fmats_methods.pyx | 9 +- .../fast_parallel_fusion_ring_braid_repn.pyx | 4 +- .../combinat/root_system/shm_managers.pyx | 111 ++++++------- 5 files changed, 141 insertions(+), 133 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 0b4ee2f5d3f..fabc75fc3ed 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -299,6 +299,7 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab #Multiprocessing attributes self.mp_thresh = 10000 + self.pool = None ####################### ### Class utilities ### @@ -424,6 +425,7 @@ def _update_poly_ring_base_field(self,field): new_poly_ring = self._poly_ring.change_ring(field) nvars = self._poly_ring.ngens() #Do some appropriate conversions + self._singles = [new_poly_ring.gen(self._var_to_idx[fx]) for fx in self._singles] self._var_to_idx = {new_poly_ring.gen(i): i for i in range(nvars)} self._var_to_sextuple = {new_poly_ring.gen(i): self._var_to_sextuple[self._poly_ring.gen(i)] for i in range(nvars)} self._poly_ring = new_poly_ring @@ -1240,13 +1242,15 @@ def _restore_state(self,filename): ### MapReduce ### ################# - def get_worker_pool(self,processes=None): + def start_worker_pool(self,processes=None): """ - Get an initialized worker pool for parallel processing, + Initialize a ``multiprocessing`` worker pool for parallel processing, which may be used e.g. to set up defining equations using :meth:`get_defining_equations`. - This pool may be reused time and again. + This method creates the attribute ``self.pool``, and the worker + pool may be used time and again. Upon initialization, each process + in the pool is attached to the necessary shared memory resources. When you are done using the worker pool, use :meth:`shutdown_worker_pool` to close the pool and properly dispose @@ -1255,23 +1259,23 @@ def get_worker_pool(self,processes=None): EXAMPLES:: sage: f = FMatrix(FusionRing("G2",1)) - sage: pool = f.get_worker_pool() - sage: he = f.get_defining_equations('hexagons',worker_pool=pool) + sage: f.start_worker_pool() + sage: he = f.get_defining_equations('hexagons') sage: sorted(he) [fx0 - 1, fx2*fx3 + (zeta60^14 + zeta60^12 - zeta60^6 - zeta60^4 + 1)*fx4^2 + (zeta60^6)*fx4, fx1*fx3 + (zeta60^14 + zeta60^12 - zeta60^6 - zeta60^4 + 1)*fx3*fx4 + (zeta60^14 - zeta60^4)*fx3, fx1*fx2 + (zeta60^14 + zeta60^12 - zeta60^6 - zeta60^4 + 1)*fx2*fx4 + (zeta60^14 - zeta60^4)*fx2, fx1^2 + (zeta60^14 + zeta60^12 - zeta60^6 - zeta60^4 + 1)*fx2*fx3 + (-zeta60^12)*fx1] - sage: pe = f.get_defining_equations('pentagons',worker_pool=pool) - sage: f.shutdown_worker_pool(pool) + sage: pe = f.get_defining_equations('pentagons') + sage: f.shutdown_worker_pool() .. WARNING:: This method is needed to initialize the worker pool using the necessary shared memory resources. Simply using the - ``multiprocessing.Pool`` constructor will not work with our class - methods. + ``multiprocessing.Pool`` constructor will not work with our + class methods. .. WARNING:: @@ -1294,11 +1298,6 @@ def get_worker_pool(self,processes=None): self._ks = KSHandler(n,self._field,use_mp=True,init_data=self._ks) ks_names = self._ks.shm.name self._shared_fvars = FvarsHandler(n,self._field,self._idx_to_sextuple,init_data=self._fvars) - # for sextuple, fvar in self._fvars.items(): - # if self._chkpt_status < 0: - # self._shared_fvars[sextuple] = poly_to_tup(fvar) - # else: - # self._shared_fvars[sextuple] = fvar fvar_names = self._shared_fvars.shm.name #Initialize worker pool processes args = (id(self), s_name, vd_name, ks_names, fvar_names) @@ -1314,17 +1313,16 @@ def init(fmats_id, solved_name, vd_name, ks_names, fvar_names): fmats_obj._fvars = FvarsHandler(n,fmats_obj._field,fmats_obj._idx_to_sextuple,name=fvar_names) fmats_obj._ks = KSHandler(n,fmats_obj._field,name=ks_names) - pool = Pool(processes=n,initializer=init,initargs=args) - return pool + self.pool = Pool(processes=n,initializer=init,initargs=args) - def shutdown_worker_pool(self,pool): + def shutdown_worker_pool(self): """ Shutdown the given worker pool and dispose of shared memory resources - created when the pool was set up using :meth:`get_worker_pool`. + created when the pool was set up using :meth:`start_worker_pool`. .. WARNING:: - Failure to call this method after using :meth:`get_worker_pool` + Failure to call this method after using :meth:`start_worker_pool` to create a process pool may result in a memory leak, since shared memory resources outlive the process that created them. @@ -1332,16 +1330,17 @@ def shutdown_worker_pool(self,pool): EXAMPLES:: sage: f = FMatrix(FusionRing("A1",3)) - sage: pool = f.get_worker_pool() - sage: he = f.get_defining_equations('hexagons', worker_pool=pool) - sage: f.shutdown_worker_pool(pool) + sage: f.start_worker_pool() + sage: he = f.get_defining_equations('hexagons') + sage: f.shutdown_worker_pool() """ - pool.close() - self._solved.shm.unlink() - self._var_degs.shm.unlink() - self._ks.shm.unlink() - self._shared_fvars.shm.unlink() - del self.__dict__['_shared_fvars'] + if self.pool is not None: + self.pool.close() + self._solved.shm.unlink() + self._var_degs.shm.unlink() + self._ks.shm.unlink() + self._shared_fvars.shm.unlink() + del self.__dict__['_shared_fvars'] def _map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_thresh=None): r""" @@ -1369,11 +1368,11 @@ def _map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_t sage: f._reset_solver_state() sage: len(f._map_triv_reduce('get_reduced_hexagons',[(0,1,False)])) 11 - sage: pool = f.get_worker_pool() - sage: mp_params = [(i,pool._processes,True) for i in range(pool._processes)] - sage: len(f._map_triv_reduce('get_reduced_pentagons',mp_params,worker_pool=pool,chunksize=1,mp_thresh=0)) + sage: f.start_worker_pool() + sage: mp_params = [(i,f.pool._processes,True) for i in range(f.pool._processes)] + sage: len(f._map_triv_reduce('get_reduced_pentagons',mp_params,worker_pool=f.pool,chunksize=1,mp_thresh=0)) 33 - sage: f.shutdown_worker_pool(pool) + sage: f.shutdown_worker_pool() """ if mp_thresh is None: mp_thresh = self.mp_thresh @@ -1449,7 +1448,7 @@ def get_orthogonality_constraints(self,output=True): return eqns self.ideal_basis.extend([poly_to_tup(eq) for eq in eqns]) - def get_defining_equations(self,option,worker_pool=None,output=True): + def get_defining_equations(self,option,output=True): r""" Get the equations defining the ideal generated by the hexagon or pentagon relations. @@ -1464,14 +1463,10 @@ def get_defining_equations(self,option,worker_pool=None,output=True): * ``'pentagons'`` - get equations imposed on the F-matrix by the pentagon relations in the definition of a monoidal category - - ``worker_pool`` -- (default: ``None``) a ``Pool`` object of the - Python ``multiprocessing`` module - - ``output`` -- (default: ``True``) a boolean indicating whether results should be returned, where the equations will be polynomials. - Otherwise, the constraints are appended to ``self.ideal_basis``. - They are stored in the internal tuple representation. The + Constraints are stored in the internal tuple representation. The ``output=False`` option is meant only for internal use by the F-matrix solver. When computing the hexagon equations with the ``output=False`` option, the initial state of the F-symbols is used. @@ -1494,12 +1489,18 @@ def get_defining_equations(self,option,worker_pool=None,output=True): sage: pe = f.get_defining_equations('pentagons') sage: len(pe) 33 + + .. NOTE:: + + To set up the defining equations using parallel processing, + use :meth:`start_worker_pool` to initialize multiple processes + *before* calling this method. """ if not hasattr(self, '_nnz'): self._reset_solver_state() - n_proc = worker_pool._processes if worker_pool is not None else 1 + n_proc = self.pool._processes if self.pool is not None else 1 params = [(child_id, n_proc, output) for child_id in range(n_proc)] - eqns = self._map_triv_reduce('get_reduced_'+option,params,worker_pool=worker_pool,chunksize=1,mp_thresh=0) + eqns = self._map_triv_reduce('get_reduced_'+option,params,worker_pool=self.pool,chunksize=1,mp_thresh=0) if output: F = self._field for i, eq_tup in enumerate(eqns): @@ -1528,10 +1529,11 @@ def _tup_to_fpoly(self,eq_tup): sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup sage: f = FMatrix(FusionRing("C3",1)) - sage: pool = f.get_worker_pool() - sage: he = f.get_defining_equations('hexagons',pool) + sage: f.start_worker_pool() + sage: he = f.get_defining_equations('hexagons') sage: all(f._tup_to_fpoly(poly_to_tup(h)) for h in he) True + sage: f.shutdown_worker_pool() """ return _tup_to_poly(eq_tup,parent=self._poly_ring) @@ -1544,17 +1546,17 @@ def _update_reduction_params(self,eqns=None): sage: f = FMatrix(FusionRing("A1",3)) sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) - sage: pool = f.get_worker_pool() - sage: f.get_defining_equations('hexagons',worker_pool=pool,output=False) - sage: f.ideal_basis = f._par_graph_gb(worker_pool=pool,verbose=False) + sage: f.start_worker_pool() + sage: f.get_defining_equations('hexagons',output=False) + sage: f.ideal_basis = f._par_graph_gb(verbose=False) sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey, poly_to_tup sage: f.ideal_basis.sort(key=poly_tup_sortkey) sage: f.mp_thresh = 0 sage: f._fvars = f._shared_fvars - sage: f._triangular_elim(worker_pool=pool,verbose=False) # indirect doctest + sage: f._triangular_elim(verbose=False) # indirect doctest sage: f.ideal_basis [] - sage: f.shutdown_worker_pool(pool) + sage: f.shutdown_worker_pool() """ if eqns is None: eqns = self.ideal_basis @@ -1569,7 +1571,7 @@ def _update_reduction_params(self,eqns=None): self._nnz = self._get_known_nonz() self._kp = compute_known_powers(self._var_degs,self._get_known_vals(),self._field.one()) - def _triangular_elim(self,eqns=None,worker_pool=None,verbose=True): + def _triangular_elim(self,eqns=None,verbose=True): r""" Perform triangular elimination of linear terms in two-term equations until no such terms exist. @@ -1599,7 +1601,7 @@ def _triangular_elim(self,eqns=None,worker_pool=None,verbose=True): ret = False while True: #Reset modification cache - if worker_pool is not None: + if self.pool is not None: self._fvars.fvars['modified'][:] = False linear_terms_exist = _solve_for_linear_terms(self,eqns) if not linear_terms_exist: @@ -1610,15 +1612,15 @@ def _triangular_elim(self,eqns=None,worker_pool=None,verbose=True): self._update_reduction_params(eqns=eqns) # n = len(eqns) // worker_pool._processes ** 2 + 1 if worker_pool is not None else len(eqns) # eqns = [eqns[i:i+n] for i in range(0,len(eqns),n)] - if worker_pool is not None and len(eqns) > self.mp_thresh: - n = worker_pool._processes + if self.pool is not None and len(eqns) > self.mp_thresh: + n = self.pool._processes chunks = [[] for i in range(n)] for i, eq_tup in enumerate(eqns): chunks[i%n].append(eq_tup) eqns = chunks else: eqns = [eqns] - eqns = self._map_triv_reduce('update_reduce',eqns,worker_pool=worker_pool) + eqns = self._map_triv_reduce('update_reduce',eqns,worker_pool=self.pool) eqns.sort(key=poly_tup_sortkey) if verbose: print("Elimination epoch completed... {} eqns remain in ideal basis".format(len(eqns))) @@ -1683,13 +1685,13 @@ def equations_graph(self,eqns=None): if not eqns: return G #Eqns could be a list of poly objects or poly tuples stored in internal repn - if type(eqns[0]) == tuple: + if isinstance(eqns[0], tuple): G.add_vertices([x for eq_tup in eqns for x in variables(eq_tup)]) else: G.add_vertices([x for eq in eqns for x in eq.variables()]) for eq in eqns: #Eqns could be a list of poly objects or poly tuples stored in internal repn - if type(eq) == tuple: + if isinstance(eq, tuple): s = [v for v in variables(eq)] else: s = [v for v in eq.variables()] @@ -1744,7 +1746,7 @@ def _partition_eqns(self,eqns=None,verbose=True): print(graph.connected_components_sizes()) return partition - def _par_graph_gb(self,worker_pool=None,eqns=None,term_order="degrevlex",verbose=True): + def _par_graph_gb(self,eqns=None,term_order="degrevlex",verbose=True): r""" Compute a Groebner basis for a list of equations partitioned according to their corresponding graph. @@ -1763,9 +1765,9 @@ def _par_graph_gb(self,worker_pool=None,eqns=None,term_order="degrevlex",verbose sage: f = FMatrix(FusionRing("F4",1)) sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) - sage: pool = f.get_worker_pool() - sage: f.get_defining_equations('hexagons',worker_pool=pool,output=False) - sage: gb = f._par_graph_gb(worker_pool=pool) + sage: f.start_worker_pool() + sage: f.get_defining_equations('hexagons',output=False) + sage: gb = f._par_graph_gb() Partitioned 10 equations into 2 components of size: [4, 1] sage: from sage.combinat.root_system.poly_tup_engine import _unflatten_coeffs @@ -1776,6 +1778,7 @@ def _par_graph_gb(self,worker_pool=None,eqns=None,term_order="degrevlex",verbose fx1 + (zeta80^24 - zeta80^16), fx0 - 1, fx3^2 + (zeta80^24 - zeta80^16)] + sage: f.shutdown_worker_pool() """ if eqns is None: eqns = self.ideal_basis small_comps = list() @@ -1787,7 +1790,7 @@ def _par_graph_gb(self,worker_pool=None,eqns=None,term_order="degrevlex",verbose # for i in range(nmax+1): # vars_by_size.append(self.get_fvars_by_size(i)) - for comp, comp_eqns in self._partition_eqns(verbose=verbose).items():#self._partition_eqns(graph,verbose=verbose).items(): + for comp, comp_eqns in self._partition_eqns(verbose=verbose).items(): #Check if component is too large to process if len(comp) > 60: # fmat_size = 0 @@ -1800,7 +1803,7 @@ def _par_graph_gb(self,worker_pool=None,eqns=None,term_order="degrevlex",verbose else: small_comps.append(comp_eqns) input_iter = zip_longest(small_comps,[],fillvalue=term_order) - small_comp_gb = self._map_triv_reduce('compute_gb',input_iter,worker_pool=worker_pool,chunksize=1,mp_thresh=50) + small_comp_gb = self._map_triv_reduce('compute_gb',input_iter,worker_pool=self.pool,chunksize=1,mp_thresh=50) ret = small_comp_gb + temp_eqns return ret @@ -1818,8 +1821,8 @@ def _get_component_variety(self,var,eqns): EXAMPLES:: sage: f = FMatrix(FusionRing("G2",2)) - sage: pool = f.get_worker_pool() - sage: f.get_defining_equations('hexagons',worker_pool=pool,output=False) # long time + sage: f.start_worker_pool() + sage: f.get_defining_equations('hexagons',output=False) # long time sage: partition = f._partition_eqns() # long time Partitioned 327 equations into 35 components of size: [27, 27, 27, 24, 24, 16, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, @@ -1829,6 +1832,7 @@ def _get_component_variety(self,var,eqns): sage: eqns = partition[c] + [poly_to_tup(f._poly_ring.gen(216)-1)] # long time sage: f._get_component_variety(c,eqns) # long time [{216: -1, 292: -1, 319: 1}] + sage: f.shutdown_worker_pool() """ #Define smaller poly ring in component vars R = PolynomialRing(self._FR.field(), len(var), 'a', order='lex') @@ -1937,7 +1941,8 @@ def _get_explicit_solution(self,eqns=None,verbose=True): for fx, rhs in self._ks.items(): if not self._solved[fx]: lt = (ETuple({fx : 2},n), one) - eqns.append((lt, (ETuple({},n), -self._field(list(rhs))))) + # eqns.append((lt, (ETuple({},n), -self._field(list(rhs))))) + eqns.append(((lt, (ETuple({},n), -rhs)))) eqns_partition = self._partition_eqns(verbose=verbose) F = self._field @@ -2115,14 +2120,14 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start if self._chkpt_status > 5: return #max(cpu_count()-1,1) - pool = self.get_worker_pool() if use_mp else None + if use_mp: self.start_worker_pool() if verbose: print("Computing F-symbols for {} with {} variables...".format(self._FR, self._poly_ring.ngens())) if self._chkpt_status < 1: #Set up hexagon equations and orthogonality constraints self.get_orthogonality_constraints(output=False) - self.get_defining_equations('hexagons',worker_pool=pool,output=False) + self.get_defining_equations('hexagons',output=False) #Report progress if verbose: print("Set up {} hex and orthogonality constraints...".format(len(self.ideal_basis))) @@ -2134,9 +2139,9 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start if self._chkpt_status < 2: #Set up equations graph. Find GB for each component in parallel. Eliminate variables - self.ideal_basis = self._par_graph_gb(worker_pool=pool,verbose=verbose) + self.ideal_basis = self._par_graph_gb(verbose=verbose) self.ideal_basis.sort(key=poly_tup_sortkey) - self._triangular_elim(worker_pool=pool,verbose=verbose) + self._triangular_elim(verbose=verbose) #Report progress if verbose: print("Hex elim step solved for {} / {} variables".format(sum(self._solved), len(self._poly_ring.gens()))) @@ -2145,7 +2150,7 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start if self._chkpt_status < 3: #Set up pentagon equations in parallel - self.get_defining_equations('pentagons',worker_pool=pool,output=False) + self.get_defining_equations('pentagons',output=False) self.ideal_basis.sort(key=poly_tup_sortkey) #Report progress if verbose: @@ -2155,7 +2160,7 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start #Simplify and eliminate variables if self._chkpt_status < 4: - self._triangular_elim(worker_pool=pool,verbose=verbose) + self._triangular_elim(verbose=verbose) #Report progress if verbose: print("Pent elim step solved for {} / {} variables".format(sum(self._solved), len(self._poly_ring.gens()))) @@ -2179,8 +2184,7 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start self._chkpt_status = 7 self.clear_equations() #Close worker pool and destroy shared resources - if use_mp: - self.shutdown_worker_pool(pool) + self.shutdown_worker_pool() if checkpoint: remove("fmatrix_solver_checkpoint_"+self.get_fr_str()+".pickle") if save_results: @@ -2211,9 +2215,7 @@ def _fix_gauge(self, algorithm=""): adding equation... fx18 - 1 adding equation... fx21 - 1 """ - # while len(self._solved) < len(self._poly_ring.gens()): while sum(1 for v in self._solved if not v) > 0: - # while self._solved.sum() < self._solved.size: #Get a variable that has not been fixed #In ascending index order, for consistent results for i, var in enumerate(self._poly_ring.gens()): diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd index f54fa2ff174..f4f12e9c0d5 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd @@ -1,2 +1,4 @@ cdef _fmat(fvars, Nk_ij, one, a, b, c, d, x, y) +cpdef _backward_subs(factory) cpdef executor(tuple params) +cpdef _solve_for_linear_terms(factory, list eqns=*) diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index 02d2ebc799f..c42b95616fe 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -18,6 +18,7 @@ from sage.combinat.root_system.poly_tup_engine cimport ( has_appropriate_linear_term, resize ) +from sage.combinat.root_system.shm_managers cimport KSHandler, FvarsHandler from sage.rings.number_field.number_field_element cimport NumberFieldElement_absolute from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular, MPolynomialRing_libsingular from sage.rings.polynomial.polydict cimport ETuple @@ -26,14 +27,13 @@ from ctypes import cast, py_object from itertools import product from multiprocessing import shared_memory from sage.rings.ideal import Ideal -from sage.combinat.root_system.shm_managers import KSHandler, FvarsHandler from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing ########################## ### Fast class methods ### ########################## -cpdef _solve_for_linear_terms(factory, eqns=None): +cpdef _solve_for_linear_terms(factory, list eqns=None): r""" Solve for a linear term occurring in a two-term equation, and for variables appearing in univariate single-term equations. @@ -66,6 +66,8 @@ cpdef _solve_for_linear_terms(factory, eqns=None): eqns = factory.ideal_basis cdef bint linear_terms_exist = False + cdef ETuple exp, rhs_exp + cdef int max_var cdef tuple eq_tup for eq_tup in eqns: # Only unflatten relevant polynomials @@ -83,7 +85,8 @@ cpdef _solve_for_linear_terms(factory, eqns=None): idx = has_appropriate_linear_term(eq_tup) if idx < 0: continue #The chosen term is guaranteed to be univariate in the largest variable - max_var = eq_tup[idx][0].nonzero_positions()[0] + exp = eq_tup[idx][0] + max_var = exp._data[0] if not factory._solved[max_var]: rhs_exp = eq_tup[(idx+1) % 2][0] rhs_coeff = -eq_tup[(idx+1) % 2][1] / eq_tup[idx][1] diff --git a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx index a9664c79139..4270243eb26 100644 --- a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx @@ -8,7 +8,7 @@ Fast FusionRing methods for computing braid group representations # https://www.gnu.org/licenses/ # **************************************************************************** -import ctypes +from ctypes import cast, py_object cimport cython from sage.combinat.root_system.fast_parallel_fmats_methods cimport _fmat @@ -287,7 +287,7 @@ cpdef executor(tuple params): """ (fn_name, fr_id), args = params #Construct a reference to global FMatrix object in this worker's memory - fusion_ring_obj = ctypes.cast(fr_id, ctypes.py_object).value + fusion_ring_obj = cast(fr_id, py_object).value #Bind module method to FMatrix object in worker process, and call the method return mappers[fn_name](fusion_ring_obj,args) diff --git a/src/sage/combinat/root_system/shm_managers.pyx b/src/sage/combinat/root_system/shm_managers.pyx index e1c8c553b2d..46e11f6aaf3 100644 --- a/src/sage/combinat/root_system/shm_managers.pyx +++ b/src/sage/combinat/root_system/shm_managers.pyx @@ -18,70 +18,70 @@ from multiprocessing import shared_memory import numpy as np cdef class KSHandler: - """ - Return a shared memory backed dict-like structure to manage the - ``_ks`` attribute of an F-matrix factory object. + def __init__(self, n_slots, field, use_mp=False, init_data={}, name=None): + """ + Return a shared memory backed dict-like structure to manage the + ``_ks`` attribute of an F-matrix factory object. - This structure implements a representation of the known squares dictionary - using a structured NumPy array backed by a contiguous shared memory - object. + This structure implements a representation of the known squares dictionary + using a structured NumPy array backed by a contiguous shared memory + object. - The structure mimics a dictionary of ``(idx, known_sq)`` pairs. Each - integer index corresponds to a variable and each ``known_sq`` is an - element of the F-matrix factory's base cyclotomic field. + The structure mimics a dictionary of ``(idx, known_sq)`` pairs. Each + integer index corresponds to a variable and each ``known_sq`` is an + element of the F-matrix factory's base cyclotomic field. - Each cyclotomic coefficient is stored as a list of numerators and a - list of denominators representing the rational coefficients. The - structured array also maintains ``known`` attribute that indicates - whether the structure contains an entry corresponding to the given index. + Each cyclotomic coefficient is stored as a list of numerators and a + list of denominators representing the rational coefficients. The + structured array also maintains ``known`` attribute that indicates + whether the structure contains an entry corresponding to the given index. - The parent process should construct this object without a - ``name`` attribute. Children processes use the ``name`` attribute, - accessed via ``self.shm.name`` to attach to the shared memory block. + The parent process should construct this object without a + ``name`` attribute. Children processes use the ``name`` attribute, + accessed via ``self.shm.name`` to attach to the shared memory block. - INPUT: + INPUT: - - ``n_slots`` -- The total number of F-symbols. - - ``field`` -- F-matrix factory's base cyclotomic field. - - ``use_mp`` -- a boolean indicating whether to construct a shared - memory block to back ``self``. - - ``name`` -- the name of a shared memory object - (used by child processes for attaching). - - ``init_data`` -- a dictionary or :class:`KSHandler` object containing - known squares for initialization, e.g. from a solver checkpoint. + - ``n_slots`` -- The total number of F-symbols. + - ``field`` -- F-matrix factory's base cyclotomic field. + - ``use_mp`` -- a boolean indicating whether to construct a shared + memory block to back ``self``. + - ``name`` -- the name of a shared memory object + (used by child processes for attaching). + - ``init_data`` -- a dictionary or :class:`KSHandler` object containing + known squares for initialization, e.g. from a solver checkpoint. - .. NOTE:: + .. NOTE:: - To properly dispose of shared memory resources, - ``self.shm.unlink()`` must be called before exiting. + To properly dispose of shared memory resources, + ``self.shm.unlink()`` must be called before exiting. - .. WARNING:: + .. WARNING:: - This structure does *not* cannot modify an entry that - has already been set. + This structure does *not* cannot modify an entry that + has already been set. - EXAMPLES:: + EXAMPLES:: - sage: from sage.combinat.root_system.shm_managers import KSHandler - sage: #Create shared data structure - sage: f = FMatrix(FusionRing("A1",2), inject_variables=True) - creating variables fx1..fx14 - Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 - sage: n = f._poly_ring.ngens() - sage: ks = KSHandler(n,f._field,use_mp=True) - sage: #In the same shell or in a different shell, attach to fvars - sage: ks2 = KSHandler(n,f._field,name=ks.shm.name) - sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup - sage: eqns = [fx1**2 - 4, fx3**2 + f._field.gen()**4 - 1/19*f._field.gen()**2] - sage: ks.update([poly_to_tup(p) for p in eqns]) - sage: for idx, sq in ks.items(): - ....: print("Index: {}, square: {}".format(idx, sq)) - ....: - Index: 1, square: 4 - Index: 3, square: -zeta32^4 + 1/19*zeta32^2 - sage: ks.shm.unlink() - """ - def __init__(self, n_slots, field, use_mp=False, init_data={}, name=None): + sage: from sage.combinat.root_system.shm_managers import KSHandler + sage: #Create shared data structure + sage: f = FMatrix(FusionRing("A1",2), inject_variables=True) + creating variables fx1..fx14 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 + sage: n = f._poly_ring.ngens() + sage: ks = KSHandler(n,f._field,use_mp=True) + sage: #In the same shell or in a different shell, attach to fvars + sage: ks2 = KSHandler(n,f._field,name=ks.shm.name) + sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: eqns = [fx1**2 - 4, fx3**2 + f._field.gen()**4 - 1/19*f._field.gen()**2] + sage: ks.update([poly_to_tup(p) for p in eqns]) + sage: for idx, sq in ks.items(): + ....: print("Index: {}, square: {}".format(idx, sq)) + ....: + Index: 1, square: 4 + Index: 3, square: -zeta32^4 + 1/19*zeta32^2 + sage: ks.shm.unlink() + """ cdef int n, d self.field = field n = n_slots @@ -192,6 +192,7 @@ cdef class KSHandler: @cython.nonecheck(False) @cython.wraparound(False) + @cython.infer_types(False) cdef setitem(self, int idx, rhs): """ Create an entry corresponding to the given index. @@ -552,13 +553,13 @@ cdef class FvarsHandler: sage: f = FMatrix(FusionRing("G2", 1), inject_variables=True) creating variables fx1..fx5 Defining fx0, fx1, fx2, fx3, fx4 - sage: p = f.get_worker_pool() + sage: f.start_worker_pool() sage: for sextuple, fvar in f._shared_fvars.items(): ....: if sextuple == (f1, f1, f1, f1, f1, f1): ....: f._tup_to_fpoly(fvar) ....: fx4 - sage: f.shutdown_worker_pool(p) + sage: f.shutdown_worker_pool() """ for sextuple in self.sext_to_idx: yield sextuple, self[sextuple] @@ -573,7 +574,7 @@ def make_FvarsHandler(n,field,idx_map,init_data): sage: from sage.combinat.root_system.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() sage: fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars) - sage: for s, fvar in loads(dumps(fvars)).items(): + sage: for s, fvar in loads(dumps(fvars)).items(): # indirect doctest ....: assert f._fvars[s] == f._tup_to_fpoly(fvar) ....: sage: fvars.shm.unlink() From 66f9431cfc5a60c317a85615c400e93128120bc0 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Sun, 2 May 2021 21:31:26 -0400 Subject: [PATCH 071/632] set pool to None after shutdown --- src/sage/combinat/root_system/f_matrix.py | 1 + .../combinat/root_system/shm_managers.pyx | 110 +++++++++--------- 2 files changed, 56 insertions(+), 55 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index fabc75fc3ed..e4d56e2bac8 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -1341,6 +1341,7 @@ def shutdown_worker_pool(self): self._ks.shm.unlink() self._shared_fvars.shm.unlink() del self.__dict__['_shared_fvars'] + self.pool = None def _map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_thresh=None): r""" diff --git a/src/sage/combinat/root_system/shm_managers.pyx b/src/sage/combinat/root_system/shm_managers.pyx index 46e11f6aaf3..00522b84dcd 100644 --- a/src/sage/combinat/root_system/shm_managers.pyx +++ b/src/sage/combinat/root_system/shm_managers.pyx @@ -393,61 +393,6 @@ cdef class FvarsHandler: fvar = poly_to_tup(fvar) self[sextuple] = fvar - @cython.nonecheck(False) - @cython.wraparound(False) - def __setitem__(self, sextuple, fvar): - """ - Given a sextuple of labels and a tuple of ``(ETuple, cyc_coeff)`` pairs, - create or overwrite an entry in the shared data structure - corresponding to the given sextuple. - - EXAMPLES:: - - sage: from sage.combinat.root_system.shm_managers import FvarsHandler - sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup - sage: f = FMatrix(FusionRing("A3", 1), inject_variables=True) - creating variables fx1..fx27 - Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19, fx20, fx21, fx22, fx23, fx24, fx25, fx26 - sage: fvars = FvarsHandler(27,f._field,f._idx_to_sextuple) - sage: fvars[(f3, f2, f1, f2, f1, f3)] = poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx21**3 - 799/2881*fx1*fx2**5*fx10) - sage: fvars[f3, f2, f3, f0, f1, f1] = poly_to_tup(f._poly_ring.zero()) - sage: fvars[f3, f3, f3, f1, f2, f2] = poly_to_tup(-1/19*f._poly_ring.one()) - sage: s, t, r = (f3, f2, f1, f2, f1, f3), (f3, f2, f3, f0, f1, f1), (f3, f3, f3, f1, f2, f2) - sage: f._tup_to_fpoly(fvars[s]) == 1/8*fx0**15 - 23/79*fx2*fx21**3 - 799/2881*fx1*fx2**5*fx10 - True - sage: f._tup_to_fpoly(fvars[t]) == 0 - True - sage: f._tup_to_fpoly(fvars[r]) == -1/19 - True - sage: fvars.shm.unlink() - """ - cdef unsigned int cum, i, j, k, idx - cdef unsigned long denom - cdef long c - cdef ETuple exp - cdef NumberFieldElement_absolute coeff - idx = self.sext_to_idx[sextuple] - #Clear entry before inserting - self.fvars[idx] = np.zeros((1,), dtype=self.fvars_t) - cum = 0 - i = 0 - for exp, coeff in fvar: - #Handle constant coefficient - if exp._nonzero > 0: - self.fvars['ticks'][idx,i] = exp._nonzero - else: - self.fvars['ticks'][idx,i] = -1 - for j in range(2*exp._nonzero): - self.fvars['exp_data'][idx,cum] = exp._data[j] - cum += 1 - denom = coeff.denominator() - self.fvars['coeff_denom'][idx,i] = denom - for k, r in enumerate(coeff._coefficients()): - c = r * denom - self.fvars['coeff_nums'][idx,i,k] = c - i += 1 - self.fvars['modified'][idx] = True - @cython.nonecheck(False) @cython.wraparound(False) def __getitem__(self, sextuple): @@ -520,6 +465,61 @@ cdef class FvarsHandler: self.obj_cache[idx] = ret return ret + @cython.nonecheck(False) + @cython.wraparound(False) + def __setitem__(self, sextuple, fvar): + """ + Given a sextuple of labels and a tuple of ``(ETuple, cyc_coeff)`` pairs, + create or overwrite an entry in the shared data structure + corresponding to the given sextuple. + + EXAMPLES:: + + sage: from sage.combinat.root_system.shm_managers import FvarsHandler + sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: f = FMatrix(FusionRing("A3", 1), inject_variables=True) + creating variables fx1..fx27 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19, fx20, fx21, fx22, fx23, fx24, fx25, fx26 + sage: fvars = FvarsHandler(27,f._field,f._idx_to_sextuple) + sage: fvars[(f3, f2, f1, f2, f1, f3)] = poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx21**3 - 799/2881*fx1*fx2**5*fx10) + sage: fvars[f3, f2, f3, f0, f1, f1] = poly_to_tup(f._poly_ring.zero()) + sage: fvars[f3, f3, f3, f1, f2, f2] = poly_to_tup(-1/19*f._poly_ring.one()) + sage: s, t, r = (f3, f2, f1, f2, f1, f3), (f3, f2, f3, f0, f1, f1), (f3, f3, f3, f1, f2, f2) + sage: f._tup_to_fpoly(fvars[s]) == 1/8*fx0**15 - 23/79*fx2*fx21**3 - 799/2881*fx1*fx2**5*fx10 + True + sage: f._tup_to_fpoly(fvars[t]) == 0 + True + sage: f._tup_to_fpoly(fvars[r]) == -1/19 + True + sage: fvars.shm.unlink() + """ + cdef unsigned int cum, i, j, k, idx + cdef unsigned long denom + cdef long c + cdef ETuple exp + cdef NumberFieldElement_absolute coeff + idx = self.sext_to_idx[sextuple] + #Clear entry before inserting + self.fvars[idx] = np.zeros((1,), dtype=self.fvars_t) + cum = 0 + i = 0 + for exp, coeff in fvar: + #Handle constant coefficient + if exp._nonzero > 0: + self.fvars['ticks'][idx,i] = exp._nonzero + else: + self.fvars['ticks'][idx,i] = -1 + for j in range(2*exp._nonzero): + self.fvars['exp_data'][idx,cum] = exp._data[j] + cum += 1 + denom = coeff.denominator() + self.fvars['coeff_denom'][idx,i] = denom + for k, r in enumerate(coeff._coefficients()): + c = r * denom + self.fvars['coeff_nums'][idx,i,k] = c + i += 1 + self.fvars['modified'][idx] = True + def __reduce__(self): """ Provide pickling / unpickling support for ``self.`` From 9c8c6c4c32292c2659f9cbe7dcf59cdea1cf2dd0 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Mon, 3 May 2021 00:51:55 -0400 Subject: [PATCH 072/632] FvarsHandler array attribute privatized --- src/sage/combinat/root_system/f_matrix.py | 40 +++++++++++-------- .../fast_parallel_fmats_methods.pyx | 14 +++++-- .../combinat/root_system/shm_managers.pxd | 4 +- .../combinat/root_system/shm_managers.pyx | 30 +++++++++----- 4 files changed, 58 insertions(+), 30 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index e4d56e2bac8..c8c0299e720 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -1116,7 +1116,9 @@ def _checkpoint(self, do_chkpt, status, verbose=True): sage: f.ideal_basis = f._par_graph_gb(verbose=False) sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey, poly_to_tup sage: f.ideal_basis.sort(key=poly_tup_sortkey) - sage: f._fvars = {sextuple : poly_to_tup(rhs) for sextuple, rhs in f._fvars.items()} + sage: from sage.combinat.root_system.shm_managers import FvarsHandler + sage: n = f._poly_ring.ngens() + sage: f._fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars) sage: f._triangular_elim(verbose=False) sage: f._update_reduction_params() sage: f._checkpoint(do_chkpt=True,status=2) @@ -1147,7 +1149,9 @@ def _checkpoint(self, do_chkpt, status, verbose=True): sage: f.ideal_basis = f._par_graph_gb(verbose=False) sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey sage: f.ideal_basis.sort(key=poly_tup_sortkey) - sage: f._fvars = {sextuple : poly_to_tup(rhs) for sextuple, rhs in f._fvars.items()} + sage: from sage.combinat.root_system.shm_managers import FvarsHandler + sage: n = f._poly_ring.ngens() + sage: f._fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars) sage: f._triangular_elim(verbose=False) sage: f._update_reduction_params() sage: f.get_defining_equations('pentagons',output=False) @@ -1191,7 +1195,9 @@ def _restore_state(self,filename): sage: f.ideal_basis = f._par_graph_gb(verbose=False) sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey, poly_to_tup sage: f.ideal_basis.sort(key=poly_tup_sortkey) - sage: f._fvars = {sextuple : poly_to_tup(rhs) for sextuple, rhs in f._fvars.items()} + sage: from sage.combinat.root_system.shm_managers import FvarsHandler + sage: n = f._poly_ring.ngens() + sage: f._fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars) sage: f._triangular_elim(verbose=False) sage: f._update_reduction_params() sage: fvars = f._fvars @@ -1205,8 +1211,9 @@ def _restore_state(self,filename): sage: f = FMatrix(FusionRing("A1",2)) sage: f._reset_solver_state() sage: f._restore_state("fmatrix_solver_checkpoint_A12.pickle") - sage: fvars == f._fvars - True + sage: for sextuple, fvar in fvars.items(): + ....: assert fvar == f._fvars[sextuple] + ....: sage: ib == f.ideal_basis True sage: ks == f._ks @@ -1297,7 +1304,7 @@ class methods. n = self._poly_ring.ngens() self._ks = KSHandler(n,self._field,use_mp=True,init_data=self._ks) ks_names = self._ks.shm.name - self._shared_fvars = FvarsHandler(n,self._field,self._idx_to_sextuple,init_data=self._fvars) + self._shared_fvars = FvarsHandler(n,self._field,self._idx_to_sextuple,use_mp=True,init_data=self._fvars) fvar_names = self._shared_fvars.shm.name #Initialize worker pool processes args = (id(self), s_name, vd_name, ks_names, fvar_names) @@ -1336,12 +1343,12 @@ def shutdown_worker_pool(self): """ if self.pool is not None: self.pool.close() + self.pool = None self._solved.shm.unlink() self._var_degs.shm.unlink() self._ks.shm.unlink() self._shared_fvars.shm.unlink() del self.__dict__['_shared_fvars'] - self.pool = None def _map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_thresh=None): r""" @@ -1590,7 +1597,9 @@ def _triangular_elim(self,eqns=None,verbose=True): sage: gb = f._par_graph_gb(verbose=False) sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey, poly_to_tup sage: f.ideal_basis = sorted(gb, key=poly_tup_sortkey) - sage: f._fvars = {sextuple : poly_to_tup(rhs) for sextuple, rhs in f._fvars.items()} + sage: from sage.combinat.root_system.shm_managers import FvarsHandler + sage: n = f._poly_ring.ngens() + sage: f._fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars) sage: f._triangular_elim() Elimination epoch completed... 0 eqns remain in ideal basis sage: f.ideal_basis @@ -1600,10 +1609,8 @@ def _triangular_elim(self,eqns=None,verbose=True): if eqns is None: eqns = self.ideal_basis ret = False + using_mp = self.pool is not None while True: - #Reset modification cache - if self.pool is not None: - self._fvars.fvars['modified'][:] = False linear_terms_exist = _solve_for_linear_terms(self,eqns) if not linear_terms_exist: break @@ -1613,7 +1620,7 @@ def _triangular_elim(self,eqns=None,verbose=True): self._update_reduction_params(eqns=eqns) # n = len(eqns) // worker_pool._processes ** 2 + 1 if worker_pool is not None else len(eqns) # eqns = [eqns[i:i+n] for i in range(0,len(eqns),n)] - if self.pool is not None and len(eqns) > self.mp_thresh: + if using_mp and len(eqns) > self.mp_thresh: n = self.pool._processes chunks = [[] for i in range(n)] for i, eq_tup in enumerate(eqns): @@ -1824,6 +1831,7 @@ def _get_component_variety(self,var,eqns): sage: f = FMatrix(FusionRing("G2",2)) sage: f.start_worker_pool() sage: f.get_defining_equations('hexagons',output=False) # long time + sage: f.shutdown_worker_pool() sage: partition = f._partition_eqns() # long time Partitioned 327 equations into 35 components of size: [27, 27, 27, 24, 24, 16, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, @@ -1833,7 +1841,6 @@ def _get_component_variety(self,var,eqns): sage: eqns = partition[c] + [poly_to_tup(f._poly_ring.gen(216)-1)] # long time sage: f._get_component_variety(c,eqns) # long time [{216: -1, 292: -1, 319: 1}] - sage: f.shutdown_worker_pool() """ #Define smaller poly ring in component vars R = PolynomialRing(self._FR.field(), len(var), 'a', order='lex') @@ -2120,7 +2127,6 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start #Loading from a pickle with solved F-symbols if self._chkpt_status > 5: return - #max(cpu_count()-1,1) if use_mp: self.start_worker_pool() if verbose: print("Computing F-symbols for {} with {} variables...".format(self._FR, self._poly_ring.ngens())) @@ -2132,10 +2138,12 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start #Report progress if verbose: print("Set up {} hex and orthogonality constraints...".format(len(self.ideal_basis))) - #Unzip _fvars and link to shared_memory structure if using multiprocessing - self._fvars = {sextuple: poly_to_tup(fvar) for sextuple, fvar in self._fvars.items()} + #Unzip _fvars and link to shared_memory structure if using multiprocessing if use_mp: self._fvars = self._shared_fvars + else: + n = self._poly_ring.ngens() + self._fvars = FvarsHandler(n,self._field,self._idx_to_sextuple,init_data=self._fvars) self._checkpoint(checkpoint,1,verbose=verbose) if self._chkpt_status < 2: diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index c42b95616fe..21b3a939b41 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -49,7 +49,9 @@ cpdef _solve_for_linear_terms(factory, list eqns=None): sage: f.ideal_basis = [poly_to_tup(p) for p in f.ideal_basis] sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey sage: f.ideal_basis.sort(key=poly_tup_sortkey) - sage: f._fvars = {sextuple : poly_to_tup(rhs) for sextuple, rhs in f._fvars.items()} + sage: from sage.combinat.root_system.shm_managers import FvarsHandler + sage: n = f._poly_ring.ngens() + sage: f._fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars) sage: from sage.combinat.root_system.fast_parallel_fmats_methods import _solve_for_linear_terms sage: _solve_for_linear_terms(f) True @@ -69,6 +71,8 @@ cpdef _solve_for_linear_terms(factory, list eqns=None): cdef ETuple exp, rhs_exp cdef int max_var cdef tuple eq_tup + cdef FvarsHandler fvars = factory._fvars + fvars.clear_modified() for eq_tup in eqns: # Only unflatten relevant polynomials if len(eq_tup) > 2: @@ -78,7 +82,7 @@ cpdef _solve_for_linear_terms(factory, list eqns=None): if len(eq_tup) == 1: vars = variables(eq_tup) if len(vars) == 1 and not factory._solved[vars[0]]: - factory._fvars[factory._idx_to_sextuple[vars[0]]] = tuple() + fvars[factory._idx_to_sextuple[vars[0]]] = tuple() factory._solved[vars[0]] = True linear_terms_exist = True if len(eq_tup) == 2: @@ -90,7 +94,7 @@ cpdef _solve_for_linear_terms(factory, list eqns=None): if not factory._solved[max_var]: rhs_exp = eq_tup[(idx+1) % 2][0] rhs_coeff = -eq_tup[(idx+1) % 2][1] / eq_tup[idx][1] - factory._fvars[factory._idx_to_sextuple[max_var]] = ((rhs_exp,rhs_coeff),) + fvars[factory._idx_to_sextuple[max_var]] = ((rhs_exp,rhs_coeff),) factory._solved[max_var] = True linear_terms_exist = True return linear_terms_exist @@ -111,7 +115,9 @@ cpdef _backward_subs(factory): sage: f.ideal_basis = [poly_to_tup(p) for p in f.ideal_basis] sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey sage: f.ideal_basis.sort(key=poly_tup_sortkey) - sage: f._fvars = {sextuple : poly_to_tup(rhs) for sextuple, rhs in f._fvars.items()} + sage: from sage.combinat.root_system.shm_managers import FvarsHandler + sage: n = f._poly_ring.ngens() + sage: f._fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars) sage: from sage.combinat.root_system.fast_parallel_fmats_methods import _solve_for_linear_terms sage: _solve_for_linear_terms(f) True diff --git a/src/sage/combinat/root_system/shm_managers.pxd b/src/sage/combinat/root_system/shm_managers.pxd index be13623646f..88f8f18aeef 100644 --- a/src/sage/combinat/root_system/shm_managers.pxd +++ b/src/sage/combinat/root_system/shm_managers.pxd @@ -19,5 +19,7 @@ cdef class FvarsHandler: cdef unsigned int ngens cdef fvars_t cdef NumberField field - cdef public np.ndarray fvars + cdef np.ndarray fvars cdef public shm + + cdef clear_modified(self) diff --git a/src/sage/combinat/root_system/shm_managers.pyx b/src/sage/combinat/root_system/shm_managers.pyx index 00522b84dcd..c8cefd53266 100644 --- a/src/sage/combinat/root_system/shm_managers.pyx +++ b/src/sage/combinat/root_system/shm_managers.pyx @@ -12,6 +12,7 @@ cimport cython from cysignals.memory cimport sig_malloc cimport numpy as np from sage.combinat.root_system.poly_tup_engine cimport poly_to_tup, tup_fixes_sq +from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular from sage.rings.polynomial.polydict cimport ETuple from multiprocessing import shared_memory @@ -303,7 +304,7 @@ def make_KSHandler(n_slots,field,init_data): return KSHandler(n_slots,field,init_data=init_data) cdef class FvarsHandler: - def __init__(self,n_slots,field,idx_to_sextuple,init_data={},name=None,max_terms=20): + def __init__(self,n_slots,field,idx_to_sextuple,use_mp=False,name=None,init_data={},max_terms=20): """ Return a shared memory backed dict-like structure to manage the ``_fvars`` attribute of an F-matrix factory object. @@ -360,7 +361,7 @@ cdef class FvarsHandler: sage: f = FMatrix(FusionRing("A2",1), inject_variables=True) creating variables fx1..fx8 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 - sage: fvars = FvarsHandler(8,f._field,f._idx_to_sextuple) + sage: fvars = FvarsHandler(8,f._field,f._idx_to_sextuple,use_mp=True) sage: #In the same shell or in a different shell, attach to fvars sage: fvars2 = FvarsHandler(8,f._field,f._idx_to_sextuple,name=fvars.shm.name) sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup @@ -382,17 +383,28 @@ cdef class FvarsHandler: self.sext_to_idx = {s: i for i, s in idx_to_sextuple.items()} self.ngens = n_slots if name is None: - self.shm = shared_memory.SharedMemory(create=True,size=self.ngens*self.fvars_t.itemsize) - self.fvars = np.ndarray((self.ngens,),dtype=self.fvars_t,buffer=self.shm.buf) + if use_mp: + self.shm = shared_memory.SharedMemory(create=True,size=self.ngens*self.fvars_t.itemsize) + self.fvars = np.ndarray((self.ngens,),dtype=self.fvars_t,buffer=self.shm.buf) + else: + self.fvars = np.ndarray((self.ngens,),dtype=self.fvars_t) else: self.shm = shared_memory.SharedMemory(name=name) self.fvars = np.ndarray((self.ngens,),dtype=self.fvars_t,buffer=self.shm.buf) #Populate with initialziation data for sextuple, fvar in init_data.items(): - if not isinstance(fvar, tuple): + if isinstance(fvar, MPolynomial_libsingular): fvar = poly_to_tup(fvar) + if isinstance(fvar, NumberFieldElement_absolute): + fvar = ((ETuple({},self.ngens), fvar),) self[sextuple] = fvar + cdef clear_modified(self): + """ + Reset tagged entries modified by parent process. + """ + self.fvars['modified'][:] = False + @cython.nonecheck(False) @cython.wraparound(False) def __getitem__(self, sextuple): @@ -411,7 +423,7 @@ cdef class FvarsHandler: sage: f = FMatrix(FusionRing("B7", 1), inject_variables=True) creating variables fx1..fx14 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 - sage: fvars = FvarsHandler(14,f._field,f._idx_to_sextuple) + sage: fvars = FvarsHandler(14,f._field,f._idx_to_sextuple,use_mp=True) sage: fvars[(f1, f2, f1, f2, f2, f2)] = poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx13**3 - 799/2881*fx1*fx2**5*fx10) sage: fvars[f2, f2, f2, f2, f0, f0] = poly_to_tup(f._poly_ring.zero()) sage: fvars[f2, f1, f2, f1, f2, f2] = poly_to_tup(-1/19*f._poly_ring.one()) @@ -480,7 +492,7 @@ cdef class FvarsHandler: sage: f = FMatrix(FusionRing("A3", 1), inject_variables=True) creating variables fx1..fx27 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19, fx20, fx21, fx22, fx23, fx24, fx25, fx26 - sage: fvars = FvarsHandler(27,f._field,f._idx_to_sextuple) + sage: fvars = FvarsHandler(27,f._field,f._idx_to_sextuple,use_mp=True) sage: fvars[(f3, f2, f1, f2, f1, f3)] = poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx21**3 - 799/2881*fx1*fx2**5*fx10) sage: fvars[f3, f2, f3, f0, f1, f1] = poly_to_tup(f._poly_ring.zero()) sage: fvars[f3, f3, f3, f1, f2, f2] = poly_to_tup(-1/19*f._poly_ring.one()) @@ -529,7 +541,7 @@ cdef class FvarsHandler: sage: f = FMatrix(FusionRing("F4",1)) sage: from sage.combinat.root_system.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() - sage: fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars) + sage: fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars,use_mp=True) sage: for s, fvar in loads(dumps(fvars)).items(): ....: assert f._fvars[s] == f._tup_to_fpoly(fvar) ....: @@ -573,7 +585,7 @@ def make_FvarsHandler(n,field,idx_map,init_data): sage: f = FMatrix(FusionRing("G2",1)) sage: from sage.combinat.root_system.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() - sage: fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars) + sage: fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars,use_mp=True) sage: for s, fvar in loads(dumps(fvars)).items(): # indirect doctest ....: assert f._fvars[s] == f._tup_to_fpoly(fvar) ....: From f7b2a9a893bf5496ccfe2236a54e3a4e0db8c3d9 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Mon, 3 May 2021 02:04:52 -0400 Subject: [PATCH 073/632] documentation touch up --- src/sage/combinat/root_system/f_matrix.py | 14 +++++++------- .../root_system/fast_parallel_fmats_methods.pyx | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index c8c0299e720..0aa2a1bdfa9 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -35,7 +35,7 @@ poly_to_tup, _tup_to_poly, tup_to_univ_poly, _unflatten_coeffs, poly_tup_sortkey, - resize, + resize ) from sage.combinat.root_system.shm_managers import KSHandler, FvarsHandler from sage.graphs.graph import Graph @@ -1479,6 +1479,12 @@ def get_defining_equations(self,option,output=True): F-matrix solver. When computing the hexagon equations with the ``output=False`` option, the initial state of the F-symbols is used. + .. NOTE:: + + To set up the defining equations using parallel processing, + use :meth:`start_worker_pool` to initialize multiple processes + *before* calling this method. + EXAMPLES:: sage: f = FMatrix(FusionRing("B2",1)) @@ -1497,12 +1503,6 @@ def get_defining_equations(self,option,output=True): sage: pe = f.get_defining_equations('pentagons') sage: len(pe) 33 - - .. NOTE:: - - To set up the defining equations using parallel processing, - use :meth:`start_worker_pool` to initialize multiple processes - *before* calling this method. """ if not hasattr(self, '_nnz'): self._reset_solver_state() diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index 21b3a939b41..cc245d717eb 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -321,10 +321,10 @@ cdef get_reduced_pentagons(factory, tuple mp_params): id_anyon = factory._FR.one() _field = factory._field cdef NumberFieldElement_absolute one = _field.one() + cdef MPolynomial_libsingular zero = factory._poly_ring.zero() + cdef KSHandler _ks = factory._ks factory._nnz = factory._get_known_nonz() cdef ETuple _nnz = factory._nnz - _ks = factory._ks - cdef MPolynomial_libsingular zero = factory._poly_ring.zero() #Computation loop it = product(basis,repeat=9) From 28bba61f9bc5aa51fdeec9afa83b5fd5cfd41126 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Mon, 3 May 2021 11:25:06 -0400 Subject: [PATCH 074/632] typed KSHandler in consumer methods --- src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx | 2 +- src/sage/combinat/root_system/poly_tup_engine.pxd | 2 +- src/sage/combinat/root_system/poly_tup_engine.pyx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index cc245d717eb..7b74852b233 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -352,7 +352,7 @@ cdef list update_reduce(factory, list eqns): #Pre-compute common parameters for speed _field = factory._field one = _field.one() - _ks = factory._ks + cdef KSHandler _ks = factory._ks #Update reduction params factory._nnz = factory._get_known_nonz() factory._kp = compute_known_powers(factory._var_degs,factory._get_known_vals(),factory._field.one()) diff --git a/src/sage/combinat/root_system/poly_tup_engine.pxd b/src/sage/combinat/root_system/poly_tup_engine.pxd index 7c889bce661..bf3a651363a 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pxd +++ b/src/sage/combinat/root_system/poly_tup_engine.pxd @@ -16,7 +16,7 @@ cpdef dict compute_known_powers(max_degs, dict val_dict, one) cdef dict subs(tuple poly_tup, dict known_powers, one) cpdef tup_to_univ_poly(tuple eq_tup, univ_poly_ring) cpdef tuple poly_tup_sortkey(tuple eq_tup) -cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, known_sq, NumberFieldElement_absolute one) +cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, KSHandler known_sq, NumberFieldElement_absolute one) cdef tuple _flatten_coeffs(tuple eq_tup) cpdef tuple _unflatten_coeffs(field, tuple eq_tup) cdef int has_appropriate_linear_term(tuple eq_tup) diff --git a/src/sage/combinat/root_system/poly_tup_engine.pyx b/src/sage/combinat/root_system/poly_tup_engine.pyx index 8db9e9f017e..bdd446ef675 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pyx +++ b/src/sage/combinat/root_system/poly_tup_engine.pyx @@ -426,7 +426,7 @@ cdef tuple to_monic(dict eq_dict, one): ret.append((ord_monoms[n-2-i], inv_lc * eq_dict[ord_monoms[n-2-i]])) return tuple(ret) -cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, known_sq, NumberFieldElement_absolute one): +cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, KSHandler known_sq, NumberFieldElement_absolute one): """ Return a tuple describing a monic polynomial with no known nonzero gcf and no known squares. From 9e4c552b215b74dab990c48f0743bc54899703d8 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Mon, 3 May 2021 17:58:57 -0400 Subject: [PATCH 075/632] usage of shm_handlers limited to cyclotomic field --- src/sage/combinat/root_system/f_matrix.py | 2 +- .../combinat/root_system/poly_tup_engine.pxd | 2 +- .../combinat/root_system/poly_tup_engine.pyx | 8 ++--- .../combinat/root_system/shm_managers.pyx | 34 +++++++++++++++---- 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 0aa2a1bdfa9..5e275b46bd9 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -2018,7 +2018,7 @@ def _get_explicit_solution(self,eqns=None,verbose=True): for fx, rhs in numeric_fvars.items(): self._fvars[self._idx_to_sextuple[fx]] = ((ETuple({},nvars),rhs),) _backward_subs(self) - self._fvars = {sextuple : constant_coeff(rhs) for sextuple, rhs in self._fvars.items()} + self._fvars = {sextuple : constant_coeff(rhs,self._field) for sextuple, rhs in self._fvars.items()} #Update base field attributes self._FR._field = self.field() diff --git a/src/sage/combinat/root_system/poly_tup_engine.pxd b/src/sage/combinat/root_system/poly_tup_engine.pxd index bf3a651363a..9be93601345 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pxd +++ b/src/sage/combinat/root_system/poly_tup_engine.pxd @@ -8,7 +8,7 @@ cpdef MPolynomial_libsingular _tup_to_poly(tuple eq_tup, MPolynomialRing_libsing cpdef tuple resize(tuple eq_tup, dict idx_map, int nvars) cpdef list get_variables_degrees(list eqns) cpdef list variables(tuple eq_tup) -cpdef constant_coeff(tuple eq_tup) +cpdef constant_coeff(tuple eq_tup, field) cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map) cdef bint tup_fixes_sq(tuple eq_tup) cdef dict subs_squares(dict eq_dict, KSHandler known_sq) diff --git a/src/sage/combinat/root_system/poly_tup_engine.pyx b/src/sage/combinat/root_system/poly_tup_engine.pyx index bdd446ef675..66d25b48551 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pyx +++ b/src/sage/combinat/root_system/poly_tup_engine.pyx @@ -277,7 +277,7 @@ cpdef list variables(tuple eq_tup): """ return degrees(eq_tup).nonzero_positions() -cpdef constant_coeff(tuple eq_tup): +cpdef constant_coeff(tuple eq_tup, field): r""" Return the constant coefficient of the polynomial represented by given tuple. @@ -287,18 +287,18 @@ cpdef constant_coeff(tuple eq_tup): sage: from sage.combinat.root_system.poly_tup_engine import constant_coeff sage: from sage.rings.polynomial.polydict import ETuple sage: poly_tup = ((ETuple([0,3,0]),2), (ETuple([0,1,0]),-1), (ETuple([0,0,0]),-2/3)) - sage: constant_coeff(poly_tup) + sage: constant_coeff(poly_tup,QQ) -2/3 sage: R. = PolynomialRing(QQ) sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup - sage: constant_coeff(poly_to_tup(x**5 + x*y*z - 9)) + sage: constant_coeff(poly_to_tup(x**5 + x*y*z - 9),QQ) -9 """ cdef ETuple exp for exp, coeff in eq_tup: if exp.is_constant(): return coeff - return 0 + return field.zero() cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map): """ diff --git a/src/sage/combinat/root_system/shm_managers.pyx b/src/sage/combinat/root_system/shm_managers.pyx index c8cefd53266..af7e157c004 100644 --- a/src/sage/combinat/root_system/shm_managers.pyx +++ b/src/sage/combinat/root_system/shm_managers.pyx @@ -1,5 +1,11 @@ """ -Shared memory managers for F-symbol attributes +Shared memory managers for F-symbol attributes. + +This module provides an implementation for shared dictionary like +state attributes required by the orthogonal F-matrix solver. + +Currently, the attributes only work when the base field of the FMatrix +factory is a cyclotomic field. """ # **************************************************************************** # Copyright (C) 2021 Guillermo Aboumrad @@ -12,6 +18,8 @@ cimport cython from cysignals.memory cimport sig_malloc cimport numpy as np from sage.combinat.root_system.poly_tup_engine cimport poly_to_tup, tup_fixes_sq +from sage.rings.integer cimport Integer +from sage.rings.rational cimport Rational from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular from sage.rings.polynomial.polydict cimport ETuple @@ -121,11 +129,16 @@ cdef class KSHandler: if self.obj_cache[idx] is not None: return self.obj_cache[idx] cdef unsigned int i, d + cdef Integer num, denom + cdef Rational quo cdef list rat = list() cdef NumberFieldElement_absolute cyc_coeff d = self.field.degree() for i in range(d): - rat.append(self.ks_dat['nums'][idx,i] / self.ks_dat['denoms'][idx,i]) + num = Integer(self.ks_dat['nums'][idx,i]) + denom = Integer(self.ks_dat['denoms'][idx,i]) + quo = num / denom + rat.append(quo) cyc_coeff = self.field(rat) self.obj_cache[idx] = cyc_coeff return cyc_coeff @@ -452,10 +465,12 @@ cdef class FvarsHandler: else: return self.obj_cache[idx] cdef ETuple e = ETuple({}, self.ngens) - cdef unsigned int cum, i, j, nnz - cdef unsigned long d - cdef list poly_tup = list() + cdef unsigned int cum, i, j, k, nnz + cdef Integer d, num + cdef list poly_tup, rats + cdef Rational quo cdef tuple ret + poly_tup = list() cum = 0 for i in range(np.count_nonzero(self.fvars['ticks'][idx])): #Construct new ETuple for each monomial @@ -469,8 +484,13 @@ cdef class FvarsHandler: cum += 1 #Construct cyclotomic field coefficient - d = self.fvars['coeff_denom'][idx,i] - cyc_coeff = self.field([num / d for num in self.fvars['coeff_nums'][idx,i]]) + d = Integer(self.fvars['coeff_denom'][idx,i]) + rats = list() + for k in range(self.field.degree()): + num = Integer(self.fvars['coeff_nums'][idx,i,k]) + quo = num / d + rats.append(quo) + cyc_coeff = self.field(rats) poly_tup.append((exp, cyc_coeff)) ret = tuple(poly_tup) From 0854a532648e96d5b00c283e951cb14b5ccb0d63 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Tue, 4 May 2021 17:20:25 -0400 Subject: [PATCH 076/632] turned on mp for update_reduce, turned off buggy FvarsHandler caching mechanism --- src/sage/combinat/root_system/f_matrix.py | 2 +- src/sage/combinat/root_system/shm_managers.pyx | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 5e275b46bd9..f92b815d840 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -1628,7 +1628,7 @@ def _triangular_elim(self,eqns=None,verbose=True): eqns = chunks else: eqns = [eqns] - eqns = self._map_triv_reduce('update_reduce',eqns,worker_pool=self.pool) + eqns = self._map_triv_reduce('update_reduce',eqns,worker_pool=self.pool,mp_thresh=0) eqns.sort(key=poly_tup_sortkey) if verbose: print("Elimination epoch completed... {} eqns remain in ideal basis".format(len(eqns))) diff --git a/src/sage/combinat/root_system/shm_managers.pyx b/src/sage/combinat/root_system/shm_managers.pyx index af7e157c004..271bf655ffb 100644 --- a/src/sage/combinat/root_system/shm_managers.pyx +++ b/src/sage/combinat/root_system/shm_managers.pyx @@ -459,11 +459,11 @@ cdef class FvarsHandler: if not sextuple in self.sext_to_idx: raise KeyError('Invalid sextuple {}'.format(sextuple)) cdef int idx = self.sext_to_idx[sextuple] - if idx in self.obj_cache: - if self.fvars['modified'][idx]: - del self.obj_cache[idx] - else: - return self.obj_cache[idx] + # if idx in self.obj_cache: + # if self.fvars['modified'][idx]: + # del self.obj_cache[idx] + # else: + # return self.obj_cache[idx] cdef ETuple e = ETuple({}, self.ngens) cdef unsigned int cum, i, j, k, nnz cdef Integer d, num @@ -494,7 +494,7 @@ cdef class FvarsHandler: poly_tup.append((exp, cyc_coeff)) ret = tuple(poly_tup) - self.obj_cache[idx] = ret + # self.obj_cache[idx] = ret return ret @cython.nonecheck(False) From 42d3e9e2bbc332dde69d97a2ce681ee02c8726e6 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Wed, 5 May 2021 12:53:01 -0400 Subject: [PATCH 077/632] using typed memoryviews in shm_managers to reduce python overhead and ensure correct typing --- .../combinat/root_system/shm_managers.pxd | 10 +- .../combinat/root_system/shm_managers.pyx | 111 +++++++++++------- 2 files changed, 77 insertions(+), 44 deletions(-) diff --git a/src/sage/combinat/root_system/shm_managers.pxd b/src/sage/combinat/root_system/shm_managers.pxd index 88f8f18aeef..77c74927915 100644 --- a/src/sage/combinat/root_system/shm_managers.pxd +++ b/src/sage/combinat/root_system/shm_managers.pxd @@ -7,7 +7,7 @@ cdef class KSHandler: cdef list obj_cache cdef np.ndarray ks_dat cdef NumberField field - cdef public shm + cdef public object shm cdef bint contains(self, int idx) cdef NumberFieldElement_absolute get(self, int idx) @@ -16,10 +16,12 @@ cdef class KSHandler: cdef class FvarsHandler: cdef dict sext_to_idx, obj_cache + cdef list modified_cache cdef unsigned int ngens - cdef fvars_t - cdef NumberField field + cdef object fvars_t cdef np.ndarray fvars - cdef public shm + cdef NumberField field + cdef public object shm cdef clear_modified(self) + # cdef update_cache(self) diff --git a/src/sage/combinat/root_system/shm_managers.pyx b/src/sage/combinat/root_system/shm_managers.pyx index 271bf655ffb..a802c4ee226 100644 --- a/src/sage/combinat/root_system/shm_managers.pyx +++ b/src/sage/combinat/root_system/shm_managers.pyx @@ -119,6 +119,7 @@ cdef class KSHandler: @cython.nonecheck(False) @cython.wraparound(False) + @cython.boundscheck(False) cdef NumberFieldElement_absolute get(self, int idx): """ Retrieve the known square corresponding to the given index, @@ -128,16 +129,23 @@ cdef class KSHandler: raise KeyError('Index {} does not correspond to a known square'.format(idx)) if self.obj_cache[idx] is not None: return self.obj_cache[idx] - cdef unsigned int i, d - cdef Integer num, denom - cdef Rational quo - cdef list rat = list() + cdef int d + cdef list rat + cdef Py_ssize_t i + cdef np.ndarray[np.int64_t,ndim=1] nums = self.ks_dat['nums'][idx] + # cdef np.int64_t[::1] num_view = nums + cdef np.ndarray[np.uint64_t,ndim=1] denoms = self.ks_dat['denoms'][idx] + # cdef np.uint64_t[::1] denom_view = denoms + cdef np.int64_t num + cdef np.uint64_t denom cdef NumberFieldElement_absolute cyc_coeff + cdef Rational quo d = self.field.degree() + rat = list() for i in range(d): - num = Integer(self.ks_dat['nums'][idx,i]) - denom = Integer(self.ks_dat['denoms'][idx,i]) - quo = num / denom + num = nums[i] + denom = denoms[i] + quo = Integer(num) / Integer(denom) rat.append(quo) cyc_coeff = self.field(rat) self.obj_cache[idx] = cyc_coeff @@ -191,9 +199,9 @@ cdef class KSHandler: This method assumes every polynomial in ``eqns`` is *monic*. """ - cdef unsigned int i, idx cdef ETuple lm cdef list rhs + cdef Py_ssize_t i, idx cdef tuple eq_tup for i in range(len(eqns)): eq_tup = eqns[i] @@ -202,7 +210,10 @@ cdef class KSHandler: #eq_tup is guaranteed univariate, so we extract variable idx from lm lm = eq_tup[0][0] idx = lm._data[0] - self.setitem(idx, rhs) + try: + self.setitem(idx, rhs) + except OverflowError: + print("KS overflowed on index {} with value {}".format(idx,self.field(rhs))) @cython.nonecheck(False) @cython.wraparound(False) @@ -214,17 +225,21 @@ cdef class KSHandler: The ``rhs`` parameter may be a cyclotomic coefficient or its list/tuple representation. """ - cdef unsigned int i - cdef long long num - cdef unsigned long long denom + cdef Py_ssize_t i + cdef np.ndarray[np.int64_t,ndim=1] nums = self.ks_dat['nums'][idx] + cdef np.ndarray[np.uint64_t,ndim=1] denoms = self.ks_dat['denoms'][idx] + cdef np.int64_t num + cdef np.uint64_t denom + cdef Rational quo self.ks_dat['known'][idx] = True if not isinstance(rhs, list): rhs = rhs._coefficients() for i in range(len(rhs)): - num = (rhs[i].numerator()) - denom = (rhs[i].denominator()) - self.ks_dat['nums'][idx,i] = num - self.ks_dat['denoms'][idx,i] = denom + quo = rhs[i] + num = quo.numerator() + denom = quo.denominator() + nums[i] = num + denoms[i] = denom cdef bint contains(self, int idx): """ @@ -362,8 +377,8 @@ cdef class FvarsHandler: .. WARNING:: - The current data structure supports up to 2**16-1 entries, - each with each monomial in each entry having at most 254 + The current data structure supports up to 2**16 entries, + with each monomial in each entry having at most 254 nonzero terms. On average, each of the ``max_terms`` monomials can have at most 50 terms. @@ -389,9 +404,9 @@ cdef class FvarsHandler: self.fvars_t = np.dtype([ ('modified','bool',(1,)), ('ticks', 'u1', (max_terms,)), - ('exp_data', 'u2', (max_terms*50,)), - ('coeff_nums','i4',(max_terms,d)), - ('coeff_denom','u4',(max_terms,)) + ('exp_data', 'u2', (max_terms*30,)), + ('coeff_nums',np.int64,(max_terms,d)), + ('coeff_denom',np.uint64,(max_terms,)) ]) self.sext_to_idx = {s: i for i, s in idx_to_sextuple.items()} self.ngens = n_slots @@ -458,40 +473,48 @@ cdef class FvarsHandler: """ if not sextuple in self.sext_to_idx: raise KeyError('Invalid sextuple {}'.format(sextuple)) - cdef int idx = self.sext_to_idx[sextuple] + cdef Py_ssize_t idx = self.sext_to_idx[sextuple] # if idx in self.obj_cache: # if self.fvars['modified'][idx]: # del self.obj_cache[idx] # else: # return self.obj_cache[idx] - cdef ETuple e = ETuple({}, self.ngens) - cdef unsigned int cum, i, j, k, nnz + cdef ETuple e, exp + cdef int nnz cdef Integer d, num cdef list poly_tup, rats + cdef NumberFieldElement_absolute cyc_coeff + cdef Py_ssize_t cum, i, j, k cdef Rational quo cdef tuple ret + #Define memory views to reduce Python overhead and ensure correct typing + cdef np.ndarray[np.uint8_t,ndim=1] ticks = self.fvars['ticks'][idx] + cdef np.ndarray[np.uint16_t,ndim=1] exp_data = self.fvars['exp_data'][idx] + cdef np.ndarray[np.int64_t,ndim=2] nums = self.fvars['coeff_nums'][idx] + cdef np.ndarray[np.uint64_t,ndim=1] denoms = self.fvars['coeff_denom'][idx] + e = ETuple({}, self.ngens) poly_tup = list() cum = 0 - for i in range(np.count_nonzero(self.fvars['ticks'][idx])): + for i in range(np.count_nonzero(ticks)): #Construct new ETuple for each monomial exp = e._new() #Handle constant coeff - nnz = self.fvars['ticks'][idx,i] if self.fvars['ticks'][idx,i] < 255 else 0 + nnz = ticks[i] if ticks[i] < 255 else 0 exp._nonzero = nnz + # if nnz: exp._data = sig_malloc(sizeof(int)*nnz*2) for j in range(2*nnz): - exp._data[j] = self.fvars['exp_data'][idx,cum] + exp._data[j] = exp_data[cum] cum += 1 #Construct cyclotomic field coefficient - d = Integer(self.fvars['coeff_denom'][idx,i]) + d = Integer(denoms[i]) rats = list() for k in range(self.field.degree()): - num = Integer(self.fvars['coeff_nums'][idx,i,k]) + num = Integer(nums[i,k]) quo = num / d rats.append(quo) cyc_coeff = self.field(rats) - poly_tup.append((exp, cyc_coeff)) ret = tuple(poly_tup) # self.obj_cache[idx] = ret @@ -525,30 +548,38 @@ cdef class FvarsHandler: True sage: fvars.shm.unlink() """ - cdef unsigned int cum, i, j, k, idx - cdef unsigned long denom - cdef long c cdef ETuple exp + cdef np.int64_t c + cdef np.uint64_t denom cdef NumberFieldElement_absolute coeff + cdef Py_ssize_t cum, i, idx, j, k idx = self.sext_to_idx[sextuple] #Clear entry before inserting self.fvars[idx] = np.zeros((1,), dtype=self.fvars_t) + #Define memory views to reduce Python overhead and ensure correct typing + cdef np.ndarray[np.uint8_t,ndim=1] ticks = self.fvars['ticks'][idx] + cdef np.ndarray[np.uint16_t,ndim=1] exp_data = self.fvars['exp_data'][idx] + cdef np.ndarray[np.int64_t,ndim=2] nums = self.fvars['coeff_nums'][idx] + cdef np.ndarray[np.uint64_t,ndim=1] denoms = self.fvars['coeff_denom'][idx] cum = 0 i = 0 for exp, coeff in fvar: #Handle constant coefficient if exp._nonzero > 0: - self.fvars['ticks'][idx,i] = exp._nonzero + ticks[i] = exp._nonzero else: - self.fvars['ticks'][idx,i] = -1 + ticks[i] = -1 for j in range(2*exp._nonzero): - self.fvars['exp_data'][idx,cum] = exp._data[j] + exp_data[cum] = exp._data[j] cum += 1 denom = coeff.denominator() - self.fvars['coeff_denom'][idx,i] = denom - for k, r in enumerate(coeff._coefficients()): - c = r * denom - self.fvars['coeff_nums'][idx,i,k] = c + denoms[i] = denom + k = 0 + for r in coeff._coefficients(): + assert Integer(r * denom).nbits() < 63, "OverflowError in FvarsHandler.setitem: c {}, r {}, denom {}".format(c, r, denom) + c = (r * denom) + nums[i,k] = c + k += 1 i += 1 self.fvars['modified'][idx] = True From 3f781285a983f1e869c1a50b09d08f76870d0f63 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Wed, 5 May 2021 20:34:32 -0400 Subject: [PATCH 078/632] adding tests for shared memory data structures --- src/sage/combinat/root_system/f_matrix.py | 22 ++++++++++++++++--- .../fast_parallel_fmats_methods.pyx | 10 +++++++++ .../combinat/root_system/poly_tup_engine.pxd | 2 +- .../combinat/root_system/poly_tup_engine.pyx | 2 +- .../combinat/root_system/shm_managers.pyx | 12 ++++++++-- 5 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index f92b815d840..68889703d8b 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -35,7 +35,8 @@ poly_to_tup, _tup_to_poly, tup_to_univ_poly, _unflatten_coeffs, poly_tup_sortkey, - resize + resize, + tup_fixes_sq ) from sage.combinat.root_system.shm_managers import KSHandler, FvarsHandler from sage.graphs.graph import Graph @@ -275,8 +276,8 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab self._FR = fusion_ring if inject_variables and (self._FR._fusion_labels is None): self._FR.fusion_labels(fusion_label, inject_variables=True) - if not self._FR.is_multiplicity_free(): - raise ValueError("FMatrix is only available for multiplicity free FusionRings") + # if not self._FR.is_multiplicity_free(): + # raise ValueError("FMatrix is only available for multiplicity free FusionRings") #Set up F-symbols entry by entry n_vars = self.findcases() self._poly_ring = PolynomialRing(self._FR.field(),n_vars,var_prefix) @@ -301,6 +302,10 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab self.mp_thresh = 10000 self.pool = None + #TESTS + self.test_fvars = dict() + self.test_ks = dict() + ####################### ### Class utilities ### ####################### @@ -1569,6 +1574,16 @@ def _update_reduction_params(self,eqns=None): if eqns is None: eqns = self.ideal_basis self._ks.update(eqns) + + #TESTS: + for i in range(len(eqns)): + eq_tup = eqns[i] + if tup_fixes_sq(eq_tup): + rhs = [-v for v in eq_tup[-1][1]] + self.test_ks[variables(eq_tup)[0]] = rhs + for i, sq in self._ks.items(): + assert sq == self._field(self.test_ks[i]), "{}: OG sq {}, shared sq {}".format(i, sq, self.test_ks[i]) + degs = get_variables_degrees(eqns) if degs: for i, d in enumerate(degs): @@ -2139,6 +2154,7 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start if verbose: print("Set up {} hex and orthogonality constraints...".format(len(self.ideal_basis))) #Unzip _fvars and link to shared_memory structure if using multiprocessing + self.test_fvars = {k: poly_to_tup(p) for k, p in self._fvars.items()} if use_mp: self._fvars = self._shared_fvars else: diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index 7b74852b233..bc7451a153b 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -85,6 +85,11 @@ cpdef _solve_for_linear_terms(factory, list eqns=None): fvars[factory._idx_to_sextuple[vars[0]]] = tuple() factory._solved[vars[0]] = True linear_terms_exist = True + + #TEST: + s = factory._idx_to_sextuple[vars[0]] + factory.test_fvars[s] = tuple() + assert factory.test_fvars[s] == fvars[s], "OG value {}, Shared: {}".format(fvars[s],factory.test_fvars[s]) if len(eq_tup) == 2: idx = has_appropriate_linear_term(eq_tup) if idx < 0: continue @@ -97,6 +102,11 @@ cpdef _solve_for_linear_terms(factory, list eqns=None): fvars[factory._idx_to_sextuple[max_var]] = ((rhs_exp,rhs_coeff),) factory._solved[max_var] = True linear_terms_exist = True + + #TEST: + s = factory._idx_to_sextuple[max_var] + factory.test_fvars[s] = ((rhs_exp,rhs_coeff),) + assert factory.test_fvars[s] == fvars[s], "OG value {}, Shared: {}".format(fvars[s],factory.test_fvars[s]) return linear_terms_exist cpdef _backward_subs(factory): diff --git a/src/sage/combinat/root_system/poly_tup_engine.pxd b/src/sage/combinat/root_system/poly_tup_engine.pxd index 9be93601345..81c13b94bac 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pxd +++ b/src/sage/combinat/root_system/poly_tup_engine.pxd @@ -10,7 +10,7 @@ cpdef list get_variables_degrees(list eqns) cpdef list variables(tuple eq_tup) cpdef constant_coeff(tuple eq_tup, field) cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map) -cdef bint tup_fixes_sq(tuple eq_tup) +cpdef bint tup_fixes_sq(tuple eq_tup) cdef dict subs_squares(dict eq_dict, KSHandler known_sq) cpdef dict compute_known_powers(max_degs, dict val_dict, one) cdef dict subs(tuple poly_tup, dict known_powers, one) diff --git a/src/sage/combinat/root_system/poly_tup_engine.pyx b/src/sage/combinat/root_system/poly_tup_engine.pyx index 66d25b48551..102825c6ad1 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pyx +++ b/src/sage/combinat/root_system/poly_tup_engine.pyx @@ -319,7 +319,7 @@ cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map): new_tup.append((exp, coeff_map(coeff))) return tuple(new_tup) -cdef inline bint tup_fixes_sq(tuple eq_tup): +cpdef inline bint tup_fixes_sq(tuple eq_tup): r""" Determine if given equation fixes the square of a variable. diff --git a/src/sage/combinat/root_system/shm_managers.pyx b/src/sage/combinat/root_system/shm_managers.pyx index a802c4ee226..1a7bd669347 100644 --- a/src/sage/combinat/root_system/shm_managers.pyx +++ b/src/sage/combinat/root_system/shm_managers.pyx @@ -238,6 +238,10 @@ cdef class KSHandler: quo = rhs[i] num = quo.numerator() denom = quo.denominator() + if num > 2**32: + print("Large num encountered in KS",num) + if denom > 2**32: + print("Large denom encountered in KS",denom) nums[i] = num denoms[i] = denom @@ -332,7 +336,7 @@ def make_KSHandler(n_slots,field,init_data): return KSHandler(n_slots,field,init_data=init_data) cdef class FvarsHandler: - def __init__(self,n_slots,field,idx_to_sextuple,use_mp=False,name=None,init_data={},max_terms=20): + def __init__(self,n_slots,field,idx_to_sextuple,use_mp=False,name=None,init_data={},max_terms=20,n_bytes=16): """ Return a shared memory backed dict-like structure to manage the ``_fvars`` attribute of an F-matrix factory object. @@ -576,8 +580,12 @@ cdef class FvarsHandler: denoms[i] = denom k = 0 for r in coeff._coefficients(): - assert Integer(r * denom).nbits() < 63, "OverflowError in FvarsHandler.setitem: c {}, r {}, denom {}".format(c, r, denom) + assert Integer(r * denom).nbits() <= 63, "OverflowError in FvarsHandler.setitem: c {}, r {}, denom {}".format(c, r, denom) c = (r * denom) + if c > 2**32: + print("Large num encountered",c) + if denom > 2**32: + print("Large denom encountered",denom) nums[i,k] = c k += 1 i += 1 From 48558843bdad2d0347604e2b719365f65d6d5a92 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Fri, 7 May 2021 11:05:45 -0400 Subject: [PATCH 079/632] large integer capability for FvarsHandler --- src/sage/combinat/root_system/f_matrix.py | 2 +- .../fast_parallel_fmats_methods.pxd | 2 +- .../fast_parallel_fmats_methods.pyx | 28 +++++-- .../combinat/root_system/shm_managers.pxd | 2 +- .../combinat/root_system/shm_managers.pyx | 73 +++++++++++-------- 5 files changed, 67 insertions(+), 40 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 68889703d8b..4fdd4b54892 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -2032,7 +2032,7 @@ def _get_explicit_solution(self,eqns=None,verbose=True): self._fvars = {sextuple : apply_coeff_map(rhs,phi) for sextuple, rhs in self._fvars.items()} for fx, rhs in numeric_fvars.items(): self._fvars[self._idx_to_sextuple[fx]] = ((ETuple({},nvars),rhs),) - _backward_subs(self) + _backward_subs(self,flatten=False) self._fvars = {sextuple : constant_coeff(rhs,self._field) for sextuple, rhs in self._fvars.items()} #Update base field attributes diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd index f4f12e9c0d5..e0908ab5884 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd @@ -1,4 +1,4 @@ cdef _fmat(fvars, Nk_ij, one, a, b, c, d, x, y) -cpdef _backward_subs(factory) +cpdef _backward_subs(factory, bint flatten=*) cpdef executor(tuple params) cpdef _solve_for_linear_terms(factory, list eqns=*) diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index bc7451a153b..2b3a158db68 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -72,12 +72,14 @@ cpdef _solve_for_linear_terms(factory, list eqns=None): cdef int max_var cdef tuple eq_tup cdef FvarsHandler fvars = factory._fvars + cdef NumberFieldElement_absolute coeff, other + cdef tuple rhs_coeff fvars.clear_modified() for eq_tup in eqns: # Only unflatten relevant polynomials - if len(eq_tup) > 2: - continue - eq_tup = _unflatten_coeffs(factory._field, eq_tup) + # if len(eq_tup) > 2: + # continue + # eq_tup = _unflatten_coeffs(factory._field, eq_tup) if len(eq_tup) == 1: vars = variables(eq_tup) @@ -98,7 +100,10 @@ cpdef _solve_for_linear_terms(factory, list eqns=None): max_var = exp._data[0] if not factory._solved[max_var]: rhs_exp = eq_tup[(idx+1) % 2][0] - rhs_coeff = -eq_tup[(idx+1) % 2][1] / eq_tup[idx][1] + # rhs_coeff = -eq_tup[(idx+1) % 2][1] / eq_tup[idx][1] + coeff = factory._field(list(eq_tup[(idx+1) % 2][1])) + other = factory._field(list(eq_tup[idx][1])) + rhs_coeff = tuple((-coeff / other)._coefficients()) fvars[factory._idx_to_sextuple[max_var]] = ((rhs_exp,rhs_coeff),) factory._solved[max_var] = True linear_terms_exist = True @@ -106,10 +111,10 @@ cpdef _solve_for_linear_terms(factory, list eqns=None): #TEST: s = factory._idx_to_sextuple[max_var] factory.test_fvars[s] = ((rhs_exp,rhs_coeff),) - assert factory.test_fvars[s] == fvars[s], "OG value {}, Shared: {}".format(fvars[s],factory.test_fvars[s]) + assert _unflatten_coeffs(factory._field,factory.test_fvars[s]) == fvars[s], "OG value {}, Shared: {}".format(factory.test_fvars[s],fvars[s]) return linear_terms_exist -cpdef _backward_subs(factory): +cpdef _backward_subs(factory, bint flatten=True): r""" Perform backward substitution on ``self.ideal_basis``, traversing variables in reverse lexicographical order. @@ -165,7 +170,11 @@ cpdef _backward_subs(factory): for var_idx in variables(rhs) if factory._solved[var_idx]} if d: kp = compute_known_powers(get_variables_degrees([rhs]), d, one) - factory._fvars[sextuple] = tuple(subs_squares(subs(rhs,kp,one), _ks).items()) + # factory._fvars[sextuple] = tuple(subs_squares(subs(rhs,kp,one), _ks).items()) + res = tuple(subs_squares(subs(rhs,kp,one), _ks).items()) + if flatten: + res = _flatten_coeffs(res) + factory._fvars[sextuple] = res cdef _fmat(fvars, _Nk_ij, id_anyon, a, b, c, d, x, y): """ @@ -272,7 +281,10 @@ cdef get_reduced_hexagons(factory, tuple mp_params): _ks = factory._ks #Computation loop - for i, sextuple in enumerate(product(basis, repeat=6)): + # for i, sextuple in enumerate(product(basis, repeat=6)): + it = product(basis, repeat=6) + for i in range(len(basis)**6): + sextuple = next(it) if i % n_proc == child_id: he = req_cy(basis,r_matrix,fvars,_Nk_ij,id_anyon,sextuple) if he: diff --git a/src/sage/combinat/root_system/shm_managers.pxd b/src/sage/combinat/root_system/shm_managers.pxd index 77c74927915..2a6a7e88ab4 100644 --- a/src/sage/combinat/root_system/shm_managers.pxd +++ b/src/sage/combinat/root_system/shm_managers.pxd @@ -17,7 +17,7 @@ cdef class KSHandler: cdef class FvarsHandler: cdef dict sext_to_idx, obj_cache cdef list modified_cache - cdef unsigned int ngens + cdef unsigned int ngens, bytes cdef object fvars_t cdef np.ndarray fvars cdef NumberField field diff --git a/src/sage/combinat/root_system/shm_managers.pyx b/src/sage/combinat/root_system/shm_managers.pyx index 1a7bd669347..8ff30d654e7 100644 --- a/src/sage/combinat/root_system/shm_managers.pyx +++ b/src/sage/combinat/root_system/shm_managers.pyx @@ -17,7 +17,7 @@ factory is a cyclotomic field. cimport cython from cysignals.memory cimport sig_malloc cimport numpy as np -from sage.combinat.root_system.poly_tup_engine cimport poly_to_tup, tup_fixes_sq +from sage.combinat.root_system.poly_tup_engine cimport poly_to_tup, tup_fixes_sq, _flatten_coeffs from sage.rings.integer cimport Integer from sage.rings.rational cimport Rational from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular @@ -336,7 +336,7 @@ def make_KSHandler(n_slots,field,init_data): return KSHandler(n_slots,field,init_data=init_data) cdef class FvarsHandler: - def __init__(self,n_slots,field,idx_to_sextuple,use_mp=False,name=None,init_data={},max_terms=20,n_bytes=16): + def __init__(self,n_slots,field,idx_to_sextuple,use_mp=False,name=None,init_data={},max_terms=20,n_bytes=32): """ Return a shared memory backed dict-like structure to manage the ``_fvars`` attribute of an F-matrix factory object. @@ -403,14 +403,16 @@ cdef class FvarsHandler: sage: fvars.shm.unlink() """ self.field = field - cdef int d = self.field.degree() self.obj_cache = dict() + cdef int d = self.field.degree() + self.bytes = n_bytes + cdef int slots = self.bytes // 8 self.fvars_t = np.dtype([ ('modified','bool',(1,)), ('ticks', 'u1', (max_terms,)), ('exp_data', 'u2', (max_terms*30,)), - ('coeff_nums',np.int64,(max_terms,d)), - ('coeff_denom',np.uint64,(max_terms,)) + ('coeff_nums',np.int64,(max_terms,d,slots)), + ('coeff_denom',np.uint64,(max_terms,d,slots)) ]) self.sext_to_idx = {s: i for i, s in idx_to_sextuple.items()} self.ngens = n_slots @@ -426,9 +428,9 @@ cdef class FvarsHandler: #Populate with initialziation data for sextuple, fvar in init_data.items(): if isinstance(fvar, MPolynomial_libsingular): - fvar = poly_to_tup(fvar) + fvar = _flatten_coeffs(poly_to_tup(fvar)) if isinstance(fvar, NumberFieldElement_absolute): - fvar = ((ETuple({},self.ngens), fvar),) + fvar = ((ETuple({},self.ngens), tuple(fvar._coefficients())),) self[sextuple] = fvar cdef clear_modified(self): @@ -494,8 +496,8 @@ cdef class FvarsHandler: #Define memory views to reduce Python overhead and ensure correct typing cdef np.ndarray[np.uint8_t,ndim=1] ticks = self.fvars['ticks'][idx] cdef np.ndarray[np.uint16_t,ndim=1] exp_data = self.fvars['exp_data'][idx] - cdef np.ndarray[np.int64_t,ndim=2] nums = self.fvars['coeff_nums'][idx] - cdef np.ndarray[np.uint64_t,ndim=1] denoms = self.fvars['coeff_denom'][idx] + cdef np.ndarray[np.int64_t,ndim=3] nums = self.fvars['coeff_nums'][idx] + cdef np.ndarray[np.uint64_t,ndim=3] denoms = self.fvars['coeff_denom'][idx] e = ETuple({}, self.ngens) poly_tup = list() cum = 0 @@ -512,11 +514,12 @@ cdef class FvarsHandler: cum += 1 #Construct cyclotomic field coefficient - d = Integer(denoms[i]) + # d = Integer(denoms[i]) rats = list() for k in range(self.field.degree()): - num = Integer(nums[i,k]) - quo = num / d + num = Integer(list(nums[i,k]),2**63) + denom = Integer(list(denoms[i,k]),2**64) + quo = num / denom rats.append(quo) cyc_coeff = self.field(rats) poly_tup.append((exp, cyc_coeff)) @@ -553,21 +556,24 @@ cdef class FvarsHandler: sage: fvars.shm.unlink() """ cdef ETuple exp - cdef np.int64_t c - cdef np.uint64_t denom - cdef NumberFieldElement_absolute coeff - cdef Py_ssize_t cum, i, idx, j, k + cdef Integer num, denom + cdef tuple coeff_tup + cdef Py_ssize_t cum, i, idx, j, k, t + cdef Rational r idx = self.sext_to_idx[sextuple] #Clear entry before inserting self.fvars[idx] = np.zeros((1,), dtype=self.fvars_t) #Define memory views to reduce Python overhead and ensure correct typing cdef np.ndarray[np.uint8_t,ndim=1] ticks = self.fvars['ticks'][idx] cdef np.ndarray[np.uint16_t,ndim=1] exp_data = self.fvars['exp_data'][idx] - cdef np.ndarray[np.int64_t,ndim=2] nums = self.fvars['coeff_nums'][idx] - cdef np.ndarray[np.uint64_t,ndim=1] denoms = self.fvars['coeff_denom'][idx] + cdef np.ndarray[np.int64_t,ndim=3] nums = self.fvars['coeff_nums'][idx] + cdef np.ndarray[np.uint64_t,ndim=3] denoms = self.fvars['coeff_denom'][idx] + cdef list digits + #Initialize denominators to 1 + denoms[:,:,0] = 1 cum = 0 i = 0 - for exp, coeff in fvar: + for exp, coeff_tup in fvar: #Handle constant coefficient if exp._nonzero > 0: ticks[i] = exp._nonzero @@ -576,17 +582,26 @@ cdef class FvarsHandler: for j in range(2*exp._nonzero): exp_data[cum] = exp._data[j] cum += 1 - denom = coeff.denominator() - denoms[i] = denom k = 0 - for r in coeff._coefficients(): - assert Integer(r * denom).nbits() <= 63, "OverflowError in FvarsHandler.setitem: c {}, r {}, denom {}".format(c, r, denom) - c = (r * denom) - if c > 2**32: - print("Large num encountered",c) - if denom > 2**32: - print("Large denom encountered",denom) - nums[i,k] = c + for r in coeff_tup: + num, denom = r.as_integer_ratio() + assert denom != 0, "zero denominator error" + if abs(num) > 2**63 or denom > 2**63: + print("Large integers encountered in FvarsHandler", num, denom) + if abs(num) < 2**63: + nums[i,k,0] = num + else: + digits = num.digits(2**63) + assert len(digits) <= self.bytes // 8, "Numerator {} is too large for shared FvarsHandler. Use at least {} bytes...".format(num,num.nbits()//8+1) + for t in range(len(digits)): + nums[i,k,t] = digits[t] + if denom < 2**64: + denoms[i,k,0] = denom + else: + digits = denom.digits(2**64) + assert len(digits) <= self.bytes // 8, "Denominator {} is too large for shared FvarsHandler. Use at least {} bytes...".format(denom,denom.nbits()//8+1) + for t in range(len(digits)): + denoms[i,k,t] = digits[t] k += 1 i += 1 self.fvars['modified'][idx] = True From edacea4a7317853ddc42bd7e9e50d8bd34b1c806 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Fri, 7 May 2021 15:38:23 -0400 Subject: [PATCH 080/632] updated doctests to conform with new FvarsHandler usage --- src/sage/combinat/root_system/f_matrix.py | 14 ++++++++-- .../combinat/root_system/shm_managers.pyx | 28 ++++++++++++++----- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 4fdd4b54892..5893840799a 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -276,8 +276,8 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab self._FR = fusion_ring if inject_variables and (self._FR._fusion_labels is None): self._FR.fusion_labels(fusion_label, inject_variables=True) - # if not self._FR.is_multiplicity_free(): - # raise ValueError("FMatrix is only available for multiplicity free FusionRings") + if not self._FR.is_multiplicity_free(): + raise ValueError("FMatrix is only available for multiplicity free FusionRings") #Set up F-symbols entry by entry n_vars = self.findcases() self._poly_ring = PolynomialRing(self._FR.field(),n_vars,var_prefix) @@ -1248,6 +1248,8 @@ def _restore_state(self,filename): self._chkpt_status = 7 return self._fvars, self._solved, self._ks, self.ideal_basis, self._chkpt_status = state + #TESTS: + self.test_ks = {i: sq for i, sq in self._ks.items()} self._update_reduction_params() ################# @@ -2154,7 +2156,13 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start if verbose: print("Set up {} hex and orthogonality constraints...".format(len(self.ideal_basis))) #Unzip _fvars and link to shared_memory structure if using multiprocessing - self.test_fvars = {k: poly_to_tup(p) for k, p in self._fvars.items()} + #TESTS: + for k, v in self._fvars.items(): + if isinstance(v, tuple): + self.test_fvars[k] = v + else: + self.test_fvars[k] = poly_to_tup(v) + # self.test_fvars = {k: poly_to_tup(p) for k, p in self._fvars.items()} if use_mp: self._fvars = self._shared_fvars else: diff --git a/src/sage/combinat/root_system/shm_managers.pyx b/src/sage/combinat/root_system/shm_managers.pyx index 8ff30d654e7..f1da0fd47fa 100644 --- a/src/sage/combinat/root_system/shm_managers.pyx +++ b/src/sage/combinat/root_system/shm_managers.pyx @@ -397,7 +397,8 @@ cdef class FvarsHandler: sage: #In the same shell or in a different shell, attach to fvars sage: fvars2 = FvarsHandler(8,f._field,f._idx_to_sextuple,name=fvars.shm.name) sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup - sage: fvars[f2, f1, f2, f2, f0, f0] = poly_to_tup(fx5**5) + sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(fx5**5)) + sage: fvars[f2, f1, f2, f2, f0, f0] = rhs sage: f._tup_to_fpoly(fvars2[f2, f1, f2, f2, f0, f0]) fx5^5 sage: fvars.shm.unlink() @@ -431,6 +432,13 @@ cdef class FvarsHandler: fvar = _flatten_coeffs(poly_to_tup(fvar)) if isinstance(fvar, NumberFieldElement_absolute): fvar = ((ETuple({},self.ngens), tuple(fvar._coefficients())),) + if isinstance(fvar, tuple): + transformed = list() + for exp, c in fvar: + if isinstance(c, NumberFieldElement_absolute): + transformed.append((exp,tuple(c._coefficients()))) + if transformed: + fvar = tuple(transformed) self[sextuple] = fvar cdef clear_modified(self): @@ -458,9 +466,12 @@ cdef class FvarsHandler: creating variables fx1..fx14 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 sage: fvars = FvarsHandler(14,f._field,f._idx_to_sextuple,use_mp=True) - sage: fvars[(f1, f2, f1, f2, f2, f2)] = poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx13**3 - 799/2881*fx1*fx2**5*fx10) - sage: fvars[f2, f2, f2, f2, f0, f0] = poly_to_tup(f._poly_ring.zero()) - sage: fvars[f2, f1, f2, f1, f2, f2] = poly_to_tup(-1/19*f._poly_ring.one()) + sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx13**3 - 799/2881*fx1*fx2**5*fx10)) + sage: fvars[(f1, f2, f1, f2, f2, f2)] = rhs + sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(f._poly_ring.zero())) + sage: fvars[f2, f2, f2, f2, f0, f0] = rhs + sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(-1/19*f._poly_ring.one())) + sage: fvars[f2, f1, f2, f1, f2, f2] = rhs sage: s, t, r = (f1, f2, f1, f2, f2, f2), (f2, f2, f2, f2, f0, f0), (f2, f1, f2, f1, f2, f2) sage: f._tup_to_fpoly(fvars[s]) == 1/8*fx0**15 - 23/79*fx2*fx13**3 - 799/2881*fx1*fx2**5*fx10 True @@ -543,9 +554,12 @@ cdef class FvarsHandler: creating variables fx1..fx27 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19, fx20, fx21, fx22, fx23, fx24, fx25, fx26 sage: fvars = FvarsHandler(27,f._field,f._idx_to_sextuple,use_mp=True) - sage: fvars[(f3, f2, f1, f2, f1, f3)] = poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx21**3 - 799/2881*fx1*fx2**5*fx10) - sage: fvars[f3, f2, f3, f0, f1, f1] = poly_to_tup(f._poly_ring.zero()) - sage: fvars[f3, f3, f3, f1, f2, f2] = poly_to_tup(-1/19*f._poly_ring.one()) + sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx21**3 - 799/2881*fx1*fx2**5*fx10)) + sage: fvars[(f3, f2, f1, f2, f1, f3)] = rhs + sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(f._poly_ring.zero())) + sage: fvars[f3, f2, f3, f0, f1, f1] = rhs + sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(-1/19*f._poly_ring.one())) + sage: fvars[f3, f3, f3, f1, f2, f2] = rhs sage: s, t, r = (f3, f2, f1, f2, f1, f3), (f3, f2, f3, f0, f1, f1), (f3, f3, f3, f1, f2, f2) sage: f._tup_to_fpoly(fvars[s]) == 1/8*fx0**15 - 23/79*fx2*fx21**3 - 799/2881*fx1*fx2**5*fx10 True From bda8e972b7ee1db872bf38e950d665de5743b173 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Sat, 8 May 2021 13:39:05 -0400 Subject: [PATCH 081/632] removed list conversion in singletons and improved findcases --- src/sage/combinat/root_system/f_matrix.py | 25 +++++++++++-------- .../combinat/root_system/poly_tup_engine.pyx | 2 +- .../combinat/root_system/shm_managers.pyx | 11 ++++---- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 5893840799a..e929614be8c 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -608,16 +608,21 @@ def findcases(self,output=False): if output: idx_map = dict() ret = dict() - for (a,b,c,d) in list(product(self._FR.basis(), repeat=4)): + # for (a,b,c,d) in list(product(self._FR.basis(), repeat=4)): + id_anyon = self._FR.one() + for (a,b,c,d) in product(self._FR.basis(), repeat=4): + if a == id_anyon or b == id_anyon or c == id_anyon: + continue for x in self.f_from(a, b, c, d): for y in self.f_to(a, b, c, d): - fm = self.fmat(a, b, c, d, x, y, data=False) - if fm is not None and fm not in [0,1]: - if output: - v = self._poly_ring.gens()[i] - ret[(a,b,c,d,x,y)] = v - idx_map[v] = (a, b, c, d, x, y) - i += 1 + # fm = self.fmat(a, b, c, d, x, y, data=False) + # if fm is not None and fm not in [0,1]: + if output: + # v = self._poly_ring.gens()[i] + v = self._poly_ring.gen(i) + ret[(a,b,c,d,x,y)] = v + idx_map[v] = (a, b, c, d, x, y) + i += 1 if output: return idx_map, ret else: @@ -640,7 +645,8 @@ def singletons(self): (ff,ft) = (self.f_from(a,b,c,d), self.f_to(a,b,c,d)) if len(ff) == 1 and len(ft) == 1: v = self._fvars.get((a,b,c,d,ff[0],ft[0]), None) - if v in self._poly_ring.gens(): + # if v in self._poly_ring.gens(): + if v in self._var_to_idx: ret.append(v) return ret @@ -2162,7 +2168,6 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start self.test_fvars[k] = v else: self.test_fvars[k] = poly_to_tup(v) - # self.test_fvars = {k: poly_to_tup(p) for k, p in self._fvars.items()} if use_mp: self._fvars = self._shared_fvars else: diff --git a/src/sage/combinat/root_system/poly_tup_engine.pyx b/src/sage/combinat/root_system/poly_tup_engine.pyx index 102825c6ad1..6ff2f3f1b32 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pyx +++ b/src/sage/combinat/root_system/poly_tup_engine.pyx @@ -123,7 +123,7 @@ cdef inline int has_appropriate_linear_term(tuple eq_tup): Determine whether the given tuple of pairs (of length 2) contains an *appropriate* linear term. - In this context, a linear term is said to be *appropriate* if the + In this context, a linear term is said to be *appropriate* if it is in the largest variable in the given polynomial (w.r.t. the degrevlex ordering), the monomial in which the linear term appears is univariate, and the linear term is not a common factor in diff --git a/src/sage/combinat/root_system/shm_managers.pyx b/src/sage/combinat/root_system/shm_managers.pyx index f1da0fd47fa..ab7389f2350 100644 --- a/src/sage/combinat/root_system/shm_managers.pyx +++ b/src/sage/combinat/root_system/shm_managers.pyx @@ -518,14 +518,13 @@ cdef class FvarsHandler: #Handle constant coeff nnz = ticks[i] if ticks[i] < 255 else 0 exp._nonzero = nnz - # if nnz: - exp._data = sig_malloc(sizeof(int)*nnz*2) - for j in range(2*nnz): - exp._data[j] = exp_data[cum] - cum += 1 + if nnz: + exp._data = sig_malloc(sizeof(int)*nnz*2) + for j in range(2*nnz): + exp._data[j] = exp_data[cum] + cum += 1 #Construct cyclotomic field coefficient - # d = Integer(denoms[i]) rats = list() for k in range(self.field.degree()): num = Integer(list(nums[i,k]),2**63) From ee5aed59fa1049bdc7806ac0101c1a4dfbd9be85 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Mon, 17 May 2021 15:21:41 -0400 Subject: [PATCH 082/632] some housekeeping --- src/sage/combinat/root_system/f_matrix.py | 8 +++---- .../fast_parallel_fmats_methods.pyx | 23 +++++++++---------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index e929614be8c..ba4d2f73cc2 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -353,7 +353,7 @@ def clear_vars(self): sage: f.get_fvars()[some_key] fx0 """ - self._fvars = {self._var_to_sextuple[key] : key for key in self._var_to_sextuple} + self._fvars = {t : fx for fx, t in self._var_to_sextuple.items()} self._solved = list(False for fx in self._fvars) def _reset_solver_state(self): @@ -430,7 +430,7 @@ def _update_poly_ring_base_field(self,field): new_poly_ring = self._poly_ring.change_ring(field) nvars = self._poly_ring.ngens() #Do some appropriate conversions - self._singles = [new_poly_ring.gen(self._var_to_idx[fx]) for fx in self._singles] + self._singles = [new_poly_ring(fx) for fx in self._singles] self._var_to_idx = {new_poly_ring.gen(i): i for i in range(nvars)} self._var_to_sextuple = {new_poly_ring.gen(i): self._var_to_sextuple[self._poly_ring.gen(i)] for i in range(nvars)} self._poly_ring = new_poly_ring @@ -641,12 +641,12 @@ def singletons(self): True """ ret = [] + gens = set(self._poly_ring.gens()) for (a, b, c, d) in product(self._FR.basis(), repeat=4): (ff,ft) = (self.f_from(a,b,c,d), self.f_to(a,b,c,d)) if len(ff) == 1 and len(ft) == 1: v = self._fvars.get((a,b,c,d,ff[0],ft[0]), None) - # if v in self._poly_ring.gens(): - if v in self._var_to_idx: + if v in gens: ret.append(v) return ret diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index 2b3a158db68..f086e637c1f 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -161,20 +161,20 @@ cpdef _backward_subs(factory, bint flatten=True): """ one = factory._field.one() _ks = factory._ks - vars = factory._poly_ring.gens() - n = len(vars) - for var in reversed(vars): - sextuple = factory._var_to_sextuple[var] - rhs = factory._fvars[sextuple] - d = {var_idx: factory._fvars[factory._idx_to_sextuple[var_idx]] - for var_idx in variables(rhs) if factory._solved[var_idx]} + fvars = factory._fvars + idx_to_sextuple = factory._idx_to_sextuple + solved = factory._solved + for i in range(len(idx_to_sextuple)-1,-1,-1): + sextuple = idx_to_sextuple[i] + rhs = fvars[sextuple] + d = {var_idx: fvars[idx_to_sextuple[var_idx]] + for var_idx in variables(rhs) if solved[var_idx]} if d: kp = compute_known_powers(get_variables_degrees([rhs]), d, one) - # factory._fvars[sextuple] = tuple(subs_squares(subs(rhs,kp,one), _ks).items()) - res = tuple(subs_squares(subs(rhs,kp,one), _ks).items()) + res = tuple(subs_squares(subs(rhs,kp,one),_ks).items()) if flatten: res = _flatten_coeffs(res) - factory._fvars[sextuple] = res + fvars[sextuple] = res cdef _fmat(fvars, _Nk_ij, id_anyon, a, b, c, d, x, y): """ @@ -258,11 +258,10 @@ cdef get_reduced_hexagons(factory, tuple mp_params): #Pre-compute common parameters for speed cdef tuple basis = tuple(factory._FR.basis()) - # cdef dict fvars = factory._fvars cdef dict fvars cdef bint must_zip_up if not output: - fvars = {v: k for k, v in factory._var_to_sextuple.items()} + fvars = {s: factory._poly_ring.gen(i) for i, s in factory._idx_to_sextuple.items()} else: #Handle both cyclotomic and orthogonal solution method for k, v in factory._fvars.items(): From 9d9a56e556f7c6baf20ffd804b978d506a9c215a Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Mon, 17 May 2021 16:33:07 -0400 Subject: [PATCH 083/632] local shared_memory imports, min required version warnings in documentation --- src/sage/combinat/root_system/f_matrix.py | 41 ++++++++++++----- src/sage/combinat/root_system/fusion_ring.py | 4 +- .../combinat/root_system/shm_managers.pyx | 45 +++++++++++-------- 3 files changed, 60 insertions(+), 30 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index ba4d2f73cc2..3baa26639a9 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -20,7 +20,7 @@ from copy import deepcopy from ctypes import cast, py_object from itertools import product, zip_longest -from multiprocessing import cpu_count, Pool, set_start_method, shared_memory +from multiprocessing import cpu_count, Pool, set_start_method import numpy as np from os import remove @@ -1279,7 +1279,8 @@ def start_worker_pool(self,processes=None): EXAMPLES:: sage: f = FMatrix(FusionRing("G2",1)) - sage: f.start_worker_pool() + sage: if sys.version_info.minor >= 8: + ....: f.start_worker_pool() # Python 3.8+ is required sage: he = f.get_defining_equations('hexagons') sage: sorted(he) [fx0 - 1, @@ -1302,7 +1303,17 @@ class methods. Failure to call :meth:`shutdown_worker_pool` may result in a memory leak, since shared memory resources outlive the process that created them. + + .. WARNING:: + + Python 3.8+ is required. This method will raise an ``ImportError`` + if your system does not meet the version requirement. """ + #Try to import module requiring Python 3.8+ locally + try: + from multiprocessing import shared_memory + except ImportError: + raise ImportError("Failed to import shared_memory module. Requires Python 3.8+") try: set_start_method('fork') except RuntimeError: @@ -1330,8 +1341,8 @@ def init(fmats_id, solved_name, vd_name, ks_names, fvar_names): fmats_obj._solved = shared_memory.ShareableList(name=solved_name) fmats_obj._var_degs = shared_memory.ShareableList(name=vd_name) n = fmats_obj._poly_ring.ngens() - fmats_obj._fvars = FvarsHandler(n,fmats_obj._field,fmats_obj._idx_to_sextuple,name=fvar_names) - fmats_obj._ks = KSHandler(n,fmats_obj._field,name=ks_names) + fmats_obj._fvars = FvarsHandler(n,fmats_obj._field,fmats_obj._idx_to_sextuple,name=fvar_names,use_mp=True) + fmats_obj._ks = KSHandler(n,fmats_obj._field,name=ks_names,use_mp=True) self.pool = Pool(processes=n,initializer=init,initargs=args) @@ -1350,7 +1361,8 @@ def shutdown_worker_pool(self): EXAMPLES:: sage: f = FMatrix(FusionRing("A1",3)) - sage: f.start_worker_pool() + sage: if sys.version_info.minor >= 8: + ....: f.start_worker_pool() # Python 3.8+ is required sage: he = f.get_defining_equations('hexagons') sage: f.shutdown_worker_pool() """ @@ -1389,7 +1401,8 @@ def _map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_t sage: f._reset_solver_state() sage: len(f._map_triv_reduce('get_reduced_hexagons',[(0,1,False)])) 11 - sage: f.start_worker_pool() + sage: if sys.version_info.minor >= 8: + ....: f.start_worker_pool() # Python 3.8+ is required sage: mp_params = [(i,f.pool._processes,True) for i in range(f.pool._processes)] sage: len(f._map_triv_reduce('get_reduced_pentagons',mp_params,worker_pool=f.pool,chunksize=1,mp_thresh=0)) 33 @@ -1550,7 +1563,8 @@ def _tup_to_fpoly(self,eq_tup): sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup sage: f = FMatrix(FusionRing("C3",1)) - sage: f.start_worker_pool() + sage: if sys.version_info.minor >= 8: + ....: f.start_worker_pool() # Python 3.8+ is required sage: he = f.get_defining_equations('hexagons') sage: all(f._tup_to_fpoly(poly_to_tup(h)) for h in he) True @@ -1567,7 +1581,8 @@ def _update_reduction_params(self,eqns=None): sage: f = FMatrix(FusionRing("A1",3)) sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) - sage: f.start_worker_pool() + sage: if sys.version_info.minor >= 8: + ....: f.start_worker_pool() # Python 3.8+ is required sage: f.get_defining_equations('hexagons',output=False) sage: f.ideal_basis = f._par_graph_gb(verbose=False) sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey, poly_to_tup @@ -1796,7 +1811,8 @@ def _par_graph_gb(self,eqns=None,term_order="degrevlex",verbose=True): sage: f = FMatrix(FusionRing("F4",1)) sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) - sage: f.start_worker_pool() + sage: if sys.version_info.minor >= 8: + ....: f.start_worker_pool() # Python 3.8+ is required sage: f.get_defining_equations('hexagons',output=False) sage: gb = f._par_graph_gb() Partitioned 10 equations into 2 components of size: @@ -1852,7 +1868,8 @@ def _get_component_variety(self,var,eqns): EXAMPLES:: sage: f = FMatrix(FusionRing("G2",2)) - sage: f.start_worker_pool() + sage: if sys.version_info.minor >= 8: + ....: f.start_worker_pool() # Python 3.8+ is required sage: f.get_defining_equations('hexagons',output=False) # long time sage: f.shutdown_worker_pool() sage: partition = f._partition_eqns() # long time @@ -2087,7 +2104,9 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start - ``use_mp`` -- (default: ``True``) a boolean indicating whether to use multiprocessing to speed up calculation. The default value ``True`` is highly recommended, since parallel processing yields - results much more quickly. + results much more quickly. Python 3.8+ is required. This method will + raise an error if it cannot import the necessary components + (``shared_memory`` sub-module of ``multiprocessing``). - ``verbose`` -- (default: ``True``) a boolean indicating whether the solver should print out intermediate progress reports. diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index db546190cb7..9555869a73d 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -1249,7 +1249,9 @@ def get_braid_generators(self, we don't run the solver again. - ``use_mp`` -- (default: ``True``) a boolean indicating whether to use multiprocessing to speed up the computation; this is - highly recommended + highly recommended. Python 3.8+ is required. This method will + raise an error if it cannot import the necessary components + (``shared_memory`` sub-module of ``multiprocessing``). - ``verbose`` -- (default: ``True``) boolean indicating whether to be verbose with the computation diff --git a/src/sage/combinat/root_system/shm_managers.pyx b/src/sage/combinat/root_system/shm_managers.pyx index ab7389f2350..f54e902e4e7 100644 --- a/src/sage/combinat/root_system/shm_managers.pyx +++ b/src/sage/combinat/root_system/shm_managers.pyx @@ -23,7 +23,6 @@ from sage.rings.rational cimport Rational from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular from sage.rings.polynomial.polydict cimport ETuple -from multiprocessing import shared_memory import numpy as np cdef class KSHandler: @@ -67,7 +66,7 @@ cdef class KSHandler: .. WARNING:: - This structure does *not* cannot modify an entry that + This structure *cannot* modify an entry that has already been set. EXAMPLES:: @@ -80,7 +79,7 @@ cdef class KSHandler: sage: n = f._poly_ring.ngens() sage: ks = KSHandler(n,f._field,use_mp=True) sage: #In the same shell or in a different shell, attach to fvars - sage: ks2 = KSHandler(n,f._field,name=ks.shm.name) + sage: ks2 = KSHandler(n,f._field,name=ks.shm.name,use_mp=True) sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup sage: eqns = [fx1**2 - 4, fx3**2 + f._field.gen()**4 - 1/19*f._field.gen()**2] sage: ks.update([poly_to_tup(p) for p in eqns]) @@ -101,18 +100,23 @@ cdef class KSHandler: ('denoms','u8',(d,)) ]) self.obj_cache = [None]*n - if name is None: - if use_mp: + if use_mp: + #Try to import module requiring Python 3.8+ locally + try: + from multiprocessing import shared_memory + except ImportError: + raise ImportError("Failed to import shared_memory module. Requires Python 3.8+") + if name is None: self.shm = shared_memory.SharedMemory(create=True,size=n*ks_t.itemsize) - self.ks_dat = np.ndarray((n,),dtype=ks_t,buffer=self.shm.buf) else: - self.ks_dat = np.ndarray((n,),dtype=ks_t) + self.shm = shared_memory.SharedMemory(name=name) + self.ks_dat = np.ndarray((n,),dtype=ks_t,buffer=self.shm.buf) + else: + self.ks_dat = np.ndarray((n,),dtype=ks_t) + if name is None: self.ks_dat['known'] = np.zeros((n,1),dtype='bool') self.ks_dat['nums'] = np.zeros((n,d),dtype='i8') self.ks_dat['denoms'] = np.ones((n,d),dtype='u8') - else: - self.shm = shared_memory.SharedMemory(name=name) - self.ks_dat = np.ndarray((n,),dtype=ks_t,buffer=self.shm.buf) #Populate initializer data for idx, sq in init_data.items(): self.setitem(idx,sq) @@ -395,7 +399,7 @@ cdef class FvarsHandler: Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 sage: fvars = FvarsHandler(8,f._field,f._idx_to_sextuple,use_mp=True) sage: #In the same shell or in a different shell, attach to fvars - sage: fvars2 = FvarsHandler(8,f._field,f._idx_to_sextuple,name=fvars.shm.name) + sage: fvars2 = FvarsHandler(8,f._field,f._idx_to_sextuple,name=fvars.shm.name,use_mp=True) sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(fx5**5)) sage: fvars[f2, f1, f2, f2, f0, f0] = rhs @@ -417,15 +421,19 @@ cdef class FvarsHandler: ]) self.sext_to_idx = {s: i for i, s in idx_to_sextuple.items()} self.ngens = n_slots - if name is None: - if use_mp: + if use_mp: + #Try to import module requiring Python 3.8+ locally + try: + from multiprocessing import shared_memory + except ImportError: + raise ImportError("Failed to import shared_memory module. Requires Python 3.8+") + if name is None: self.shm = shared_memory.SharedMemory(create=True,size=self.ngens*self.fvars_t.itemsize) - self.fvars = np.ndarray((self.ngens,),dtype=self.fvars_t,buffer=self.shm.buf) else: - self.fvars = np.ndarray((self.ngens,),dtype=self.fvars_t) - else: - self.shm = shared_memory.SharedMemory(name=name) + self.shm = shared_memory.SharedMemory(name=name) self.fvars = np.ndarray((self.ngens,),dtype=self.fvars_t,buffer=self.shm.buf) + else: + self.fvars = np.ndarray((self.ngens,),dtype=self.fvars_t) #Populate with initialziation data for sextuple, fvar in init_data.items(): if isinstance(fvar, MPolynomial_libsingular): @@ -652,7 +660,8 @@ cdef class FvarsHandler: sage: f = FMatrix(FusionRing("G2", 1), inject_variables=True) creating variables fx1..fx5 Defining fx0, fx1, fx2, fx3, fx4 - sage: f.start_worker_pool() + sage: if sys.version_info.minor >= 8: + ....: f.start_worker_pool() # Python 3.8+ is required sage: for sextuple, fvar in f._shared_fvars.items(): ....: if sextuple == (f1, f1, f1, f1, f1, f1): ....: f._tup_to_fpoly(fvar) From b819ed95f45940363ec6f9f0b5d8c53f3c1e8072 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Tue, 18 May 2021 14:54:54 -0400 Subject: [PATCH 084/632] switch to single process if shared_memory is not available --- src/sage/combinat/root_system/f_matrix.py | 44 +++++++++++++------ .../fast_parallel_fmats_methods.pyx | 1 - .../combinat/root_system/shm_managers.pyx | 8 ++-- 3 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 3baa26639a9..6f1441ff9eb 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -1268,14 +1268,32 @@ def start_worker_pool(self,processes=None): which may be used e.g. to set up defining equations using :meth:`get_defining_equations`. - This method creates the attribute ``self.pool``, and the worker + This method sets ``self``'s ``pool`` attribute. The worker pool may be used time and again. Upon initialization, each process - in the pool is attached to the necessary shared memory resources. + in the pool attaches to the necessary shared memory resources. When you are done using the worker pool, use :meth:`shutdown_worker_pool` to close the pool and properly dispose of shared memory resources. + .. NOTE:: + + Python 3.8+ is required, since the ``multiprocessing.shared_memory`` + module must be imported. If we fail to import the ``shared_memory`` + module, ``self.pool`` is set to ``None``, no worker pool is created, + and all subsequent calculations will run serially. + + INPUT:: + + - ``processes`` -- an integer indicating the number of workers + in the pool. If left unspecified, the number of workers is + equals the number of processors available. + + OUTPUT:: + + Returns a boolean indicating whether a worker pool was successfully + initialized. + EXAMPLES:: sage: f = FMatrix(FusionRing("G2",1)) @@ -1303,17 +1321,13 @@ class methods. Failure to call :meth:`shutdown_worker_pool` may result in a memory leak, since shared memory resources outlive the process that created them. - - .. WARNING:: - - Python 3.8+ is required. This method will raise an ``ImportError`` - if your system does not meet the version requirement. """ #Try to import module requiring Python 3.8+ locally try: from multiprocessing import shared_memory except ImportError: - raise ImportError("Failed to import shared_memory module. Requires Python 3.8+") + self.pool = None + return False try: set_start_method('fork') except RuntimeError: @@ -1345,6 +1359,7 @@ def init(fmats_id, solved_name, vd_name, ks_names, fvar_names): fmats_obj._ks = KSHandler(n,fmats_obj._field,name=ks_names,use_mp=True) self.pool = Pool(processes=n,initializer=init,initargs=args) + return True def shutdown_worker_pool(self): """ @@ -2104,9 +2119,10 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start - ``use_mp`` -- (default: ``True``) a boolean indicating whether to use multiprocessing to speed up calculation. The default value ``True`` is highly recommended, since parallel processing yields - results much more quickly. Python 3.8+ is required. This method will - raise an error if it cannot import the necessary components - (``shared_memory`` sub-module of ``multiprocessing``). + results much more quickly. Python 3.8+ is required, since the + ``multiprocessing.shared_memory`` module must be imported. If we + fail to import the ``shared_memory`` module, the solver runs + serially. - ``verbose`` -- (default: ``True``) a boolean indicating whether the solver should print out intermediate progress reports. @@ -2169,7 +2185,9 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start #Loading from a pickle with solved F-symbols if self._chkpt_status > 5: return - if use_mp: self.start_worker_pool() + loads_shared_memory = False + if use_mp: + loads shared_memory = self.start_worker_pool() if verbose: print("Computing F-symbols for {} with {} variables...".format(self._FR, self._poly_ring.ngens())) @@ -2187,7 +2205,7 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start self.test_fvars[k] = v else: self.test_fvars[k] = poly_to_tup(v) - if use_mp: + if use_mp and loads_shared_memory: self._fvars = self._shared_fvars else: n = self._poly_ring.ngens() diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index f086e637c1f..f2f09a038e4 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -25,7 +25,6 @@ from sage.rings.polynomial.polydict cimport ETuple from ctypes import cast, py_object from itertools import product -from multiprocessing import shared_memory from sage.rings.ideal import Ideal from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing diff --git a/src/sage/combinat/root_system/shm_managers.pyx b/src/sage/combinat/root_system/shm_managers.pyx index f54e902e4e7..e2a646b9269 100644 --- a/src/sage/combinat/root_system/shm_managers.pyx +++ b/src/sage/combinat/root_system/shm_managers.pyx @@ -318,7 +318,7 @@ cdef class KSHandler: Index: 25, sq: 1 Index: 26, sq: 1 """ - cdef unsigned int i + cdef Py_ssize_t i for i in range(self.ks_dat.size): if self.ks_dat['known'][i]: yield i, self.get(i) @@ -505,7 +505,7 @@ cdef class FvarsHandler: # else: # return self.obj_cache[idx] cdef ETuple e, exp - cdef int nnz + cdef int count, nnz cdef Integer d, num cdef list poly_tup, rats cdef NumberFieldElement_absolute cyc_coeff @@ -520,7 +520,9 @@ cdef class FvarsHandler: e = ETuple({}, self.ngens) poly_tup = list() cum = 0 - for i in range(np.count_nonzero(ticks)): + # for i in range(np.count_nonzero(ticks)): + count = np.count_nonzero(ticks) + for i in range(count): #Construct new ETuple for each monomial exp = e._new() #Handle constant coeff From f7cbee769faa284566b792c8f47337d5c2939869 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Wed, 19 May 2021 12:45:11 -0400 Subject: [PATCH 085/632] syntax error loads shared_memory corrected --- src/sage/combinat/root_system/f_matrix.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 6f1441ff9eb..cc06159d657 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -1283,16 +1283,16 @@ def start_worker_pool(self,processes=None): module, ``self.pool`` is set to ``None``, no worker pool is created, and all subsequent calculations will run serially. - INPUT:: + INPUT: - ``processes`` -- an integer indicating the number of workers in the pool. If left unspecified, the number of workers is equals the number of processors available. - OUTPUT:: + OUTPUT: - Returns a boolean indicating whether a worker pool was successfully - initialized. + This method returns a boolean indicating whether a worker pool + was successfully initialized. EXAMPLES:: @@ -2187,7 +2187,7 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start return loads_shared_memory = False if use_mp: - loads shared_memory = self.start_worker_pool() + loads_shared_memory = self.start_worker_pool() if verbose: print("Computing F-symbols for {} with {} variables...".format(self._FR, self._poly_ring.ngens())) From 0dbea214bcb9bf3e90adc438f0ecdadbc842d773 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Thu, 20 May 2021 16:42:08 -0400 Subject: [PATCH 086/632] corrected doctest usage for Python < 3.8 --- src/sage/combinat/root_system/f_matrix.py | 42 +++++------ .../combinat/root_system/shm_managers.pyx | 70 ++++++++++++++----- 2 files changed, 72 insertions(+), 40 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index cc06159d657..e31a823c96b 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -208,13 +208,12 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab :: - sage: f.get_defining_equations("pentagons")[1:3] - [fx9*fx12 - fx2*fx13, fx3*fx8 - fx4*fx9] - sage: f.get_defining_equations("hexagons")[1:3] - [fx11*fx12 + (-zeta128^32)*fx13^2 + (-zeta128^48)*fx13, - fx10*fx11 + (-zeta128^32)*fx11*fx13 + (-zeta128^16)*fx11] - sage: f.get_orthogonality_constraints()[1:3] - [fx1^2 - 1, fx2^2 - 1] + sage: sorted(f.get_defining_equations("pentagons"))[1:3] + [fx9*fx12 - fx2*fx13, fx4*fx11 - fx2*fx13] + sage: sorted(f.get_defining_equations("hexagons"))[1:3] + [fx6 - 1, fx2 + 1] + sage: sorted(f.get_orthogonality_constraints())[1:3] + [fx10*fx11 + fx12*fx13, fx10*fx11 + fx12*fx13] There are two methods available to compute an F-matrix. The first, :meth:`find_cyclotomic_solution` uses only @@ -1297,8 +1296,7 @@ def start_worker_pool(self,processes=None): EXAMPLES:: sage: f = FMatrix(FusionRing("G2",1)) - sage: if sys.version_info.minor >= 8: - ....: f.start_worker_pool() # Python 3.8+ is required + sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ sage: he = f.get_defining_equations('hexagons') sage: sorted(he) [fx0 - 1, @@ -1376,8 +1374,7 @@ def shutdown_worker_pool(self): EXAMPLES:: sage: f = FMatrix(FusionRing("A1",3)) - sage: if sys.version_info.minor >= 8: - ....: f.start_worker_pool() # Python 3.8+ is required + sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ sage: he = f.get_defining_equations('hexagons') sage: f.shutdown_worker_pool() """ @@ -1416,9 +1413,11 @@ def _map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_t sage: f._reset_solver_state() sage: len(f._map_triv_reduce('get_reduced_hexagons',[(0,1,False)])) 11 - sage: if sys.version_info.minor >= 8: - ....: f.start_worker_pool() # Python 3.8+ is required - sage: mp_params = [(i,f.pool._processes,True) for i in range(f.pool._processes)] + sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ + sage: if is_shared_memory_available: + ....: mp_params = [(i,f.pool._processes,True) for i in range(f.pool._processes)] + ....: else: + ....: mp_params = [(0,1,True)] sage: len(f._map_triv_reduce('get_reduced_pentagons',mp_params,worker_pool=f.pool,chunksize=1,mp_thresh=0)) 33 sage: f.shutdown_worker_pool() @@ -1578,8 +1577,7 @@ def _tup_to_fpoly(self,eq_tup): sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup sage: f = FMatrix(FusionRing("C3",1)) - sage: if sys.version_info.minor >= 8: - ....: f.start_worker_pool() # Python 3.8+ is required + sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ sage: he = f.get_defining_equations('hexagons') sage: all(f._tup_to_fpoly(poly_to_tup(h)) for h in he) True @@ -1596,14 +1594,14 @@ def _update_reduction_params(self,eqns=None): sage: f = FMatrix(FusionRing("A1",3)) sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) - sage: if sys.version_info.minor >= 8: - ....: f.start_worker_pool() # Python 3.8+ is required + sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ sage: f.get_defining_equations('hexagons',output=False) sage: f.ideal_basis = f._par_graph_gb(verbose=False) sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey, poly_to_tup sage: f.ideal_basis.sort(key=poly_tup_sortkey) sage: f.mp_thresh = 0 - sage: f._fvars = f._shared_fvars + sage: if is_shared_memory_available: + ....: f._fvars = f._shared_fvars sage: f._triangular_elim(verbose=False) # indirect doctest sage: f.ideal_basis [] @@ -1826,8 +1824,7 @@ def _par_graph_gb(self,eqns=None,term_order="degrevlex",verbose=True): sage: f = FMatrix(FusionRing("F4",1)) sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) - sage: if sys.version_info.minor >= 8: - ....: f.start_worker_pool() # Python 3.8+ is required + sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ sage: f.get_defining_equations('hexagons',output=False) sage: gb = f._par_graph_gb() Partitioned 10 equations into 2 components of size: @@ -1883,8 +1880,7 @@ def _get_component_variety(self,var,eqns): EXAMPLES:: sage: f = FMatrix(FusionRing("G2",2)) - sage: if sys.version_info.minor >= 8: - ....: f.start_worker_pool() # Python 3.8+ is required + sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ sage: f.get_defining_equations('hexagons',output=False) # long time sage: f.shutdown_worker_pool() sage: partition = f._partition_eqns() # long time diff --git a/src/sage/combinat/root_system/shm_managers.pyx b/src/sage/combinat/root_system/shm_managers.pyx index e2a646b9269..0ccb3cabcaf 100644 --- a/src/sage/combinat/root_system/shm_managers.pyx +++ b/src/sage/combinat/root_system/shm_managers.pyx @@ -53,7 +53,10 @@ cdef class KSHandler: - ``n_slots`` -- The total number of F-symbols. - ``field`` -- F-matrix factory's base cyclotomic field. - ``use_mp`` -- a boolean indicating whether to construct a shared - memory block to back ``self``. + memory block to back ``self``. Requires Python 3.8+, since we + must import the ``multiprocessing.shared_memory`` module. Attempting + to initialize when ``multiprocessing.shared_memory`` is not available + results in an ``ImportError``. - ``name`` -- the name of a shared memory object (used by child processes for attaching). - ``init_data`` -- a dictionary or :class:`KSHandler` object containing @@ -77,9 +80,12 @@ cdef class KSHandler: creating variables fx1..fx14 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 sage: n = f._poly_ring.ngens() - sage: ks = KSHandler(n,f._field,use_mp=True) + sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ + sage: ks = KSHandler(n,f._field,use_mp=is_shared_memory_available) sage: #In the same shell or in a different shell, attach to fvars - sage: ks2 = KSHandler(n,f._field,name=ks.shm.name,use_mp=True) + sage: ks2 = KSHandler(n,f._field,name=ks.shm.name,use_mp=is_shared_memory_available) + sage: if not is_shared_memory_available: + ....: ks2 = ks sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup sage: eqns = [fx1**2 - 4, fx3**2 + f._field.gen()**4 - 1/19*f._field.gen()**2] sage: ks.update([poly_to_tup(p) for p in eqns]) @@ -89,6 +95,7 @@ cdef class KSHandler: Index: 1, square: 4 Index: 3, square: -zeta32^4 + 1/19*zeta32^2 sage: ks.shm.unlink() + sage: f.shutdown_worker_pool() """ cdef int n, d self.field = field @@ -267,12 +274,16 @@ cdef class KSHandler: sage: f.get_orthogonality_constraints(output=False) sage: from sage.combinat.root_system.shm_managers import KSHandler sage: n = f._poly_ring.ngens() - sage: ks = KSHandler(n,f._field,use_mp=True,init_data=f._ks) + sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ + sage: ks = KSHandler(n,f._field,use_mp=is_shared_memory_available,init_data=f._ks) sage: #In the same shell or in a different one, attach to shared memory handler - sage: k2 = KSHandler(n,f._field,name=ks.shm.name) + sage: k2 = KSHandler(n,f._field,name=ks.shm.name,use_mp=is_shared_memory_available) + sage: if not is_shared_memory_available: + ....: k2 = ks sage: ks == k2 True sage: ks.shm.unlink() + sage: f.shutdown_worker_pool() """ ret = True for idx, sq in self.items(): @@ -377,18 +388,32 @@ cdef class FvarsHandler: - ``max_terms`` -- maximum number of terms in each entry. Since we use contiguous C-style memory blocks, the size of the block must be known in advance. + - ``use_mp`` -- a boolean indicating whether the array backing + ``self`` should be placed in shared memory. This requires + Python 3.8+, since we must import the + ``multiprocessing.shared_memory`` module. Attempting to initialize + when ``multiprocessing.shared_memory`` is not available results in + an ``ImportError``. + - ``n_bytes`` -- the number of bytes that should be allocated for + each numerator and each denominator stored by the structure. .. NOTE:: To properly dispose of shared memory resources, ``self.shm.unlink()`` must be called before exiting. + .. NOTE:: + + If you ever encounter an ``OverflowError`` when running the + :meth:`FMatrix.find_orthogonal_solution`` solver, consider + increasing the parameter ``n_bytes``. + .. WARNING:: The current data structure supports up to 2**16 entries, with each monomial in each entry having at most 254 nonzero terms. On average, each of the ``max_terms`` monomials - can have at most 50 terms. + can have at most 30 terms. EXAMPLES:: @@ -397,15 +422,19 @@ cdef class FvarsHandler: sage: f = FMatrix(FusionRing("A2",1), inject_variables=True) creating variables fx1..fx8 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 - sage: fvars = FvarsHandler(8,f._field,f._idx_to_sextuple,use_mp=True) + sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ + sage: fvars = FvarsHandler(8,f._field,f._idx_to_sextuple,use_mp=is_shared_memory_available) sage: #In the same shell or in a different shell, attach to fvars - sage: fvars2 = FvarsHandler(8,f._field,f._idx_to_sextuple,name=fvars.shm.name,use_mp=True) + sage: fvars2 = FvarsHandler(8,f._field,f._idx_to_sextuple,name=fvars.shm.name,use_mp=is_shared_memory_available) + sage: if not is_shared_memory_available: + ....: fvars2 = fvars sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(fx5**5)) sage: fvars[f2, f1, f2, f2, f0, f0] = rhs sage: f._tup_to_fpoly(fvars2[f2, f1, f2, f2, f0, f0]) fx5^5 sage: fvars.shm.unlink() + sage: f.shutdown_worker_pool() """ self.field = field self.obj_cache = dict() @@ -473,7 +502,8 @@ cdef class FvarsHandler: sage: f = FMatrix(FusionRing("B7", 1), inject_variables=True) creating variables fx1..fx14 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 - sage: fvars = FvarsHandler(14,f._field,f._idx_to_sextuple,use_mp=True) + sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ + sage: fvars = FvarsHandler(14,f._field,f._idx_to_sextuple,use_mp=is_shared_memory_available) sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx13**3 - 799/2881*fx1*fx2**5*fx10)) sage: fvars[(f1, f2, f1, f2, f2, f2)] = rhs sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(f._poly_ring.zero())) @@ -488,6 +518,7 @@ cdef class FvarsHandler: sage: f._tup_to_fpoly(fvars[r]) == -1/19 True sage: fvars.shm.unlink() + sage: f.shutdown_worker_pool() .. NOTE:: @@ -562,7 +593,8 @@ cdef class FvarsHandler: sage: f = FMatrix(FusionRing("A3", 1), inject_variables=True) creating variables fx1..fx27 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19, fx20, fx21, fx22, fx23, fx24, fx25, fx26 - sage: fvars = FvarsHandler(27,f._field,f._idx_to_sextuple,use_mp=True) + sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ + sage: fvars = FvarsHandler(27,f._field,f._idx_to_sextuple,use_mp=is_shared_memory_available) sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx21**3 - 799/2881*fx1*fx2**5*fx10)) sage: fvars[(f3, f2, f1, f2, f1, f3)] = rhs sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(f._poly_ring.zero())) @@ -577,6 +609,7 @@ cdef class FvarsHandler: sage: f._tup_to_fpoly(fvars[r]) == -1/19 True sage: fvars.shm.unlink() + sage: f.shutdown_worker_pool() """ cdef ETuple exp cdef Integer num, denom @@ -638,11 +671,13 @@ cdef class FvarsHandler: sage: f = FMatrix(FusionRing("F4",1)) sage: from sage.combinat.root_system.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() - sage: fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars,use_mp=True) + sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ + sage: fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars,use_mp=is_shared_memory_available) sage: for s, fvar in loads(dumps(fvars)).items(): ....: assert f._fvars[s] == f._tup_to_fpoly(fvar) ....: sage: fvars.shm.unlink() + sage: f.shutdown_worker_pool() """ n = self.fvars.size idx_map = {i: s for s, i in self.sext_to_idx.items()} @@ -662,14 +697,13 @@ cdef class FvarsHandler: sage: f = FMatrix(FusionRing("G2", 1), inject_variables=True) creating variables fx1..fx5 Defining fx0, fx1, fx2, fx3, fx4 - sage: if sys.version_info.minor >= 8: - ....: f.start_worker_pool() # Python 3.8+ is required - sage: for sextuple, fvar in f._shared_fvars.items(): + sage: from sage.combinat.root_system.shm_managers import FvarsHandler + sage: shared_fvars = FvarsHandler(5,f._field,f._idx_to_sextuple,init_data=f._fvars) + sage: for sextuple, fvar in shared_fvars.items(): ....: if sextuple == (f1, f1, f1, f1, f1, f1): ....: f._tup_to_fpoly(fvar) ....: fx4 - sage: f.shutdown_worker_pool() """ for sextuple in self.sext_to_idx: yield sextuple, self[sextuple] @@ -683,10 +717,12 @@ def make_FvarsHandler(n,field,idx_map,init_data): sage: f = FMatrix(FusionRing("G2",1)) sage: from sage.combinat.root_system.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() - sage: fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars,use_mp=True) - sage: for s, fvar in loads(dumps(fvars)).items(): # indirect doctest + sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ + sage: fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars,use_mp=is_shared_memory_available) + sage: for s, fvar in loads(dumps(fvars)).items(): # indirect doctest ....: assert f._fvars[s] == f._tup_to_fpoly(fvar) ....: sage: fvars.shm.unlink() + sage: f.shutdown_worker_pool() """ return FvarsHandler(n,field,idx_map,init_data=init_data) From 2c628c5d6c5fd0212e0697203527756ca7fc960c Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Fri, 21 May 2021 10:35:21 -0400 Subject: [PATCH 087/632] new caching mechanism for shm handlers --- src/sage/combinat/root_system/f_matrix.py | 31 ++++--- .../fast_parallel_fmats_methods.pyx | 8 -- .../combinat/root_system/shm_managers.pxd | 9 +- .../combinat/root_system/shm_managers.pyx | 92 ++++++++++++++----- 4 files changed, 89 insertions(+), 51 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index e31a823c96b..e87156feebc 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -22,7 +22,7 @@ from itertools import product, zip_longest from multiprocessing import cpu_count, Pool, set_start_method import numpy as np -from os import remove +from os import getpid, remove from sage.combinat.root_system.fast_parallel_fmats_methods import ( _backward_subs, _solve_for_linear_terms, @@ -1333,6 +1333,9 @@ class methods. if not hasattr(self, '_nnz'): self._reset_solver_state() #Set up shared memory resource handlers + n_proc = cpu_count() if processes is None else processes + self._pid_list = shared_memory.ShareableList([0]*(n_proc+1)) + pids_name = self._pid_list.shm.name self._solved = shared_memory.ShareableList(self._solved) s_name = self._solved.shm.name self._var_degs = shared_memory.ShareableList(self._var_degs) @@ -1340,12 +1343,11 @@ class methods. n = self._poly_ring.ngens() self._ks = KSHandler(n,self._field,use_mp=True,init_data=self._ks) ks_names = self._ks.shm.name - self._shared_fvars = FvarsHandler(n,self._field,self._idx_to_sextuple,use_mp=True,init_data=self._fvars) + self._shared_fvars = FvarsHandler(n,self._field,self._idx_to_sextuple,use_mp=n_proc,pids_name=pids_name,init_data=self._fvars) fvar_names = self._shared_fvars.shm.name #Initialize worker pool processes - args = (id(self), s_name, vd_name, ks_names, fvar_names) - n = cpu_count() if processes is None else processes - def init(fmats_id, solved_name, vd_name, ks_names, fvar_names): + args = (id(self), s_name, vd_name, ks_names, fvar_names, n_proc, pids_name) + def init(fmats_id, solved_name, vd_name, ks_names, fvar_names, n_proc, pids_name): """ Connect worker process to shared memory resources """ @@ -1353,10 +1355,14 @@ def init(fmats_id, solved_name, vd_name, ks_names, fvar_names): fmats_obj._solved = shared_memory.ShareableList(name=solved_name) fmats_obj._var_degs = shared_memory.ShareableList(name=vd_name) n = fmats_obj._poly_ring.ngens() - fmats_obj._fvars = FvarsHandler(n,fmats_obj._field,fmats_obj._idx_to_sextuple,name=fvar_names,use_mp=True) - fmats_obj._ks = KSHandler(n,fmats_obj._field,name=ks_names,use_mp=True) - - self.pool = Pool(processes=n,initializer=init,initargs=args) + K = fmats_obj._field + fmats_obj._fvars = FvarsHandler(n,K,fmats_obj._idx_to_sextuple,name=fvar_names,use_mp=n_proc,pids_name=pids_name) + fmats_obj._ks = KSHandler(n,K,name=ks_names,use_mp=True) + + self.pool = Pool(processes=n_proc,initializer=init,initargs=args) + self._pid_list[0] = getpid() + for i, p in enumerate(self.pool._pool): + self._pid_list[i+1] = p.pid return True def shutdown_worker_pool(self): @@ -1385,6 +1391,7 @@ def shutdown_worker_pool(self): self._var_degs.shm.unlink() self._ks.shm.unlink() self._shared_fvars.shm.unlink() + self._pid_list.shm.unlink() del self.__dict__['_shared_fvars'] def _map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_thresh=None): @@ -1660,18 +1667,18 @@ def _triangular_elim(self,eqns=None,verbose=True): if eqns is None: eqns = self.ideal_basis ret = False - using_mp = self.pool is not None + # using_mp = self.pool is not None while True: linear_terms_exist = _solve_for_linear_terms(self,eqns) if not linear_terms_exist: break _backward_subs(self) - #Compute new reduction params, send to child processes if any, and update eqns + #Compute new reduction params and update eqns self._update_reduction_params(eqns=eqns) # n = len(eqns) // worker_pool._processes ** 2 + 1 if worker_pool is not None else len(eqns) # eqns = [eqns[i:i+n] for i in range(0,len(eqns),n)] - if using_mp and len(eqns) > self.mp_thresh: + if self.pool is not None and len(eqns) > self.mp_thresh: n = self.pool._processes chunks = [[] for i in range(n)] for i, eq_tup in enumerate(eqns): diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index f2f09a038e4..029b321ac58 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -73,13 +73,7 @@ cpdef _solve_for_linear_terms(factory, list eqns=None): cdef FvarsHandler fvars = factory._fvars cdef NumberFieldElement_absolute coeff, other cdef tuple rhs_coeff - fvars.clear_modified() for eq_tup in eqns: - # Only unflatten relevant polynomials - # if len(eq_tup) > 2: - # continue - # eq_tup = _unflatten_coeffs(factory._field, eq_tup) - if len(eq_tup) == 1: vars = variables(eq_tup) if len(vars) == 1 and not factory._solved[vars[0]]: @@ -99,7 +93,6 @@ cpdef _solve_for_linear_terms(factory, list eqns=None): max_var = exp._data[0] if not factory._solved[max_var]: rhs_exp = eq_tup[(idx+1) % 2][0] - # rhs_coeff = -eq_tup[(idx+1) % 2][1] / eq_tup[idx][1] coeff = factory._field(list(eq_tup[(idx+1) % 2][1])) other = factory._field(list(eq_tup[idx][1])) rhs_coeff = tuple((-coeff / other)._coefficients()) @@ -279,7 +272,6 @@ cdef get_reduced_hexagons(factory, tuple mp_params): _ks = factory._ks #Computation loop - # for i, sextuple in enumerate(product(basis, repeat=6)): it = product(basis, repeat=6) for i in range(len(basis)**6): sextuple = next(it) diff --git a/src/sage/combinat/root_system/shm_managers.pxd b/src/sage/combinat/root_system/shm_managers.pxd index 2a6a7e88ab4..32dd0073091 100644 --- a/src/sage/combinat/root_system/shm_managers.pxd +++ b/src/sage/combinat/root_system/shm_managers.pxd @@ -16,12 +16,9 @@ cdef class KSHandler: cdef class FvarsHandler: cdef dict sext_to_idx, obj_cache - cdef list modified_cache - cdef unsigned int ngens, bytes - cdef object fvars_t + cdef int bytes, ngens cdef np.ndarray fvars cdef NumberField field + cdef object fvars_t, pid_list + cdef Py_ssize_t child_id cdef public object shm - - cdef clear_modified(self) - # cdef update_cache(self) diff --git a/src/sage/combinat/root_system/shm_managers.pyx b/src/sage/combinat/root_system/shm_managers.pyx index 0ccb3cabcaf..674ba9fc205 100644 --- a/src/sage/combinat/root_system/shm_managers.pyx +++ b/src/sage/combinat/root_system/shm_managers.pyx @@ -24,6 +24,7 @@ from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libs from sage.rings.polynomial.polydict cimport ETuple import numpy as np +from os import getpid cdef class KSHandler: def __init__(self, n_slots, field, use_mp=False, init_data={}, name=None): @@ -351,7 +352,7 @@ def make_KSHandler(n_slots,field,init_data): return KSHandler(n_slots,field,init_data=init_data) cdef class FvarsHandler: - def __init__(self,n_slots,field,idx_to_sextuple,use_mp=False,name=None,init_data={},max_terms=20,n_bytes=32): + def __init__(self,n_slots,field,idx_to_sextuple,use_mp=0,pids_name=None,name=None,init_data={},max_terms=20,n_bytes=32): """ Return a shared memory backed dict-like structure to manage the ``_fvars`` attribute of an F-matrix factory object. @@ -388,12 +389,15 @@ cdef class FvarsHandler: - ``max_terms`` -- maximum number of terms in each entry. Since we use contiguous C-style memory blocks, the size of the block must be known in advance. - - ``use_mp`` -- a boolean indicating whether the array backing - ``self`` should be placed in shared memory. This requires - Python 3.8+, since we must import the + - ``use_mp`` -- an integer indicating the number of child processes + used for multiprocessing. If running serially, use 0. + Multiprocessing requires Python 3.8+, since we must import the ``multiprocessing.shared_memory`` module. Attempting to initialize when ``multiprocessing.shared_memory`` is not available results in an ``ImportError``. + - ``pids_name`` -- the name of a ``ShareableList`` contaning the + process ``pid``'s for every process in the pool (including the + parent process). - ``n_bytes`` -- the number of bytes that should be allocated for each numerator and each denominator stored by the structure. @@ -423,9 +427,15 @@ cdef class FvarsHandler: creating variables fx1..fx8 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ - sage: fvars = FvarsHandler(8,f._field,f._idx_to_sextuple,use_mp=is_shared_memory_available) + sage: if is_shared_memory_available: + ....: n_proc = f.pool._processes + ....: pids_name = f._pid_list.shm.name + ....: else: + ....: n_proc = 0 + ....: pids_name = None + sage: fvars = FvarsHandler(8,f._field,f._idx_to_sextuple,use_mp=n_proc,pids_name=pids_name) sage: #In the same shell or in a different shell, attach to fvars - sage: fvars2 = FvarsHandler(8,f._field,f._idx_to_sextuple,name=fvars.shm.name,use_mp=is_shared_memory_available) + sage: fvars2 = FvarsHandler(8,f._field,f._idx_to_sextuple,name=fvars.shm.name,use_mp=n_proc,pids_name=pids_name) sage: if not is_shared_memory_available: ....: fvars2 = fvars sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup @@ -441,8 +451,9 @@ cdef class FvarsHandler: cdef int d = self.field.degree() self.bytes = n_bytes cdef int slots = self.bytes // 8 + cdef int n_proc = use_mp + 1 self.fvars_t = np.dtype([ - ('modified','bool',(1,)), + ('modified',np.int8,(n_proc,)), ('ticks', 'u1', (max_terms,)), ('exp_data', 'u2', (max_terms*30,)), ('coeff_nums',np.int64,(max_terms,d,slots)), @@ -461,8 +472,11 @@ cdef class FvarsHandler: else: self.shm = shared_memory.SharedMemory(name=name) self.fvars = np.ndarray((self.ngens,),dtype=self.fvars_t,buffer=self.shm.buf) + self.pid_list = shared_memory.ShareableList(name=pids_name) + self.child_id = -1 else: self.fvars = np.ndarray((self.ngens,),dtype=self.fvars_t) + self.child_id = 0 #Populate with initialziation data for sextuple, fvar in init_data.items(): if isinstance(fvar, MPolynomial_libsingular): @@ -478,14 +492,9 @@ cdef class FvarsHandler: fvar = tuple(transformed) self[sextuple] = fvar - cdef clear_modified(self): - """ - Reset tagged entries modified by parent process. - """ - self.fvars['modified'][:] = False - @cython.nonecheck(False) @cython.wraparound(False) + @cython.boundscheck(False) def __getitem__(self, sextuple): """ Retrieve a record from the shared memory data structure by @@ -503,7 +512,13 @@ cdef class FvarsHandler: creating variables fx1..fx14 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ - sage: fvars = FvarsHandler(14,f._field,f._idx_to_sextuple,use_mp=is_shared_memory_available) + sage: if is_shared_memory_available: + ....: n_proc = f.pool._processes + ....: pids_name = f._pid_list.shm.name + ....: else: + ....: n_proc = 0 + ....: pids_name = None + sage: fvars = FvarsHandler(14,f._field,f._idx_to_sextuple,use_mp=n_proc,pids_name=pids_name) sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx13**3 - 799/2881*fx1*fx2**5*fx10)) sage: fvars[(f1, f2, f1, f2, f2, f2)] = rhs sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(f._poly_ring.zero())) @@ -530,11 +545,17 @@ cdef class FvarsHandler: if not sextuple in self.sext_to_idx: raise KeyError('Invalid sextuple {}'.format(sextuple)) cdef Py_ssize_t idx = self.sext_to_idx[sextuple] - # if idx in self.obj_cache: - # if self.fvars['modified'][idx]: - # del self.obj_cache[idx] - # else: - # return self.obj_cache[idx] + #Each process builds its own cache, so each process must know + #whether the entry it wants to retrieve has been modified. + #Each process needs to know where to look, so we use an index + #every process agrees on. The pid_list[0] belongs to the main process. + if self.child_id < 0: + self.child_id = self.pid_list.index(getpid()) + if idx in self.obj_cache: + if self.fvars['modified'][idx,self.child_id]: + del self.obj_cache[idx] + else: + return self.obj_cache[idx] cdef ETuple e, exp cdef int count, nnz cdef Integer d, num @@ -548,10 +569,10 @@ cdef class FvarsHandler: cdef np.ndarray[np.uint16_t,ndim=1] exp_data = self.fvars['exp_data'][idx] cdef np.ndarray[np.int64_t,ndim=3] nums = self.fvars['coeff_nums'][idx] cdef np.ndarray[np.uint64_t,ndim=3] denoms = self.fvars['coeff_denom'][idx] + cdef np.ndarray[np.int8_t,ndim=1] modified = self.fvars['modified'][idx] e = ETuple({}, self.ngens) poly_tup = list() cum = 0 - # for i in range(np.count_nonzero(ticks)): count = np.count_nonzero(ticks) for i in range(count): #Construct new ETuple for each monomial @@ -575,7 +596,9 @@ cdef class FvarsHandler: cyc_coeff = self.field(rats) poly_tup.append((exp, cyc_coeff)) ret = tuple(poly_tup) - # self.obj_cache[idx] = ret + #Cache object and reset modified + self.obj_cache[idx] = ret + modified[self.child_id] = 0 return ret @cython.nonecheck(False) @@ -594,7 +617,13 @@ cdef class FvarsHandler: creating variables fx1..fx27 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19, fx20, fx21, fx22, fx23, fx24, fx25, fx26 sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ - sage: fvars = FvarsHandler(27,f._field,f._idx_to_sextuple,use_mp=is_shared_memory_available) + sage: if is_shared_memory_available: + ....: n_proc = f.pool._processes + ....: pids_name = f._pid_list.shm.name + ....: else: + ....: n_proc = 0 + ....: pids_name = None + sage: fvars = FvarsHandler(27,f._field,f._idx_to_sextuple,use_mp=n_proc,pids_name=pids_name) sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx21**3 - 799/2881*fx1*fx2**5*fx10)) sage: fvars[(f3, f2, f1, f2, f1, f3)] = rhs sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(f._poly_ring.zero())) @@ -624,6 +653,7 @@ cdef class FvarsHandler: cdef np.ndarray[np.uint16_t,ndim=1] exp_data = self.fvars['exp_data'][idx] cdef np.ndarray[np.int64_t,ndim=3] nums = self.fvars['coeff_nums'][idx] cdef np.ndarray[np.uint64_t,ndim=3] denoms = self.fvars['coeff_denom'][idx] + cdef np.ndarray[np.int8_t,ndim=1] modified = self.fvars['modified'][idx] cdef list digits #Initialize denominators to 1 denoms[:,:,0] = 1 @@ -660,7 +690,7 @@ cdef class FvarsHandler: denoms[i,k,t] = digits[t] k += 1 i += 1 - self.fvars['modified'][idx] = True + modified[:] = 1 def __reduce__(self): """ @@ -672,7 +702,13 @@ cdef class FvarsHandler: sage: from sage.combinat.root_system.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ - sage: fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars,use_mp=is_shared_memory_available) + sage: if is_shared_memory_available: + ....: n_proc = f.pool._processes + ....: pids_name = f._pid_list.shm.name + ....: else: + ....: n_proc = 0 + ....: pids_name = None + sage: fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars,use_mp=n_proc,pids_name=pids_name) sage: for s, fvar in loads(dumps(fvars)).items(): ....: assert f._fvars[s] == f._tup_to_fpoly(fvar) ....: @@ -718,7 +754,13 @@ def make_FvarsHandler(n,field,idx_map,init_data): sage: from sage.combinat.root_system.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ - sage: fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars,use_mp=is_shared_memory_available) + sage: if is_shared_memory_available: + ....: n_proc = f.pool._processes + ....: pids_name = f._pid_list.shm.name + ....: else: + ....: n_proc = 0 + ....: pids_name = None + sage: fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars,use_mp=n_proc,pids_name=pids_name) sage: for s, fvar in loads(dumps(fvars)).items(): # indirect doctest ....: assert f._fvars[s] == f._tup_to_fpoly(fvar) ....: From 23c9f5fa44ec8d5eacbbacc9f4a926512ac11026 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Fri, 21 May 2021 14:35:59 -0400 Subject: [PATCH 088/632] corrections from testing on Python 3.6.5, removed internal tests for shm handlers --- src/sage/combinat/root_system/f_matrix.py | 53 ++++++++++--------- .../fast_parallel_fmats_methods.pyx | 16 +++--- .../combinat/root_system/shm_managers.pyx | 31 ++++++----- 3 files changed, 53 insertions(+), 47 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index e87156feebc..0a97d012860 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -302,8 +302,8 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab self.pool = None #TESTS - self.test_fvars = dict() - self.test_ks = dict() + # self.test_fvars = dict() + # self.test_ks = dict() ####################### ### Class utilities ### @@ -1254,7 +1254,7 @@ def _restore_state(self,filename): return self._fvars, self._solved, self._ks, self.ideal_basis, self._chkpt_status = state #TESTS: - self.test_ks = {i: sq for i, sq in self._ks.items()} + # self.test_ks = {i: sq for i, sq in self._ks.items()} self._update_reduction_params() ################# @@ -1535,18 +1535,18 @@ def get_defining_equations(self,option,output=True): EXAMPLES:: sage: f = FMatrix(FusionRing("B2",1)) - sage: f.get_defining_equations('hexagons') - [fx0 - 1, - fx10^2 + (-zeta32^8)*fx11*fx12 + (-zeta32^12)*fx10, - fx11*fx12 + (-zeta32^8)*fx13^2 + (zeta32^12)*fx13, - fx2 + 1, - fx7 + 1, - fx3*fx8 - fx6, - fx1*fx5 + fx2, + sage: sorted(f.get_defining_equations('hexagons')) + [fx7 + 1, fx6 - 1, - fx4*fx9 + fx7, + fx2 + 1, + fx0 - 1, + fx11*fx12 + (-zeta32^8)*fx13^2 + (zeta32^12)*fx13, + fx10*fx12 + (-zeta32^8)*fx12*fx13 + (zeta32^4)*fx12, fx10*fx11 + (-zeta32^8)*fx11*fx13 + (zeta32^4)*fx11, - fx10*fx12 + (-zeta32^8)*fx12*fx13 + (zeta32^4)*fx12] + fx10^2 + (-zeta32^8)*fx11*fx12 + (-zeta32^12)*fx10, + fx4*fx9 + fx7, + fx3*fx8 - fx6, + fx1*fx5 + fx2] sage: pe = f.get_defining_equations('pentagons') sage: len(pe) 33 @@ -1609,6 +1609,9 @@ def _update_reduction_params(self,eqns=None): sage: f.mp_thresh = 0 sage: if is_shared_memory_available: ....: f._fvars = f._shared_fvars + ....: else: + ....: from sage.combinat.root_system.shm_managers import FvarsHandler + ....: f._fvars = FvarsHandler(f._poly_ring.ngens(),f._field,f._idx_to_sextuple,init_data=f._fvars) sage: f._triangular_elim(verbose=False) # indirect doctest sage: f.ideal_basis [] @@ -1619,13 +1622,13 @@ def _update_reduction_params(self,eqns=None): self._ks.update(eqns) #TESTS: - for i in range(len(eqns)): - eq_tup = eqns[i] - if tup_fixes_sq(eq_tup): - rhs = [-v for v in eq_tup[-1][1]] - self.test_ks[variables(eq_tup)[0]] = rhs - for i, sq in self._ks.items(): - assert sq == self._field(self.test_ks[i]), "{}: OG sq {}, shared sq {}".format(i, sq, self.test_ks[i]) + # for i in range(len(eqns)): + # eq_tup = eqns[i] + # if tup_fixes_sq(eq_tup): + # rhs = [-v for v in eq_tup[-1][1]] + # self.test_ks[variables(eq_tup)[0]] = rhs + # for i, sq in self._ks.items(): + # assert sq == self._field(self.test_ks[i]), "{}: OG sq {}, shared sq {}".format(i, sq, self.test_ks[i]) degs = get_variables_degrees(eqns) if degs: @@ -2203,11 +2206,11 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start print("Set up {} hex and orthogonality constraints...".format(len(self.ideal_basis))) #Unzip _fvars and link to shared_memory structure if using multiprocessing #TESTS: - for k, v in self._fvars.items(): - if isinstance(v, tuple): - self.test_fvars[k] = v - else: - self.test_fvars[k] = poly_to_tup(v) + # for k, v in self._fvars.items(): + # if isinstance(v, tuple): + # self.test_fvars[k] = v + # else: + # self.test_fvars[k] = poly_to_tup(v) if use_mp and loads_shared_memory: self._fvars = self._shared_fvars else: diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index 029b321ac58..3bdf2d4daf0 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -81,10 +81,10 @@ cpdef _solve_for_linear_terms(factory, list eqns=None): factory._solved[vars[0]] = True linear_terms_exist = True - #TEST: - s = factory._idx_to_sextuple[vars[0]] - factory.test_fvars[s] = tuple() - assert factory.test_fvars[s] == fvars[s], "OG value {}, Shared: {}".format(fvars[s],factory.test_fvars[s]) + #TESTS: + # s = factory._idx_to_sextuple[vars[0]] + # factory.test_fvars[s] = tuple() + # assert factory.test_fvars[s] == fvars[s], "OG value {}, Shared: {}".format(fvars[s],factory.test_fvars[s]) if len(eq_tup) == 2: idx = has_appropriate_linear_term(eq_tup) if idx < 0: continue @@ -100,10 +100,10 @@ cpdef _solve_for_linear_terms(factory, list eqns=None): factory._solved[max_var] = True linear_terms_exist = True - #TEST: - s = factory._idx_to_sextuple[max_var] - factory.test_fvars[s] = ((rhs_exp,rhs_coeff),) - assert _unflatten_coeffs(factory._field,factory.test_fvars[s]) == fvars[s], "OG value {}, Shared: {}".format(factory.test_fvars[s],fvars[s]) + #TESTS: + # s = factory._idx_to_sextuple[max_var] + # factory.test_fvars[s] = ((rhs_exp,rhs_coeff),) + # assert _unflatten_coeffs(factory._field,factory.test_fvars[s]) == fvars[s], "OG value {}, Shared: {}".format(factory.test_fvars[s],fvars[s]) return linear_terms_exist cpdef _backward_subs(factory, bint flatten=True): diff --git a/src/sage/combinat/root_system/shm_managers.pyx b/src/sage/combinat/root_system/shm_managers.pyx index 674ba9fc205..dc73a98f00a 100644 --- a/src/sage/combinat/root_system/shm_managers.pyx +++ b/src/sage/combinat/root_system/shm_managers.pyx @@ -84,7 +84,8 @@ cdef class KSHandler: sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ sage: ks = KSHandler(n,f._field,use_mp=is_shared_memory_available) sage: #In the same shell or in a different shell, attach to fvars - sage: ks2 = KSHandler(n,f._field,name=ks.shm.name,use_mp=is_shared_memory_available) + sage: name = ks.shm.name if is_shared_memory_available else None + sage: ks2 = KSHandler(n,f._field,name=name,use_mp=is_shared_memory_available) sage: if not is_shared_memory_available: ....: ks2 = ks sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup @@ -95,7 +96,7 @@ cdef class KSHandler: ....: Index: 1, square: 4 Index: 3, square: -zeta32^4 + 1/19*zeta32^2 - sage: ks.shm.unlink() + sage: if is_shared_memory_available: ks.shm.unlink() sage: f.shutdown_worker_pool() """ cdef int n, d @@ -278,12 +279,13 @@ cdef class KSHandler: sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ sage: ks = KSHandler(n,f._field,use_mp=is_shared_memory_available,init_data=f._ks) sage: #In the same shell or in a different one, attach to shared memory handler - sage: k2 = KSHandler(n,f._field,name=ks.shm.name,use_mp=is_shared_memory_available) + sage: name = ks.shm.name if is_shared_memory_available else None + sage: k2 = KSHandler(n,f._field,name=name,use_mp=is_shared_memory_available) sage: if not is_shared_memory_available: ....: k2 = ks sage: ks == k2 True - sage: ks.shm.unlink() + sage: if is_shared_memory_available: ks.shm.unlink() sage: f.shutdown_worker_pool() """ ret = True @@ -352,7 +354,7 @@ def make_KSHandler(n_slots,field,init_data): return KSHandler(n_slots,field,init_data=init_data) cdef class FvarsHandler: - def __init__(self,n_slots,field,idx_to_sextuple,use_mp=0,pids_name=None,name=None,init_data={},max_terms=20,n_bytes=32): + def __init__(self,n_slots,field,idx_to_sextuple,init_data={},use_mp=0,pids_name=None,name=None,max_terms=20,n_bytes=32): """ Return a shared memory backed dict-like structure to manage the ``_fvars`` attribute of an F-matrix factory object. @@ -435,7 +437,8 @@ cdef class FvarsHandler: ....: pids_name = None sage: fvars = FvarsHandler(8,f._field,f._idx_to_sextuple,use_mp=n_proc,pids_name=pids_name) sage: #In the same shell or in a different shell, attach to fvars - sage: fvars2 = FvarsHandler(8,f._field,f._idx_to_sextuple,name=fvars.shm.name,use_mp=n_proc,pids_name=pids_name) + sage: name = fvars.shm.name if is_shared_memory_available else None + sage: fvars2 = FvarsHandler(8,f._field,f._idx_to_sextuple,name=name,use_mp=n_proc,pids_name=pids_name) sage: if not is_shared_memory_available: ....: fvars2 = fvars sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup @@ -443,7 +446,7 @@ cdef class FvarsHandler: sage: fvars[f2, f1, f2, f2, f0, f0] = rhs sage: f._tup_to_fpoly(fvars2[f2, f1, f2, f2, f0, f0]) fx5^5 - sage: fvars.shm.unlink() + sage: if is_shared_memory_available: fvars.shm.unlink() sage: f.shutdown_worker_pool() """ self.field = field @@ -532,7 +535,7 @@ cdef class FvarsHandler: True sage: f._tup_to_fpoly(fvars[r]) == -1/19 True - sage: fvars.shm.unlink() + sage: if is_shared_memory_available: fvars.shm.unlink() sage: f.shutdown_worker_pool() .. NOTE:: @@ -637,7 +640,7 @@ cdef class FvarsHandler: True sage: f._tup_to_fpoly(fvars[r]) == -1/19 True - sage: fvars.shm.unlink() + sage: if is_shared_memory_available: fvars.shm.unlink() sage: f.shutdown_worker_pool() """ cdef ETuple exp @@ -671,21 +674,21 @@ cdef class FvarsHandler: k = 0 for r in coeff_tup: num, denom = r.as_integer_ratio() - assert denom != 0, "zero denominator error" + # assert denom != 0, "zero denominator error" if abs(num) > 2**63 or denom > 2**63: print("Large integers encountered in FvarsHandler", num, denom) if abs(num) < 2**63: nums[i,k,0] = num else: digits = num.digits(2**63) - assert len(digits) <= self.bytes // 8, "Numerator {} is too large for shared FvarsHandler. Use at least {} bytes...".format(num,num.nbits()//8+1) + # assert len(digits) <= self.bytes // 8, "Numerator {} is too large for shared FvarsHandler. Use at least {} bytes...".format(num,num.nbits()//8+1) for t in range(len(digits)): nums[i,k,t] = digits[t] if denom < 2**64: denoms[i,k,0] = denom else: digits = denom.digits(2**64) - assert len(digits) <= self.bytes // 8, "Denominator {} is too large for shared FvarsHandler. Use at least {} bytes...".format(denom,denom.nbits()//8+1) + # assert len(digits) <= self.bytes // 8, "Denominator {} is too large for shared FvarsHandler. Use at least {} bytes...".format(denom,denom.nbits()//8+1) for t in range(len(digits)): denoms[i,k,t] = digits[t] k += 1 @@ -712,7 +715,7 @@ cdef class FvarsHandler: sage: for s, fvar in loads(dumps(fvars)).items(): ....: assert f._fvars[s] == f._tup_to_fpoly(fvar) ....: - sage: fvars.shm.unlink() + sage: if is_shared_memory_available: fvars.shm.unlink() sage: f.shutdown_worker_pool() """ n = self.fvars.size @@ -764,7 +767,7 @@ def make_FvarsHandler(n,field,idx_map,init_data): sage: for s, fvar in loads(dumps(fvars)).items(): # indirect doctest ....: assert f._fvars[s] == f._tup_to_fpoly(fvar) ....: - sage: fvars.shm.unlink() + sage: if is_shared_memory_available: fvars.shm.unlink() sage: f.shutdown_worker_pool() """ return FvarsHandler(n,field,idx_map,init_data=init_data) From 76e2831c9b2d26432140df535022a280ac8e2862 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Mon, 24 May 2021 22:22:55 -0400 Subject: [PATCH 089/632] increased coverage by making tup_fixes_sq method cdef again --- src/sage/combinat/root_system/f_matrix.py | 2 +- src/sage/combinat/root_system/poly_tup_engine.pxd | 3 ++- src/sage/combinat/root_system/poly_tup_engine.pyx | 5 +++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 0a97d012860..e2abf9ca007 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -36,7 +36,7 @@ _unflatten_coeffs, poly_tup_sortkey, resize, - tup_fixes_sq + # tup_fixes_sq ) from sage.combinat.root_system.shm_managers import KSHandler, FvarsHandler from sage.graphs.graph import Graph diff --git a/src/sage/combinat/root_system/poly_tup_engine.pxd b/src/sage/combinat/root_system/poly_tup_engine.pxd index 81c13b94bac..533f2c2d272 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pxd +++ b/src/sage/combinat/root_system/poly_tup_engine.pxd @@ -10,7 +10,8 @@ cpdef list get_variables_degrees(list eqns) cpdef list variables(tuple eq_tup) cpdef constant_coeff(tuple eq_tup, field) cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map) -cpdef bint tup_fixes_sq(tuple eq_tup) +# cpdef bint tup_fixes_sq(tuple eq_tup) +cdef bint tup_fixes_sq(tuple eq_tup) cdef dict subs_squares(dict eq_dict, KSHandler known_sq) cpdef dict compute_known_powers(max_degs, dict val_dict, one) cdef dict subs(tuple poly_tup, dict known_powers, one) diff --git a/src/sage/combinat/root_system/poly_tup_engine.pyx b/src/sage/combinat/root_system/poly_tup_engine.pyx index 6ff2f3f1b32..80d8d3073c6 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pyx +++ b/src/sage/combinat/root_system/poly_tup_engine.pyx @@ -123,7 +123,7 @@ cdef inline int has_appropriate_linear_term(tuple eq_tup): Determine whether the given tuple of pairs (of length 2) contains an *appropriate* linear term. - In this context, a linear term is said to be *appropriate* if + In this context, a linear term is said to be *appropriate* if it is in the largest variable in the given polynomial (w.r.t. the degrevlex ordering), the monomial in which the linear term appears is univariate, and the linear term is not a common factor in @@ -319,7 +319,8 @@ cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map): new_tup.append((exp, coeff_map(coeff))) return tuple(new_tup) -cpdef inline bint tup_fixes_sq(tuple eq_tup): +# cpdef inline bint tup_fixes_sq(tuple eq_tup): +cdef inline bint tup_fixes_sq(tuple eq_tup): r""" Determine if given equation fixes the square of a variable. From 0c7b7f2e104260036ed4290619a848c90806f655 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Tue, 25 May 2021 15:36:47 -0400 Subject: [PATCH 090/632] simplified data structures with an eye towards nmf support --- src/sage/combinat/root_system/f_matrix.py | 128 +++++------------- .../fast_parallel_fmats_methods.pyx | 9 +- .../combinat/root_system/poly_tup_engine.pxd | 2 +- .../combinat/root_system/poly_tup_engine.pyx | 12 +- .../combinat/root_system/shm_managers.pyx | 1 - 5 files changed, 43 insertions(+), 109 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index e2abf9ca007..41b2b0ac1ce 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -20,7 +20,7 @@ from copy import deepcopy from ctypes import cast, py_object from itertools import product, zip_longest -from multiprocessing import cpu_count, Pool, set_start_method +from multiprocessing import Pool, cpu_count, set_start_method import numpy as np from os import getpid, remove @@ -283,16 +283,12 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab if inject_variables: print("creating variables %s%s..%s%s"%(var_prefix,1,var_prefix,n_vars)) self._poly_ring.inject_variables(get_main_globals()) - self._var_to_sextuple, self._fvars = self.findcases(output=True) - self._var_to_idx = {var: idx for idx, var in enumerate(self._poly_ring.gens())} - self._idx_to_sextuple = {i: self._var_to_sextuple[self._poly_ring.gen(i)] for i in range(self._poly_ring.ngens())} - self._singles = self.singletons() + self._idx_to_sextuple, self._fvars = self.findcases(output=True) #Base field attributes self._field = self._FR.field() r = self._field.defining_polynomial().roots(ring=QQbar, multiplicities=False)[0] self._qqbar_embedding = self._field.hom([r], QQbar) - self._non_cyc_roots = list() #Warm starting self._chkpt_status = -1 @@ -352,7 +348,7 @@ def clear_vars(self): sage: f.get_fvars()[some_key] fx0 """ - self._fvars = {t : fx for fx, t in self._var_to_sextuple.items()} + self._fvars = {t: self._poly_ring.gen(idx) for idx, t in self._idx_to_sextuple.items()} self._solved = list(False for fx in self._fvars) def _reset_solver_state(self): @@ -395,7 +391,8 @@ def _reset_solver_state(self): """ self._FR._basecoer = None self._field = self._FR.field() - self._update_poly_ring_base_field(field=self._field) + self._non_cyc_roots = list() + self._poly_ring = self._poly_ring.change_ring(self._field) self._chkpt_status = -1 self.clear_vars() self.clear_equations() @@ -403,6 +400,7 @@ def _reset_solver_state(self): self._var_degs = [0]*n self._kp = dict() self._ks = KSHandler(n,self._field) + self._singles = self.get_fvars_by_size(1,indices=True) self._nnz = self._get_known_nonz() #Clear relevant caches @@ -410,30 +408,6 @@ def _reset_solver_state(self): self._FR.r_matrix.clear_cache() self._FR.s_ij.clear_cache() - def _update_poly_ring_base_field(self,field): - r""" - Change base field of ``PolynomialRing`` and the corresponding - index attributes - - EXAMPLES:: - - sage: f = FMatrix(FusionRing("D4",1)) - sage: f._update_poly_ring_base_field(QQ) - sage: f._poly_ring.base_ring() == QQ - True - sage: all(fx in f._poly_ring for fx in f._var_to_idx) - True - sage: all(fx in f._poly_ring for fx in f._var_to_sextuple) - True - """ - new_poly_ring = self._poly_ring.change_ring(field) - nvars = self._poly_ring.ngens() - #Do some appropriate conversions - self._singles = [new_poly_ring(fx) for fx in self._singles] - self._var_to_idx = {new_poly_ring.gen(i): i for i in range(nvars)} - self._var_to_sextuple = {new_poly_ring.gen(i): self._var_to_sextuple[self._poly_ring.gen(i)] for i in range(nvars)} - self._poly_ring = new_poly_ring - def fmat(self, a, b, c, d, x, y, data=True): r""" Return the F-Matrix coefficient `(F^{a,b,c}_d)_{x,y}`. @@ -592,11 +566,11 @@ def findcases(self,output=False): sage: f.findcases() 5 sage: f.findcases(output=True) - ({fx4: (t, t, t, t, t, t), - fx3: (t, t, t, t, t, i0), - fx2: (t, t, t, t, i0, t), - fx1: (t, t, t, t, i0, i0), - fx0: (t, t, t, i0, t, t)}, + ({0: (t, t, t, i0, t, t), + 1: (t, t, t, t, i0, i0), + 2: (t, t, t, t, i0, t), + 3: (t, t, t, t, t, i0), + 4: (t, t, t, t, t, t)}, {(t, t, t, i0, t, t): fx0, (t, t, t, t, i0, i0): fx1, (t, t, t, t, i0, t): fx2, @@ -607,48 +581,23 @@ def findcases(self,output=False): if output: idx_map = dict() ret = dict() - # for (a,b,c,d) in list(product(self._FR.basis(), repeat=4)): id_anyon = self._FR.one() for (a,b,c,d) in product(self._FR.basis(), repeat=4): if a == id_anyon or b == id_anyon or c == id_anyon: continue for x in self.f_from(a, b, c, d): for y in self.f_to(a, b, c, d): - # fm = self.fmat(a, b, c, d, x, y, data=False) - # if fm is not None and fm not in [0,1]: if output: - # v = self._poly_ring.gens()[i] v = self._poly_ring.gen(i) ret[(a,b,c,d,x,y)] = v - idx_map[v] = (a, b, c, d, x, y) + # idx_map[v] = (a, b, c, d, x, y) + idx_map[i] = (a, b, c, d, x, y) i += 1 if output: return idx_map, ret else: return i - def singletons(self): - r""" - Find `x_i` that are automatically nonzero, because their F-matrix is - `1 \times 1`. - - EXAMPLES:: - - sage: f = FMatrix(FusionRing("E7",1)) - sage: singles = f.singletons() - sage: all(f.fmatrix(*f._var_to_sextuple[v][:4]).nrows() == 1 for v in singles) - True - """ - ret = [] - gens = set(self._poly_ring.gens()) - for (a, b, c, d) in product(self._FR.basis(), repeat=4): - (ff,ft) = (self.f_from(a,b,c,d), self.f_to(a,b,c,d)) - if len(ff) == 1 and len(ft) == 1: - v = self._fvars.get((a,b,c,d,ff[0],ft[0]), None) - if v in gens: - ret.append(v) - return ret - def f_from(self,a,b,c,d): r""" Return the possible `x` such that there are morphisms @@ -936,7 +885,7 @@ def _get_known_nonz(self): (100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100) """ - nonz = {self._var_to_idx[var] : 100 for var in self._singles} + nonz = {idx: 100 for idx in self._singles} for idx, v in self._ks.items(): nonz[idx] = 100 return ETuple(nonz, self._poly_ring.ngens()) @@ -1630,13 +1579,8 @@ def _update_reduction_params(self,eqns=None): # for i, sq in self._ks.items(): # assert sq == self._field(self.test_ks[i]), "{}: OG sq {}, shared sq {}".format(i, sq, self.test_ks[i]) - degs = get_variables_degrees(eqns) - if degs: - for i, d in enumerate(degs): - self._var_degs[i] = d - else: - for i in range(self._poly_ring.ngens()): - self._var_degs[i] = 0 + for i, d in enumerate(get_variables_degrees(eqns,self._poly_ring.ngens())): + self._var_degs[i] = d self._nnz = self._get_known_nonz() self._kp = compute_known_powers(self._var_degs,self._get_known_vals(),self._field.one()) @@ -1666,17 +1610,14 @@ def _triangular_elim(self,eqns=None,verbose=True): sage: f.ideal_basis [] """ - ret = True if eqns is None: eqns = self.ideal_basis - ret = False - # using_mp = self.pool is not None + while True: linear_terms_exist = _solve_for_linear_terms(self,eqns) if not linear_terms_exist: break _backward_subs(self) - #Compute new reduction params and update eqns self._update_reduction_params(eqns=eqns) # n = len(eqns) // worker_pool._processes ** 2 + 1 if worker_pool is not None else len(eqns) @@ -1693,8 +1634,6 @@ def _triangular_elim(self,eqns=None,verbose=True): eqns.sort(key=poly_tup_sortkey) if verbose: print("Elimination epoch completed... {} eqns remain in ideal basis".format(len(eqns))) - if ret: - return eqns self.ideal_basis = eqns ##################### @@ -1815,7 +1754,7 @@ def _partition_eqns(self,eqns=None,verbose=True): print(graph.connected_components_sizes()) return partition - def _par_graph_gb(self,eqns=None,term_order="degrevlex",verbose=True): + def _par_graph_gb(self,eqns=None,term_order="degrevlex",largest_comp=60,verbose=True): r""" Compute a Groebner basis for a list of equations partitioned according to their corresponding graph. @@ -1861,7 +1800,7 @@ def _par_graph_gb(self,eqns=None,term_order="degrevlex",verbose=True): for comp, comp_eqns in self._partition_eqns(verbose=verbose).items(): #Check if component is too large to process - if len(comp) > 60: + if len(comp) > largest_comp: # fmat_size = 0 # #For informative print statement # for i in range(1,nmax+1): @@ -2010,7 +1949,6 @@ def _get_explicit_solution(self,eqns=None,verbose=True): for fx, rhs in self._ks.items(): if not self._solved[fx]: lt = (ETuple({fx : 2},n), one) - # eqns.append((lt, (ETuple({},n), -self._field(list(rhs))))) eqns.append(((lt, (ETuple({},n), -rhs)))) eqns_partition = self._partition_eqns(verbose=verbose) @@ -2064,9 +2002,8 @@ def _get_explicit_solution(self,eqns=None,verbose=True): numeric_fvars = {k : phi(v) for k, v in numeric_fvars.items()} for i, elt in enumerate(bf_elts): numeric_fvars[non_cyclotomic_roots[i][0]] = elt - #Update polynomial ring - self._update_poly_ring_base_field(self._field) + self._poly_ring = self._poly_ring.change_ring(self._field) #Ensure all F-symbols are known for fx in numeric_fvars: @@ -2181,9 +2118,9 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start the F-symbols is determined based on the ``CartanType`` associated to ``self``. See :meth:`attempt_number_field_computation` for details. """ - self._reset_solver_state() if self._poly_ring.ngens() == 0: return + self._reset_solver_state() #Resume computation from checkpoint if warm_start: @@ -2226,7 +2163,6 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start #Report progress if verbose: print("Hex elim step solved for {} / {} variables".format(sum(self._solved), len(self._poly_ring.gens()))) - self._checkpoint(checkpoint,2,verbose=verbose) if self._chkpt_status < 3: @@ -2236,7 +2172,6 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start #Report progress if verbose: print("Set up {} reduced pentagons...".format(len(self.ideal_basis))) - self._checkpoint(checkpoint,3,verbose=verbose) #Simplify and eliminate variables @@ -2245,7 +2180,6 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start #Report progress if verbose: print("Pent elim step solved for {} / {} variables".format(sum(self._solved), len(self._poly_ring.gens()))) - self._checkpoint(checkpoint,4,verbose=verbose) #Try adding degrevlex gb -> elim loop until len(ideal_basis) does not change @@ -2255,16 +2189,13 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start self.ideal_basis = self._par_graph_gb(term_order="lex",verbose=verbose) self.ideal_basis.sort(key=poly_tup_sortkey) self._triangular_elim(verbose=verbose) - self._checkpoint(checkpoint,5,verbose=verbose) #Find numeric values for each F-symbol self._get_explicit_solution(verbose=verbose) - - #The calculation was successful, so we may delete checkpoints + #The calculation was successful, so we may delete checkpoints and shared resources self._chkpt_status = 7 self.clear_equations() - #Close worker pool and destroy shared resources self.shutdown_worker_pool() if checkpoint: remove("fmatrix_solver_checkpoint_"+self.get_fr_str()+".pickle") @@ -2288,6 +2219,7 @@ def _fix_gauge(self, algorithm=""): sage: f = FMatrix(FusionRing("A3",1)) # long time sage: f._reset_solver_state() # long time + sage: f._var_to_sextuple = {f._poly_ring.gen(i): s for i, s in f._idx_to_sextuple.items()} # long time sage: eqns = f.get_defining_equations("hexagons")+f.get_defining_equations("pentagons") # long time sage: f.ideal_basis = set(Ideal(eqns).groebner_basis()) # long time sage: _, _ = f._substitute_degree_one() # long time @@ -2322,6 +2254,7 @@ def _substitute_degree_one(self, eqns=None): creating variables fx1..fx27 Defining fx0, ..., fx26 sage: f._reset_solver_state() + sage: f._var_to_sextuple = {f._poly_ring.gen(i): s for i, s in f._idx_to_sextuple.items()} sage: f.ideal_basis = [fx0 - 8, fx4**2 - 3, fx4 + fx10 + 3, fx4 + fx9] sage: _, _ = f._substitute_degree_one() sage: f._fvars[f._var_to_sextuple[fx0]] @@ -2335,17 +2268,18 @@ def _substitute_degree_one(self, eqns=None): new_knowns = set() useless = set() for eq in eqns: - if eq.degree() == 1 and sum(eq.degrees()) <= 2 and not self._solved[self._var_to_idx[eq.lm()]]: + if eq.degree() == 1 and sum(eq.degrees()) <= 2 and eq.lm() not in self._solved: self._fvars[self._var_to_sextuple[eq.lm()]] = -sum(c * m for c, m in zip(eq.coefficients()[1:], eq.monomials()[1:])) / eq.lc() #Add variable to set of known values and remove this equation new_knowns.add(eq.lm()) useless.add(eq) #Update fvars depending on other variables - for fx in new_knowns: - self._solved[self._var_to_idx[fx]] = fx + for idx, fx in enumerate(self._poly_ring.gens()): + if fx in new_knowns: + self._solved[idx] = fx for sextuple, rhs in self._fvars.items(): - d = {var: self._fvars[self._var_to_sextuple[var]] for var in rhs.variables() if self._solved[self._var_to_idx[var]]} + d = {var: self._fvars[self._var_to_sextuple[var]] for var in rhs.variables() if var in self._solved} if d: self._fvars[sextuple] = rhs.subs(d) return new_knowns, useless @@ -2360,6 +2294,7 @@ def _update_equations(self): creating variables fx1..fx27 Defining fx0, ..., fx26 sage: f._reset_solver_state() + sage: f._var_to_sextuple = {f._poly_ring.gen(i): s for i, s in f._idx_to_sextuple.items()} sage: f.ideal_basis = [fx0 - 8, fx4 + fx9, fx4**2 + fx3 - fx9**2] sage: _, _ = f._substitute_degree_one() sage: f._update_equations() @@ -2424,9 +2359,10 @@ def find_cyclotomic_solution(self, equations=None, algorithm="", verbose=True, o sage: f.get_defining_equations("pentagons") [] """ - self._reset_solver_state() if self._poly_ring.ngens() == 0: return + self._reset_solver_state() + self._var_to_sextuple = {self._poly_ring.gen(i): s for i, s in self._idx_to_sextuple.items()} if equations is None: if verbose: diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index 3bdf2d4daf0..7f6013dfdb9 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -154,15 +154,18 @@ cpdef _backward_subs(factory, bint flatten=True): one = factory._field.one() _ks = factory._ks fvars = factory._fvars - idx_to_sextuple = factory._idx_to_sextuple solved = factory._solved - for i in range(len(idx_to_sextuple)-1,-1,-1): + cdef dict idx_to_sextuple = factory._idx_to_sextuple + cdef int nvars = len(idx_to_sextuple) + # for i in range(len(idx_to_sextuple)-1,-1,-1): + for i in range(nvars-1,-1,-1): sextuple = idx_to_sextuple[i] rhs = fvars[sextuple] d = {var_idx: fvars[idx_to_sextuple[var_idx]] for var_idx in variables(rhs) if solved[var_idx]} if d: - kp = compute_known_powers(get_variables_degrees([rhs]), d, one) + # kp = compute_known_powers(get_variables_degrees([rhs]), d, one) + kp = compute_known_powers(get_variables_degrees([rhs],nvars), d, one) res = tuple(subs_squares(subs(rhs,kp,one),_ks).items()) if flatten: res = _flatten_coeffs(res) diff --git a/src/sage/combinat/root_system/poly_tup_engine.pxd b/src/sage/combinat/root_system/poly_tup_engine.pxd index 533f2c2d272..f89cd31c713 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pxd +++ b/src/sage/combinat/root_system/poly_tup_engine.pxd @@ -6,7 +6,7 @@ from sage.rings.polynomial.polydict cimport ETuple cpdef tuple poly_to_tup(MPolynomial_libsingular poly) cpdef MPolynomial_libsingular _tup_to_poly(tuple eq_tup, MPolynomialRing_libsingular parent) cpdef tuple resize(tuple eq_tup, dict idx_map, int nvars) -cpdef list get_variables_degrees(list eqns) +cpdef list get_variables_degrees(list eqns, int nvars) cpdef list variables(tuple eq_tup) cpdef constant_coeff(tuple eq_tup, field) cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map) diff --git a/src/sage/combinat/root_system/poly_tup_engine.pyx b/src/sage/combinat/root_system/poly_tup_engine.pyx index 80d8d3073c6..9345853b20e 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pyx +++ b/src/sage/combinat/root_system/poly_tup_engine.pyx @@ -232,7 +232,7 @@ cdef inline ETuple degrees(tuple poly_tup): max_degs = max_degs.emax( ( poly_tup[i])[0]) return max_degs -cpdef list get_variables_degrees(list eqns): +cpdef list get_variables_degrees(list eqns, int nvars): r""" Find maximum degrees for each variable in equations. @@ -242,11 +242,11 @@ cpdef list get_variables_degrees(list eqns): sage: R. = PolynomialRing(QQ) sage: polys = [x**2 + 1, x*y*z**2 - 4*x*y, x*z**3 - 4/3*y + 1] sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup - sage: get_variables_degrees([poly_to_tup(p) for p in polys]) + sage: get_variables_degrees([poly_to_tup(p) for p in polys], 3) [2, 1, 3] """ if not eqns: - return list() + return [0]*nvars cdef ETuple max_deg cdef int i max_deg = degrees(eqns[0]) @@ -342,7 +342,6 @@ cdef inline bint tup_fixes_sq(tuple eq_tup): ### Simplification ### ###################### -# cdef dict subs_squares(dict eq_dict, known_sq): cdef dict subs_squares(dict eq_dict, KSHandler known_sq): r""" Substitute for known squares into a given polynomial. @@ -366,10 +365,7 @@ cdef dict subs_squares(dict eq_dict, KSHandler known_sq): for exp, coeff in eq_dict.items(): new_e = dict() for idx, power in exp.sparse_iter(): - # if idx in known_sq: if known_sq.contains(idx): - # coeff *= known_sq[idx] ** (power // 2) - # coeff *= pow(known_sq[idx], power // 2) coeff *= pow(known_sq.get(idx), power // 2) #New power is 1 if power is odd if power & True: @@ -464,7 +460,7 @@ cpdef dict compute_known_powers(max_degs, dict val_dict, one): sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup sage: known_val = { 0 : poly_to_tup(R(-1)), 2 : poly_to_tup(y**2) } sage: from sage.combinat.root_system.poly_tup_engine import get_variables_degrees - sage: max_deg = get_variables_degrees([poly_to_tup(p) for p in polys]) + sage: max_deg = get_variables_degrees([poly_to_tup(p) for p in polys], 3) sage: compute_known_powers(max_deg, known_val, R.base_ring().one()) {0: [(((0, 0, 0), 1),), (((0, 0, 0), -1),), diff --git a/src/sage/combinat/root_system/shm_managers.pyx b/src/sage/combinat/root_system/shm_managers.pyx index dc73a98f00a..b1f0c450096 100644 --- a/src/sage/combinat/root_system/shm_managers.pyx +++ b/src/sage/combinat/root_system/shm_managers.pyx @@ -674,7 +674,6 @@ cdef class FvarsHandler: k = 0 for r in coeff_tup: num, denom = r.as_integer_ratio() - # assert denom != 0, "zero denominator error" if abs(num) > 2**63 or denom > 2**63: print("Large integers encountered in FvarsHandler", num, denom) if abs(num) < 2**63: From b5f6783f704686d02172f6c85af941a7e0121af3 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Tue, 25 May 2021 19:27:46 -0400 Subject: [PATCH 091/632] removed some code that was commented out --- src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index 7f6013dfdb9..1f634985fcc 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -157,14 +157,12 @@ cpdef _backward_subs(factory, bint flatten=True): solved = factory._solved cdef dict idx_to_sextuple = factory._idx_to_sextuple cdef int nvars = len(idx_to_sextuple) - # for i in range(len(idx_to_sextuple)-1,-1,-1): for i in range(nvars-1,-1,-1): sextuple = idx_to_sextuple[i] rhs = fvars[sextuple] d = {var_idx: fvars[idx_to_sextuple[var_idx]] for var_idx in variables(rhs) if solved[var_idx]} if d: - # kp = compute_known_powers(get_variables_degrees([rhs]), d, one) kp = compute_known_powers(get_variables_degrees([rhs],nvars), d, one) res = tuple(subs_squares(subs(rhs,kp,one),_ks).items()) if flatten: @@ -321,7 +319,6 @@ cdef get_reduced_pentagons(factory, tuple mp_params): #Pre-compute common parameters for speed cdef tuple basis = tuple(factory._FR.basis()) - # cdef dict fvars = factory._fvars #Handle both cyclotomic and orthogonal solution method cdef bint must_zip_up for k, v in factory._fvars.items(): From c82aefc4d9ab894b972d8daffdf5afaacf6591c0 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Wed, 26 May 2021 16:36:13 -0400 Subject: [PATCH 092/632] E83 is multiplicity free --- src/sage/combinat/root_system/f_matrix.py | 8 ++++++-- .../fast_parallel_fmats_methods.pyx | 20 +++++++++---------- src/sage/combinat/root_system/fusion_ring.py | 7 ++++++- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 41b2b0ac1ce..2bed456f3e2 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -349,7 +349,7 @@ def clear_vars(self): fx0 """ self._fvars = {t: self._poly_ring.gen(idx) for idx, t in self._idx_to_sextuple.items()} - self._solved = list(False for fx in self._fvars) + self._solved = [False]*self._poly_ring.ngens() def _reset_solver_state(self): r""" @@ -1798,7 +1798,7 @@ def _par_graph_gb(self,eqns=None,term_order="degrevlex",largest_comp=60,verbose= # for i in range(nmax+1): # vars_by_size.append(self.get_fvars_by_size(i)) - for comp, comp_eqns in self._partition_eqns(verbose=verbose).items(): + for comp, comp_eqns in self._partition_eqns(eqns=eqns,verbose=verbose).items(): #Check if component is too large to process if len(comp) > largest_comp: # fmat_size = 0 @@ -1905,6 +1905,8 @@ def attempt_number_field_computation(self): if ct.letter == 'E': if ct.n < 8 and k == 2: return False + if ct.n == 8 and k == 3: + return False if ct.letter == 'F' and k == 2: return False if ct.letter == 'G' and k == 2: @@ -2021,6 +2023,8 @@ def _get_explicit_solution(self,eqns=None,verbose=True): #Update base field attributes self._FR._field = self.field() self._FR._basecoer = self.get_coerce_map_from_fr_cyclotomic_field() + if self._FR._basecoer: + self._FR.r_matrix.clear_cache() def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start="", use_mp=True, verbose=True): r""" diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx index 1f634985fcc..883bf90c76d 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx @@ -152,9 +152,9 @@ cpdef _backward_subs(factory, bint flatten=True): 1 """ one = factory._field.one() - _ks = factory._ks fvars = factory._fvars solved = factory._solved + cdef KSHandler _ks = factory._ks cdef dict idx_to_sextuple = factory._idx_to_sextuple cdef int nvars = len(idx_to_sextuple) for i in range(nvars-1,-1,-1): @@ -252,18 +252,18 @@ cdef get_reduced_hexagons(factory, tuple mp_params): #Pre-compute common parameters for speed cdef tuple basis = tuple(factory._FR.basis()) cdef dict fvars - cdef bint must_zip_up + cdef bint must_zip_up = False if not output: fvars = {s: factory._poly_ring.gen(i) for i, s in factory._idx_to_sextuple.items()} else: - #Handle both cyclotomic and orthogonal solution method - for k, v in factory._fvars.items(): - must_zip_up = isinstance(v, tuple) - break - if must_zip_up: - fvars = {k: _tup_to_poly(v,parent=factory._poly_ring) for k, v in factory._fvars.items()} - else: - fvars = factory._fvars + #Handle both cyclotomic and orthogonal solution method + for k, v in factory._fvars.items(): + must_zip_up = isinstance(v, tuple) + break + if must_zip_up: + fvars = {k: _tup_to_poly(v,parent=factory._poly_ring) for k, v in factory._fvars.items()} + else: + fvars = factory._fvars r_matrix = factory._FR.r_matrix _Nk_ij = factory._FR.Nk_ij id_anyon = factory._FR.one() diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index 9555869a73d..67df34abe07 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -1071,12 +1071,17 @@ def is_multiplicity_free(self): if ct.n == 1: return True return k <= 2 - if ct.letter in ['B','D','G','F','E']: + # if ct.letter in ['B','D','G','F','E']: + if ct.letter in ['B','D','F','G']: return k <= 2 if ct.letter == 'C': if ct.n == 2: return k <= 2 return k == 1 + if ct.letter == 'E': + if ct.n == 8: + return k <= 3 + return k <= 2 ################################### ### Braid group representations ### From 13deb9670abad544bfc18e3e59d534dd8f81ce9a Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Fri, 28 May 2021 14:45:13 -0700 Subject: [PATCH 093/632] f_matrix.py docstring: remember E8 at level 3 is multiplicity free --- src/sage/combinat/root_system/f_matrix.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/combinat/root_system/f_matrix.py index 2bed456f3e2..228b6f13f90 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/combinat/root_system/f_matrix.py @@ -102,7 +102,9 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab +------------------------+----------+ | `D_r, r\geq 4` | `\leq 2` | +------------------------+----------+ - | `G_2,F_4,E_r` | `\leq 2` | + | `G_2,F_4,E_6,E_7` | `\leq 2` | + +------------------------+----------+ + | `E_8` | `\leq 3` | +------------------------+----------+ Beyond this limitation, computation of the F-matrix From c2e8f44a09a862f98b032df9ec80382c0bd40cb5 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 29 Jun 2021 11:10:36 +1000 Subject: [PATCH 094/632] Finishing review, including moving fusion ring files to subfolder of algebras. --- .../en/reference/algebras/fusion_rings.rst | 20 ++ src/doc/en/reference/algebras/index.rst | 1 + .../media/fusiontree.png | Bin .../media/fusiontree.tex | 0 src/doc/en/reference/combinat/module_list.rst | 2 - src/sage/algebras/all.py | 10 +- src/sage/algebras/fusion_rings/__init__.py | 0 src/sage/algebras/fusion_rings/all.py | 21 ++ .../fusion_rings}/f_matrix.py | 259 +++++++++--------- .../fast_parallel_fmats_methods.pxd | 0 .../fast_parallel_fmats_methods.pyx | 27 +- .../fast_parallel_fusion_ring_braid_repn.pxd | 0 .../fast_parallel_fusion_ring_braid_repn.pyx | 212 +++++++------- .../fusion_rings}/fusion_ring.py | 32 ++- .../fusion_rings}/poly_tup_engine.pxd | 2 +- .../fusion_rings}/poly_tup_engine.pyx | 78 +++--- .../fusion_rings}/shm_managers.pxd | 0 .../fusion_rings}/shm_managers.pyx | 92 ++++--- src/sage/combinat/root_system/__init__.py | 4 +- src/sage/combinat/root_system/all.py | 3 - 20 files changed, 416 insertions(+), 347 deletions(-) create mode 100644 src/doc/en/reference/algebras/fusion_rings.rst rename src/doc/en/reference/{combinat => algebras}/media/fusiontree.png (100%) rename src/doc/en/reference/{combinat => algebras}/media/fusiontree.tex (100%) create mode 100644 src/sage/algebras/fusion_rings/__init__.py create mode 100644 src/sage/algebras/fusion_rings/all.py rename src/sage/{combinat/root_system => algebras/fusion_rings}/f_matrix.py (91%) rename src/sage/{combinat/root_system => algebras/fusion_rings}/fast_parallel_fmats_methods.pxd (100%) rename src/sage/{combinat/root_system => algebras/fusion_rings}/fast_parallel_fmats_methods.pyx (94%) rename src/sage/{combinat/root_system => algebras/fusion_rings}/fast_parallel_fusion_ring_braid_repn.pxd (100%) rename src/sage/{combinat/root_system => algebras/fusion_rings}/fast_parallel_fusion_ring_braid_repn.pyx (60%) rename src/sage/{combinat/root_system => algebras/fusion_rings}/fusion_ring.py (98%) rename src/sage/{combinat/root_system => algebras/fusion_rings}/poly_tup_engine.pxd (95%) rename src/sage/{combinat/root_system => algebras/fusion_rings}/poly_tup_engine.pyx (87%) rename src/sage/{combinat/root_system => algebras/fusion_rings}/shm_managers.pxd (100%) rename src/sage/{combinat/root_system => algebras/fusion_rings}/shm_managers.pyx (90%) diff --git a/src/doc/en/reference/algebras/fusion_rings.rst b/src/doc/en/reference/algebras/fusion_rings.rst new file mode 100644 index 00000000000..06d2473c558 --- /dev/null +++ b/src/doc/en/reference/algebras/fusion_rings.rst @@ -0,0 +1,20 @@ +Fusion Rings +============ + +.. toctree:: + :maxdepth: 2 + + sage/algebras/fusion_rings/fusion_ring + sage/algebras/fusion_rings/f_matrix + +F-Matrix Backend +---------------- + +.. toctree:: + :maxdepth: 2 + + sage/algebras/fusion_rings/fast_parallel_fmats_methods + sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn + sage/algebras/fusion_rings/poly_tup_engine + sage/algebras/fusion_rings/shm_managers + diff --git a/src/doc/en/reference/algebras/index.rst b/src/doc/en/reference/algebras/index.rst index 1a415c7c337..2860b133467 100644 --- a/src/doc/en/reference/algebras/index.rst +++ b/src/doc/en/reference/algebras/index.rst @@ -47,6 +47,7 @@ Named associative algebras sage/algebras/clifford_algebra sage/algebras/cluster_algebra sage/combinat/descent_algebra + fusion_rings sage/algebras/hall_algebra sage/combinat/posets/incidence_algebras sage/algebras/group_algebra diff --git a/src/doc/en/reference/combinat/media/fusiontree.png b/src/doc/en/reference/algebras/media/fusiontree.png similarity index 100% rename from src/doc/en/reference/combinat/media/fusiontree.png rename to src/doc/en/reference/algebras/media/fusiontree.png diff --git a/src/doc/en/reference/combinat/media/fusiontree.tex b/src/doc/en/reference/algebras/media/fusiontree.tex similarity index 100% rename from src/doc/en/reference/combinat/media/fusiontree.tex rename to src/doc/en/reference/algebras/media/fusiontree.tex diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index b546fdb81fb..5ac1f5f01d5 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -291,8 +291,6 @@ Comprehensive Module list sage/combinat/root_system/weight_lattice_realizations sage/combinat/root_system/weight_space sage/combinat/root_system/weyl_characters - sage/combinat/root_system/fusion_ring - sage/combinat/root_system/f_matrix sage/combinat/root_system/weyl_group sage/combinat/rooted_tree sage/combinat/rsk diff --git a/src/sage/algebras/all.py b/src/sage/algebras/all.py index 671282406c6..ac06fa76ab8 100644 --- a/src/sage/algebras/all.py +++ b/src/sage/algebras/all.py @@ -20,18 +20,18 @@ import sage.algebras.catalog as algebras -from .quantum_groups.all import * from .quatalg.all import * +from .steenrod.all import * +from .fusion_rings.all import * +from .lie_algebras.all import * +from .quantum_groups.all import * +from .lie_conformal_algebras.all import * # Algebra base classes from .algebra import Algebra from .free_algebra import FreeAlgebra from .free_algebra_quotient import FreeAlgebraQuotient -from .steenrod.all import * -from .lie_algebras.all import * -from .quantum_groups.all import * -from .lie_conformal_algebras.all import * from .finite_dimensional_algebras.all import FiniteDimensionalAlgebra diff --git a/src/sage/algebras/fusion_rings/__init__.py b/src/sage/algebras/fusion_rings/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/algebras/fusion_rings/all.py b/src/sage/algebras/fusion_rings/all.py new file mode 100644 index 00000000000..6742afd5d55 --- /dev/null +++ b/src/sage/algebras/fusion_rings/all.py @@ -0,0 +1,21 @@ +""" +Fusion Rings +""" + +# **************************************************************************** +# Copyright (C) 2021 Travis Scrimshaw +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.misc.lazy_import import lazy_import + +lazy_import('sage.algebras.fusion_rings.fusion_ring', ['FusionRing']) +lazy_import('sage.algebras.fusion_rings.f_matrix', ['FMatrix']) + +del lazy_import + diff --git a/src/sage/combinat/root_system/f_matrix.py b/src/sage/algebras/fusion_rings/f_matrix.py similarity index 91% rename from src/sage/combinat/root_system/f_matrix.py rename to src/sage/algebras/fusion_rings/f_matrix.py index 228b6f13f90..60375e7d49b 100644 --- a/src/sage/combinat/root_system/f_matrix.py +++ b/src/sage/algebras/fusion_rings/f_matrix.py @@ -24,11 +24,11 @@ import numpy as np from os import getpid, remove -from sage.combinat.root_system.fast_parallel_fmats_methods import ( +from sage.algebras.fusion_rings.fast_parallel_fmats_methods import ( _backward_subs, _solve_for_linear_terms, executor ) -from sage.combinat.root_system.poly_tup_engine import ( +from sage.algebras.fusion_rings.poly_tup_engine import ( apply_coeff_map, constant_coeff, compute_known_powers, get_variables_degrees, variables, @@ -38,7 +38,7 @@ resize, # tup_fixes_sq ) -from sage.combinat.root_system.shm_managers import KSHandler, FvarsHandler +from sage.algebras.fusion_rings.shm_managers import KSHandler, FvarsHandler from sage.graphs.graph import Graph from sage.matrix.constructor import matrix from sage.misc.misc import get_main_globals @@ -197,7 +197,7 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab ([i0, p], [i0, p]) The last two statments show that the possible values of - `X` and `Y` when `A=B=C=D=s` are `i_0` and `p`. + `X` and `Y` when `A = B = C = D = s` are `i_0` and `p`. The F-matrix is computed by solving the so-called pentagon and hexagon equations. The *pentagon equations* @@ -257,7 +257,7 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab cyclotomic field that is the base ring of the underlying :class:`FusionRing`, but sometimes in an extension field adjoining some square roots. When this happens, the :class:`FusionRing` is - modified, adding an attribute :attr:`_basecoer` that is + modified, adding an attribute ``_basecoer`` that is a coercion from the cyclotomic field to the field containing the F-matrix. The field containing the F-matrix is available through :meth:`field`. @@ -278,7 +278,7 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab if inject_variables and (self._FR._fusion_labels is None): self._FR.fusion_labels(fusion_label, inject_variables=True) if not self._FR.is_multiplicity_free(): - raise ValueError("FMatrix is only available for multiplicity free FusionRings") + raise NotImplementedError("FMatrix is only available for multiplicity free FusionRings") #Set up F-symbols entry by entry n_vars = self.findcases() self._poly_ring = PolynomialRing(self._FR.field(),n_vars,var_prefix) @@ -311,7 +311,7 @@ def __repr__(self): """ EXAMPLES:: - sage: FMatrix(FusionRing("B2",1)) + sage: FMatrix(FusionRing("B2", 1)) F-Matrix factory for The Fusion Ring of Type B2 and level 1 with Integer Ring coefficients """ return "F-Matrix factory for %s"%self._FR @@ -322,7 +322,7 @@ def clear_equations(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("E6",1)) + sage: f = FMatrix(FusionRing("E6", 1)) sage: f.get_defining_equations('hexagons', output=False) sage: len(f.ideal_basis) 6 @@ -360,7 +360,7 @@ def _reset_solver_state(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("G2",1)) + sage: f = FMatrix(FusionRing("G2", 1)) sage: f._reset_solver_state() sage: K = f.field() sage: len(f._nnz.nonzero_positions()) @@ -416,7 +416,7 @@ def fmat(self, a, b, c, d, x, y, data=True): EXAMPLES:: - sage: f=FMatrix(FusionRing("G2",1,fusion_labels=("i0","t"),inject_variables=True)) + sage: f=FMatrix(FusionRing("G2", 1, fusion_labels=("i0","t"), inject_variables=True)) sage: [f.fmat(t,t,t,t,x,y) for x in f._FR.basis() for y in f._FR.basis()] [fx1, fx2, fx3, fx4] sage: f.find_cyclotomic_solution(output=True) @@ -476,7 +476,7 @@ def fmatrix(self,a,b,c,d): EXAMPLES:: - sage: f = FMatrix(FusionRing("A1",2,fusion_labels="c",inject_variables=True)) + sage: f = FMatrix(FusionRing("A1", 2, fusion_labels="c", inject_variables=True)) sage: f.fmatrix(c1,c1,c1,c1) [fx0 fx1] [fx2 fx3] @@ -506,15 +506,15 @@ def field(self): The field may change after running :meth:`find_orthogonal_solution`. At that point, this method could return the associated :class:`FusionRing`'s cyclotomic field, an - appropriate :class:`NumberField` that was computed on the fly - by the F-matrix solver, or the :class:`AlgebraicField` ``QQbar``. + appropriate :func:`NumberField` that was computed on the fly + by the F-matrix solver, or the :class:`QQbar`. Depending on the ``CartanType`` of ``self``, the solver may need to compute an extension field containing certain square roots that do not belong to the associated :class:`FusionRing`'s cyclotomic field. - In certain cases we revert to ``QQbar`` because the extension field - computation does not seem to terminate. See + In certain cases we revert to :class:`QQbar` because + the extension field computation does not seem to terminate. See :meth:`attempt_number_field_computation` for more details. The method :meth:`get_non_cyclotomic_roots` returns a list of @@ -536,7 +536,7 @@ def field(self): .. NOTE:: Consider using ``self.field().optimized_representation()`` to - obtain an equivalent :class:`NumberField` with a defining + obtain an equivalent :func:`NumberField` with a defining polynomial with smaller coefficients, for a more efficient element representation. """ @@ -564,7 +564,7 @@ def findcases(self,output=False): EXAMPLES:: - sage: f = FMatrix(FusionRing("G2",1,fusion_labels=("i0","t"))) + sage: f = FMatrix(FusionRing("G2", 1, fusion_labels=("i0","t"))) sage: f.findcases() 5 sage: f.findcases(output=True) @@ -635,16 +635,16 @@ def f_to(self,a,b,c,d): EXAMPLES:: - sage: b22 = FusionRing("B2",2) - sage: b22.fusion_labels("b",inject_variables=True) + sage: b22 = FusionRing("B2", 2) + sage: b22.fusion_labels("b", inject_variables=True) sage: B=FMatrix(b22) - sage: B.fmatrix(b2,b4,b2,b4) + sage: B.fmatrix(b2, b4, b2, b4) [fx266 fx267 fx268] [fx269 fx270 fx271] [fx272 fx273 fx274] - sage: B.f_from(b2,b4,b2,b4) + sage: B.f_from(b2, b4, b2, b4) [b1, b3, b5] - sage: B.f_to(b2,b4,b2,b4) + sage: B.f_to(b2, b4, b2, b4) [b1, b3, b5] """ return [y for y in self._FR.basis() @@ -666,7 +666,7 @@ def get_fvars(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("A2",1), inject_variables=True) + sage: f = FMatrix(FusionRing("A2", 1), inject_variables=True) creating variables fx1..fx8 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 sage: f.get_fvars()[(f1, f1, f1, f0, f2, f2)] @@ -683,7 +683,7 @@ def get_poly_ring(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("B6",1)) + sage: f = FMatrix(FusionRing("B6", 1)) sage: f.get_poly_ring() Multivariate Polynomial Ring in fx0, ..., fx13 over Cyclotomic Field of order 96 and degree 32 @@ -693,8 +693,9 @@ def get_poly_ring(self): def get_non_cyclotomic_roots(self): r""" Return a list of roots that define the extension of the associated - :class:`FusionRing`'s base :class:`CyclotomicField` containing all - the F-symbols. + :class:`FusionRing`'s base + :class:`Cyclotomic field`, + containing all the F-symbols. OUTPUT: @@ -706,13 +707,13 @@ def get_non_cyclotomic_roots(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("E6",1)) + sage: f = FMatrix(FusionRing("E6", 1)) sage: f.find_orthogonal_solution(verbose=False) sage: f.field() == f.FR().field() True sage: f.get_non_cyclotomic_roots() [] - sage: f = FMatrix(FusionRing("G2",1)) + sage: f = FMatrix(FusionRing("G2", 1)) sage: f.find_orthogonal_solution(verbose=False) sage: f.field() == f.FR().field() False @@ -722,23 +723,25 @@ def get_non_cyclotomic_roots(self): When ``self.field()`` is a ``NumberField``, one may use :meth:`get_qqbar_embedding` to embed the resulting values into - ``QQbar``. + :class:`QQbar`. """ return sorted(set(self._non_cyc_roots)) def get_qqbar_embedding(self): r""" Return an embedding from the base field containing F-symbols (the - associated :class:`FusionRing`'s :class:`CyclotomicField`, a - :class:`NumberField`, or ``QQbar``) into ``QQbar``. + associated :class:`FusionRing`'s + :class:`Cyclotomic field`, + a :func:`NumberField`, or :class:`QQbar`) into + :class:`QQbar`. This embedding is useful for getting a better sense for the F-symbols, particularly when they are computed as elements of a - :class:`NumberField`. See also :meth:`get_non_cyclotomic_roots`. + :func:`NumberField`. See also :meth:`get_non_cyclotomic_roots`. EXAMPLES:: - sage: f = FMatrix(FusionRing("G2",1), fusion_label="g", inject_variables=True) + sage: f = FMatrix(FusionRing("G2", 1), fusion_label="g", inject_variables=True) creating variables fx1..fx5 Defining fx0, fx1, fx2, fx3, fx4 sage: f.find_orthogonal_solution() @@ -765,17 +768,18 @@ def get_coerce_map_from_fr_cyclotomic_field(self): r""" Return a coercion map from the associated :class:`FusionRing`'s cyclotomic field into the base field containing all F-symbols - (this could be the :class:`FusionRing`'s :class:`CyclotomicField`, a - :class:`NumberField`, or ``QQbar``). + (this could be the :class:`FusionRing`'s + :class:`Cyclotomic field`, + a :func:`NumberField`, or :class:`QQbar`). EXAMPLES:: - sage: f = FMatrix(FusionRing("G2",1)) + sage: f = FMatrix(FusionRing("G2", 1)) sage: f.find_orthogonal_solution(verbose=False) sage: f.FR().field() Cyclotomic Field of order 60 and degree 16 sage: f.field() - Number Field in a with defining polynomial y^32 - 6*y^30 - 7*y^28 + 62*y^26 - 52*y^24 - 308*y^22 + 831*y^20 + 7496*y^18 + 18003*y^16 - 2252*y^14 + 42259*y^12 - 65036*y^10 + 29368*y^8 - 3894*y^6 + 377*y^4 - 22*y^2 + 1 + Number Field in a with defining polynomial y^32 - ... - 22*y^2 + 1 sage: phi = f.get_coerce_map_from_fr_cyclotomic_field() sage: phi.domain() == f.FR().field() True @@ -783,13 +787,14 @@ def get_coerce_map_from_fr_cyclotomic_field(self): True When F-symbols are computed as elements of the associated - :class:`FusionRing`'s base :class:`CyclotomicField`, + :class:`FusionRing`'s base + :class:`Cyclotomic field`, we have ``self.field() == self.FR().field()`` and this method returns the identity map on ``self.field()``. :: - sage: f = FMatrix(FusionRing("A2",1)) + sage: f = FMatrix(FusionRing("A2", 1)) sage: f.find_orthogonal_solution(verbose=False) sage: phi = f.get_coerce_map_from_fr_cyclotomic_field() sage: f.field() @@ -812,14 +817,15 @@ def get_coerce_map_from_fr_cyclotomic_field(self): def get_fvars_in_alg_field(self): r""" - Return F-symbols as elements of the :class:`AlgebraicField` ``QQbar``. + Return F-symbols as elements of the :class:`QQbar`. + This method uses the embedding defined by :meth:`get_qqbar_embedding` to coerce - F-symbols into ``QQbar``. + F-symbols into :class:`QQbar`. EXAMPLES:: - sage: f = FMatrix(FusionRing("G2",1), fusion_label="g", inject_variables=True) + sage: f = FMatrix(FusionRing("G2", 1), fusion_label="g", inject_variables=True) creating variables fx1..fx5 Defining fx0, fx1, fx2, fx3, fx4 sage: f.find_orthogonal_solution(verbose=False) @@ -840,7 +846,7 @@ def get_radical_expression(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("G2",1)) + sage: f = FMatrix(FusionRing("G2", 1)) sage: f.FR().fusion_labels("g", inject_variables=True) sage: f.find_orthogonal_solution(verbose=False) sage: radical_fvars = f.get_radical_expression() # long time (~1.5s) @@ -860,7 +866,7 @@ def _get_known_vals(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("D4",1)) + sage: f = FMatrix(FusionRing("D4", 1)) sage: f._reset_solver_state() sage: len(f._get_known_vals()) == 0 True @@ -881,7 +887,7 @@ def _get_known_nonz(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("D5",1)) # indirect doctest + sage: f = FMatrix(FusionRing("D5", 1)) # indirect doctest sage: f._reset_solver_state() sage: f._nnz (100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, @@ -902,7 +908,7 @@ def largest_fmat_size(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("B3",2)) + sage: f = FMatrix(FusionRing("B3", 2)) sage: f.largest_fmat_size() 4 """ @@ -916,8 +922,7 @@ def get_fvars_by_size(self,n,indices=False): INPUT: - `n` -- a positive integer - - - ``indices`` -- (default: ``False``) a boolean. + - ``indices`` -- boolean (default: ``False``) If ``indices`` is ``False`` (default), this method returns a set of sextuples `(a,b,c,d,x,y)` identifying @@ -989,7 +994,7 @@ def save_fvars(self, filename): EXAMPLES:: - sage: f = FMatrix(FusionRing("A2",1)) + sage: f = FMatrix(FusionRing("A2", 1)) sage: f.find_orthogonal_solution(verbose=False) sage: fvars = f.get_fvars() sage: K = f.field() @@ -1057,7 +1062,7 @@ def get_fr_str(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("B3",1)) + sage: f = FMatrix(FusionRing("B3", 1)) sage: f.get_fr_str() 'B31' """ @@ -1070,14 +1075,14 @@ def _checkpoint(self, do_chkpt, status, verbose=True): EXAMPLES:: - sage: f = FMatrix(FusionRing("A1",3)) + sage: f = FMatrix(FusionRing("A1", 3)) sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) sage: f.get_defining_equations('hexagons',output=False) sage: f.ideal_basis = f._par_graph_gb(verbose=False) - sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey, poly_to_tup + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey, poly_to_tup sage: f.ideal_basis.sort(key=poly_tup_sortkey) - sage: from sage.combinat.root_system.shm_managers import FvarsHandler + sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() sage: f._fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars) sage: f._triangular_elim(verbose=False) @@ -1108,11 +1113,11 @@ def _checkpoint(self, do_chkpt, status, verbose=True): sage: f.get_orthogonality_constraints(output=False) sage: f.get_defining_equations('hexagons',output=False) sage: f.ideal_basis = f._par_graph_gb(verbose=False) - sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey sage: f.ideal_basis.sort(key=poly_tup_sortkey) - sage: from sage.combinat.root_system.shm_managers import FvarsHandler + sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() - sage: f._fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars) + sage: f._fvars = FvarsHandler(n, f._field, f._idx_to_sextuple, init_data=f._fvars) sage: f._triangular_elim(verbose=False) sage: f._update_reduction_params() sage: f.get_defining_equations('pentagons',output=False) @@ -1121,7 +1126,7 @@ def _checkpoint(self, do_chkpt, status, verbose=True): sage: f._checkpoint(do_chkpt=True,status=4) Checkpoint 4 reached! sage: del f - sage: f = FMatrix(FusionRing("A1",2)) + sage: f = FMatrix(FusionRing("A1", 2)) sage: f.find_orthogonal_solution(warm_start="fmatrix_solver_checkpoint_A12.pickle") Computing F-symbols for The Fusion Ring of Type A1 and level 2 with Integer Ring coefficients with 14 variables... Partitioned 0 equations into 0 components of size: @@ -1142,23 +1147,23 @@ def _checkpoint(self, do_chkpt, status, verbose=True): if verbose: print(f"Checkpoint {status} reached!") - def _restore_state(self,filename): + def _restore_state(self, filename): r""" Load solver state from file. Use this method both for warm-starting :meth:`find_orthogonal_solution` and to load pickled results. EXAMPLES:: - sage: f = FMatrix(FusionRing("A1",2)) + sage: f = FMatrix(FusionRing("A1", 2)) sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) - sage: f.get_defining_equations('hexagons',output=False) + sage: f.get_defining_equations('hexagons', output=False) sage: f.ideal_basis = f._par_graph_gb(verbose=False) - sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey, poly_to_tup + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey, poly_to_tup sage: f.ideal_basis.sort(key=poly_tup_sortkey) - sage: from sage.combinat.root_system.shm_managers import FvarsHandler + sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() - sage: f._fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars) + sage: f._fvars = FvarsHandler(n, f._field, f._idx_to_sextuple, init_data=f._fvars) sage: f._triangular_elim(verbose=False) sage: f._update_reduction_params() sage: fvars = f._fvars @@ -1166,10 +1171,10 @@ def _restore_state(self,filename): sage: solved = f._solved sage: ks = f._ks sage: status = f._chkpt_status - sage: f._checkpoint(do_chkpt=True,status=2) + sage: f._checkpoint(do_chkpt=True, status=2) Checkpoint 2 reached! sage: del f - sage: f = FMatrix(FusionRing("A1",2)) + sage: f = FMatrix(FusionRing("A1", 2)) sage: f._reset_solver_state() sage: f._restore_state("fmatrix_solver_checkpoint_A12.pickle") sage: for sextuple, fvar in fvars.items(): @@ -1187,10 +1192,10 @@ def _restore_state(self,filename): TESTS:: - sage: f = FMatrix(FusionRing("A1",3)) + sage: f = FMatrix(FusionRing("A1", 3)) sage: f.find_orthogonal_solution(save_results="test.pickle",verbose=False) # long time sage: del f - sage: f = FMatrix(FusionRing("A1",3)) + sage: f = FMatrix(FusionRing("A1", 3)) sage: f.find_orthogonal_solution(warm_start="test.pickle") # long time sage: f._chkpt_status == 7 # long time True @@ -1212,7 +1217,7 @@ def _restore_state(self,filename): ### MapReduce ### ################# - def start_worker_pool(self,processes=None): + def start_worker_pool(self, processes=None): """ Initialize a ``multiprocessing`` worker pool for parallel processing, which may be used e.g. to set up defining equations using @@ -1246,7 +1251,7 @@ def start_worker_pool(self,processes=None): EXAMPLES:: - sage: f = FMatrix(FusionRing("G2",1)) + sage: f = FMatrix(FusionRing("G2", 1)) sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ sage: he = f.get_defining_equations('hexagons') sage: sorted(he) @@ -1330,7 +1335,7 @@ def shutdown_worker_pool(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("A1",3)) + sage: f = FMatrix(FusionRing("A1", 3)) sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ sage: he = f.get_defining_equations('hexagons') sage: f.shutdown_worker_pool() @@ -1345,7 +1350,7 @@ def shutdown_worker_pool(self): self._pid_list.shm.unlink() del self.__dict__['_shared_fvars'] - def _map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_thresh=None): + def _map_triv_reduce(self, mapper, input_iter, worker_pool=None, chunksize=None, mp_thresh=None): r""" Apply the given mapper to each element of the given input iterable and return the results (with no duplicates) in a list. @@ -1367,7 +1372,7 @@ def _map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_t EXAMPLES:: - sage: f = FMatrix(FusionRing("A1",2)) + sage: f = FMatrix(FusionRing("A1", 2)) sage: f._reset_solver_state() sage: len(f._map_triv_reduce('get_reduced_hexagons',[(0,1,False)])) 11 @@ -1409,7 +1414,7 @@ def _map_triv_reduce(self,mapper,input_iter,worker_pool=None,chunksize=None,mp_t ### Equations set up ### ######################## - def get_orthogonality_constraints(self,output=True): + def get_orthogonality_constraints(self, output=True): r""" Get equations imposed on the F-matrix by orthogonality. @@ -1454,7 +1459,7 @@ def get_orthogonality_constraints(self,output=True): return eqns self.ideal_basis.extend([poly_to_tup(eq) for eq in eqns]) - def get_defining_equations(self,option,output=True): + def get_defining_equations(self, option, output=True): r""" Get the equations defining the ideal generated by the hexagon or pentagon relations. @@ -1485,7 +1490,7 @@ def get_defining_equations(self,option,output=True): EXAMPLES:: - sage: f = FMatrix(FusionRing("B2",1)) + sage: f = FMatrix(FusionRing("B2", 1)) sage: sorted(f.get_defining_equations('hexagons')) [fx7 + 1, fx6 - 1, @@ -1518,7 +1523,7 @@ def get_defining_equations(self,option,output=True): ### Equations processing ### ############################ - def _tup_to_fpoly(self,eq_tup): + def _tup_to_fpoly(self, eq_tup): r""" Assemble a polynomial object from its tuple representation. @@ -1529,12 +1534,12 @@ def _tup_to_fpoly(self,eq_tup): It is meant for internal use by the F-matrix solver. This method is a left inverse of - :meth:`sage.combinat.root_system.poly_tup_engine.poly_to_tup`. + :meth:`sage.algebras.fusion_rings.poly_tup_engine.poly_to_tup`. EXAMPLES:: - sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup - sage: f = FMatrix(FusionRing("C3",1)) + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup + sage: f = FMatrix(FusionRing("C3", 1)) sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ sage: he = f.get_defining_equations('hexagons') sage: all(f._tup_to_fpoly(poly_to_tup(h)) for h in he) @@ -1543,25 +1548,25 @@ def _tup_to_fpoly(self,eq_tup): """ return _tup_to_poly(eq_tup,parent=self._poly_ring) - def _update_reduction_params(self,eqns=None): + def _update_reduction_params(self, eqns=None): r""" Update reduction parameters that are solver state attributes. EXAMPLES:: - sage: f = FMatrix(FusionRing("A1",3)) + sage: f = FMatrix(FusionRing("A1", 3)) sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ - sage: f.get_defining_equations('hexagons',output=False) + sage: f.get_defining_equations('hexagons', output=False) sage: f.ideal_basis = f._par_graph_gb(verbose=False) - sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey, poly_to_tup + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey, poly_to_tup sage: f.ideal_basis.sort(key=poly_tup_sortkey) sage: f.mp_thresh = 0 sage: if is_shared_memory_available: ....: f._fvars = f._shared_fvars ....: else: - ....: from sage.combinat.root_system.shm_managers import FvarsHandler + ....: from sage.algebras.fusion_rings.shm_managers import FvarsHandler ....: f._fvars = FvarsHandler(f._poly_ring.ngens(),f._field,f._idx_to_sextuple,init_data=f._fvars) sage: f._triangular_elim(verbose=False) # indirect doctest sage: f.ideal_basis @@ -1586,7 +1591,7 @@ def _update_reduction_params(self,eqns=None): self._nnz = self._get_known_nonz() self._kp = compute_known_powers(self._var_degs,self._get_known_vals(),self._field.one()) - def _triangular_elim(self,eqns=None,verbose=True): + def _triangular_elim(self, eqns=None, verbose=True): r""" Perform triangular elimination of linear terms in two-term equations until no such terms exist. @@ -1598,15 +1603,15 @@ def _triangular_elim(self,eqns=None,verbose=True): EXAMPLES:: - sage: f = FMatrix(FusionRing("D3",1)) - sage: f.get_defining_equations('hexagons',output=False) + sage: f = FMatrix(FusionRing("D3", 1)) + sage: f.get_defining_equations('hexagons', output=False) sage: f.get_orthogonality_constraints(output=False) sage: gb = f._par_graph_gb(verbose=False) - sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey, poly_to_tup + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey, poly_to_tup sage: f.ideal_basis = sorted(gb, key=poly_tup_sortkey) - sage: from sage.combinat.root_system.shm_managers import FvarsHandler + sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() - sage: f._fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars) + sage: f._fvars = FvarsHandler(n, f._field, f._idx_to_sextuple, init_data=f._fvars) sage: f._triangular_elim() Elimination epoch completed... 0 eqns remain in ideal basis sage: f.ideal_basis @@ -1642,7 +1647,7 @@ def _triangular_elim(self,eqns=None,verbose=True): ### Graph methods ### ##################### - def equations_graph(self,eqns=None): + def equations_graph(self, eqns=None): r""" Construct a graph corresponding to the given equations. @@ -1680,7 +1685,7 @@ def equations_graph(self,eqns=None): EXAMPLES:: - sage: f = FMatrix(FusionRing("A3",1)) + sage: f = FMatrix(FusionRing("A3", 1)) sage: f.get_poly_ring().ngens() 27 sage: he = f.get_defining_equations('hexagons') @@ -1711,7 +1716,7 @@ def equations_graph(self,eqns=None): G.add_edge(x,y) return G - def _partition_eqns(self,eqns=None,verbose=True): + def _partition_eqns(self, eqns=None, verbose=True): r""" Partition equations corresponding to edges in a disconnected graph. @@ -1729,7 +1734,7 @@ def _partition_eqns(self,eqns=None,verbose=True): sage: partition = f._partition_eqns() Partitioned 11 equations into 5 components of size: [4, 3, 3, 3, 1] - sage: from sage.combinat.root_system.poly_tup_engine import variables + sage: from sage.algebras.fusion_rings.poly_tup_engine import variables sage: for c, e in partition.items(): ....: assert set(v for eq_tup in e for v in variables(eq_tup)) == set(c) sage: vars_in_partition = set() @@ -1756,7 +1761,7 @@ def _partition_eqns(self,eqns=None,verbose=True): print(graph.connected_components_sizes()) return partition - def _par_graph_gb(self,eqns=None,term_order="degrevlex",largest_comp=60,verbose=True): + def _par_graph_gb(self, eqns=None, term_order="degrevlex", largest_comp=60, verbose=True): r""" Compute a Groebner basis for a list of equations partitioned according to their corresponding graph. @@ -1780,7 +1785,7 @@ def _par_graph_gb(self,eqns=None,term_order="degrevlex",largest_comp=60,verbose= sage: gb = f._par_graph_gb() Partitioned 10 equations into 2 components of size: [4, 1] - sage: from sage.combinat.root_system.poly_tup_engine import _unflatten_coeffs + sage: from sage.algebras.fusion_rings.poly_tup_engine import _unflatten_coeffs sage: ret = [f._tup_to_fpoly(_unflatten_coeffs(f.field(), t)) for t in gb] sage: ret.sort(); ret [fx4 + (-zeta80^24 + zeta80^16), @@ -1812,8 +1817,8 @@ def _par_graph_gb(self,eqns=None,term_order="degrevlex",largest_comp=60,verbose= temp_eqns.extend(comp_eqns) else: small_comps.append(comp_eqns) - input_iter = zip_longest(small_comps,[],fillvalue=term_order) - small_comp_gb = self._map_triv_reduce('compute_gb',input_iter,worker_pool=self.pool,chunksize=1,mp_thresh=50) + input_iter = zip_longest(small_comps, [], fillvalue=term_order) + small_comp_gb = self._map_triv_reduce('compute_gb', input_iter, worker_pool=self.pool, chunksize=1, mp_thresh=50) ret = small_comp_gb + temp_eqns return ret @@ -1830,18 +1835,18 @@ def _get_component_variety(self,var,eqns): EXAMPLES:: - sage: f = FMatrix(FusionRing("G2",2)) + sage: f = FMatrix(FusionRing("G2", 2)) sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ - sage: f.get_defining_equations('hexagons',output=False) # long time + sage: f.get_defining_equations('hexagons', output=False) # long time sage: f.shutdown_worker_pool() sage: partition = f._partition_eqns() # long time Partitioned 327 equations into 35 components of size: [27, 27, 27, 24, 24, 16, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 9, 9, 6, 6, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1] sage: c = (216, 292, 319) - sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup sage: eqns = partition[c] + [poly_to_tup(f._poly_ring.gen(216)-1)] # long time - sage: f._get_component_variety(c,eqns) # long time + sage: f._get_component_variety(c, eqns) # long time [{216: -1, 292: -1, 319: 1}] """ #Define smaller poly ring in component vars @@ -1866,23 +1871,23 @@ def attempt_number_field_computation(self): r""" Based on the ``CartanType`` of ``self`` and data known on March 17, 2021, determine whether to attempt - to find a :class:`NumberField` containing all the F-symbols. + to find a :func:`NumberField` containing all the F-symbols. This method is used by :meth:`find_orthogonal_solution` to determine a field containing all F-symbols. See :meth:`field` and :meth:`get_non_cyclotomic_roots`. - For certain :class:`FusionRing `, the number field + For certain :class:`fusion rings `, the number field computation does not terminate in reasonable time. In these cases, we report F-symbols as elements - of the :class:`AlgebraicField` ``QQbar``. + of the :class:`QQbar`. EXAMPLES:: - sage: f = FMatrix(FusionRing("F4",2)) + sage: f = FMatrix(FusionRing("F4", 2)) sage: f.attempt_number_field_computation() False - sage: f = FMatrix(FusionRing("G2",1)) + sage: f = FMatrix(FusionRing("G2", 1)) sage: f.attempt_number_field_computation() True @@ -1890,10 +1895,10 @@ def attempt_number_field_computation(self): In certain cases, F-symbols are found in the associated :class:`FusionRing`'s cyclotomic field and a - :class:`NumberField` computation is not needed. In these + :func:`NumberField` computation is not needed. In these cases this method returns ``True`` but the :meth:`find_orthogonal_solution` solver does *not* - undertake a :class:`NumberField` computation. + undertake a :func:`NumberField` computation. """ ct = self._FR.cartan_type() k = self._FR._k @@ -1924,7 +1929,7 @@ def _get_explicit_solution(self,eqns=None,verbose=True): EXAMPLES:: - sage: f = FMatrix(FusionRing("A1",3)) # indirect doctest + sage: f = FMatrix(FusionRing("A1", 3)) # indirect doctest sage: f.find_orthogonal_solution() # long time Computing F-symbols for The Fusion Ring of Type A1 and level 3 with Integer Ring coefficients with 71 variables... Set up 134 hex and orthogonality constraints... @@ -2084,7 +2089,7 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start EXAMPLES:: - sage: f = FMatrix(FusionRing("B5",1), fusion_label="b", inject_variables=True) + sage: f = FMatrix(FusionRing("B5", 1), fusion_label="b", inject_variables=True) creating variables fx1..fx14 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 sage: f.find_orthogonal_solution() @@ -2119,10 +2124,15 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start True In any case, the F-symbols are obtained as elements of the associated - :class:`FusionRing`'s :class:`CyclotomicField`, a computed - :class:`NumberField`, or ``QQbar``. Currently, the field containing - the F-symbols is determined based on the ``CartanType`` associated - to ``self``. See :meth:`attempt_number_field_computation` for details. + :class:`FusionRing`'s + :class:`Cyclotomic field`, + a computed :func:`NumberField`, or :class:`QQbar`. + Currently, the field containing the F-symbols is determined based + on the ``CartanType`` associated to ``self``. + + .. SEEALSO:: + + :meth:`attempt_number_field_computation` """ if self._poly_ring.ngens() == 0: return @@ -2223,7 +2233,7 @@ def _fix_gauge(self, algorithm=""): EXAMPLES:: - sage: f = FMatrix(FusionRing("A3",1)) # long time + sage: f = FMatrix(FusionRing("A3", 1)) sage: f._reset_solver_state() # long time sage: f._var_to_sextuple = {f._poly_ring.gen(i): s for i, s in f._idx_to_sextuple.items()} # long time sage: eqns = f.get_defining_equations("hexagons")+f.get_defining_equations("pentagons") # long time @@ -2256,7 +2266,7 @@ def _substitute_degree_one(self, eqns=None): EXAMPLES:: - sage: f = FMatrix(FusionRing("D3",1), inject_variables=True) + sage: f = FMatrix(FusionRing("D3", 1), inject_variables=True) creating variables fx1..fx27 Defining fx0, ..., fx26 sage: f._reset_solver_state() @@ -2296,7 +2306,7 @@ def _update_equations(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("D3",1), inject_variables=True) + sage: f = FMatrix(FusionRing("D3", 1), inject_variables=True) creating variables fx1..fx27 Defining fx0, ..., fx26 sage: f._reset_solver_state() @@ -2333,7 +2343,7 @@ def find_cyclotomic_solution(self, equations=None, algorithm="", verbose=True, o EXAMPLES:: - sage: f = FMatrix(FusionRing("A2",1,fusion_labels="a",inject_variables=True),inject_variables=True) + sage: f = FMatrix(FusionRing("A2", 1, fusion_labels="a", inject_variables=True), inject_variables=True) creating variables fx1..fx8 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 sage: f.find_cyclotomic_solution(output=True) @@ -2408,8 +2418,8 @@ def certify_pentagons(self,use_mp=True,verbose=False): EXAMPLES:: - sage: f = FMatrix(FusionRing("C3",1)) # long time - sage: f.find_orthogonal_solution() # long time + sage: f = FMatrix(FusionRing("C3", 1)) + sage: f.find_orthogonal_solution() # long time Computing F-symbols for The Fusion Ring of Type C3 and level 1 with Integer Ring coefficients with 71 variables... Set up 134 hex and orthogonality constraints... Partitioned 134 equations into 17 components of size: @@ -2457,7 +2467,7 @@ def fmats_are_orthogonal(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("D4",1)) + sage: f = FMatrix(FusionRing("D4", 1)) sage: f.find_orthogonal_solution(verbose=False) sage: f.fmats_are_orthogonal() True @@ -2474,7 +2484,7 @@ def fvars_are_real(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("A1",3)) # long time + sage: f = FMatrix(FusionRing("A1", 3)) sage: f.find_orthogonal_solution(verbose=False) # long time sage: f.fvars_are_real() # long time True @@ -2483,6 +2493,7 @@ def fvars_are_real(self): for k, v in self._fvars.items(): AA(self._qqbar_embedding(v)) except ValueError: - print("The F-symbol {} (key {}) has a nonzero imaginary part!".format(v,k)) + print("the F-symbol {} (key {}) has a nonzero imaginary part!".format(v,k)) return False return True + diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd b/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pxd similarity index 100% rename from src/sage/combinat/root_system/fast_parallel_fmats_methods.pxd rename to src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pxd diff --git a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx b/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx similarity index 94% rename from src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx rename to src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx index 883bf90c76d..c3158585904 100644 --- a/src/sage/combinat/root_system/fast_parallel_fmats_methods.pyx +++ b/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx @@ -9,7 +9,7 @@ Fast F-Matrix methods # **************************************************************************** cimport cython -from sage.combinat.root_system.poly_tup_engine cimport ( +from sage.algebras.fusion_rings.poly_tup_engine cimport ( compute_known_powers, get_variables_degrees, variables, poly_to_tup, _tup_to_poly, @@ -18,7 +18,7 @@ from sage.combinat.root_system.poly_tup_engine cimport ( has_appropriate_linear_term, resize ) -from sage.combinat.root_system.shm_managers cimport KSHandler, FvarsHandler +from sage.algebras.fusion_rings.shm_managers cimport KSHandler, FvarsHandler from sage.rings.number_field.number_field_element cimport NumberFieldElement_absolute from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular, MPolynomialRing_libsingular from sage.rings.polynomial.polydict cimport ETuple @@ -44,14 +44,14 @@ cpdef _solve_for_linear_terms(factory, list eqns=None): Defining fx0, ..., fx26 sage: f._reset_solver_state() sage: f.ideal_basis = [fx0**3, fx0 + fx3**4, fx2**2 - fx3, fx2 - fx3**2, fx4 - fx2] - sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup sage: f.ideal_basis = [poly_to_tup(p) for p in f.ideal_basis] - sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey sage: f.ideal_basis.sort(key=poly_tup_sortkey) - sage: from sage.combinat.root_system.shm_managers import FvarsHandler + sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() sage: f._fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars) - sage: from sage.combinat.root_system.fast_parallel_fmats_methods import _solve_for_linear_terms + sage: from sage.algebras.fusion_rings.fast_parallel_fmats_methods import _solve_for_linear_terms sage: _solve_for_linear_terms(f) True sage: f._tup_to_fpoly(f._fvars[f._idx_to_sextuple[0]]) @@ -118,14 +118,14 @@ cpdef _backward_subs(factory, bint flatten=True): Defining fx0, ..., fx26 sage: f._reset_solver_state() sage: f.ideal_basis = [fx0**3, fx0 + fx3**4, fx2**2 - fx3, fx2 - fx3**2, fx4 - fx2] - sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup sage: f.ideal_basis = [poly_to_tup(p) for p in f.ideal_basis] - sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey sage: f.ideal_basis.sort(key=poly_tup_sortkey) - sage: from sage.combinat.root_system.shm_managers import FvarsHandler + sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() sage: f._fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars) - sage: from sage.combinat.root_system.fast_parallel_fmats_methods import _solve_for_linear_terms + sage: from sage.algebras.fusion_rings.fast_parallel_fmats_methods import _solve_for_linear_terms sage: _solve_for_linear_terms(f) True sage: f._tup_to_fpoly(f._fvars[f._idx_to_sextuple[0]]) @@ -140,7 +140,7 @@ cpdef _backward_subs(factory, bint flatten=True): sage: f.ideal_basis.sort(key=poly_tup_sortkey) sage: _solve_for_linear_terms(f) True - sage: from sage.combinat.root_system.fast_parallel_fmats_methods import _backward_subs + sage: from sage.algebras.fusion_rings.fast_parallel_fmats_methods import _backward_subs sage: _backward_subs(f) sage: f._tup_to_fpoly(f._fvars[f._idx_to_sextuple[0]]) 0 @@ -457,7 +457,7 @@ cdef dict mappers = { cpdef executor(tuple params): r""" Execute a function defined in this module - (``sage.combinat.root_system.fast_parallel_fmats_methods``) in a worker + (``sage.algebras.fusion_rings.fast_parallel_fmats_methods``) in a worker process, and supply the factory parameter by constructing a reference to the ``FMatrix`` object in the worker's memory adress space from its ``id``. @@ -479,7 +479,7 @@ cpdef executor(tuple params): TESTS:: - sage: from sage.combinat.root_system.fast_parallel_fmats_methods import executor + sage: from sage.algebras.fusion_rings.fast_parallel_fmats_methods import executor sage: fmats = FMatrix(FusionRing("A1",3)) sage: fmats._reset_solver_state() sage: params = (('get_reduced_hexagons', id(fmats)), (0,1,True)) @@ -539,3 +539,4 @@ cdef pent_verify(factory, tuple mp_params): feq_verif(factory,worker_results,fvars,Nk_ij,id_anyon,nonuple) if i % 50000000 == 0 and i and verbose: print("{:5d}m equations checked... {} potential misses so far...".format(i // 1000000,len(worker_results))) + diff --git a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pxd b/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pxd similarity index 100% rename from src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pxd rename to src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pxd diff --git a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx b/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx similarity index 60% rename from src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx rename to src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx index 4270243eb26..562e44fb6c9 100644 --- a/src/sage/combinat/root_system/fast_parallel_fusion_ring_braid_repn.pyx +++ b/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx @@ -10,7 +10,7 @@ Fast FusionRing methods for computing braid group representations from ctypes import cast, py_object cimport cython -from sage.combinat.root_system.fast_parallel_fmats_methods cimport _fmat +from sage.algebras.fusion_rings.fast_parallel_fmats_methods cimport _fmat from itertools import product from sage.rings.qqbar import QQbar @@ -37,8 +37,9 @@ cdef mid_sig_ij(fusion_ring,row,col,a,b): xi, yi = row xj, yj = col entry = 0 - for c in fusion_ring.basis(): - for d in fusion_ring.basis(): + cdef list basis = list(fusion_ring.basis()) + for c in basis: + for d in basis: ##Warning: We assume F-matrices are orthogonal!!! (using transpose for inverse) f1 = _fmat(_fvars,_Nk_ij,one,a,a,yi,b,xi,c) f2 = _fmat(_fvars,_Nk_ij,one,a,a,a,c,d,yi) @@ -100,7 +101,7 @@ cdef cached_odd_one_out_ij(fusion_ring,xi,xj,a,b): @cython.cdivision(True) cdef sig_2k(fusion_ring, tuple args): r""" - Compute entries of the 2k-th braid generator + Compute entries of the `2k`-th braid generator """ #Pre-compute common parameters for efficiency _fvars = fusion_ring.fmats._fvars @@ -114,62 +115,65 @@ cdef sig_2k(fusion_ring, tuple args): cdef list worker_results = list() #Get computational basis cdef list comp_basis = fusion_ring.get_computational_basis(a,b,n_strands) - cdef dict basis_dict = { elt : i for i, elt in enumerate(comp_basis) } + cdef dict basis_dict = {elt: i for i, elt in enumerate(comp_basis)} cdef int dim = len(comp_basis) cdef set coords = set() cdef int i #Avoid pickling cyclotomic field element objects cdef bint must_flatten_coeff = fusion_ring.fvars_field() != QQbar + cdef list basis = list(fusion_ring.basis()) for i in range(dim): - for f,e,q in product(fusion_ring.basis(), repeat=3): - #Distribute work amongst processes - ctr += 1 - if ctr % n_proc != child_id: - continue - - #Compute appropriate possible nonzero row index - nnz_pos = list(comp_basis[i]) - nnz_pos[k-1] = f - nnz_pos[k] = e - #Handle the special case k = 1 - if k > 1: - nnz_pos[n_strands//2+k-2] = q - nnz_pos = tuple(nnz_pos) - - #Skip repeated entries when k = 1 - if nnz_pos in comp_basis and (basis_dict[nnz_pos],i) not in coords: - m, l = comp_basis[i][:n_strands//2], comp_basis[i][n_strands//2:] - #A few special cases - top_left = m[0] - if k >= 3: - top_left = l[k-3] - root = b - if k - 1 < len(l): - root = l[k-1] - - #Handle the special case k = 1 - if k == 1: - entry = cached_mid_sig_ij(fusion_ring,m[:2],(f,e),a,root) - - #Avoid pickling cyclotomic field element objects - if must_flatten_coeff: - entry = entry.list() - - worker_results.append(((basis_dict[nnz_pos],i), entry)) - coords.add((basis_dict[nnz_pos],i)) - continue - - entry = 0 - for p in fusion_ring.basis(): - f1 = _fmat(_fvars,_Nk_ij,one,top_left,m[k-1],m[k],root,l[k-2],p) - f2 = _fmat(_fvars,_Nk_ij,one,top_left,f,e,root,q,p) - entry += f1 * cached_mid_sig_ij(fusion_ring,(m[k-1],m[k]),(f,e),a,p) * f2 - - #Avoid pickling cyclotomic field element objects - if must_flatten_coeff: - entry = entry.list() - - worker_results.append(((basis_dict[nnz_pos], i), entry)) + for f in basis: + for e in basis: + for q in basis: + #Distribute work amongst processes + ctr += 1 + if ctr % n_proc != child_id: + continue + + #Compute appropriate possible nonzero row index + nnz_pos = list(comp_basis[i]) + nnz_pos[k-1] = f + nnz_pos[k] = e + #Handle the special case k = 1 + if k > 1: + nnz_pos[n_strands//2+k-2] = q + nnz_pos = tuple(nnz_pos) + + #Skip repeated entries when k = 1 + if nnz_pos in comp_basis and (basis_dict[nnz_pos],i) not in coords: + m, l = comp_basis[i][:n_strands//2], comp_basis[i][n_strands//2:] + #A few special cases + top_left = m[0] + if k >= 3: + top_left = l[k-3] + root = b + if k - 1 < len(l): + root = l[k-1] + + #Handle the special case k = 1 + if k == 1: + entry = cached_mid_sig_ij(fusion_ring,m[:2],(f,e),a,root) + + #Avoid pickling cyclotomic field element objects + if must_flatten_coeff: + entry = entry.list() + + worker_results.append(((basis_dict[nnz_pos],i), entry)) + coords.add((basis_dict[nnz_pos],i)) + continue + + entry = 0 + for p in fusion_ring.basis(): + f1 = _fmat(_fvars,_Nk_ij,one,top_left,m[k-1],m[k],root,l[k-2],p) + f2 = _fmat(_fvars,_Nk_ij,one,top_left,f,e,root,q,p) + entry += f1 * cached_mid_sig_ij(fusion_ring,(m[k-1],m[k]),(f,e),a,p) * f2 + + #Avoid pickling cyclotomic field element objects + if must_flatten_coeff: + entry = entry.list() + + worker_results.append(((basis_dict[nnz_pos], i), entry)) return worker_results @cython.nonecheck(False) @@ -184,63 +188,68 @@ cdef odd_one_out(fusion_ring, tuple args): _Nk_ij = fusion_ring.Nk_ij one = fusion_ring.one() - cdef list worker_results = list() - cdef int child_id, n_proc + cdef list worker_results = [] + cdef list nnz_pos_temp + cdef tuple nnz_pos + cdef int child_id, n_proc, i child_id, n_proc, fn_args = args a, b, n_strands = fn_args cdef int ctr = -1 #Get computational basis - comp_basis = fusion_ring.get_computational_basis(a, b, n_strands) - basis_dict = {elt: i for i, elt in enumerate(comp_basis)} - dim = len(comp_basis) + cdef list comp_basis = fusion_ring.get_computational_basis(a, b, n_strands) + cdef dict basis_dict = {elt: i for i, elt in enumerate(comp_basis)} + cdef int dim = len(comp_basis) #Avoid pickling cyclotomic field element objects cdef bint must_flatten_coeff = fusion_ring.fvars_field() != QQbar + + cdef list basis = list(fusion_ring.basis()) for i in range(dim): - for f, q in product(fusion_ring.basis(),repeat=2): - #Distribute work amongst processes - ctr += 1 - if ctr % n_proc != child_id: - continue - - #Compute appropriate possible nonzero row index - nnz_pos = list(comp_basis[i]) - nnz_pos[n_strands//2-1] = f - #Handle small special case - if n_strands > 3: - nnz_pos[-1] = q - nnz_pos = tuple(nnz_pos) - - if nnz_pos in comp_basis: - m, l = comp_basis[i][:n_strands//2], comp_basis[i][n_strands//2:] - - #Handle a couple of small special cases - if n_strands == 3: - entry = cached_odd_one_out_ij(fusion_ring,m[-1],f,a,b) + for f in basis: + for q in basis: + #Distribute work amongst processes + ctr += 1 + if ctr % n_proc != child_id: + continue + + #Compute appropriate possible nonzero row index + nnz_pos_temp = list(comp_basis[i]) + nnz_pos_temp[n_strands//2-1] = f + #Handle small special case + if n_strands > 3: + nnz_pos_temp[-1] = q + nnz_pos = tuple(nnz_pos_temp) + + if nnz_pos in comp_basis: + m, l = comp_basis[i][:n_strands//2], comp_basis[i][n_strands//2:] + + #Handle a couple of small special cases + if n_strands == 3: + entry = cached_odd_one_out_ij(fusion_ring,m[-1],f,a,b) + + #Avoid pickling cyclotomic field element objects + if must_flatten_coeff: + entry = entry.list() + + worker_results.append(((basis_dict[nnz_pos], i), entry)) + continue + top_left = m[0] + if n_strands > 5: + top_left = l[-2] + root = b + + #Compute relevant entry + entry = 0 + for p in basis: + f1 = _fmat(_fvars,_Nk_ij,one,top_left,m[-1],a,root,l[-1],p) + f2 = _fmat(_fvars,_Nk_ij,one,top_left,f,a,root,q,p) + entry += f1 * cached_odd_one_out_ij(fusion_ring,m[-1],f,a,p) * f2 #Avoid pickling cyclotomic field element objects if must_flatten_coeff: entry = entry.list() - worker_results.append(((basis_dict[nnz_pos], i), entry)) - continue - top_left = m[0] - if n_strands > 5: - top_left = l[-2] - root = b - - #Compute relevant entry - entry = 0 - for p in fusion_ring.basis(): - f1 = _fmat(_fvars,_Nk_ij,one,top_left,m[-1],a,root,l[-1],p) - f2 = _fmat(_fvars,_Nk_ij,one,top_left,f,a,root,q,p) - entry += f1 * cached_odd_one_out_ij(fusion_ring,m[-1],f,a,p) * f2 - - #Avoid pickling cyclotomic field element objects - if must_flatten_coeff: - entry = entry.list() - - worker_results.append(((basis_dict[nnz_pos],i), entry)) + worker_results.append(((basis_dict[nnz_pos],i), entry)) return worker_results ############################## @@ -270,14 +279,14 @@ cpdef executor(tuple params): TESTS:: - sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import executor + sage: from sage.algebras.fusion_rings.fast_parallel_fusion_ring_braid_repn import executor sage: FR = FusionRing("A1",4) sage: FR.fusion_labels(['idd','one','two','three','four'],inject_variables=True) sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time sage: params = (('sig_2k',id(FR)),(0,1,(1,one,one,5))) # long time sage: len(executor(params)) == 13 # long time True - sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import executor + sage: from sage.algebras.fusion_rings.fast_parallel_fusion_ring_braid_repn import executor sage: FR = FusionRing("A1",2) sage: FR.fusion_labels("a",inject_variables=True) sage: FR.fmats.find_orthogonal_solution(verbose=False) @@ -305,7 +314,7 @@ cpdef _unflatten_entries(fusion_ring, list entries): EXAMPLES:: - sage: from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import _unflatten_entries + sage: from sage.algebras.fusion_rings.fast_parallel_fusion_ring_braid_repn import _unflatten_entries sage: fr = FusionRing("B2",2) sage: F = fr.field() sage: coeff = [F.random_element() for i in range(2)] @@ -319,3 +328,4 @@ cpdef _unflatten_entries(fusion_ring, list entries): if F != QQbar: for i, (coord, entry) in enumerate(entries): entries[i] = (coord, F(entry)) + diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/algebras/fusion_rings/fusion_ring.py similarity index 98% rename from src/sage/combinat/root_system/fusion_ring.py rename to src/sage/algebras/fusion_rings/fusion_ring.py index 67df34abe07..868843ced25 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/algebras/fusion_rings/fusion_ring.py @@ -14,7 +14,7 @@ from itertools import product, zip_longest from multiprocessing import Pool, set_start_method from sage.combinat.q_analogues import q_int -from sage.combinat.root_system.fast_parallel_fusion_ring_braid_repn import ( +from sage.algebras.fusion_rings.fast_parallel_fusion_ring_braid_repn import ( executor, _unflatten_entries ) @@ -526,8 +526,8 @@ def fvars_field(self): Depending on the ``CartanType`` associated to ``self`` and whether a call to an F-matrix solver has been made, this method - will return the same field as :meth:`field`, a ``NumberField``, - or the ``AlgebraicField`` ``QQbar``. + will return the same field as :meth:`field`, a :func:`NumberField`, + or the :class:`QQbar`. See :meth:`FMatrix.attempt_number_field_computation` for more details. Before running an F-matrix solver, the output of this method matches @@ -560,9 +560,10 @@ def fvars_field(self): sage: A13.field() Cyclotomic Field of order 40 and degree 16 - In some cases, the :meth:`NumberField.optimized_representation` + In some cases, the :meth:`NumberField.optimized_representation() + ` may be used to obtain a better defining polynomial for the - computed ``NumberField``. + computed :func:`NumberField`. """ if self.is_multiplicity_free(): return self.fmats.field() @@ -968,7 +969,9 @@ def r_matrix(self, i, j, k, base_coercion=True): def global_q_dimension(self, base_coercion=True): r""" Return `\sum d_i^2`, where the sum is over all simple objects - and `d_i` is the quantum dimension. It is a positive real number. + and `d_i` is the quantum dimension. + + The global `q`-dimension is a positive real number. EXAMPLES:: @@ -982,9 +985,11 @@ def global_q_dimension(self, base_coercion=True): def total_q_order(self, base_coercion=True): r""" - Return the positive square root of ``self.global_q_dimension()`` - as an element of ``self.field()``. Implemented as `D_{+}e^{-i\pi c/4}` - where `D_+` is ``self.D_plus()`` and `c` is ``self.virasoro_central_charge()`` + Return the positive square root of :meth:`self.global_q_dimension() + ` as an element of :meth:`self.field() `. + + This is implemented as `D_{+}e^{-i\pi c/4}`, where `D_+` is + :meth:`D_plus()` and `c` is :meth:`virasoro_central_charge()`. EXAMPLES:: @@ -1028,7 +1033,6 @@ def D_plus(self, base_coercion=True): return ret return self._basecoer(ret) - def D_minus(self, base_coercion=True): r""" Return `\sum d_i^2\theta_i^{-1}` where `i` runs through the simple @@ -1117,7 +1121,7 @@ def get_computational_basis(self,a,b,n_strands): l_{k-2} \in l_{k-3} \otimes m_{k-1},\\ \cdots,\\ l_2 \in l_1 \otimes m_3,\\ - l_1 \in m_1 \otimes m_2. + l_1 \in m_1 \otimes m_2, \end{array} where `z \in x \otimes y` means `N_{xy}^z \neq 0`. @@ -1168,7 +1172,7 @@ def fmats(self): sage: A15.fmats F-Matrix factory for The Fusion Ring of Type A1 and level 5 with Integer Ring coefficients """ - from sage.combinat.root_system.f_matrix import FMatrix + from sage.algebras.fusion_rings.f_matrix import FMatrix return FMatrix(self) def _emap(self, mapper, input_args, worker_pool=None): @@ -1261,7 +1265,7 @@ def get_braid_generators(self, to be verbose with the computation For more information on the optional parameters, see - :meth:`find_orthogonal_solution` of the :class:`FMatrix` module. + :meth:`FMatrix.find_orthogonal_solution`. Given a simple object in the fusion category, here called ``fusing_anyon`` allowing the universal R-matrix to act on adjacent @@ -1279,7 +1283,7 @@ def get_braid_generators(self, is as follows. See :meth:`get_computational_basis` for more information. - .. image:: ../../../media/fusiontree.png + .. IMAGE:: ../../../media/fusiontree.png ``sig`` is a list of braid group generators as matrices. In some cases these will be represented as sparse matrices. diff --git a/src/sage/combinat/root_system/poly_tup_engine.pxd b/src/sage/algebras/fusion_rings/poly_tup_engine.pxd similarity index 95% rename from src/sage/combinat/root_system/poly_tup_engine.pxd rename to src/sage/algebras/fusion_rings/poly_tup_engine.pxd index f89cd31c713..a4ddf9b92ae 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pxd +++ b/src/sage/algebras/fusion_rings/poly_tup_engine.pxd @@ -1,4 +1,4 @@ -from sage.combinat.root_system.shm_managers cimport KSHandler +from sage.algebras.fusion_rings.shm_managers cimport KSHandler from sage.rings.number_field.number_field_element cimport NumberFieldElement_absolute from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular, MPolynomialRing_libsingular from sage.rings.polynomial.polydict cimport ETuple diff --git a/src/sage/combinat/root_system/poly_tup_engine.pyx b/src/sage/algebras/fusion_rings/poly_tup_engine.pyx similarity index 87% rename from src/sage/combinat/root_system/poly_tup_engine.pyx rename to src/sage/algebras/fusion_rings/poly_tup_engine.pyx index 9345853b20e..f0e277921c2 100644 --- a/src/sage/combinat/root_system/poly_tup_engine.pyx +++ b/src/sage/algebras/fusion_rings/poly_tup_engine.pyx @@ -19,7 +19,7 @@ cpdef inline tuple poly_to_tup(MPolynomial_libsingular poly): EXAMPLES:: - sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup sage: R. = PolynomialRing(QQ) sage: poly_to_tup(x**2 + 1) (((2, 0), 1), ((0, 0), 1)) @@ -49,20 +49,20 @@ cpdef inline MPolynomial_libsingular _tup_to_poly(tuple eq_tup, MPolynomialRing_ EXAMPLES:: - sage: from sage.combinat.root_system.poly_tup_engine import _tup_to_poly + sage: from sage.algebras.fusion_rings.poly_tup_engine import _tup_to_poly sage: K = CyclotomicField(20) sage: R. = PolynomialRing(K) sage: poly_tup = (((2,0),K.one()), ((0,0),K.one())) sage: _tup_to_poly(poly_tup, parent=R) x^2 + 1 sage: poly = x**2*y**4 - 4/5*x*y**2 + 1/3 * y - sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup sage: _tup_to_poly(poly_to_tup(poly), parent=R) == poly True TESTS:: - sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup, _tup_to_poly + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup, _tup_to_poly sage: R. = PolynomialRing(CyclotomicField(20)) sage: r = R.random_element() sage: _tup_to_poly(poly_to_tup(r), parent=R) == r @@ -98,10 +98,10 @@ cpdef tuple _unflatten_coeffs(field, tuple eq_tup): EXAMPLES:: - sage: from sage.combinat.root_system.poly_tup_engine import _unflatten_coeffs + sage: from sage.algebras.fusion_rings.poly_tup_engine import _unflatten_coeffs sage: fm = FMatrix(FusionRing("A2",2)) sage: p = fm._poly_ring.random_element() - sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup sage: flat_poly_tup = list() sage: for exp, cyc_coeff in poly_to_tup(p): ....: flat_poly_tup.append((exp, tuple(cyc_coeff._coefficients()))) @@ -134,7 +134,7 @@ cdef inline int has_appropriate_linear_term(tuple eq_tup): If the given polynomial contains an appropriate linear term, this method returns the index of the monomial in which the term appears. - Otherwise, the method returns -1. + Otherwise, the method returns `-1`. """ max_var = degrees(eq_tup).nonzero_positions()[0] cdef ETuple m @@ -159,7 +159,7 @@ cpdef inline tup_to_univ_poly(tuple eq_tup, univ_poly_ring): EXAMPLES:: - sage: from sage.combinat.root_system.poly_tup_engine import tup_to_univ_poly + sage: from sage.algebras.fusion_rings.poly_tup_engine import tup_to_univ_poly sage: from sage.rings.polynomial.polydict import ETuple sage: K = CyclotomicField(56) sage: poly_tup = ((ETuple([0,3,0]),K(2)), (ETuple([0,1,0]),K(-1)), (ETuple([0,0,0]),K(-2/3))) @@ -175,7 +175,7 @@ cpdef inline tup_to_univ_poly(tuple eq_tup, univ_poly_ring): """ cdef ETuple exp cdef NumberFieldElement_absolute c - return univ_poly_ring({ exp._data[1] if exp._nonzero else 0 : c for exp, c in eq_tup }) + return univ_poly_ring({exp._data[1] if exp._nonzero else 0: c for exp, c in eq_tup}) cpdef inline tuple resize(tuple eq_tup, dict idx_map, int nvars): """ @@ -189,7 +189,7 @@ cpdef inline tuple resize(tuple eq_tup, dict idx_map, int nvars): EXAMPLES:: - sage: from sage.combinat.root_system.poly_tup_engine import resize + sage: from sage.algebras.fusion_rings.poly_tup_engine import resize sage: from sage.rings.polynomial.polydict import ETuple sage: K = CyclotomicField(56) sage: poly_tup = ((ETuple([0,3,0,2]),K(2)), (ETuple([0,1,0,1]),K(-1)), (ETuple([0,0,0,0]),K(-2/3))) @@ -201,7 +201,7 @@ cpdef inline tuple resize(tuple eq_tup, dict idx_map, int nvars): sage: R.inject_variables() Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19 sage: sparse_poly = R(fx0**2 * fx17 + fx3) - sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup, _tup_to_poly + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup, _tup_to_poly sage: S. = PolynomialRing(K) sage: _tup_to_poly(resize(poly_to_tup(sparse_poly),{0:0,3:1,17:2},3), parent=S) x^2*z + y @@ -238,10 +238,10 @@ cpdef list get_variables_degrees(list eqns, int nvars): EXAMPLES:: - sage: from sage.combinat.root_system.poly_tup_engine import get_variables_degrees + sage: from sage.algebras.fusion_rings.poly_tup_engine import get_variables_degrees sage: R. = PolynomialRing(QQ) sage: polys = [x**2 + 1, x*y*z**2 - 4*x*y, x*z**3 - 4/3*y + 1] - sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup sage: get_variables_degrees([poly_to_tup(p) for p in polys], 3) [2, 1, 3] """ @@ -252,7 +252,7 @@ cpdef list get_variables_degrees(list eqns, int nvars): max_deg = degrees(eqns[0]) for i in range(1, len(eqns)): max_deg = max_deg.emax(degrees( (eqns[i]) )) - cdef list dense = [0]*len(max_deg) + cdef list dense = [0] * len(max_deg) for i in range(max_deg._nonzero): dense[max_deg._data[2*i]] = max_deg._data[2*i+1] return dense @@ -263,12 +263,12 @@ cpdef list variables(tuple eq_tup): EXAMPLES:: - sage: from sage.combinat.root_system.poly_tup_engine import variables + sage: from sage.algebras.fusion_rings.poly_tup_engine import variables sage: from sage.rings.polynomial.polydict import ETuple sage: poly_tup = ((ETuple([0,3,0]),2), (ETuple([0,1,0]),-1), (ETuple([0,0,0]),-2/3)) sage: variables(poly_tup) [1] - sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup sage: R. = PolynomialRing(QQ) sage: variables(poly_to_tup(x*2*y + y**3 - 4/3*x)) [0, 1] @@ -284,13 +284,13 @@ cpdef constant_coeff(tuple eq_tup, field): EXAMPLES:: - sage: from sage.combinat.root_system.poly_tup_engine import constant_coeff + sage: from sage.algebras.fusion_rings.poly_tup_engine import constant_coeff sage: from sage.rings.polynomial.polydict import ETuple sage: poly_tup = ((ETuple([0,3,0]),2), (ETuple([0,1,0]),-1), (ETuple([0,0,0]),-2/3)) sage: constant_coeff(poly_tup,QQ) -2/3 sage: R. = PolynomialRing(QQ) - sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup sage: constant_coeff(poly_to_tup(x**5 + x*y*z - 9),QQ) -9 """ @@ -306,10 +306,10 @@ cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map): EXAMPLES:: - sage: from sage.combinat.root_system.poly_tup_engine import apply_coeff_map + sage: from sage.algebras.fusion_rings.poly_tup_engine import apply_coeff_map sage: sq = lambda x : x**2 sage: R. = PolynomialRing(ZZ) - sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup, _tup_to_poly + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup, _tup_to_poly sage: _tup_to_poly(apply_coeff_map(poly_to_tup(x + 2*y + 3*z), sq), parent=R) x + 4*y + 9*z """ @@ -349,10 +349,10 @@ cdef dict subs_squares(dict eq_dict, KSHandler known_sq): INPUT: - ``eq_dict`` -- a dictionary of ``(ETuple, coeff)`` pairs representing - a polynomial. + a polynomial - ``known_sq`` -- a dictionary of ``(int i, NumberFieldElement a)`` pairs - such that `x_i^2 - a = 0`. + such that `x_i^2 - a = 0` OUTPUT: @@ -447,31 +447,30 @@ cpdef dict compute_known_powers(max_degs, dict val_dict, one): - ``max_deg`` -- an ``ETuple`` indicating the maximal degree of each variable - - ``val_dict`` -- a dictionary of ``(var_idx, poly_tup)`` key-value - pairs + - ``val_dict`` -- a dictionary of ``(var_idx, poly_tup)`` key-value pairs - ``poly_tup`` -- a tuple of ``(ETuple, coeff)`` pairs reperesenting a multivariate polynomial EXAMPLES:: - sage: from sage.combinat.root_system.poly_tup_engine import compute_known_powers + sage: from sage.algebras.fusion_rings.poly_tup_engine import compute_known_powers sage: R. = PolynomialRing(QQ) sage: polys = [x**3 + 1, x**2*y + z**3, y**2 - 3*y] - sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup sage: known_val = { 0 : poly_to_tup(R(-1)), 2 : poly_to_tup(y**2) } - sage: from sage.combinat.root_system.poly_tup_engine import get_variables_degrees + sage: from sage.algebras.fusion_rings.poly_tup_engine import get_variables_degrees sage: max_deg = get_variables_degrees([poly_to_tup(p) for p in polys], 3) sage: compute_known_powers(max_deg, known_val, R.base_ring().one()) {0: [(((0, 0, 0), 1),), - (((0, 0, 0), -1),), - (((0, 0, 0), 1),), - (((0, 0, 0), -1),)], + (((0, 0, 0), -1),), + (((0, 0, 0), 1),), + (((0, 0, 0), -1),)], 2: [(((0, 0, 0), 1),), - (((0, 2, 0), 1),), - (((0, 4, 0), 1),), - (((0, 6, 0), 1),)]} + (((0, 2, 0), 1),), + (((0, 4, 0), 1),), + (((0, 6, 0), 1),)]} """ - assert (max_degs and max(max_degs) <= 100) or True, "Cannot substitute for degree larger than 100" + assert (max_degs and max(max_degs) <= 100) or True, "cannot substitute for degree larger than 100" cdef ETuple max_deg = ETuple(list(max_degs)) max_deg = max_deg.emin(ETuple({idx: 100 for idx in val_dict}, len(max_deg))) cdef dict known_powers @@ -528,8 +527,8 @@ cdef tuple tup_mul(tuple p1, tuple p2): cdef tuple monom_sortkey(ETuple exp): r""" - Produce a sortkey for a monomial exponent w.r.t. degree reversed - lexicographic ordering. + Produce a sortkey for a monomial exponent with respect to degree + reversed lexicographic ordering. """ cdef int deg = exp.unweighted_degree() # for i in range(exp._nonzero): @@ -551,11 +550,11 @@ cpdef tuple poly_tup_sortkey(tuple eq_tup): EXAMPLES:: - sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey sage: R. = PolynomialRing(QQ, order='deglex') sage: p1 = x*y*z - x**2 + 3/2 sage: p2 = x*y*z - x*y + 1/2 - sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup sage: (p1 < p2) == (poly_tup_sortkey(poly_to_tup(p1)) < poly_tup_sortkey(poly_to_tup(p2))) True sage: R. = PolynomialRing(CyclotomicField(20), order='deglex') @@ -567,7 +566,7 @@ cpdef tuple poly_tup_sortkey(tuple eq_tup): TESTS:: - sage: from sage.combinat.root_system.poly_tup_engine import poly_tup_sortkey, poly_to_tup + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey, poly_to_tup sage: R. = PolynomialRing(CyclotomicField(20)) sage: p1 = R.random_element() sage: p2 = R.random_element() @@ -591,3 +590,4 @@ cpdef tuple poly_tup_sortkey(tuple eq_tup): key.append(-exp._data[2*i]) key.append(exp._data[2*i+1]) return tuple(key) + diff --git a/src/sage/combinat/root_system/shm_managers.pxd b/src/sage/algebras/fusion_rings/shm_managers.pxd similarity index 100% rename from src/sage/combinat/root_system/shm_managers.pxd rename to src/sage/algebras/fusion_rings/shm_managers.pxd diff --git a/src/sage/combinat/root_system/shm_managers.pyx b/src/sage/algebras/fusion_rings/shm_managers.pyx similarity index 90% rename from src/sage/combinat/root_system/shm_managers.pyx rename to src/sage/algebras/fusion_rings/shm_managers.pyx index b1f0c450096..416739f04d2 100644 --- a/src/sage/combinat/root_system/shm_managers.pyx +++ b/src/sage/algebras/fusion_rings/shm_managers.pyx @@ -17,7 +17,7 @@ factory is a cyclotomic field. cimport cython from cysignals.memory cimport sig_malloc cimport numpy as np -from sage.combinat.root_system.poly_tup_engine cimport poly_to_tup, tup_fixes_sq, _flatten_coeffs +from sage.algebras.fusion_rings.poly_tup_engine cimport poly_to_tup, tup_fixes_sq, _flatten_coeffs from sage.rings.integer cimport Integer from sage.rings.rational cimport Rational from sage.rings.polynomial.multi_polynomial_libsingular cimport MPolynomial_libsingular @@ -75,7 +75,7 @@ cdef class KSHandler: EXAMPLES:: - sage: from sage.combinat.root_system.shm_managers import KSHandler + sage: from sage.algebras.fusion_rings.shm_managers import KSHandler sage: #Create shared data structure sage: f = FMatrix(FusionRing("A1",2), inject_variables=True) creating variables fx1..fx14 @@ -88,7 +88,7 @@ cdef class KSHandler: sage: ks2 = KSHandler(n,f._field,name=name,use_mp=is_shared_memory_available) sage: if not is_shared_memory_available: ....: ks2 = ks - sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup sage: eqns = [fx1**2 - 4, fx3**2 + f._field.gen()**4 - 1/19*f._field.gen()**2] sage: ks.update([poly_to_tup(p) for p in eqns]) sage: for idx, sq in ks.items(): @@ -114,18 +114,18 @@ cdef class KSHandler: try: from multiprocessing import shared_memory except ImportError: - raise ImportError("Failed to import shared_memory module. Requires Python 3.8+") + raise ImportError("failed to import shared_memory module; requires Python 3.8+") if name is None: - self.shm = shared_memory.SharedMemory(create=True,size=n*ks_t.itemsize) + self.shm = shared_memory.SharedMemory(create=True, size=n*ks_t.itemsize) else: self.shm = shared_memory.SharedMemory(name=name) - self.ks_dat = np.ndarray((n,),dtype=ks_t,buffer=self.shm.buf) + self.ks_dat = np.ndarray((n,), dtype=ks_t, buffer=self.shm.buf) else: - self.ks_dat = np.ndarray((n,),dtype=ks_t) + self.ks_dat = np.ndarray((n,), dtype=ks_t) if name is None: - self.ks_dat['known'] = np.zeros((n,1),dtype='bool') - self.ks_dat['nums'] = np.zeros((n,d),dtype='i8') - self.ks_dat['denoms'] = np.ones((n,d),dtype='u8') + self.ks_dat['known'] = np.zeros((n,1), dtype='bool') + self.ks_dat['nums'] = np.zeros((n,d), dtype='i8') + self.ks_dat['denoms'] = np.ones((n,d), dtype='u8') #Populate initializer data for idx, sq in init_data.items(): self.setitem(idx,sq) @@ -139,7 +139,7 @@ cdef class KSHandler: if it exists. """ if not self.ks_dat['known'][idx]: - raise KeyError('Index {} does not correspond to a known square'.format(idx)) + raise KeyError('index {} does not correspond to a known square'.format(idx)) if self.obj_cache[idx] is not None: return self.obj_cache[idx] cdef int d @@ -252,9 +252,9 @@ cdef class KSHandler: num = quo.numerator() denom = quo.denominator() if num > 2**32: - print("Large num encountered in KS",num) + print("WARNING: Large num encountered in KS", num) if denom > 2**32: - print("Large denom encountered in KS",denom) + print("WARNING: Large denom encountered in KS", denom) nums[i] = num denoms[i] = denom @@ -274,7 +274,7 @@ cdef class KSHandler: sage: f = FMatrix(FusionRing("C2",2)) sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) - sage: from sage.combinat.root_system.shm_managers import KSHandler + sage: from sage.algebras.fusion_rings.shm_managers import KSHandler sage: n = f._poly_ring.ngens() sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ sage: ks = KSHandler(n,f._field,use_mp=is_shared_memory_available,init_data=f._ks) @@ -354,7 +354,8 @@ def make_KSHandler(n_slots,field,init_data): return KSHandler(n_slots,field,init_data=init_data) cdef class FvarsHandler: - def __init__(self,n_slots,field,idx_to_sextuple,init_data={},use_mp=0,pids_name=None,name=None,max_terms=20,n_bytes=32): + def __init__(self, n_slots, field,idx_to_sextuple, init_data={}, use_mp=0, + pids_name=None, name=None, max_terms=20, n_bytes=32): """ Return a shared memory backed dict-like structure to manage the ``_fvars`` attribute of an F-matrix factory object. @@ -385,23 +386,25 @@ cdef class FvarsHandler: INPUT: - - ``factory`` -- an F-matrix factory object. + - ``factory`` -- an F-matrix factory object - ``name`` -- the name of a shared memory object - (used by child processes for attaching). - - ``max_terms`` -- maximum number of terms in each entry. Since + (used by child processes for attaching) + - ``max_terms`` -- maximum number of terms in each entry; since we use contiguous C-style memory blocks, the size of the block - must be known in advance. + must be known in advance - ``use_mp`` -- an integer indicating the number of child processes - used for multiprocessing. If running serially, use 0. + used for multiprocessing; if running serially, use 0. + Multiprocessing requires Python 3.8+, since we must import the ``multiprocessing.shared_memory`` module. Attempting to initialize when ``multiprocessing.shared_memory`` is not available results in an ``ImportError``. + - ``pids_name`` -- the name of a ``ShareableList`` contaning the process ``pid``'s for every process in the pool (including the - parent process). + parent process) - ``n_bytes`` -- the number of bytes that should be allocated for - each numerator and each denominator stored by the structure. + each numerator and each denominator stored by the structure .. NOTE:: @@ -416,14 +419,14 @@ cdef class FvarsHandler: .. WARNING:: - The current data structure supports up to 2**16 entries, + The current data structure supports up to `2^16` entries, with each monomial in each entry having at most 254 nonzero terms. On average, each of the ``max_terms`` monomials can have at most 30 terms. EXAMPLES:: - sage: from sage.combinat.root_system.shm_managers import FvarsHandler + sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: #Create shared data structure sage: f = FMatrix(FusionRing("A2",1), inject_variables=True) creating variables fx1..fx8 @@ -441,7 +444,7 @@ cdef class FvarsHandler: sage: fvars2 = FvarsHandler(8,f._field,f._idx_to_sextuple,name=name,use_mp=n_proc,pids_name=pids_name) sage: if not is_shared_memory_available: ....: fvars2 = fvars - sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(fx5**5)) sage: fvars[f2, f1, f2, f2, f0, f0] = rhs sage: f._tup_to_fpoly(fvars2[f2, f1, f2, f2, f0, f0]) @@ -469,16 +472,16 @@ cdef class FvarsHandler: try: from multiprocessing import shared_memory except ImportError: - raise ImportError("Failed to import shared_memory module. Requires Python 3.8+") + raise ImportError("failed to import shared_memory module; requires Python 3.8+") if name is None: - self.shm = shared_memory.SharedMemory(create=True,size=self.ngens*self.fvars_t.itemsize) + self.shm = shared_memory.SharedMemory(create=True, size=self.ngens*self.fvars_t.itemsize) else: self.shm = shared_memory.SharedMemory(name=name) - self.fvars = np.ndarray((self.ngens,),dtype=self.fvars_t,buffer=self.shm.buf) + self.fvars = np.ndarray((self.ngens,), dtype=self.fvars_t, buffer=self.shm.buf) self.pid_list = shared_memory.ShareableList(name=pids_name) self.child_id = -1 else: - self.fvars = np.ndarray((self.ngens,),dtype=self.fvars_t) + self.fvars = np.ndarray((self.ngens,), dtype=self.fvars_t) self.child_id = 0 #Populate with initialziation data for sextuple, fvar in init_data.items(): @@ -490,7 +493,7 @@ cdef class FvarsHandler: transformed = list() for exp, c in fvar: if isinstance(c, NumberFieldElement_absolute): - transformed.append((exp,tuple(c._coefficients()))) + transformed.append((exp, tuple(c._coefficients()))) if transformed: fvar = tuple(transformed) self[sextuple] = fvar @@ -499,7 +502,7 @@ cdef class FvarsHandler: @cython.wraparound(False) @cython.boundscheck(False) def __getitem__(self, sextuple): - """ + r""" Retrieve a record from the shared memory data structure by unflattening its representation and constructing relevant Python objects. @@ -509,8 +512,8 @@ cdef class FvarsHandler: EXAMPLES:: - sage: from sage.combinat.root_system.shm_managers import FvarsHandler - sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup sage: f = FMatrix(FusionRing("B7", 1), inject_variables=True) creating variables fx1..fx14 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 @@ -522,7 +525,8 @@ cdef class FvarsHandler: ....: n_proc = 0 ....: pids_name = None sage: fvars = FvarsHandler(14,f._field,f._idx_to_sextuple,use_mp=n_proc,pids_name=pids_name) - sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx13**3 - 799/2881*fx1*fx2**5*fx10)) + sage: rhs = tuple((exp, tuple(c._coefficients())) + ....: for exp, c in poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx13**3 - 799/2881*fx1*fx2**5*fx10)) sage: fvars[(f1, f2, f1, f2, f2, f2)] = rhs sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(f._poly_ring.zero())) sage: fvars[f2, f2, f2, f2, f0, f0] = rhs @@ -546,7 +550,7 @@ cdef class FvarsHandler: modified entry, tagged via its ``modified`` property. """ if not sextuple in self.sext_to_idx: - raise KeyError('Invalid sextuple {}'.format(sextuple)) + raise KeyError('invalid sextuple {}'.format(sextuple)) cdef Py_ssize_t idx = self.sext_to_idx[sextuple] #Each process builds its own cache, so each process must know #whether the entry it wants to retrieve has been modified. @@ -614,11 +618,11 @@ cdef class FvarsHandler: EXAMPLES:: - sage: from sage.combinat.root_system.shm_managers import FvarsHandler - sage: from sage.combinat.root_system.poly_tup_engine import poly_to_tup + sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup sage: f = FMatrix(FusionRing("A3", 1), inject_variables=True) creating variables fx1..fx27 - Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19, fx20, fx21, fx22, fx23, fx24, fx25, fx26 + Defining fx0, ..., fx26 sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ sage: if is_shared_memory_available: ....: n_proc = f.pool._processes @@ -627,7 +631,8 @@ cdef class FvarsHandler: ....: n_proc = 0 ....: pids_name = None sage: fvars = FvarsHandler(27,f._field,f._idx_to_sextuple,use_mp=n_proc,pids_name=pids_name) - sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx21**3 - 799/2881*fx1*fx2**5*fx10)) + sage: rhs = tuple((exp, tuple(c._coefficients())) + ....: for exp, c in poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx21**3 - 799/2881*fx1*fx2**5*fx10)) sage: fvars[(f3, f2, f1, f2, f1, f3)] = rhs sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(f._poly_ring.zero())) sage: fvars[f3, f2, f3, f0, f1, f1] = rhs @@ -701,7 +706,7 @@ cdef class FvarsHandler: TESTS:: sage: f = FMatrix(FusionRing("F4",1)) - sage: from sage.combinat.root_system.shm_managers import FvarsHandler + sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ sage: if is_shared_memory_available: @@ -735,7 +740,7 @@ cdef class FvarsHandler: sage: f = FMatrix(FusionRing("G2", 1), inject_variables=True) creating variables fx1..fx5 Defining fx0, fx1, fx2, fx3, fx4 - sage: from sage.combinat.root_system.shm_managers import FvarsHandler + sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: shared_fvars = FvarsHandler(5,f._field,f._idx_to_sextuple,init_data=f._fvars) sage: for sextuple, fvar in shared_fvars.items(): ....: if sextuple == (f1, f1, f1, f1, f1, f1): @@ -753,7 +758,7 @@ def make_FvarsHandler(n,field,idx_map,init_data): TESTS:: sage: f = FMatrix(FusionRing("G2",1)) - sage: from sage.combinat.root_system.shm_managers import FvarsHandler + sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ sage: if is_shared_memory_available: @@ -769,4 +774,5 @@ def make_FvarsHandler(n,field,idx_map,init_data): sage: if is_shared_memory_available: fvars.shm.unlink() sage: f.shutdown_worker_pool() """ - return FvarsHandler(n,field,idx_map,init_data=init_data) + return FvarsHandler(n, field, idx_map, init_data=init_data) + diff --git a/src/sage/combinat/root_system/__init__.py b/src/sage/combinat/root_system/__init__.py index 694205392d1..7a14d809bea 100644 --- a/src/sage/combinat/root_system/__init__.py +++ b/src/sage/combinat/root_system/__init__.py @@ -77,8 +77,8 @@ --------------------- - :ref:`sage.combinat.root_system.weyl_characters` -- :ref:`sage.combinat.root_system.fusion_ring` -- :ref:`sage.combinat.root_system.f_matrix` +- :ref:`sage.algebras.fusion_rings.fusion_ring` +- :ref:`sage.algebras.fusion_rings.f_matrix` - :ref:`sage.combinat.root_system.integrable_representations` - :ref:`sage.combinat.root_system.branching_rules` - :ref:`sage.combinat.root_system.hecke_algebra_representation` diff --git a/src/sage/combinat/root_system/all.py b/src/sage/combinat/root_system/all.py index 7c620b9cac2..7ac32456dfd 100644 --- a/src/sage/combinat/root_system/all.py +++ b/src/sage/combinat/root_system/all.py @@ -19,9 +19,6 @@ lazy_import('sage.combinat.root_system.coxeter_group', 'CoxeterGroup') lazy_import('sage.combinat.root_system.weyl_characters', ['WeylCharacterRing', 'WeightRing']) -lazy_import('sage.combinat.root_system.fusion_ring', ['FusionRing']) -lazy_import('sage.combinat.root_system.f_matrix', ['FMatrix']) -lazy_import('sage.combinat.root_system.map_reduce_engine', ['MapReduceEngine']) from .branching_rules import BranchingRule, branching_rule_from_plethysm, branching_rule lazy_import('sage.combinat.root_system.non_symmetric_macdonald_polynomials', 'NonSymmetricMacdonaldPolynomials') From 8408cf2609ef1f1e82f70cf0dbe80ae01a18c77f Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Tue, 29 Jun 2021 11:28:03 +1000 Subject: [PATCH 095/632] Fixing corruption with fusiontree.png. --- .../reference/algebras/media/fusiontree.png | Bin 37424 -> 22635 bytes src/sage/algebras/fusion_rings/fusion_ring.py | 2 ++ 2 files changed, 2 insertions(+) diff --git a/src/doc/en/reference/algebras/media/fusiontree.png b/src/doc/en/reference/algebras/media/fusiontree.png index ecad6ab87eae6f8f1870de6963e6cb9db617560f..082e87ab99c5f70f3754ea2acdb560b079c47511 100644 GIT binary patch literal 22635 zcmeHvc{r7A*Y_o&L1ZWr_70&zB4vn8AykGNB@Gl25gE!58>!5*Qo@#$p`<}63EdGT zks%F2NZOQfvuzvSIxky!@Av)wc;4f9zvKOm<9UwbxwH56yVkYNb*{C}^IYq9?JyG~ zy(No87GW5+#6Vxi6vNo*=>H2i;Ty-#7D@0=n6LRhPtzk_#FOr>_KwbWL{FcSc0@aG zM|%wO{&Xt(-I5m@7wAl1$&fr;#uaI%doOW`8rdzi%G#fBQte~qyYVNS&!nTy-#$ZR z+pu2l+aU?>Yi|r6*BtV2b3Jk6=;zG)9EvF?Gz&fl^n{n4^w6se(q4PWXjxP;EyjLr zt;iGY9Za>xkD5~r9g9t{^+})BT?oOjMT;CkseOjK)ofi)Y&de%)y8gv_lc9BErzLU zdY?RE>tyFiw6Qzp=%OLf|Fl$s=y+5^!eX=GM#GcZb`FmEzV3ErzDDM@zD~BPMff~u;j{6BxJD&7(baf#jnMZ6~PkCxcNWgU>OE{W<-_l(?SVjPI z$fGOr3L7@c&xLzB+W%kR=*k~Bx>?P{(c8{>pN``RI~Na7K|?}eqrw(k+Mmmo|J7wA zOU>ZKkz;lijz>Km=G^$_ioTuWF$WmTZ<+WdV|Pb8umc(~T#}GS)|6+N6J|&L_w*ne z{m=jJ-w*khZ2WI?{o7psk_G-H@V~w5-{$(4EbuRZ|LtA>f0=91pADX!3+(E=VEguC z;5iYtejFa#4a^t7|NIsl4aG1bW}vgp-1}4C7oWtYZ~m2o@*KJcPjc-J-J7>CEi?RY zKB36J?$*R%(mR${nhPXJ2S_Q@sBXCzrm^Ldj?-q1=k^L)Tud&CqDm}+oq#r z(!3>LfTAbM*c2S=Ge6j0J<;+xc4@QX<4bJKn_}NTY{@dsF{ciOF!QGw`H$%x5w=sz zaRu*zc3}-)Pi|3_jomd@g`D2s4!g4H*vlg!V#~o=H;pOc`6Z0w1rHxq+nCU z7li)4e;nO^Y^a3Ml)ra6{b}F*T?gG?t={b48bTVmMvWP3@i3l#7qr|%F4*bgL+(4r zh!m$MpVP3sLnCLGJFr=JmKIC=kvH!?By$Pyz#6NHNaU<#UV-1&<$H|&rFEsne6F-HtLwIOS zAE43K{wrzig77Ox!gzx(inTCKEZ_Zs%;^FmfVCnxH(dGp2tj)K-O zQw?9+M%k0A=ZX~PWoq-NX5Oql11_po96uOl>}Qh@WBKs-p5pQDtKxihXQv1L*rW2{ z!QnFG!Xck0eim9AQoP#y$112zw{8_~-L>S?r%xSc+9uNUIFy3^G1fz;Ki+upbALk6 z*8buLFMeK+R?B_ybFhxnHawBKz`dgWYg6cwJgaAFC=Hn1+&$0|JmrL8gk!_BrB0N#`{7W@a@6EfQiB;N*n#qGurSe+7zkW1i8GmE+=JyXcp$NBQ(g!b!o&Io5@z~pxoi|K# ziW$^f7s2le`S~s$d2wM+EBuYVXO0$@=5Y&eOn09e|85c3{U^@yom72?xTXeU0uFw3 z%t~GB<%OJ=1sZqC9yp;^MpOdKQe)p9ij}`?%YU4}B9Okq0{}E(Y_cWy2GY!?@QQ z$n1Ia+2!f$oUF9{mW3{K zC$}GbSysjc|DE&aKT-G%{|SZPFh|y$Kj+B8|4ip*52y%VHaJu*Ke5wP&Fjmlq^)cN zZ8EZ6-oLoc;$`}_ULuj0)Vh^voFzCQe^z;Wzt6XMBk*ydolAm_uukyTR~hTFtk`l1(zl>ivkDP5s>o9QA%x+8Z3nPo0eh zKI_GrQ(9E!vzy)|N$8;q(W{O2vbPB=R9EGcT`q5GvlP=!JdoUW$JNDJHiz^?Bkjhx z*6U10*OmpDBQ`-w;ByvbO9@P0=I#D`2=t9b_l=WOCGW_1$?1B+OataFq zC8{>pxHc;{%cq!A8VRMKjf|5MjakT%AkO*~vv`8Ln}rjdj$(q_fZl-szNH}gP@ z0*KjLrGdnZ$!7voJRigy12HS{LHzj6*yDtLV@*;+~hz_xe(IVef%Q@&)ir4RNr}6$9FuPsG@6ZEJq%67Jq6h_#NIU;tmNtH)r zg6|#698XQ?an;E+env2^_X|u*pqlk>Bi4kdgY54d0h_Mx&NX&bJvA?zuvw6XzL$05 z_A{0m5T2)hS;!ESSl~#ukYZe2)s$5jLvSQ%P3qHr{KO5LIewsK@`!9<4ilq#<|qA;r$m6o2!Vu7knoEo;i21E>rRIRYQwUuomkEFOBu_- z;ez=qQ3S>C1xEV(wJaEWy)C&-gMU)QfcAqrxT=QB0St0z$vk9vxL`E!RJ9|M;rwO| zzgBN{R!^i7VJN+P#?j`b>l9aoZ^L}zyT>ARO0NQp=^fk6UJz|giQ)(wBe`GY zdH=Vn1Zq-@nM7cRsl~;FKaXz~4O}b#w+II(Tc$y>HGzN9DxR9u-z!(cCA%SqJkB}u z{Cr*+`Y92A(@Cc4vU&O;pK?W!LM5J)VK-qS7936x(7f2CkQYYS*eptnQDf>h~)+Rtbj`( zAVw#L?3*%#b?r*MC_P8O3hY#;?!jQpVTmKzLw{r?Xwcfc3k1jmepWz;08=_@M-F*V zwTVrZ_u*cVIX2jVDMbpMS~4$PHFLaX^*dL7fR&QKnTKMjLIF!rYN=;-yPs|A;#<=s zFP^1>eY?@RcCHEwF~d$>D^P*kk?f>Da&>pEb#Z=(5+S`flTm?Ztzf0n%_;4L4(2;) z2WoScU_ToVR{V(u^|*n}Sh`(tkJD0&wyMTeE{q(ZPdgAWy=$1wL8i%dlI6{L-#AJu zB+YW${EQs252B|moRD%mMNuj@mTLKq30@Eif`LF(RuJF0D&&xd`&}JL*j33U*D02a zigu1t;&qVWZDfaKU;K0nMvc2ej12!TTWnEv#NV83CMzAu_w+{=Z_jO`Pgo@LYUO1z zqVTi{1KF3-F64Y}C#|=34L7E7W9m3JYc@K=&P@mg9`%m4ka%wL!U2p!*sIMgE~*^S@v_PHsmdn4q>{t#8(mr(b7t3%Ywi${ox41 z3~LjBwFT7sVN~7Rwh2~hiKtmfey*2mUOJI-AFGq4-c5^}$#0}I{l~Rf)diurCG%w0 zIg&-B81rSC`llvW*Kno(mC5+R8ieK&qXB^xd8U(hRQIr@TQ=G}oFgCxtGXm~5d!UL7GgKP>e>fm z?yGBz6Ld5cOeYQdd0`&%B%0bhSq`P7`rO#FaVIS`88Z<*eji4Q&m;lnkqb+_B$T>j zUe$Re%@h2SIu|vXlDgSVr0k4oH^yUm9m$_k+GR-Y)t9MTSF@#Ozuuo@@cU+FDqFkp zYq~@MB&FEy@7ppckMG}Lfce62JLi-@?pRe*3mcBzJ6uq*jiXNIg}XF9?fc2a}@Gd_wbaTBTZ zIyak^%*w-qI)76ZV#%4I^6o1+P{vj@7}<5x0ogiGaov8F>ea6nk`468e7Mbvvt&14 z{e62zfw4W7-V{ENQHal3(p8<(%>AiUi2Zi|JR_F&PgI}Fk5{?sqOk|QeVFT#fIGY} zY%i21!_FxYFtz4zq37TshPlQPG3RG!NuCn2%vTWWH@;M2y# zNiqzOfF&>RlTxRy8*chz+Wh_!8G~p5*zx8b*7zT+lA(JFe~r>d-cYwGD!1()0@57I zBf4<6F+L{6@HFX1H)0wV_`l#c4X_M)s=e-w!!;mL_q>>KT5iAODV^;ArTrBnR5PQ$ zTwG5>PAhKeYzqD__zp$R?QHFGJzrzohDtk(dsXnf6C7mRK+%WrOjqz7k|@G$Xtp?v zbfEDIu0m7;{x7ItwpfPVsPxIn03UJq0S_(MO5AN06H!u9BNzDxyY+Tuuhw(4D$-WI?5 z0XVd_Jmv~q{>kTis%@>tSR`yCGL4dBVDKmC8UZmHqnFL*VGe^t?_W>8bI;RnslT!7 z*7UrWjX7riSC@Q|ewSrY98AIr$V=%~hx92*6yK9)jq{zEcNmzK2Un06zGG7uaC{Oq z?IWJWi5*^weUYrmvXWvno;N38$(m?CLUJV0x;Q9Bu~e`Xp3KwBpOj#Q<`r?Hh01MkOmH#qbAC+~!-hj?N4muZ}AyiVnLRt~6p_Zz%Y;eC;dV1MTzjH2Ts6!v-eo{z+lXw* z?nu%F+gyUtn2i1wzD6U5gmDIK!*UY3%>?=V7j4Vs)mkw-SpAfCvjrgTHGVGRS`Fdr ztdq+dxDCtPk!<`ufgD|{P8tsrzp2y&sk6&Hd|`&^wsaO zVqs`qDy*s1?dG6t2X$gU|2AcvAv_Jv8xEP{f~h)`?Fn%81-c5R%q&|-IxpSCMWp@K z{#p{qgHypRgJFa7Iut2*w!RyBHkih=gJb546Tg+o-h>9rR_6>?CEv-cfoGY{>Mp4I z9RDJmgtWtls&pCG-9gEbloQ{RAO$z;in4~92yg?qKm3nnl&;|LY-NBa^du-D_^fnM z$c2so->>modclnJ9yv$Slh~ew`2f$Gg{J^~F~F1mzy%|5={*|&9@mqw4B$1ha8XcZ z9l%BAz*A=6;YyT)25`nVCj{=`X8G9Ll=g(xa6b({1_pym68I&XxXOsw7U>!pFMj{~ zVC8gi#h?UM0i*tvvV%B`i3IHw@+l0=C}o_4W+oHDL9gSFA}&3={C%iG0ZG zcQ}Q-0l>xu>3*5xiBUQf(>QRz>REUkz;yv`@!Nm3yYV@V)pR7;fyX4mCr{kSkPPg= zV@$zg?hgL;7!ySpI7CN`66JnEkJ)kfgyzqY1cB5A_u_|=^l~4118Sf=5^0oRS*SJ903YM!AeBUl4 zjlgKrz5n11XYdTb_XA!C;b-Ohg8UH(`*hkLyxa`F74ZIm=K*})ikVO%P%9Ub8eu@` z^bH^#>H27Pvc|y|1Ry6D%?2O0Ull|btOtr99u9NY z=7acD)_TC32g>gU010c2yD3@uAmb`R+|d6suRv5D(7o{%Bs3De6s(d~zqYK3Qxo2=*e zmtC4eGPyLXyEo__q)!V{M5cu*5%}@l`KJEh$ENT^oA@4WHUnDF!Qb-TK|W~bjywqt z@Lb$}=_`?ZOb~PrH_YIj57Xv8JFnp|uY!;itB(A(dyo~x1)L6%t%G$4{GhC<7|ZbW zUpZ#Jm?Ith1f&<7g{4~JUC235)f~kHtlJ?YD$nsR1pfqy12Kn$f&_F|G8kF4&D1|V zd)5hxV5n!oSbVHB#>P%?fNCVbqX1q2@FM`1orN<1{x^cpk-iFt<25M#kyY#f-!ls* z0URu?weAlbSsJ8ktf~nq2Dl`^Eob3mfbRhKraAD9IQ${ZYnl`z7M@j?&6=iF)1zqmP z_Gm8zJEo!Jk22>sF#Aq$$dRLeW+@UJ5~^ATa23qX58nT97S2hEz4u=fy<_``-q0FHsq7 z3z&Y~o$O@Ol)J}mutKO3isc=j+on#}lvbiXhm-Zh{=T~}cofJ?`o5>9^SH2&U?o+E zYJQ07t1CIyWHy_qkIr%d#@oiGua*qZV&?OtgQS zVktJp#}~}SduNKyrAgmrSlXQG)oM98xc9(`WrV4L5RIi(XO&-ZuoKYuVmpZ5V_&>F ztF5QpnKf-p!$|0?y~x*~HJUXMG#NeDH&2qG4`M;9yuKqUJg>0z6MIk>eivJaU~-(ev(lC-h}D_$`-#6tsc^nVU=8n^-b%Ufy}g$*aXpuXyy6 zQ`2Y4H1qpY=H!+)!-cKBy(-{`>caYPbPpZaz*x%17o$F;FM6u+FSqxW$0|Ur<{y=) zM@1g5wti`}^`~Hr=9IS|qk$3){mgaHB(yfy>>jKn#Qb%AM+J1U_e?(dR-IId`uf|a zC!gISC3-E*9Jn*i$JcCi<^=Og+vFj{JAf#li(NQz;zX0?1P~`g(DXS!Y}$K(If>k1 zhG5{=AwUWB8SeJcie2BI%v<1b*7(P>;kR2Ae1^K{E#FCnAKeIi3V!r~&`|G5T5~5v zR79otne`ihV8BE0fWY$-9`m0VjS6DtT0TXJrE05kZQI#)YpdPs#C-)0@1#4)C_sm6 zlr3BXpaJ{Sy+z)-q*(2jq~Ej#th2CqFkh82t4qsK^D;z}`E|Lb%Zup;2Ol|G?!Kb9 zdh~;B3=d)WChoTsKt2xd5aQgF!Li)YMLL2%{P5>vQrOWoM##jseLnUcBI~Px6f-y<; zP7yWt=&y(bBefhxthTwA&+udX+Ok&A>k9@g5Os`jut=F7=;Uf`WH$eiO6J*lM*19{a-KwZXd!JK=z zrlA?Ht`DMb!;pF1pu=zBN;&!W!mCouLra(tSL+{rv|q`~9WRBX!+!UwW&k+_C>r-@ z5Ep};@AIt*5km5JT1nLG>mBR_y4LBj5OV_e*~Avl2168P)Sm*z>i4Q^Eld1-)F($8 znm&VR%%R=FPS`RuOqo`n`azk>sg5~N?Co~j>RHi?vRXJobDuSL-nW7#RU0!fIR^E8 z2%)+#F|U?;)m{gF^({Q|ONP|C2T+1}o|D1**lBZ)0t)ut;%ki+cwBeK8l_m0NCC*A9YWMz~_T5c4R>n*79f z>-YZBHdwy6&t`lY@c&C9gCQU2{OXX`m!_|e`b%Xt*ZaXX$m$b);gKswewmEJO9h)= z2A8H}UMj)bW6qz40^0Rl^I3i`$NgJVh5 z;B6*4Fot8?Vrm7xXCP$&XCY!kku0Q!66#N6lIj47W#Ld=5~iDQ^}f&R1zRH4&q!~x zYXrqC$K0kd`!BT69P`lBM|R2B^j76Mqto(%{fW`eJ5Rck>F*0mn!bLA7+DjCVx;jo zB3Mt`KLqWD} z!nE{CdghEq!65{)$_}8lKtvqceR<=mR`1RzLsB=y7{o>(J5SHh*t5R-)g6$CBV+u! zBrU&K~;?_L;2t57o!yWb6{R2 za1DAV`VC+s)^mph6on3#bZ0AnTGNMs>B&xDY2;3|T(q!Sk(J`UlXm)sX&6d(X#rE? z)LIQ#=?0?QWLeK^kQp7Tn}wiAUFY;;gp z9ylZXyDqI?qx*IOXakkt0?UjwCq0XYUa!N|yR2Z_0gG28BE+Dq=vq2?T1RJhii%r= zfu>+Zfn!6i{QH_Tjn6+c>TZyK&3oMg^rD`Pza%9%c|<>a9o^h~PykqD{Wd*y*P4`F zeSyofW#UW;5O~~PugNM;DjB)4zS6&ZB8LnM=;o#|4cI~3Ob`%}~*(IUPEZ8Ut@x{hA!Y2TDn?=9Wl=?d)$m;$4I z>3d93akJ@r+!^im^}LY1e;XcFXN9c&6d)s5H&nh&QGM`YF`Iz&?t#PC>N3Y+*Y{=P z>AnviV40DW=qP9~ihyjtM{oM<{KXxilF^}Yl6o&8Q3?aMCgPK=IGyinfVBktRCjozcDDMDbjbJ-5GKObxl=5-nD449s1vuKl2 zlITeBcst0`C71@85fZ@#NFNG_*oSCLh|7k!fhv%ZI)O12g`U4qCS~b^W3|!bNro<3 z`NP-U3H7MeP__z~q^STW$QDQ&3b3GjfXTv#06dSEU%cAHp8kAvV9hVhev~8D-9Gem z3$m~`?Aatcp25pKuswW4R~54IKEecF0xkp)b}EuenRdL(7a1n=j$Bawc)bzG8ZEGG zK!bSM4`UEk_dbcDg2Z?ibDA-@ZtrchK2`vWEyI{XQgHro7vC}g=gf9Zf9Zcg@-VN< z*mifU^^67Awmb4glfVm#f(@+~{Ry(y;KO|XyoQ*7f#+QK@K5!=*9F#M=hQ@h?Wl%n zm9}M@l)%P;pm_s#AWJDVv^??Xge5SqReX!{K16j%EvaHaq?UgTA&A3j7Fpsy@$zA_ z)k?E0BU<3Kx?(3%-2s08^vX;|ov$6k>I>L+2eN7Y6g9sCA|eK&|8av`>G7sq`kP}U z0VD(LUIBsV*kXp9Q9Pxc{h4%A$onN{6*I?Is(ni|v-#EX#BF+Vtg`N!w(>ab{9>V( ziEGR{AfO_61L!Z@zMN{V0EK=%-+m2WU}U*-NWvU7_kC$pVNJ4M3>{<40CyRh*=4 zRqU!kSb)oa7Pg5aVi+4cXSqcwqe%rAjkZ~-r-u$-8^HZm@O9*hO&NK4azKhPru?GR zvInPMbAd>E3=}L|AW7Y`sR&221u2RUHrQENSso@}3mY~Xan(Y*^=`mg(uaZt)DfZ3 zo!(mfO58+wWi+=8z3%b0jcy&~p`pTxNA(Yq$G`m&)HlDi!j=1ecD&N+ywQipuHYvc?+Q42(krM^MLFdP`-ig zBFGNc%HS;ku8rbsfkN=SfM5EzOh)3VKbD~b=>@MT$9J>qO1$G*q@8Pg1&s_2;#}b` z7m`ebP5oah{?Xm}f;k}=(?1V<8Hq5Gi3$*kPRIIM^Y^49xDobpIP1YFADe( zp_velml>hJ0;W_7!{M;$iB$yndVq_thN_BaRO&OES_q+ z6#EHwT%kV_)CX*rn%VfURC&&IAILvhe;iUvI+Fq98Y^(f-C%Y{Al1B`g?|M2PH>{U zIq;h}TpZw$0RN#+`&d5XC$owJSu6xDbl%x+)}99Wg2bqML(G8wel5@h;4m|Tee(i!5BNe6nL z!vIZg*#t~(6qwUtV-C50cD5XNV78V>H}&K01fB+YSZg_&twivhbRQ`OW+>6r-#Y6I zHBeegH^^i>M9CJ_TVc3I4RT$VYF&ZZN(N`BJ8C*9H=ByP<{SiH&OrF2O8Ug5dwaJXJvQcWCYyHFtxuff!zavrpFep zm%(eYQT4av%OF{B_Z^%qa;=;Vw8-8DpYX;yyI>L+Jspk-z(j$rj@CU0R@v->s3*@- zqmKOvG9mCscuoAk1J3SJMeTEO7=t7Di^<6x)Yz{I;pc)yJDtaLR%xObfORqxfgb>b zE5E=3n0$SmwR6;P2sAhK$GhuX`Y}2LqJl2YjzDT8gU(Vnv*&`(X53_{5xW5#D&(k$v*^Y>!sQjVgtj<25bL`{2GX$0yow5BF(IlehlEBbc9PE){ktv5@ zUqTe9!L7r)ng>=dEw;_;z+r(9;}^(;8CPtmnw^y{XclYPi(EtlChQc2R1wIbw00Kk z2f0FVPwj6oG@-O^QvU5pUtZoWDTP7H?menV5jX$!bpV=H~+v03WvI+)-f3T><2Vm z(7`r=el)DA8N6Cq@If6uJStyE!eEuq9)#yZJoIeIAqhqR!RHRl=m+RS6r{n2{ot^N zBhYpqZUNf7uVF549^#$t<@d*qOEiUaL(-}f1Ff=Q(~%5MM-N2%+9xn8o)B1pF0iZY zgoUMNA!H`hlN4d0UI6MIphgk_B@U=bgjxcq5!jJXS*Qj;!IFxNTmlrdD7E_mmCc1v z;jq1Bp{fDp1*padK*4IGJ%&&+fI?J{A{Gkvc-PJ#)K!Fnu{Xm9rLzJ+#Diw$As7l| zKND|L{||~_(9=8?ax6&Wsix z%-`$>G&4V7W10##XTd}i)sRe*R6CWu`ZM#3zC zPQL<7ql)Zw3GUv32MiV#M}43G-#>vcj@_I?;)8o%KVJwpBv1w5NQ4aDWk5rgTn?55 z_Jg$%f}j|_0eeRoxEGx1=#E@#Ao*gicYvQj3%459E3Flo-V{R8uW5vB8(HojYbIL81MI+T%}#$x3VB zr4~K{y^L$icnXvz@Q#NqVPYS%@0g|&qsrCEDPrbr6GoUNyz$Zp@2v0*S|#^?mP2m} zrSmLbzi+9Y*eUbkDMoMV6yuc)MbzTZsXZemHy)44tOgQwGJy$iw?agSqy!Q!M_*Fm)9t_iJ~H7Us3NJx28Q76;N#< zK*1!clpawU3hzd3Eg7$IeHkW`_Tj$fQ?DlD7^qwYMuYVmE1*+?mzg5PHPn7V-OZ4n zc^XQ(5pB$FW%RlsYNH%3(yiGva_&!%f3mX|1(*9TFw8#s+BT zIH{d?C5)kV1yr9TA3;fR`=mzkE%H5|WxkuiE9jIkx^Wp9h zx8A(Mm(VS;?$OK#j5xvoQ-v%2Yj-V$fL<`F_5bBDc%3kry|n+5^|~Cg0t8sP6Rb84 z+0)G0>BxJwl`m0Iln!rM#y38W0dhMW@X`9AB7H{+!i0el|u${3s6NN zghVLcZqx?qK_d~hJ=4@xb4m-%OBGnYJP#U9(acp;^Vsq zFN-=rV(85;Evatn9BG&?eKxT;uV|`MWr}n)@7<20)Seg<)2oWB5@ns&_hu|#Cis_- zu0T;;M!HK;sEdTGMDDEtn~{*vSwuH41Vg$8vQPS zt7|coZ#_z;uZ4(*E4O+}imY6@X}sSxSF?2YFq>98du(7~8+ogE0CP}STs(SobhKe= z_q~0QQ7e^SqW0_{t$M@L^=ba2A3qd0nswJcHxD3RhU{F%MO?M@bYCbWVNDk(U2mG| zD=X9TgC6R;PoG3?S>#7Uve8IRPIhC5$i91ziub_b zagQ-x^?|_JFYINiito4reU9c@i#7ORmjhe|*ba+dp;}bVJTo&nXX?roaP1S9d)T#> zT&7aez;hd;SNdVuY-W=Un;`ANFBe>~$(?QklcYb|J;IiLVK;612;_%{-#LN8U1dH^ z08PQ<#D?88LjYMJ5YKX|c^f>0TDb%SKHr{7y^32V@StU=nLp@~Eid4YWnVw9*rW_O znBfu3i=8SkxyfM3YF_>~et-Ot3|?$uRqxD)PIw}JY;#TtXt}@-+h;PQg5Q4h3aEc} zXhv4oljmH49N+A6T|vot1Z;VMI<6Q|3=|{IzpsYhGIA20xF)t~R@UK5>y425#6t+B zdw)jOh#Nx1Uz|m?2UN}IRsTwdfSJnX;bYO4SB9zB?l?P0Y>L-01r1oM2q_kRV@8)B z$Y7@Hrr?7TMSL&)DKK8s&>_C;Y-kq?n>}$?r#U z)Kub+972k@w>xK{X^AC(_Yk?>o}Lw^ANSx2KFSj0J<;Y{al1|Y{`fa|zPso+FNuCwM`vVgOwYo?;`Y9K5)xBax-EvUj*TrW0t&r0e$RRWSAoORSx_mXHXZ2LinrD_z!k~_h2Z@{=;=}JPd>E0RR1S zea=mPuK%wTI-?Xmyg!=GjO3rg$FKi$`1nZwIsE?>LUE=3SK9o)rkv$Lb^{hRU0=xCl04ouY^)&$ji z(Bu^s6N`fFPsRJZ1DzLEx@6O*fWg-lixDdq!A~`G31h?nai6X@@1GBwB_uNL-BWm3 zS=kAqJU3#oW+;0Rw??Q{_vjqFLd!0W05;l5bi?H=r1T6bqm0|&hT zN;Kf5|EDTGLq}0!`~@c*)E*TR#kvB~dJ(5_X0#mk*lXua=a)`}Bg)7nd24aR1rt?u zm*OFiMB%lbg9U|!dakankufn_)v+oSw1!AN_zsM+>LmFXr~GGIZr;4v@q$+~681L_ zna?O>#u{H=-;B~y%@QD!35$wGuHJP?MF*>r#czG;)xV+C#nCbBO#d@wH8nLGXmKwC zA{m^MEXsGuKZ6}6Y*+?+uE(fpVO4io9$Zxd2a56vA?t$*@A`lI2m>0N%3EL&HI!uS zTaWV*x1NSy(7$ zH6@q>EfP|Wqu=`%pWhBd!`1r_9H^z2fEoo%-{w1qc=&O5%KS(vwSHi4czgfD+}trl zk^9yh!Xvh}mWHn~jUS=I+l)Jx;PJw)ChxM&&W6&KRM zL{D2=`&;v^y?NoVecMATf!{sK?eO_gf8$+aW0w}%dSbLKs;ZWQ;}geS`z0limku8M zSwKMI%hV^}cS~%2eT$gIuqDrJRo2iDgBV$6{S*$uVzOvwL|n+lD}LL%1divoeB16E zdTQh6SIjJi-$sI??N<05BOLQ!djHc)moD}6_1&iW_m@tSD?GqC(9a3+2;#On_v+QF zpYuiv;)v27ko?D37Tfrf9a;TMzOP^kd_`(8T*YdWH(?$FUOGc$Ak{3wsw zR>X@&nEo;ie(1@;#U+7xJy8R#16 K+}VEk?EeAhwv4g> literal 37424 zcmeGFc|6r!_dkxmS1Azyo z&dC%E$a45|)`59ll=n;&CeBx%Im>iyzdbgC3c6o3jkj;=Y^@d5S7Y|ws~kX=Iz$32XXKGioM=hf6s0k`*78@77~?++TkaSV^wJ8x{}mE99|xtXjFsWtQ5 zaZq@$;po)hPqr<0Tsn4q@tWeqbLBCkg;SSK z+UWUm#Q2wK!-Aa*E?+kEKK7++lH+R!*PpXWQo{l`P zVzIyT;uF)hw0;c!d2_{i{`hje(B6XIdrJbsJ59YQNcldoC};okDGGn(WgPtQYnis7 zCA!XU#a8jX4k)Vi4;bA7>fq|Wh~OycfVyb+A3dJ&3JIH9va#1ZNtT3 zJ5K8JhJEG@w%I>@QD^0YA)k1s|M6+lkUsnVWoTZn#-jwJ~^dVEuUq>%Hou4p+C{b&OOu zwEY;(I6hJ2{^gCZGWxc?i_V;#xVpnXK+x#Ov_)S3Vuu<8U9xV?%z&3K02M= zkhApis$=sfeU7PXE@}^|NM3)fQ2+hbl6~bFian3kx`9dMSAN`SJ`=afZ}@wk=ho%J z%p2#XeG1byX&To>XRBk{*W>;d1}Qw+#>oFQ_TTe3bn5SS-sL|tdlr?c2ITpFh~AJ^ zer8)yM$Do&+rA!q-uN{kVbQ$u3n@?LU4Q5GFx)()y{)&wl zYg>=jW{t926;Pe!Q?PLM>DfBlgeP;LB=72~BZ31ie0_uYJpbqHans7*Jtvj+O3>uE z4)zBn7Prlhb%~g|#p1~N_SWAEe2W5omtS#z`Z~||)kz>v<-WcSa zT{7&L&!RWLHPZY?^a8U2*f?GgBHd{Jq!1 zQR8&py2h=1cIoE#isu~~t{7kYrR9ReK>{`M`JVcV_x~eWvi+kUNT>%aQ|9fW9WeP z<|5su2D9dG(OcfEIlKF~|Lft4=C#!r|5pe>cBAc%Z!O+^;^Os~MW(-Ay9^6zbJ|#p zWpZH4JI~n(9qU9V#EdM3Xi?$s74r@8P*6=U2CWIJL1@d*iJGZFOhOA8&0z*V_Lx^ib$C)_zQn zi62F~k2gmIx46LIRfXpqJKCv(XcGB3k2u3T@HRi^_nUQ>VFQch3=SI7sY91Cj_R(w zW*Ph9{IZ^b0SSS>4mG6iyuDku_`-xfoBZ7kRFlKh;netlpZzpzHQD5&{y+Fix}V!Y$vZ3Zsi?V>xS zuKMD&H|J`+FRi$4v@)>%#WN$QsowRz?ajg#^weLY(DXk6Z-tu6U8dLFC&dMUlFDQ3~Ugw}6vfoXDMh?#bw z1sk&{+cK8satyjWPAB+Rq1(u*Mg5l9A27Q0^WKS~w@^N}*3=anZ;!udpE`DwokaMu zYT*A&x%Eh`vUkvs^j}vmKi>Kcu1dRk+ofAI53YC=O0I1BbnnDhE6Z5JL-lT>(_3ml z2icM3H#VJEpz%MdKm7EgIKwWw0h>&l%1%!3`-atGNj^lH=b5QG;S`yiiw@~#E4^~{ z%EQM!iLZ83AH8ZyotuMS`RRWw-mN)%?8~2>IY-EviI{K8T9(;+&hq==Uo^SC9_HxT z<)HfW2ZDaxQyy)V(ERS}v$V~X&tUiCo=hwWuRA?Da822f@sD~2{R|;-`IH`*eI_EP z?xfk0|5+C4E;gcdmv3)LDqMX;b&$r{OnHB#)na9a@}E z{z&k7HuulGgNw8Vct095?s&-G*R55}S?k-)iemtBtWIR1#aEDnVg>svtcl$tlLyo!9 zFqk)s(LwXp{5<~qmfOk8*FX4G-p$f*n6ef2#Vzm>{O+1f*gy18H?p5t?|lUR+41S$ zv;J&vY^lxrzm_MlGl;fX-sOAlumr1zaibT2bhoCj=Iw{tT7LweU4Q+(NmWXoS^17W z^XJUg4fOH*eY0rlGw-QCGZfb})_g8Mu~TPW#MEE5Z?GDk-D&=QIB2h-s&odU3$Ux(=4Xzw@*8D ze+?F9M4fi2e06z&Lrby7^Cinxe$HYfHCThEPVP4yF||fIWR(gNpY`VIy64@79gPfv z=(mj#UAO>y6NruQ{Jbf(;TnoOagJ{Al!4fflsmLH#c7;$i8UP9{4^pc2D{5kJ}>Ru zPQzXJPZN0@4g6JDe#q_k+_T5FEN}V|++NvPK{V)2?Xe%HpjyVGR$Mc@-BfXYRWF`$ z$Xy=&cyamF5%mA6N{XYq3wT89*@Pg!U^qa+^0WMOdEyaCG|7JI(KZoQe6+ zw6EIrVeGbrB13!c$K%T%jJ#|${Im~tU#CIat5toO_%DTS3D)Lq)e(Qr+t`}jaQ#mG zsL|acLoJO`g%$U{V_`4A4q&H7XI`YT(GAD7*BoH*%c0n{fBU%K&A2?yr$X7GVfykr z0gVN(maW?6+SKyZ{70yQX)W;$W^r9b+J@XHy){{T`mKTM|5?mrC@F{u(~iW=-ShE&*9oiK2_gR@;%<#G@+^B>@7Ys%_F{>IM?mjdfyd_F?d=g=3vCkui^AyQMhhhUi)9 z3o}JVxjPgxg!0CH{XVf1Qj;D@!xYv>cT*Jkp|LWuo8m5OeZycQyRkrc#ads5sTd?{ z@JtRRjkT2sElgD0qd`%DrK}e2UWhQ{p6e zF1*Kx`^y{7(cMPw)w4VUI`l!1AVuB)K}*#^P;QvQW+G?{2(p$DbYThz8m6sOKm@%3 zLDXOgg7=(MVUlCN1g5^amqMF1Zm3~M9>jE z%R!)Ht`r1amp6zYlhmYppra%T1WiF>2|CbsvpB=h1FDMIdVQ3bZIQA@uOn8rDzUQf zgXlYTK!g4622IS?Q1*Qmeb>i#G}!N@ z_qu}-R>LrkEAWOLkV#Aj^hB#FBDkk08Vfp~gR`JhJzCH56Q-j|jJA_E*zfkX;DZQk zilvaJ&lsvqg?n=X^wNq~u ziBtXp=w5Q}j;yaydYXm&GF`BK*EGOkYD9TZlCT?er-^3dd_XlMNJjYu-O_u1MX81~ zKq5SSz{w@ml&NQFBXR1L021$mG1amOy&N5DI4w798kCOdb;Lsxb7A?LE)DGrzEl%2 z=Ag<-MofK#W$Z^`CQpa-pi3ct7e|al*bRE)3>rj1AjTHhp$3)1Qj?BHs)p1MyUc+` zMLtZNW$gcaZ9` z4(qU#L>0eR)&TGMCPOt|dP|%lNhXec(HJHZZTu4u1bjh+cp(r_1BhKTF|TOVkXuq9 zE&)TR0aj=8y_`ldTWUTF=~j7Dj1hgA4iYhoA~J z5TZMDf);uBy{OQA9yhD5LHPz_X5#s17|CX)8EUUN1Bhw~kvxWQI}rBn;F_1DRl;aq@xejqKv1Jv0jzfYB=2 zKMiClYtTUPJM1-Gpj@;fm`PynS~*3FFf|B0oG6lRmoV#D1D5kr)@#&U!W@uQ#&Zr=qr!)l=nrc4q>&qr!aHZ92sBI9CN3i z#E%x3YFVE`{@);y*1M53^lA4({$N7hG{+>|K#qK*k)3;}ho%}48#q+k;tx4mJxlfF zprV}QWEQP@k|ReimA@S1?8bLSm}w9vqs9l+T*Q=!N=D)oLrwxh@&YmE(Q6<0n=x6| zpp{Z8Fbbb94O`d={1w4{7=+4(DZQtsd@NG+BoV=k%Vj%|)<}1=--r0p_mpT=wgI&~ z&?l8|3TpNfHD88lTU?UOYRCj5yE0g}Nu*8j39<%pVSt?^e|RC^i+n5JRMsGYPl{3= zqAUSj;F@Cq8q?(y_#72#C=ENHNQ74BvJIA?Lwd?~Ae1&y%nQhPyv&wJ57}$l9Z)@~ zOy8rywvlKMp_Ahbg|kc`1+dv_zN|s&-ObQWy2nG4wpC1Ggsee?_Lhbo&@1G>;ek*a zb2J9aH1qVP-088xO!p9QRM0AG5JzL~gHX^x+bYo)Dcc}I4R(Vd(9w+udW^z9 zS%b!(iS>T~KcX8W2tq?X&dg81n;SNU9lr&5(IC9Lz##fgeoYi0Yp~yE8ioIjAH!*! zHeqHwbd?>yBr5d3-!-wINTml$geTl3PVBDArbi*4wo$~NU8VQBPBRIQAx)e&R6F~v zToYlkckr~clYXy+YVm?4PPba`JcMb}>ti8|rC)^+;#6TKnJSDU#W=G}E+o)64~A*K zbdbqQ?U<$d^aHAu@Wza&CO52JzI(01t=$P}SbYSIGC<u^IjXlP+l5Sf8pvrR7O%E&yqz|gMiToXex8vcebvsgYWvjfUQj0=DqH#1aS2OuIpX?=qDAk9g-b@J^AdJ+KKiN_p za-Bph6OSlNlJAz3QlgbZRHb2?XV7%#=7w#S?OyruM=aG}i#%*6uyn@A8rZglG}=ir zl!Te=E=sUc)?n#eV5AZjsk&|_A>JIVx(>o*5yN2ccX-&+=7>o=B5TkA zK^)Rdu-6P1m|V~y6L!Vg&iWYiC{djnt*R}jIx#^{*-BvIp-;%>4bnD| z>%EP6DnAj(RuD3SeIBpn+u4xSH-y`@q+zPI^{01L2=-1qA#7YIyflO^)X z4Z{MPi8(z!pd6xAKVBPCW(4I;M^)t{9R^8j3toA=wsSj1r&__&ro$b-Lq5Uy3Kx@a zhu2-2i`%Ui1+R#$F>B*iZkdaFunz^V0;Pn282K2CRI1a{HZkn;3sK;+xXk&GjueHy z|12ov%Y+~vXk;f=2+R#reYnFeiD7IccZ8Xk8mZndTZuriDUwh#ymN34+;?f{w2;47 z(~rz9BQKg3$=N;5Qr+FAz$2bG`E=G}j*2Sii_)w|^dpn6p_bw#*RST%d*aRuCG;ax zXMKv`XeH#U#u?`Sg#!RFzDa)`H+&g*c!f7z#>6{&Sh7eR(}rj$jT#~6V$s14E~bHt z%NMVe_aDi2pcUwoN8g><@63lqY=g)zJ8rM(BaV@g?{T4np2#ksXZs=^uLEQHKCNzy?~RJzxcsA(S;pZ5ko4aGwLL+(mw}21_VRPXDzdWrWtR z9Y+l!H0G|JvM}+YjB^F&*#=3{rUwYM(UuYFZoxL#^*R6qC7+QIlz&0LgCIK)lzmu6 zkRV0YAc8(8gP`$pg0>j54I=0PMBh14PEggke+kOfQ|>FiBqQj`dA7k4WMrvsAQ&hk zsLXlYf7SGpBrUcwlkxmaz0O`4`y^Z2tfloX8Qf{?Zu-In}i>OXVBn zC(6G+oXCyCQ;IXR5igXD6B#FKkg5#8IAZw{h&(H6u-`#XQlX4Nk=Ik%27NaKJ&q}^ z?8@V9L}S&q&;3Aa2BfcpxxG&Ec#SYOd0(cpW~}PuwuTbddrT)R=B=goc%t&cr0cW{ z1s~41WtzZ;_S$QudA~HObEo*@s{Rlq!<4S~Gx5mG0aS5hw~!l#b}ST97(84bCIiaP<@mZ$mrqXFz=uX%lbaOL+0{{^kCziGiscTDq$ zHS8tbJNiz%SI zMjWB11sK`!c!jVdQhA^vzb9yj^q_e{f7oT;6XVP=QDK-GxjNVFv{dhE#eR`*uY*(U!m95ADE z2_{7j^Fd!jX)1zsIF;a#MvG-Dwkl;~2zjEM85v=)U^(nStK>v$PfMso598Z^95{N%=V#UKU70uvqY#cg3$BiTPRUlZm7aR8o8JJTqSr968o@y zaQbJts1Gi(U&I_PYv9snN2d4#T)iUVI-L`7Tfs1Ta&>W!(o-hejm?i-K>5b0e6a~} zC`LXp@fu9*4WYN;keLA4RTe!36Nu>bK(-bEVMg#>j_eJPxrY-n3Lp^A+_6;hH3=Do z4#=nEn*iAuLRQKlL#S1hO~@hh**>)vC(s;(i$CC23WDXKoxj33HJYR=l1YCt6u?Il{S&;H{kKv@am~7!kc* zex{M)Y?zNA2g4keD^;X2h~7*@8_TT=PXoxFu*m+9BRdMBkFm%$$dRRjXj2y1dpR;M z7?ZN2glsd1>;RBWB4qmVAX?-|AoC_^HUIAyOLg%?8io$(G;lA-%Vq$L(;#Q_-;v6Sdx-ILw?XDu&==%6 z6M6T^IT8>;V>X5aw3P#bwD#bl2{@far`(JD8a;Kz$d0$Kn7v z*B7P=74DH74CH1@fOv&KlL-DSpVTfKsJhM|peb^orvya0V<11T3Ur}OpGf4TIMOlPOrBIkPDK1+2TJBKOU7tE^H zacG6$x11GC^FSawD}OnGf!K7HsnX0ha8Qs{KJNDzrb=$H%O*o{ClEb!I%X;K1%)4I zUhX&-lOPxn*ppalET5L>x-?YbHpwb~ifkbRC|52n{!x3)d{zge)K2rzkBP5HPKV8rssyJF%_d(Dh^$U{Uyc{2239V0tt1j*xX`C6vKJJXMbR+A&M z84YB62x=Ec5Kfk&Zq$dfP&Ve=7d5$_<*NXS?ep&!hzzF&TzJS$roh|Kj#*3lkQjM5 zU$h$LumBe$DD{wQ-ea%aPB!Cc8hzzFGzJgFtBD`o_}0~t?4j_e8{b0TCt1`d1Otr0m!Zpva-MAGkZ_SXlEkkw1dvVVpl?@Ek`B|0kVgL%tVfi&cDn+ zLUx!#hLbO2O2~{kWVf(7Tg?4^bzEHobvCseQj6=ld-=QZ^TPj9JLta7D%{h4(W?;) z_qf~t-EH3_tF`L}XD^NLvs$r#`K0x(##cw|F5hkW?&`uG7ZZmcd^1yJkwVI1>jM4h z(+X}d<61*Dx!&*%OKom$`;hlFz4=J0@5CRE7RG-yE4pq?gsz5e&4H;FTzf54aAzgA z5VLs(vl}@z%#c}6I~`+U!W(>+ARs7aMQ6dmnG^~Yq+N+GOCf|cSd~(v8S0{Nvd(L9 z`x!|q(gQmJStoU_lgSZ7razek6$*~CgXcwd=D842C%D1Qz!@QJcKD=D?BHDpjy#CU zOe}ZWax^+gJ4NF7#|ZSeP8v|>a9SuMV4~US-NE#v6G5iqK+tmqtEDtzkg|Z?5~%^~ zA0m7hfzg&|Fy^`lRxombi5=sGtN~^ZokCb_uE5p@*lesgY-G5OV!k40EK3^11v1Ff` zWu(G83oh}j<@o39wN@7$1=%7WZfKI+B-%Ev;W&A?^d94dQ~~B$2Qtt&X9sv&+JTIw zMxvA=k}pGM4P5Cy?HIh3etDqlpK2+>P9GDZz5(j7TF%wY;^(_}BWNITisV6yoGaH=B`i2JkfjS4Y}5gY+7Uk}=5!9B!u=NAj-3$_yL8<>O+FQ6WD~ z3aHr~aEs|ItjJ2=N|TuZH4)6B!I8 zi$cb7u9Lc#6Qe>W%a`(n!(2_u?UWG-j@t;iPD`O)D!5!{X2CbGgjWpGP?B|8%yrTM zx>Dp<^T*zAJ|bj_@tz@NFS{{}5cq z{CW^vH^9B2J9e7i6`J2D!gsR+j_@J!%eZTB_~sMbaKJ6^fTQ`1B7EniJL-QUjr0EP z-ygJ<)eU!{R^)z<$=`aR1Y^!oq$GyT7f?cAWamu#PR#N(D@{CF8miT*gG|05Y17*i zj60Nc73tW=ZhSes&iT!iHK%?3E_=V<{3;7IU>dJh`L92^`K6s|qaU(atsWhnG!DfQ zeRBqQTLo0DbJ)9oe-HCWK_-iUs6?g)b9k=4$3>)m>g}IWBSk9VT*6d+Hn{0*^`X1~ zeUqYq+IW-o%hTI6e%ICAj~V`FMU!8OTi*UY^Cu2kdU5#@)0?P;Dn|Lu9^HA6*gbG? zhC1^Gwn()4K(`f@6Ve;4eKv2-D$d$lLb;~)C~Zjo9J?actnKBBjaf($@3-#dh26V$ zsSUBXAKd;sI0w1cOKV@X6r45dSNr90rSBFbVc&mrXk4pfCsiCd1+hM9e=t`v(>z|A zRzi`H)RVLeDd+bas!LpuXgX$FO=e$1L@r=FFdEY1A;t=jn-N}|KJf8JB} ztj^Mi(}~a;KjMt_k)<~(`GHM!@e?+#8i4AIr_Y~{`#5zWRc!=CylOw~dX;K+Y;XH* zE^NHvS<(W+Q;>st{FP%DdYHM$&_wV1I-hrIblb0`# zelq#Tt!II@rlzLF$gDMd7iha`>!(lx|2rjR+=g4#*{?lP9*+q6fcgM+L_A0Oe-8_Z zJKbLs(4SjOHoUv>di9a@G$bs7Ks~j3 zYKBE!eSOxuRPPcLy6nx`tC4g5@*S`90LUm*R^Gh%8*;e}{i>g{aeqFVt(6t&hWGO|3>MK0d(p#%G zc6qc{RfxR)WQ2h%Hg4RQUW+Vf-kAjn-@Eipe2WCXVv%AGgZ0Vb{m1El$xGO{x3lP) z_Lttw;$KK$&hkxfe$Os5BtZ8w|C-m{l2?L~q`f0A-}1`un@v+k2|@%?xYKHa!*{Cg zr95*gNolx0%xs~w^8NGYeW;jVc)yqL-w#~5a%JlxXU1l?)$8UBsMN7V)_1@+7hP?i zjNWRkAKoQBd7bQXUMFeh%$A=!%?-8D7~pwdEl99Ejn=EHmlM+uMH&;c_i1ZDr2DDP zraCn*(C8Ao=Jyj9o_t_-`TF$G@y!bnQ8O+^rrJSN~0|2Mfm&q6`yx# zT?+*_@x(P|wwrl#q2$#zRN)mP1Ap(vz{(YB$y?65q6j1z2-Ituh)UH%Nxw(gCh>?p zM?55RTPl{N?-_{X_l&&T4aIv)&Yizh;rkLxBfaJGXcvroscD_=){ooA1h=(FQgl&@ z5;SLf8j-#kmhojRsz2|eGExiGty=;rR}}3*t*5wHq~}~Uda$3LU(@?X^)(MiN2Gdx zwU~K7q^FtY*M#68qPhCDOT>}^NiDVJ?cM``;53<`iWldb*ZcIfUa(+6`b=A2U*F+_ zl$c87w*GiJTX)~CT_q9bLHP=n4h{}fapwAU_Q1`HtrcU*J1hvQiI#Z(!xvLvzI6K_ z<%}OwQ1VEyoR-N#! z7uDGodZWs%a3YFROWykyjcR@ktE}7{i0zhRqVQf*^T#gcnzS+lP^YU~2kU!8WAvQ{ z=fXzZKQDRyDiAUp@gd?f)%?xqUVuzM(Gy zXH$kktByGliz>t{iR;7Sp6x$-x;#5Iw_k$qizUmlz?P9g9;lxfQTMy#RP_7`-}m3s z+uLrSinvwW$%cwI?!#!<`%PI}kVszW#O2($J9VeGqW%f$ye>ZviKcHKJPGTmt3y@A z)()>)ZAGiei`l~(oX&B{4&OcU(v5qe3OsKZy}`vTwHLR1>c`~*I!f-Yl4wgJbw_du6n+1 zsg{JqocvVf;s6g_B zsqWcuYx}h&WNId2=_;Re5+1=t3PZuN)JoAnFjH94?qE^w8XP5WGXGCHVw@1C#2urCqk zO-31q%iX^^d;YlnJDc22mjRmu#08W0K7SU)*Vo}c{;$?Uwc+gREq5!}sEl>0--h1B zFxA5+CqO@Zw!Hk+c5~i8N!BKX_a{Huewdcq>@Y*Y2`a=@Q}cW<{OtW7tsj28sMBh% zcW7U-#l)|!OmqC&0*eGH!7-*nvZ7tnE<6m|r)zFD_(NV8v^dbH2aYv6UbwxyV3!=w zSR<+3+#Yc;OYqyJF@L97>0gRVu-E5BAo@3b#qYg`bEAj7s%ybcX%y8(OKM@XA7rhk zt3lztiexrJ_w}d}+=FuHYhN6c7Qb=cy(#6@ z>az0k^0=;*-@jklTC$3HHl)we^ba3CG{glr#z|_irp>}s&yU)~&u_JDnc6k+bNWIO z@oRK*7~H)g-M%Vx@IzS60B@nQF`BNGh(U2*J7X6lINAAc)>k8 zog-)89Sdh|9Vu|~+85^}wU;g#X`J1SE$^Ng`WHNp2e&m!QeXgYe0k++$FTzx{zr>x)h$PR3?V?C)iI#+%Fm?gM zG|qO$;Lr5>ylkj!MD6auW0%|8pRNT-vDhNs_?TfqP`wF?YPW(~N_69ny>(uLI|-3b z?vC-RU_}cHQKRkfd|j%pvdv{^>rzNjh|N~P$DQUaP8aM_&U||}T4Q-7Y|b8(`d+NF z1(of>ds>%Io*Y8YAV_MLU!T9>cKypezg;F3?;2w7;dnCE(5^S^#kTTfY!|n=;aD6( zHBZH<*0C>-jB!ClivXdP!)~b08Z~*vH4nQNms4={-Ebtm@rb0>DKjkVtagtM6t6rBG2FmBnm$X6*@HRSs5G-Qp=#^$l4(<@v+(~J(UySJm8s~hdV z&iU=L9DUnZ2X#z)bfdc~n&3K3v=~Rd#pTsGx|d%MGXIv8xz_LY!I8@>u#V~UKm~T4 zUg1uhmyV%+l<2l^wK_U6xu}pfKf!HZgTv}t*zM4XxaMaHA&5`$T%lr1v|wDW_OPS6 z=B>~+XS_KO4rlzd^Ce`i3xaZYT+POgr1`XUb5oP^gP7sTe)Sa!3AOO3W|9XV)(KMe2EDgwefcS(nm)o zjK0(K;&OJ5*{|dqc=w_BH_jv_SS@?#p^TCK?-v9V_xYaFiKV1~D7muw@!^Iq6Vm%b zZPpJ!;UK%y0Rr{*YTfoRWwqE@>h9GTZ8TYTs-KT_@P5>Tk08%F#3Fpx=-|eOcgCpV z9y?NZtkcB_uy?jMHaYqD`sUCkR&8C16TRBtfem9a%6#wRlvMm@LepYc+xygE!n;i~ zrqmbwnM_19<^`|%b$F3eh5(DupcA}Mq|u#*Vmdc8eI5f@s=-zCuiJ(a+jUvB^+jsS z_bHFQ#UDJl?OR@(A=G|yQM?lN0@9v`l72PNUSQnnN4w~3*hVv<^T9+^%F=-<;=qvE z*(e)VcW8PRS4=xdht`T^)dw#J=$THQ+#GoMCA~b5nkbkBlh=3JY^=$IhfLqxzh~`r zPH+9S7w5H@K#$i+PStoQz{1U~QN<6H#L2j-UHop(4Q$xX&p+}G$6Pv#HNe>oKt*48 z&%m-7cP6R&G?kytfvsBQ7l0E3?b0glF~a+p3cJTyv)8pCabdFK-A*eE`{R*>oGduJ zXFohh^z?`aPF40iae|&@?0tnU18m+n_aC>~>QRDuk-9RIix6GkCr2lFkLY1LcrYcW zEFOT3xV`WD&DE6-?WWgIi~U*{ZQT^;QU2`olGgt4kPb1|Mq)$1S}|6icxQAdsbw2p z=_n~`t15c%WWm(>#fs`;N_w7!6K3Bhc|pHY6wJ4vqIu~QJPr}86KC%=7brHrSkk)T zl8##S^Mp1ZsY5W1EuJ;G!*Z}?aIPr$M`B=&5Df(!8@ZUstu{ePq5*1{`NZg^y1g_S^yO2 zTU>8N@pwARV2?g(eX@^_&wcm;k7VP!*pg{uU9QCm>xR#@EcF%lGkcm3p!aQ-TpsWL z>)S+B-8Z+-kenPstGOh5pLg>1DERuaii!~!Xv-Gj>;{}4PvNd5GzC5-tNxXP!y8`z zG0ro;-Ku?3O`gomjimbAdO{YaRf1z{xMxXr79GH2l?>I?f@_8a_ojDB%B$%_K?Ukl zP#28Tj^Y(w z_cy;OPZg?N}q+uqe}9*h65UFa4x5}`9T%FE`bBE zI$|=4_PEuXX;VJ2y`{DU)wd%6c2-G#Y7yJm$0$N<#zdZM^Cn_hO&NLPjLeq*VB zVRPHBjqN69<{w*L?c$QNzv{Pd!oJkXOE;a1@9%DXg3WncL+*m0-a}^Z_etK~8}4k+ zgUHSXx9CiYDt&eH*75}6+MA=U6zn)K|Rl#5)8z zSlqp9zve{{Ll96JNLzkH*Cy-FKdOlrfG+QSZvmaH!p3Mh4#Xah4v#-yoI}5)R}CUp zkwlX5P{@7kZ`xy@z<8C=k&HGuS_bfEVrf|FAA+ZWHPHzd{BH`Op<|rx?TF_){iS?( zA;M23IS~=Ney2D=nG^OZ<|EL1@oJlmFp%CU5NR77q`2Ei|BG;v&qSSND;4p4u?^U? z9BiuZf%@q9$-W!XQ)q*LC|3#I)EMw=-8(!%y#n%diq>45=lwFwdb{CPI} z`2zE)r3&=!lPG4Z1T}ir};8o zZ~%y%3%ud=U+w7Dht31LpPrnZLppwPc7#q{EmfsApblIIZv?F<_aOg*J?)SlW(|%S zec3_QQAC=O`}QWgo8A6^o9aa$vHYJ-Z&Er>{3=2I%OuiH)(r1;hn-*%7)9ep7;48feF0uPHKJ ziAdnn_wQHwy+h;&PIQyg@^A21`_o$`ni9M$yscmA$vMz0cEGr*mkvlAAD6VM4L@6QC|c!8q2+_SdbJnupc& zZWn@eeaKPb(SR(3>(nVflU_I(gZN#;yRT*%J9Yfr={XQ1O?C@ehD%D(blf(%50FQ% z1*!An7|~$uNnS$Iz|%RFaYp-~UU{JkiFXl2u+f4$v^q34OVpV+h{DZlU~D`;g=%Oe zF*aKfIpIOEE4aIj#7B{x8AO8F6D-}6iMGQ$3UH0kao@w|<4Pt8mHeU#98l=yj8VeY znyJKLSkqKaRYo?zGW;-f808aDEI!E-$i$rhcNfP27A_1|K+FjEU`@&&r~4kqvQm_k zQ>X16ou23`Gb%)rxqNWJX2{X87mUOY?owzmN;4QG1p0rPah>WtBuqvxP(F*haZ22W zMWTXQ#*W(^B*1}8wiA{sdvTL^43{g3f1#5xh-O*P1TQG)C(INLmS5*G1%&P#_JDnN z%Lx|;(d?8^oL_-y&}|m850|IRc+74QH@5qLR6c_O1kH~79zhB2Tt%j!@*i$&<5*Bh z#k+Gl#;zu&^uXBMeGjt_*LRE-P7G2?Z=~B6fj3d9h>^B+phJ=gTxIutB*a1iQ-Vi>@7hvH`2%k3aJuZ0lN`r1>9vz?R zU**t*OzJ3*&J^MvkXa8UFn-RhGgvI`BxWeCtr$rr)b!A7 zJyYaVFI=v|-3f+p>MqFvoDiwURQfEZgl3=wN~oNNF8CQ?9Bw?>PCdC!Ti~QbDp-T( zxV0=b!L^RG7pAGjQSc3k5VB5)6R_$oHN$F-oQ5|Xvboh}EO@AiJutf}dFG{f zRv-i`0AU;1(ygKC3FAF9lLm1V+#!aAfzb$)%E&B5qOhz}$Es{2eII~D!EwG3hLM>B zE|SBPk%Sx~MhdYSbjTuzCO6=KYDh7+N=oU5t`ybJeUSw(BeI={M6$KmwqtCPsuyPR zj57gqLI?~=KW}*tF%`(B;S^6b*=0 z4Y|T`Yl}vea;%d zELRZO9hce5xl4E(55yX5cz9rdyM$*5N&u*<92AQyN;TvQhpQLa(NN6wF{jy%WM~Xv zpxlk*Br=sO7-p8ufhC!NAqJR-yCK&}8+X4Vr2dCwa~J+egP{JHhOtO4579jM7MM)- z2^12LXgkhn3?3lhl2RB&966a7NTZ%3S%v(V0o1d@Bfi!IgPKb_)rNB5AZvsr@Xr4h#)P!7PxQW@QVq%U0Fp&}!SsK^EjWLQ~ zS{pa^;)htDNl1C2d>hzoMyw9jBN7+3&_}Kf%vvP2G9K9Jb$CeeQfQ-S5Ne?*eUBAl zK_=#=iOjP-l*>&DBDhUNQYS8PgDD_vHsjHWGV<9@L$G9+C8$T=%%Rgl>JXE7*hBLl zZYKE#gzhkE#|LxM-ACr2z{(`NfKzpMq^>iR!gs2JfRiu>OohI-MR$%$V_MfX6AJk? z+;&5dLUKcu=8zv9osg--h@FI)(jgqW$Ar!pDPG$+If)Btc3}rphivSCL(&l={scHh z4(_5O!6D1rjuV}D2@3~&l^ooaJp{J~a2?aT$l^mzsa*%Y7X)_(_||p69VWPPz;)vA zRk8Sx!d}H$N%2Y+4pMmE0mse{D(2p$1CH=r!Tc_Ez`ZB9Sd^K+=zwGA_Ze`MR8P*2 zu_Js}Fu%tf9B(8G2YiJboG_H=Tn{*<4md3$DeF0GFZloO1xn zF4AA()Zz1Ztk}Mzh`Yl$EvD5PHAp*_OX6b|t%jBionc~#2rt80nNuXSIbj~QZ5>0? zKAblRL(4M;8F5z10$`$0dLGCzf6qN)zF}dK2d79jLt*#?5@DuSmCU3F3kM_&mW=GR~ocv|svl5J^WGn~4&qi=6x!f)WWc4|AT9S;s;R(S8}gLD{lK))r;t z9F*ucwhBzBp7zVFT&FTRnHXUH9ez*{Ov1j0DsvT1v@zo#k?spjE(CF4iB<$Qr7)?9 z+lPwB!ZR}IL$sH2Cj~}@H1Z=9=^V!r9OW>#7U*AxFXT@oIPKSb?JYRB=bV}wZ6B{n zOkJO8^_uR!=wViDVKe;5(VFnIuXz#?=VQA0+|V&WcdAzaF*Sf;S^v z@2S}0(Ff8bO_c1ra&&8&f^i}RJZ8XYyI?aGB%_2{cFq$pFUVmhsY#sHbxh6=I>BfU zqAFO{JBr?rN_gyGt3l}m$E+odaNM zedMrhPWEMsQE5ZRxEm6;H_y{!XF_R}qHGdw;-AR7&sp`K zk#vZYO1Um)nZDY&7Wi+L-aJH-1s#X~Vw9OJJJ)H3`+(j~_`HtKEgKsEA?i+ga@f8cJjaJJ|F182^{t()~PTp^F<>U{BEIG#2O zH#g;9I4P2ik(9s2;y-Xmt!47{AO8n#1q)|)@;`8_Oib3z{14nvf?K`sKX6E%!Fxk9 zFa9fVA<~T*fki>oziN4n7OHUOsQ+{#uP+Mk&sN#9dp4$!;y96G@ZuN8Jf1FT>e$u) zQRmVS7Ea6HUzKY?N(m!PoL89SmJn!yTPF{w-b{?al|(HPY*&1yeH}it zW4z^SU!$ilmeUHTp@Xj=`*Hl3AzNNM?F@1oIB=ln!%^1}@*Mg%qIvoYj@xS`QQ#iY zKVb$4^`u4I|GJZjp>vh`L$%ohJ{2=VasAKm5{POq=S+{+h~eo(>sxy{A^n_wlTM4u z%eheEI%dna*A4ZTg9jq)DvwPX>M03=kbPsZY}v9kdno}AS%KYF0Lvs&tH?guc(aX? zbdLERS5}O4p*7ivtZh8wg-92rwlk5zL=@%KM+Vcc*N}&zTzpXrt=Bm$^Nu10KuPY5 z$cNl+DFs`}@iCNL&#>wBHE>o8Zxl>7n>WwXB^EtHW;YY*Ey0ZrEHIMt$w*S_y%gtb zMr2Ipk#g$UoOb3EGM*VWeZCM|8YU7|nT%*A!U#fa9~i+KQX5K>XH(gkXRPcsK`0&a z7y~8heILEAz}qo`F*si_bBIuKbxhx#q+x$!*5v0HHg}#)p3f&I$7DGZQ#?T^c{m&? zi7AvM@6HnHjB^+>2fIr_Eh$OgXwrti5EO9lLGviaO*>=I6{L_8V+>fr*hE0nP!(jssi2Q3`5Q=yv2(`hBQNV=|hX>pe!yF{0@CJhmN)!;v5Rh{1BEl(9 z$7qJ5JEMlEBytGR3L!rvz#~6!?iej;3~_~$;z@Bb^2q~H_B)QE3^Rk=4^>i$sMv~e zOr)U`5y~*YSpvR;^pUdQ1yp&(*r<{lpj>u??(DCTea>WvDP)O|5zc;Im^=GxoG9Q_ zoM`YtVA3KSsTl#Kmol2Y5rCUTOe&ZGEtLHl+gwJ#8c>^l_;o+_*Q8g-P6<;O&JM0d zi;c41BiX^V?qVIYwOcjFRANLHhQnY*(wmZ`1>F~w0CEUIlQ7^gFKC#Vgc9`48s`*cDo+w*;Z?T83;TE&QT_uMiz|)fo2T2VDqf$vpmq5oqlTHgR z_9yTW03Sf$(oU(QexzwQWa2@)46I#dEyDA2SXEl8N!^iz&kH5u?8wattVvf`=-w{mV#Z@GD(qWGb}TX0KpsL%8)?f#3?80TP$bsy&J$~d6rA6~j*f>CWWPR5 zx)SNS0fLDfj$r0ac63u3{Sa>9=_JNP_9Qt8p{Tvs!Od>4gXe}#^CZQL)Ud!z#v<`# z55lvIXbPQwc+m(=#j}I;A=0JB2uqTYVrsIn!~|ymO-C;#ayC0Sq8oit-iya3SSh6< z99WOiEj%?bmPk`Z94UbvZ5*-D3sL?G9Kq223kfw9q}H>~RNWd!-AAH3Yxonp(CDQg zy@2I<#dcD$(kKKH>G3zF{vHuy1Y*o+DmDvBNmP1>;SD2ND|WNpL0l>Oc2zw~`z7j5 zwA)0m-4VW#%0@px-3vP7XCoH;ASrMf=0cTGum=c7Vu?z_rcvz_W0S`!@Fo1bg&pn( zNvQz#LW;Nm!|V}|IZ4CZ3Rw+PWBI-k(W*DBz8kRJL2RNb+kGb69r3=ate6hSx<@0r z#&&mSyMx$7Rt|gSu-%b~^@8oL#;VL95c8QOW-&>%I~YBJW$a*<_Fj>Ve2wFr-uQM$4XBwL!^&;mHNm~iV%u$Gi7b(o-p<0Py0$94IV_L>z zA*`Qj?8wj;P4ZgI>*K^kT-}A-rD=%Ti020M>jCuZvOHQxa2>DJ=tqOcpo768W%ZuD zgkuDF5|r|U7)M7UBqIku&t`wlA(s3cBXRnvz@nOziXj@{%Ntl<^6ekPND7wc%hGVHKM!TFn7!rfk`;sccC1T%w0wgyh+)YCdT(6T})Dx zIQ3vFQ{$dKgCcBm2T+vfT$! zcj9=)Pui%`mE6M7?A#hixg4eV8v_^F>~y5c@2jtTm4gf>t>TSX*1S-now46w0}#Z1XX$4`OVSoo5-Fe5sAP3PM-~{)|_kAj*}<=FsaB zOGz5Pih|n}at(4%&jG zR7$y{!iJ~$`AL~Y{)jZN$vH?L8LboW^R9w<&S%g>3b3s-aRpYSN`AD(f?7JG-1{<$jxl$MS z{@JtMm|*RMKI6x4{BX;C{ralrw5wOge|d7O(h=_gsjz5#C}SQ;MYMw95cWbcb^MixQ_zO4q6sE85+kkg=Tiy1tUZAy@f(taQL z4}XI(Po~s9qD1|0%`t!T+gn{T6ZJ?j6MK+iy0S%bND(c;pUR3zd15+}{`!I@vJy@_ z!CGf`Hlhl0sp1QifjkkJrp441f1Ab}dAf^kY}dmBz^q3QRd})+t~Kk{?ZxZDLy*T& zj6ZR*HvxZT2m-e$8;5A}VX_Y5G?vj>_|r`;NTomm$3P?vuCzyXN)J6@W+LKyAds%J znXoFVprJntfMO8mPxnHVVk0mNd-N1H(QJ8k$f~nPg1%?Tz6sg*^HN$=kyL^|X|RQ_ z2YWP^H3iz_PogNB6RAHgC^VvymTW4OK$3|sU$H@Gav?Q!5{wW6&Pl8VtfHMgUwFi1 z{T`YQWhGNCKFV5x$0U8bKP<%wrABxem^G&b`^lWD;88#AVoKw?cI}#`c~HQV|F6Ak z|ED@z|DV@!j?QaFq{%eZc1e>;Dm5{x;%^&|Cm1K5`; z265>_9r*W?69lAyP0fRu-H#48OEkhqTopDS!Rgpu0 z$R-1Si^K`)O$QY8!SQLEctfTx*VsxDY9TePKtkJGEUzyBD3#RN zu24x&Q^ccwas5JRSpNM^y?Hj>mX-v<=j`^;=DrI8aF8;rsCLJ25Cw?0~^pzLvgSL`H!oqLDTjUpIsnSH#I`DJay!31$0fJ zlCe6w(7j0)z28Zgt!7ByFlS>!!wLdz5xdeU0wim!tOVD9HSPomygNt>ZIC(-mEjU) z2ro+b0|ugF!<0f%#(b2q*8t|^h{6<94rU2+jeCfpw6!4+=TVmuYwK4?bo51*Z6&b{ zvZ7V{fAOq)`gAU#yT&F6$K!_7Pv5HfG1k_1(DJ4I`6*`sc6Vs3@$uuwNVvPAu%ZqA zaYKFet}5p(@%7+YTn&ys3>>AlWNqMdjk_gi?bBFq$-JvTWHhUnId*;xW|UK~?HURs^O|W`mBVcHRU6qW(<2 z@M~3ITZIn~FVvO*_jAloWFBZI{m#>q56puR5OW#|)mjF|#+69yg@Eqvj)j0WJ^hCi zxqOC_!&AVcxyO@~_){@y6#A~~46uRd;?iEVAd&Z0ioRrqR7?+-WLu==XJ8rzibU4F zatPnOJeE?nxPf1w7umTG+;Jx`n-8!RbWPQnQp*>B62esprM3oX`HoIbV`XKM*rk%_c|PA`J(AWEe^u!>7VKsRF_bh* zN*PgOqN2n0!UB$qeC6fiBUm5z@nG1q`g%3Um;n8Qo|EIFVG6+IA=6t4PoCsUJYt2F zt~X{Yy@)ov1EGNla+gGolZT1&OfEY!mU3p~0+7#UT1kT}XT>BnyY@PGj1BjC;8kS)N=hZ8Udqqfu@E6aSPIf^4#T(e-e>!n+m2$dT@n zBO|Y1;^o(|Ad9)ig5TH< zOW53z1Cm?HMr$Chq6CoVOit6qXmuFT8#Xo*+Ov|p8~W%uIaOMg#i+>bPkVAZhD`)9 zvN~E7PE$7W7e?Ncg}_B7vQ;^Zd-FWavyVAt>|6?6qsUdtg)*m1q<6y>Hg+btDKJZQ z+J1QNpcZpg(XW}gq(6f!w@8aZ*Cz52ashW}Mcy8tKE|h!3qGUuwn9zrL^hNuQ||BE zK9H3Nz3oCnM~26;VzwBY?Pk}dD)PL;b}JxNgHyCGqEHj;rdjlqE>`Phl~cLTDGtFY zWo?v`XEE}CoEF*WRv*yOD%dtCAAKuXOPr~LNJg%eOuKVCz21o;2Ds&!Zl>=!c!DAU zP=eTGJ}C;8|62KTx+nGBgGA)cfOulm0$dAC; ze{oIz*^o5t$k=QT`PYr;wB_& zv|CC{%{grAm4Dt$`))Yu`b zpT0=8R_;e>W36hGWVMnx_hTe^v(BL420Q(^Bw{!(S)2y9*FLuNUOs!}=OT`l-m2y` z3)`+EYE4#;^SpK(kf1wOf| zcw3Kh3*L6BR|L6DT02*js`8VoW{dk8K+_1bNaC1VRY8lXm`8BD_Bd@44Xr;sUW*Oby4WO#a1st^IpJ@m^Z~ z_g)!CR0WuPRm#PS7q^!l@{LEyB63pw>pD!0-rrQb-wziqy*@_~Q1&M2HtD5!y?^un za1mVowg6A%^%|bMxZ2uU>aAQdW(&k0mFA-2K$)2pYoURjsVkt zf2f4dRk(l~9%z24^JU5`IDnnic{nXgWSVLqK|razKv&w-ud~Zq??Z+Az5fj9iS_4# zyR&`a-me7SdIsQ|8w`wkM-p|mUD|Tc6aW8$>Ar?f;BHMc8`zi%uPUL!eTIebr~_ad zivma0Y;rP{-Vfmkst|P-rE;D~LlD2vFTlf{VG91L4CL87zOt)HdflGP2M4ARe)P1d zX|shfDaAX;m&C2gfR{)7Q*?AR+~XyG_8idtzrWlUiQ)7@VHfB!(* zh;m@N5pg)U8V~NlY=)HXmoJYE4Go2hGD+{vCCigDymsLErnE|o*70|yn=g@lpx7yw zmAy5sa^R(Hgo(0QpH&PrVs}akfC7*1!Y9VfJ}%jgfy|fx&Rg(ZtlE_frFS$ z=5^6tSC68+Tu1qz;Z))tZOT>=T8^|vwF7kBJ3LMW4;(IkRRNFE?EJEKUax@lCV2O1 z1cTc73k2Vvrj0f9##uS*hVSR>rw9$gNQ2c!0pwpwl^^(gszOEZY$~LIf@q5rL!TVq zpfQJ>>Z!hW=O{y2&zj5Zt`tOe#BH5d;cWcS7hc=-PEJk$8m6vZ30SEb;jk$X#;U5_ z@c-5fC9L{^k|c*BwPB{<2HdP&DR|bd)Lsyng+9C2-sgMc;1Y z;RrIP{GDSTjZM5N-rpoaNxt~B@s34dBJzIzJT*Dy1{}Qd9L#UAW~d6*$2wc&wuXuy zP_7r@>Pv(CcEU$U<(`sZvuMwqJ9l?)^L0W8zitVEf7sN83_fN#(j@ieKZdJ+{ZMtuxb@yK1{;*cQ}DVbjePo}9i*^un0)#q)$s!f&W z20{`N68aIou_zlz&R2RDhLgzv%s~4-8}^3RlLgAW{SRwm_DT^?X?L#J>%RIN9pE1( zDW2r>sdmHs7+&GCpAPmsu0Z$p)&h84V#$5RIT+usB1h_M=TP4|Qx%i0^l*_6YGB1o zhqW8ePFW5Pd?oyYW9CzYDJ2)?Kg6au9N{AJ_StQuU0{A6dxwyakXtIHiTsh;z_O1B zjyiz9gp8T@fUObfsLjCPRdXfY$;q+T&CSS|zr}p;k*hFp;@#lFv|I3rDy!o${!BWn zokOP0$^Q1rN`xd#bBD_SL0*^^7!Y9Mk5$r^t&-B)AU#w^(-^ur?Z!QXtQ4nV^X&h| z=ADAz2UYAD4u$WsJXng=lq49pEFKlUc;WG%bX&6v_-$hM9jMTDPo*r8Pem`(%`gGx zLrVABl%NMld%{Y$yGlL6%$e?*CasYQy#FE4E%tdf7X=c!hf8^2)H@&9Dvg-4Cs@Kn zf$n(-M8=b)yam-(#ybcEP9Axl#lL?nnj#%eLOvDl+i5Ry-NCF0T5eol{iAk*LE?AJpk>{k%@{=^r~^{GiBI+VS+)U$K>seFI&z`y}f?yXyv7UAQbjo6X-*5sj?f#G{ioOka0 zQR=vBR|3%D=SM-=EDU*4h`F!h(}g(S#>VDGE#lYNA-IbrU?=l8+1c5L2D_8jgCCBn zvfqkNObk4?V)=4OYinzwG-zySKZ0uqYtEYm&6J97mAK>A?6iq3_!$`)=T+*Yy}gqU z)aT8c*WXqib7jXh$Ap@inxzr#xENY(kJ0bCdU$x;s7%^VCiS5#EgLs_+-Ra=OcKNx)Z@X{wdUOb=w)n&DO=UamIcGN@p?&dQ= zMMcG-p;wK2T&{sufMK~ z7YObOLxv@tqO_~mug{UT2;+o8VRfg5*`XOrmoDueANAXeUS>Y2bfGo#J${ Date: Tue, 29 Jun 2021 11:31:01 +1000 Subject: [PATCH 096/632] Removing unneed import. --- .../fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx b/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx index 562e44fb6c9..e82d36ce5d8 100644 --- a/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx +++ b/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx @@ -12,7 +12,6 @@ from ctypes import cast, py_object cimport cython from sage.algebras.fusion_rings.fast_parallel_fmats_methods cimport _fmat -from itertools import product from sage.rings.qqbar import QQbar ############### From 5ecf8f8df2bfef9571201da9b543ac58cfb04035 Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Wed, 30 Jun 2021 11:06:15 -0700 Subject: [PATCH 097/632] minor docstring edit: link back to FusionRing.get_braid_generators where braid group representations are discussed --- src/sage/algebras/fusion_rings/f_matrix.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sage/algebras/fusion_rings/f_matrix.py b/src/sage/algebras/fusion_rings/f_matrix.py index 60375e7d49b..2911df25a2d 100644 --- a/src/sage/algebras/fusion_rings/f_matrix.py +++ b/src/sage/algebras/fusion_rings/f_matrix.py @@ -78,7 +78,10 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab :class:`FusionRing` captures much information about a fusion category, but to complete the picture, the F-matrices or 6j-symbols are needed. For example these are required in - order to construct braid group representations. + order to construct braid group representations. This + can be done using the :class:`FusionRing` method + :meth:`FusionRing.get_braid_generators`, which uses + the F-matrix. We only undertake to compute the F-matrix if the :class:`FusionRing` is *multiplicity free* meaning that From 92acd43985dde3139657136e6a4cc2481f0b3aa7 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Fri, 14 Jan 2022 17:38:51 -0500 Subject: [PATCH 098/632] Trac #33176: fix Cython warnings in sage.libs.ntl. To fix some Cython warnings like warning: sage/libs/ntl/ntl_ZZ.pyx:274:23: local variable 'ans' referenced before assignment we initialize a few int/long variables to zero when they are declared. These variables are all ultimately passed by reference to NTL to be overwritten, so the existing code is not wrong, but the warnings are unattractive. --- src/sage/libs/ntl/ntl_ZZ.pyx | 2 +- src/sage/libs/ntl/ntl_ZZ_pX.pyx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/libs/ntl/ntl_ZZ.pyx b/src/sage/libs/ntl/ntl_ZZ.pyx index e488b7adc43..3d4134376e4 100644 --- a/src/sage/libs/ntl/ntl_ZZ.pyx +++ b/src/sage/libs/ntl/ntl_ZZ.pyx @@ -270,7 +270,7 @@ cdef class ntl_ZZ(object): AUTHOR: David Harvey (2006-08-05) """ - cdef int ans + cdef int ans = 0 ZZ_conv_to_int(ans, self.x) return ans diff --git a/src/sage/libs/ntl/ntl_ZZ_pX.pyx b/src/sage/libs/ntl/ntl_ZZ_pX.pyx index bd34a79c85c..56dbe3ee82b 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pX.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_pX.pyx @@ -276,7 +276,7 @@ cdef class ntl_ZZ_pX(object): """ self.c.restore_c() cdef ZZ_p_c r - cdef long l + cdef long l = 0 sig_on() r = ZZ_pX_coeff( self.x, i) ZZ_conv_to_long(l, ZZ_p_rep(r)) @@ -1152,7 +1152,7 @@ cdef class ntl_ZZ_pX(object): ZZ_pX_Modulus_build(mod, modulus.x) cdef ntl_ZZ_pX mod_prime cdef ntl_ZZ_pContext_class ctx - cdef long mini, minval + cdef long mini = 0, minval = 0 if Integer(modulus[0].lift()).valuation(p) == 1: eisenstein = True for c in modulus.list()[1:-1]: From 5594ba894abd548e403593378c588a6571fb9db6 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Fri, 14 Jan 2022 17:54:25 -0500 Subject: [PATCH 099/632] Trac #33176: update API usage in sage.libs.gap.element. The C_NEW_STRING() macro in GAP's stringobj.h is deprecated (according to the source), and should be replaced with MakeStringWithLen(). So that's what we do. The conversion is straightforward and happens to fix the Cython warnings, warning: sage/libs/gap/element.pyx:267:21: local variable 'result' referenced before assignment warning: sage/libs/gap/element.pyx:268:15: local variable 'result' referenced before assignment because now the return value of MakeStringWithLen() is assigned to the "result" variable. --- src/sage/libs/gap/element.pyx | 2 +- src/sage/libs/gap/gap_includes.pxd | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/libs/gap/element.pyx b/src/sage/libs/gap/element.pyx index e6a7b3800e9..c26c80b2cb6 100644 --- a/src/sage/libs/gap/element.pyx +++ b/src/sage/libs/gap/element.pyx @@ -264,7 +264,7 @@ cdef Obj make_gap_string(sage_string) except NULL: try: GAP_Enter() b = str_to_bytes(sage_string) - C_NEW_STRING(result, len(b), b) + result = MakeStringWithLen(b, len(b)) return result finally: GAP_Leave() diff --git a/src/sage/libs/gap/gap_includes.pxd b/src/sage/libs/gap/gap_includes.pxd index 5a9ab486f77..6d22e32540b 100644 --- a/src/sage/libs/gap/gap_includes.pxd +++ b/src/sage/libs/gap/gap_includes.pxd @@ -182,4 +182,4 @@ cdef extern from "gap/stringobj.h" nogil: bint IS_STRING(Obj obj) bint IsStringConv(Obj obj) Obj NEW_STRING(Int) - void C_NEW_STRING(Obj new_gap_string, int length, char* c_string) + Obj MakeStringWithLen(const char* buf, size_t len) From 01176df32fc2e748c6e0a6330a22ea8e93547c89 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Fri, 14 Jan 2022 18:20:35 -0500 Subject: [PATCH 100/632] Trac #33176: fix a Cython warning in sage.libs.singular.groebner_strategy. In the NCPolynomial_plural normal_form() method, the int "max_ind" is passed by reference into Singular's redNF() function where it is promptly overwritten. This is fine, but Cython cannot know that, and throws warnings: warning: sage/libs/singular/groebner_strategy.pyx:540:67: local variable 'max_ind' referenced before assignment warning: sage/libs/singular/groebner_strategy.pyx:542:32: local variable 'max_ind' referenced before assignment Here we initialize "max_ind" to zero to avoid them. --- src/sage/libs/singular/groebner_strategy.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/libs/singular/groebner_strategy.pyx b/src/sage/libs/singular/groebner_strategy.pyx index 20751eafa71..e5807a7a9d4 100644 --- a/src/sage/libs/singular/groebner_strategy.pyx +++ b/src/sage/libs/singular/groebner_strategy.pyx @@ -536,7 +536,7 @@ cdef class NCGroebnerStrategy(SageObject): if unlikely(self._parent._ring != currRing): rChangeCurrRing(self._parent._ring) - cdef int max_ind + cdef int max_ind = 0 cdef poly *_p = redNF(p_Copy(p._poly, self._parent._ring), max_ind, 0, self._strat) if likely(_p!=NULL): _p = redtailBba(_p, max_ind, self._strat) From 93f2e907d774599444c8b9ff4f3e89a132fbaa6b Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Fri, 14 Jan 2022 18:55:23 -0500 Subject: [PATCH 101/632] Trac #33176: fix Cython warning in sage.matrix.matrix_modn_dense_template. The linbox_det() method in this file creates a float/double variable "d" and passes it by reference to the FFLAS/FFPACK pDet() method, which in turn passes it by reference to the Det() method to be overwritten with the return value from that function. This is all fine, but Cython doesn't know what the uninitialized variable is used for and throws warnings like, warning: sage/matrix/matrix_modn_dense_template.pxi:283:19: local variable 'd' referenced before assignment Here we initialize it to zero to avoid the warnings. --- src/sage/matrix/matrix_modn_dense_template.pxi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/matrix/matrix_modn_dense_template.pxi b/src/sage/matrix/matrix_modn_dense_template.pxi index ffdf576df34..9f945e9440b 100644 --- a/src/sage/matrix/matrix_modn_dense_template.pxi +++ b/src/sage/matrix/matrix_modn_dense_template.pxi @@ -274,7 +274,7 @@ cdef inline celement linbox_det(celement modulus, celement* entries, Py_ssize_t cdef ModField *F = new ModField(modulus) cdef celement *cpy = linbox_copy(modulus, entries, n, n) - cdef celement d + cdef celement d = 0 cdef size_t nbthreads nbthreads = Parallelism().get('linbox') From ae1e07083862e25f6df01fc9795d619527468053 Mon Sep 17 00:00:00 2001 From: Daniel Bump <75940445+dwbmscz@users.noreply.github.com> Date: Sun, 24 Apr 2022 11:26:10 -0700 Subject: [PATCH 102/632] fixed import in weyl_characters to build sage --- src/sage/combinat/root_system/weyl_characters.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index d2d1d9f967b..56163c5ca85 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -18,7 +18,8 @@ from sage.misc.lazy_attribute import lazy_attribute from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet from sage.misc.functional import is_even -from sage.rings.integer_ring import ZZ, Integer +from sage.rings.integer import Integer +from sage.rings.integer_ring import ZZ class WeylCharacterRing(CombinatorialFreeModule): r""" From 10e1361ce2000f9558a2fd377efc476cc6a25f21 Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Fri, 6 May 2022 12:03:36 -0700 Subject: [PATCH 103/632] revision of doctest in poly_tup_engine --- .../algebras/fusion_rings/poly_tup_engine.pyx | 41 +++++++------------ 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/src/sage/algebras/fusion_rings/poly_tup_engine.pyx b/src/sage/algebras/fusion_rings/poly_tup_engine.pyx index f0e277921c2..be804b4da8b 100644 --- a/src/sage/algebras/fusion_rings/poly_tup_engine.pyx +++ b/src/sage/algebras/fusion_rings/poly_tup_engine.pyx @@ -546,36 +546,23 @@ cpdef tuple poly_tup_sortkey(tuple eq_tup): term by term (we assume the tuple representation is sorted so that the leading term with respect to the degree reverse lexicographical order comes first). For each term, we first compare degrees, then the monomials - themselves. + themselves. Different polynomials can have the same sortkey. EXAMPLES:: - sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey - sage: R. = PolynomialRing(QQ, order='deglex') - sage: p1 = x*y*z - x**2 + 3/2 - sage: p2 = x*y*z - x*y + 1/2 - sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup - sage: (p1 < p2) == (poly_tup_sortkey(poly_to_tup(p1)) < poly_tup_sortkey(poly_to_tup(p2))) - True - sage: R. = PolynomialRing(CyclotomicField(20), order='deglex') - sage: zeta20 = R.base_ring().gen() - sage: p1 = zeta20**2 * x*z**2 - 2*zeta20 - sage: p2 = y**3 + 1/4 - sage: (p1 < p2) == (poly_tup_sortkey(poly_to_tup(p1)) < poly_tup_sortkey(poly_to_tup(p2))) - True - - TESTS:: - - sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey, poly_to_tup - sage: R. = PolynomialRing(CyclotomicField(20)) - sage: p1 = R.random_element() - sage: p2 = R.random_element() - sage: (p1 < p2) == (poly_tup_sortkey(poly_to_tup(p1)) < poly_tup_sortkey(poly_to_tup(p2))) - True - sage: (p1 > p2) == (poly_tup_sortkey(poly_to_tup(p1)) > poly_tup_sortkey(poly_to_tup(p2))) - True - sage: poly_tup_sortkey(poly_to_tup(p1)) == poly_tup_sortkey(poly_to_tup(p1)) - True + sage: F = CyclotomicField(20) + sage: zeta20 = F.gen() + sage: R. = PolynomialRing(F) + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey, poly_to_tup + sage: p = (zeta20 + 1)*x^2 + (zeta20^3 + 6)*x*z + (zeta20^2 + 7*zeta20)*z^2 + (2/3*zeta20 + 1/4)*x + y + sage: p1 = poly_to_tup(p); p1 + (((2, 0, 0), zeta20 + 1), + ((1, 0, 1), zeta20^3 + 6), + ((0, 0, 2), zeta20^2 + 7*zeta20), + ((1, 0, 0), 2/3*zeta20 + 1/4), + ((0, 1, 0), 1)) + sage: poly_tup_sortkey(p1) + (2, 0, 2, 2, 0, 1, -2, 1, 2, -2, 2, 1, 0, 1, 1, -1, 1) """ cdef ETuple exp cdef int i, l, nnz From 2fd832c289d60805a1dcdd1eaf1a17bbc2c5b41b Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Mon, 9 May 2022 14:58:10 -0700 Subject: [PATCH 104/632] default number of strands in test_braid_representation changed from 6 to 4 to speed up --long doctest --- src/sage/algebras/fusion_rings/fusion_ring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/algebras/fusion_rings/fusion_ring.py b/src/sage/algebras/fusion_rings/fusion_ring.py index a3e70b5866e..c9730ac7676 100644 --- a/src/sage/algebras/fusion_rings/fusion_ring.py +++ b/src/sage/algebras/fusion_rings/fusion_ring.py @@ -377,7 +377,7 @@ def _test_total_q_order(self, **options): tester.assertTrue(tqo.is_real_positive()) tester.assertEqual(tqo**2, self.global_q_dimension(base_coercion=False)) - def test_braid_representation(self, max_strands=6): + def test_braid_representation(self, max_strands=4): """ Check that we can compute valid braid group representations. From cf166d78dc0df38f1e70e6350c9911259a3d0cee Mon Sep 17 00:00:00 2001 From: Daniel Bump Date: Mon, 9 May 2022 15:40:12 -0700 Subject: [PATCH 105/632] fusion_ring: use F41 instead of B22 in test_braid_representation doctest to save time --- src/sage/algebras/fusion_rings/fusion_ring.py | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/sage/algebras/fusion_rings/fusion_ring.py b/src/sage/algebras/fusion_rings/fusion_ring.py index c9730ac7676..05b560e193a 100644 --- a/src/sage/algebras/fusion_rings/fusion_ring.py +++ b/src/sage/algebras/fusion_rings/fusion_ring.py @@ -377,13 +377,14 @@ def _test_total_q_order(self, **options): tester.assertTrue(tqo.is_real_positive()) tester.assertEqual(tqo**2, self.global_q_dimension(base_coercion=False)) - def test_braid_representation(self, max_strands=4): + def test_braid_representation(self, max_strands=6, anyon=None): """ Check that we can compute valid braid group representations. INPUT: - ``max_strands`` -- (default: 6): maximum number of braid group strands + - ``anyon`` -- (optional) run this test on this particular simple object Create a braid group representation using :meth:`get_braid_generators` and confirms the braid relations. This test indirectly partially @@ -391,15 +392,16 @@ def test_braid_representation(self, max_strands=4): code were incorrect the method would not be deterministic because the fusing anyon is chosen randomly. (A different choice is made for each number of strands tested.) However the doctest is deterministic since - it will always return ``True``. + it will always return ``True``. If the anyon parameter is omitted, + a random anyon is tested for each number of strands up to ``max_strands``. EXAMPLES:: sage: A21 = FusionRing("A2",1) sage: A21.test_braid_representation(max_strands=4) True - sage: B22 = FusionRing("B2",2) # long time - sage: B22.test_braid_representation() # long time + sage: F41 = FusionRing("F4",1) # long time + sage: F41.test_braid_representation() # long time True """ if not self.is_multiplicity_free(): # Braid group representation is not available if self is not multiplicity free @@ -410,10 +412,13 @@ def test_braid_representation(self, max_strands=4): for n_strands in range(3,max_strands+1): #Randomly select a fusing anyon. Skip the identity element, since #its braiding matrices are trivial - while True: - a = b.random_element() - if a != self.one(): - break + if anyon is not None: + a = anyon + else: + while True: + a = b.random_element() + if a != self.one(): + break pow = a ** n_strands d = pow.monomials()[0] #Try to find 'interesting' braid group reps i.e. skip 1-d reps From 03b5fc98a3e27621079b3b619614ee77ddeaa1bf Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 20 May 2022 15:12:52 +0200 Subject: [PATCH 106/632] implement _an_element_ --- src/sage/databases/findstat.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/sage/databases/findstat.py b/src/sage/databases/findstat.py index 4a84ef1edb3..9b175aa4be3 100644 --- a/src/sage/databases/findstat.py +++ b/src/sage/databases/findstat.py @@ -2488,6 +2488,17 @@ def __iter__(self): for st in self._identifiers: yield FindStatStatistic(st) + def _an_element_(self): + """ + Return a FindStat statistic. + + EXAMPLES:: + + sage: findstat(domain="Permutations").an_element() # optional -- internet + St000001: The number of reduced words for a permutation. + """ + return next(iter(self)) + Element = FindStatStatistic @@ -3420,6 +3431,17 @@ def __iter__(self): for mp in self._identifiers: yield FindStatMap(mp) + def _an_element_(self): + """ + Return a FindStat map. + + EXAMPLES:: + + sage: findmap(domain="Dyck paths", codomain="Posets").an_element() # optional -- internet + Mp00232: parallelogram poset + """ + return next(iter(self)) + Element = FindStatMap From e3541086568776b0ea34e50e498f4b98147ebe5c Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Wed, 1 Jun 2022 02:17:37 +0800 Subject: [PATCH 107/632] implement .to_integer() as an inverse to .fetch_int() --- src/sage/rings/finite_rings/element_base.pyx | 58 ++++++++++++++++++- .../rings/finite_rings/finite_field_base.pyx | 36 +++++++----- 2 files changed, 78 insertions(+), 16 deletions(-) diff --git a/src/sage/rings/finite_rings/element_base.pyx b/src/sage/rings/finite_rings/element_base.pyx index d4dd6b22ee4..0b92450940e 100755 --- a/src/sage/rings/finite_rings/element_base.pyx +++ b/src/sage/rings/finite_rings/element_base.pyx @@ -19,6 +19,7 @@ AUTHORS: from sage.structure.element cimport Element from sage.structure.parent cimport Parent +from sage.rings.integer_ring import ZZ from sage.rings.integer import Integer def is_FiniteFieldElement(x): @@ -670,7 +671,6 @@ cdef class FinitePolyExtElement(FiniteRingElement): 1 """ if self.is_zero(): - from sage.rings.integer import Integer return Integer(1) return self.parent().characteristic() @@ -878,7 +878,6 @@ cdef class FinitePolyExtElement(FiniteRingElement): else: raise ValueError if extend: raise NotImplementedError - from sage.rings.integer import Integer n = Integer(n) return self._nth_root_common(n, all, algorithm, cunningham) @@ -983,6 +982,61 @@ cdef class FinitePolyExtElement(FiniteRingElement): return self.pth_power(k=k) + def to_integer(self, reverse=False): + r""" + Return an integer representation of this finite-field element + obtained by lifting its representative polynomial to `\ZZ` and + evaluating it at the characteristic `p`. + + If ``reverse`` is set to ``True`` (default: ``False``), + the list of coefficients is reversed prior to evaluation. + + Inverse of :meth:`sage.rings.finite_rings.finite_field_base.FiniteField.fetch_int`. + + EXAMPLES:: + + sage: F. = GF(7^5) + sage: F(5).to_integer() + 5 + sage: t.to_integer() + 7 + sage: (t^2).to_integer() + 49 + sage: (t^2+1).to_integer() + 50 + sage: (t^2+t+1).to_integer() + 57 + + :: + + sage: F. = GF(2^8) + sage: u = F.fetch_int(0xd1) + sage: bin(u.to_integer(False)) + '0b11010001' + sage: bin(u.to_integer(True)) + '0b10001011' + + TESTS:: + + sage: p = random_prime(2^99) + sage: k = randrange(2,10) + sage: F. = GF((p, k)) + sage: rev = bool(randrange(2)) + sage: u = F.random_element() + sage: 0 <= u.to_integer(rev) < F.cardinality() + True + sage: F.fetch_int(u.to_integer(rev), rev) == u + True + sage: n = randrange(F.cardinality()) + sage: F.fetch_int(n, rev).to_integer(rev) == n + True + """ + p = self.parent().characteristic() + f = self.polynomial().change_ring(ZZ) + if reverse: + f = f.reverse(self.parent().degree()-1) + return f(p) + cdef class Cache_base(SageObject): cpdef FinitePolyExtElement fetch_int(self, number): """ diff --git a/src/sage/rings/finite_rings/finite_field_base.pyx b/src/sage/rings/finite_rings/finite_field_base.pyx index 582f59971f6..a725dab2193 100644 --- a/src/sage/rings/finite_rings/finite_field_base.pyx +++ b/src/sage/rings/finite_rings/finite_field_base.pyx @@ -41,7 +41,7 @@ from sage.misc.cachefunc import cached_method from sage.misc.prandom import randrange from sage.rings.integer cimport Integer import sage.rings.abc -from sage.misc.superseded import deprecation_cython as deprecation +from sage.misc.superseded import deprecation_cython as deprecation, deprecated_function_alias # Copied from sage.misc.fast_methods, used in __hash__() below. cdef int SIZEOF_VOID_P_SHIFT = 8*sizeof(void *) - 4 @@ -406,15 +406,20 @@ cdef class FiniteField(Field): if lim == (-1): raise NotImplementedError("iterating over all elements of a large finite field is not supported") - def fetch_int(self, n): + def fetch_int(self, n, reverse=False): r""" - Return the element of ``self`` that equals `n` under the condition that - :meth:`gen()` is set to the characteristic of the finite field ``self``. + Return the finite-field element obtained by reinterpreting the base-`p` + expansion of `n` as a polynomial and evaluating it at the generator of + this finite field. + + If ``reverse`` is set to ``True`` (default: ``False``), + the list of digits is reversed prior to evaluation. + + Inverse of :meth:`sage.rings.finite_rings.element_base.FinitePolyExtElement.to_integer`. INPUT: - - `n` -- integer. Must not be negative, and must be less than the - cardinality of ``self``. + - `n` -- integer between `0` and the cardinality of this field minus `1`. EXAMPLES:: @@ -425,6 +430,8 @@ cdef class FiniteField(Field): 100*a^3 + 37*a^2 + 12*a + 6 sage: F.fetch_int(n) in F True + sage: F.fetch_int(n, reverse=True) + 6*a^3 + 12*a^2 + 37*a + 100 TESTS:: @@ -436,20 +443,21 @@ cdef class FiniteField(Field): sage: F.fetch_int(-5) Traceback (most recent call last): ... - TypeError: n must be between 0 and self.order() + ValueError: n must be between 0 and self.order() sage: F.fetch_int(F.cardinality()) Traceback (most recent call last): ... - TypeError: n must be between 0 and self.order() + ValueError: n must be between 0 and self.order() """ n = Integer(n) - if (n < 0) or (n >= self.order()): - raise TypeError("n must be between 0 and self.order()") - if n == 0: - return self.zero() - cdef list digs = n.digits(base=self.characteristic()) + if not 0 <= n < self.order(): + raise ValueError("n must be between 0 and self.order()") + cdef list digs = n.digits(self.characteristic()) g = self.gen() - return self.sum(self(d) * g**i for i, d in enumerate(digs) if d) + r = self.zero() + for d in (digs if reverse else digs[::-1]): + r = r * g + self(d) + return r def _is_valid_homomorphism_(self, codomain, im_gens, base_map=None): """ From 4c82bfe4547c62fa934d29f6ac5f7177f1ae6882 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Wed, 1 Jun 2022 11:51:12 +0800 Subject: [PATCH 108/632] rename .fetch_int() to .from_integer() --- src/sage/rings/finite_rings/element_base.pyx | 8 ++++---- .../rings/finite_rings/finite_field_base.pyx | 16 +++++++++------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/sage/rings/finite_rings/element_base.pyx b/src/sage/rings/finite_rings/element_base.pyx index 0b92450940e..4761183fa0c 100755 --- a/src/sage/rings/finite_rings/element_base.pyx +++ b/src/sage/rings/finite_rings/element_base.pyx @@ -991,7 +991,7 @@ cdef class FinitePolyExtElement(FiniteRingElement): If ``reverse`` is set to ``True`` (default: ``False``), the list of coefficients is reversed prior to evaluation. - Inverse of :meth:`sage.rings.finite_rings.finite_field_base.FiniteField.fetch_int`. + Inverse of :meth:`sage.rings.finite_rings.finite_field_base.FiniteField.from_integer`. EXAMPLES:: @@ -1010,7 +1010,7 @@ cdef class FinitePolyExtElement(FiniteRingElement): :: sage: F. = GF(2^8) - sage: u = F.fetch_int(0xd1) + sage: u = F.from_integer(0xd1) sage: bin(u.to_integer(False)) '0b11010001' sage: bin(u.to_integer(True)) @@ -1025,10 +1025,10 @@ cdef class FinitePolyExtElement(FiniteRingElement): sage: u = F.random_element() sage: 0 <= u.to_integer(rev) < F.cardinality() True - sage: F.fetch_int(u.to_integer(rev), rev) == u + sage: F.from_integer(u.to_integer(rev), rev) == u True sage: n = randrange(F.cardinality()) - sage: F.fetch_int(n, rev).to_integer(rev) == n + sage: F.from_integer(n, rev).to_integer(rev) == n True """ p = self.parent().characteristic() diff --git a/src/sage/rings/finite_rings/finite_field_base.pyx b/src/sage/rings/finite_rings/finite_field_base.pyx index a725dab2193..6c478c755d1 100644 --- a/src/sage/rings/finite_rings/finite_field_base.pyx +++ b/src/sage/rings/finite_rings/finite_field_base.pyx @@ -406,7 +406,7 @@ cdef class FiniteField(Field): if lim == (-1): raise NotImplementedError("iterating over all elements of a large finite field is not supported") - def fetch_int(self, n, reverse=False): + def from_integer(self, n, reverse=False): r""" Return the finite-field element obtained by reinterpreting the base-`p` expansion of `n` as a polynomial and evaluating it at the generator of @@ -426,25 +426,25 @@ cdef class FiniteField(Field): sage: p = 4091 sage: F = GF(p^4, 'a') sage: n = 100*p^3 + 37*p^2 + 12*p + 6 - sage: F.fetch_int(n) + sage: F.from_integer(n) 100*a^3 + 37*a^2 + 12*a + 6 - sage: F.fetch_int(n) in F + sage: F.from_integer(n) in F True - sage: F.fetch_int(n, reverse=True) + sage: F.from_integer(n, reverse=True) 6*a^3 + 12*a^2 + 37*a + 100 TESTS:: sage: F = GF(19^5) - sage: F.fetch_int(0) + sage: F.from_integer(0) 0 sage: _.parent() Finite Field in ... of size 19^5 - sage: F.fetch_int(-5) + sage: F.from_integer(-5) Traceback (most recent call last): ... ValueError: n must be between 0 and self.order() - sage: F.fetch_int(F.cardinality()) + sage: F.from_integer(F.cardinality()) Traceback (most recent call last): ... ValueError: n must be between 0 and self.order() @@ -459,6 +459,8 @@ cdef class FiniteField(Field): r = r * g + self(d) return r + fetch_int = deprecated_function_alias(33941, from_integer) + def _is_valid_homomorphism_(self, codomain, im_gens, base_map=None): """ Return ``True`` if the map from self to codomain sending From 2792fdcb973f3af993bcbabe2c7d0947feb913ba Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Tue, 28 Jun 2022 14:23:30 -0700 Subject: [PATCH 109/632] fixing doctest failures on f_matrix --- src/sage/algebras/fusion_rings/f_matrix.py | 160 +++++++++--------- .../algebras/fusion_rings/shm_managers.pyx | 3 +- 2 files changed, 83 insertions(+), 80 deletions(-) diff --git a/src/sage/algebras/fusion_rings/f_matrix.py b/src/sage/algebras/fusion_rings/f_matrix.py index 2911df25a2d..07f07eb7949 100644 --- a/src/sage/algebras/fusion_rings/f_matrix.py +++ b/src/sage/algebras/fusion_rings/f_matrix.py @@ -693,6 +693,7 @@ def get_poly_ring(self): """ return self._poly_ring + #TODO: this method is incredibly slow... improve by keeping track of the cyclotomic polynomials, NOT their roots in QQbar def get_non_cyclotomic_roots(self): r""" Return a list of roots that define the extension of the associated @@ -1764,7 +1765,8 @@ def _partition_eqns(self, eqns=None, verbose=True): print(graph.connected_components_sizes()) return partition - def _par_graph_gb(self, eqns=None, term_order="degrevlex", largest_comp=60, verbose=True): + #Try lowering larest_comp to 45... This might help w parallelism + def _par_graph_gb(self, eqns=None, term_order="degrevlex", largest_comp=45, verbose=True): r""" Compute a Groebner basis for a list of equations partitioned according to their corresponding graph. @@ -1870,6 +1872,7 @@ def _get_component_variety(self,var,eqns): ### Solution method ### ####################### + #TODO: this can probably be improved by constructing a set of defining polynomials and checking, one by one, if it's irreducible over the current field. If it is, we construct an extension. Perhaps it's best to go one by one here... def attempt_number_field_computation(self): r""" Based on the ``CartanType`` of ``self`` and data @@ -2123,8 +2126,9 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start sage: f.fmats_are_orthogonal() True - sage: f.fvars_are_real() - True + + # sage: f.fvars_are_real() + # True In any case, the F-symbols are obtained as elements of the associated :class:`FusionRing`'s @@ -2211,11 +2215,12 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start self._checkpoint(checkpoint,5,verbose=verbose) #Find numeric values for each F-symbol + self.shutdown_worker_pool() self._get_explicit_solution(verbose=verbose) #The calculation was successful, so we may delete checkpoints and shared resources self._chkpt_status = 7 self.clear_equations() - self.shutdown_worker_pool() + if checkpoint: remove("fmatrix_solver_checkpoint_"+self.get_fr_str()+".pickle") if save_results: @@ -2405,61 +2410,61 @@ def find_cyclotomic_solution(self, equations=None, algorithm="", verbose=True, o ### Verifications ### ##################### - def certify_pentagons(self,use_mp=True,verbose=False): - r""" - Obtain a certificate of satisfaction for the pentagon equations, - up to floating-point error. - - This method converts the computed F-symbols (available through - :meth:`get_fvars`) to native Python floats and then checks whether - the pentagon equations are satisfied using floating point arithmetic. - - When ``self.FR().basis()`` has many elements, verifying satisfaction - of the pentagon relations exactly using :meth:`get_defining_equations` - with ``option="pentagons"`` may take a long time. This method is - faster, but it cannot provide mathematical guarantees. - - EXAMPLES:: - - sage: f = FMatrix(FusionRing("C3", 1)) - sage: f.find_orthogonal_solution() # long time - Computing F-symbols for The Fusion Ring of Type C3 and level 1 with Integer Ring coefficients with 71 variables... - Set up 134 hex and orthogonality constraints... - Partitioned 134 equations into 17 components of size: - [12, 12, 6, 6, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1] - Elimination epoch completed... 10 eqns remain in ideal basis - Elimination epoch completed... 0 eqns remain in ideal basis - Hex elim step solved for 51 / 71 variables - Set up 121 reduced pentagons... - Elimination epoch completed... 18 eqns remain in ideal basis - Elimination epoch completed... 5 eqns remain in ideal basis - Pent elim step solved for 64 / 71 variables - Partitioned 5 equations into 1 components of size: - [4] - Elimination epoch completed... 0 eqns remain in ideal basis - Partitioned 6 equations into 6 components of size: - [1, 1, 1, 1, 1, 1] - Computing appropriate NumberField... - sage: f.certify_pentagons() # long time (~1.5s) - Success!!! Found valid F-symbols for The Fusion Ring of Type C3 and level 1 with Integer Ring coefficients - """ - fvars_copy = deepcopy(self._fvars) - self._fvars = {sextuple: float(rhs) for sextuple, rhs in self.get_fvars_in_alg_field().items()} - if use_mp: - pool = Pool() - else: - pool = None - n_proc = pool._processes if pool is not None else 1 - params = [(child_id,n_proc,verbose) for child_id in range(n_proc)] - pe = self._map_triv_reduce('pent_verify',params,worker_pool=pool,chunksize=1,mp_thresh=0) - if np.all(np.isclose(np.array(pe),0,atol=1e-7)): - print("Success!!! Found valid F-symbols for {}".format(self._FR)) - pe = None - else: - print("Ooops... something went wrong... These pentagons remain:") - print(pe) - self._fvars = fvars_copy - return pe + # def certify_pentagons(self,use_mp=True,verbose=False): + # r""" + # Obtain a certificate of satisfaction for the pentagon equations, + # up to floating-point error. + # + # This method converts the computed F-symbols (available through + # :meth:`get_fvars`) to native Python floats and then checks whether + # the pentagon equations are satisfied using floating point arithmetic. + # + # When ``self.FR().basis()`` has many elements, verifying satisfaction + # of the pentagon relations exactly using :meth:`get_defining_equations` + # with ``option="pentagons"`` may take a long time. This method is + # faster, but it cannot provide mathematical guarantees. + # + # EXAMPLES:: + # + # sage: f = FMatrix(FusionRing("C3", 1)) + # sage: f.find_orthogonal_solution() # long time + # Computing F-symbols for The Fusion Ring of Type C3 and level 1 with Integer Ring coefficients with 71 variables... + # Set up 134 hex and orthogonality constraints... + # Partitioned 134 equations into 17 components of size: + # [12, 12, 6, 6, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1] + # Elimination epoch completed... 10 eqns remain in ideal basis + # Elimination epoch completed... 0 eqns remain in ideal basis + # Hex elim step solved for 51 / 71 variables + # Set up 121 reduced pentagons... + # Elimination epoch completed... 18 eqns remain in ideal basis + # Elimination epoch completed... 5 eqns remain in ideal basis + # Pent elim step solved for 64 / 71 variables + # Partitioned 5 equations into 1 components of size: + # [4] + # Elimination epoch completed... 0 eqns remain in ideal basis + # Partitioned 6 equations into 6 components of size: + # [1, 1, 1, 1, 1, 1] + # Computing appropriate NumberField... + # sage: f.certify_pentagons() # long time (~1.5s) + # Success!!! Found valid F-symbols for The Fusion Ring of Type C3 and level 1 with Integer Ring coefficients + # """ + # fvars_copy = deepcopy(self._fvars) + # self._fvars = {sextuple: float(rhs) for sextuple, rhs in self.get_fvars_in_alg_field().items()} + # if use_mp: + # pool = Pool() + # else: + # pool = None + # n_proc = pool._processes if pool is not None else 1 + # params = [(child_id,n_proc,verbose) for child_id in range(n_proc)] + # pe = self._map_triv_reduce('pent_verify',params,worker_pool=pool,chunksize=1,mp_thresh=0) + # if np.all(np.isclose(np.array(pe),0,atol=1e-7)): + # print("Success!!! Found valid F-symbols for {}".format(self._FR)) + # pe = None + # else: + # print("Ooops... something went wrong... These pentagons remain:") + # print(pe) + # self._fvars = fvars_copy + # return pe def fmats_are_orthogonal(self): r""" @@ -2481,22 +2486,21 @@ def fmats_are_orthogonal(self): is_orthog.append(mat.T * mat == matrix.identity(mat.nrows())) return all(is_orthog) - def fvars_are_real(self): - r""" - Test whether all F-symbols are real. - - EXAMPLES:: - - sage: f = FMatrix(FusionRing("A1", 3)) - sage: f.find_orthogonal_solution(verbose=False) # long time - sage: f.fvars_are_real() # long time - True - """ - try: - for k, v in self._fvars.items(): - AA(self._qqbar_embedding(v)) - except ValueError: - print("the F-symbol {} (key {}) has a nonzero imaginary part!".format(v,k)) - return False - return True - + # def fvars_are_real(self): + # r""" + # Test whether all F-symbols are real. + # + # EXAMPLES:: + # + # sage: f = FMatrix(FusionRing("A1", 3)) + # sage: f.find_orthogonal_solution(verbose=False) # long time + # sage: f.fvars_are_real() # long time + # True + # """ + # try: + # for k, v in self._fvars.items(): + # AA(self._qqbar_embedding(v)) + # except ValueError: + # print("the F-symbol {} (key {}) has a nonzero imaginary part!".format(v,k)) + # return False + # return True diff --git a/src/sage/algebras/fusion_rings/shm_managers.pyx b/src/sage/algebras/fusion_rings/shm_managers.pyx index 416739f04d2..062a999fec4 100644 --- a/src/sage/algebras/fusion_rings/shm_managers.pyx +++ b/src/sage/algebras/fusion_rings/shm_managers.pyx @@ -354,7 +354,7 @@ def make_KSHandler(n_slots,field,init_data): return KSHandler(n_slots,field,init_data=init_data) cdef class FvarsHandler: - def __init__(self, n_slots, field,idx_to_sextuple, init_data={}, use_mp=0, + def __init__(self, n_slots, field, idx_to_sextuple, init_data={}, use_mp=0, pids_name=None, name=None, max_terms=20, n_bytes=32): """ Return a shared memory backed dict-like structure to manage the @@ -775,4 +775,3 @@ def make_FvarsHandler(n,field,idx_map,init_data): sage: f.shutdown_worker_pool() """ return FvarsHandler(n, field, idx_map, init_data=init_data) - From a4fac33a145f16bb77da2cc6599c5e4902ed8220 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Fri, 1 Jul 2022 16:57:16 -0500 Subject: [PATCH 110/632] cleaner version, fmatrix working for fr of rank at most 10 --- src/sage/algebras/fusion_rings/f_matrix.py | 129 +----------------- src/sage/algebras/fusion_rings/fusion_ring.py | 2 +- 2 files changed, 7 insertions(+), 124 deletions(-) diff --git a/src/sage/algebras/fusion_rings/f_matrix.py b/src/sage/algebras/fusion_rings/f_matrix.py index 07f07eb7949..db3d1c7b7f2 100644 --- a/src/sage/algebras/fusion_rings/f_matrix.py +++ b/src/sage/algebras/fusion_rings/f_matrix.py @@ -302,10 +302,6 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab self.mp_thresh = 10000 self.pool = None - #TESTS - # self.test_fvars = dict() - # self.test_ks = dict() - ####################### ### Class utilities ### ####################### @@ -595,7 +591,6 @@ def findcases(self,output=False): if output: v = self._poly_ring.gen(i) ret[(a,b,c,d,x,y)] = v - # idx_map[v] = (a, b, c, d, x, y) idx_map[i] = (a, b, c, d, x, y) i += 1 if output: @@ -1213,8 +1208,6 @@ def _restore_state(self, filename): self._chkpt_status = 7 return self._fvars, self._solved, self._ks, self.ideal_basis, self._chkpt_status = state - #TESTS: - # self.test_ks = {i: sq for i, sq in self._ks.items()} self._update_reduction_params() ################# @@ -1580,16 +1573,6 @@ def _update_reduction_params(self, eqns=None): if eqns is None: eqns = self.ideal_basis self._ks.update(eqns) - - #TESTS: - # for i in range(len(eqns)): - # eq_tup = eqns[i] - # if tup_fixes_sq(eq_tup): - # rhs = [-v for v in eq_tup[-1][1]] - # self.test_ks[variables(eq_tup)[0]] = rhs - # for i, sq in self._ks.items(): - # assert sq == self._field(self.test_ks[i]), "{}: OG sq {}, shared sq {}".format(i, sq, self.test_ks[i]) - for i, d in enumerate(get_variables_degrees(eqns,self._poly_ring.ngens())): self._var_degs[i] = d self._nnz = self._get_known_nonz() @@ -1631,8 +1614,6 @@ def _triangular_elim(self, eqns=None, verbose=True): _backward_subs(self) #Compute new reduction params and update eqns self._update_reduction_params(eqns=eqns) - # n = len(eqns) // worker_pool._processes ** 2 + 1 if worker_pool is not None else len(eqns) - # eqns = [eqns[i:i+n] for i in range(0,len(eqns),n)] if self.pool is not None and len(eqns) > self.mp_thresh: n = self.pool._processes chunks = [[] for i in range(n)] @@ -1765,7 +1746,6 @@ def _partition_eqns(self, eqns=None, verbose=True): print(graph.connected_components_sizes()) return partition - #Try lowering larest_comp to 45... This might help w parallelism def _par_graph_gb(self, eqns=None, term_order="degrevlex", largest_comp=45, verbose=True): r""" Compute a Groebner basis for a list of equations partitioned @@ -1803,22 +1783,9 @@ def _par_graph_gb(self, eqns=None, term_order="degrevlex", largest_comp=45, verb if eqns is None: eqns = self.ideal_basis small_comps = list() temp_eqns = list() - - # #For informative print statement - # nmax = self.largest_fmat_size() - # vars_by_size = list() - # for i in range(nmax+1): - # vars_by_size.append(self.get_fvars_by_size(i)) - for comp, comp_eqns in self._partition_eqns(eqns=eqns,verbose=verbose).items(): #Check if component is too large to process if len(comp) > largest_comp: - # fmat_size = 0 - # #For informative print statement - # for i in range(1,nmax+1): - # if set(comp).issubset(vars_by_size[i]): - # fmat_size = i - # print("Component of size {} with vars in F-mats of size {} is too large to find GB".format(len(comp),fmat_size)) temp_eqns.extend(comp_eqns) else: small_comps.append(comp_eqns) @@ -2127,9 +2094,6 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start sage: f.fmats_are_orthogonal() True - # sage: f.fvars_are_real() - # True - In any case, the F-symbols are obtained as elements of the associated :class:`FusionRing`'s :class:`Cyclotomic field`, @@ -2164,13 +2128,8 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start #Report progress if verbose: print("Set up {} hex and orthogonality constraints...".format(len(self.ideal_basis))) + #Unzip _fvars and link to shared_memory structure if using multiprocessing - #TESTS: - # for k, v in self._fvars.items(): - # if isinstance(v, tuple): - # self.test_fvars[k] = v - # else: - # self.test_fvars[k] = poly_to_tup(v) if use_mp and loads_shared_memory: self._fvars = self._shared_fvars else: @@ -2191,14 +2150,14 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start if self._chkpt_status < 3: #Set up pentagon equations in parallel self.get_defining_equations('pentagons',output=False) - self.ideal_basis.sort(key=poly_tup_sortkey) #Report progress if verbose: print("Set up {} reduced pentagons...".format(len(self.ideal_basis))) self._checkpoint(checkpoint,3,verbose=verbose) - #Simplify and eliminate variables if self._chkpt_status < 4: + #Simplify and eliminate variables + self.ideal_basis.sort(key=poly_tup_sortkey) self._triangular_elim(verbose=verbose) #Report progress if verbose: @@ -2213,14 +2172,13 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start self.ideal_basis.sort(key=poly_tup_sortkey) self._triangular_elim(verbose=verbose) self._checkpoint(checkpoint,5,verbose=verbose) + self.shutdown_worker_pool() #Find numeric values for each F-symbol - self.shutdown_worker_pool() self._get_explicit_solution(verbose=verbose) - #The calculation was successful, so we may delete checkpoints and shared resources + #The calculation was successful, so we may delete checkpoints self._chkpt_status = 7 self.clear_equations() - if checkpoint: remove("fmatrix_solver_checkpoint_"+self.get_fr_str()+".pickle") if save_results: @@ -2410,63 +2368,7 @@ def find_cyclotomic_solution(self, equations=None, algorithm="", verbose=True, o ### Verifications ### ##################### - # def certify_pentagons(self,use_mp=True,verbose=False): - # r""" - # Obtain a certificate of satisfaction for the pentagon equations, - # up to floating-point error. - # - # This method converts the computed F-symbols (available through - # :meth:`get_fvars`) to native Python floats and then checks whether - # the pentagon equations are satisfied using floating point arithmetic. - # - # When ``self.FR().basis()`` has many elements, verifying satisfaction - # of the pentagon relations exactly using :meth:`get_defining_equations` - # with ``option="pentagons"`` may take a long time. This method is - # faster, but it cannot provide mathematical guarantees. - # - # EXAMPLES:: - # - # sage: f = FMatrix(FusionRing("C3", 1)) - # sage: f.find_orthogonal_solution() # long time - # Computing F-symbols for The Fusion Ring of Type C3 and level 1 with Integer Ring coefficients with 71 variables... - # Set up 134 hex and orthogonality constraints... - # Partitioned 134 equations into 17 components of size: - # [12, 12, 6, 6, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1] - # Elimination epoch completed... 10 eqns remain in ideal basis - # Elimination epoch completed... 0 eqns remain in ideal basis - # Hex elim step solved for 51 / 71 variables - # Set up 121 reduced pentagons... - # Elimination epoch completed... 18 eqns remain in ideal basis - # Elimination epoch completed... 5 eqns remain in ideal basis - # Pent elim step solved for 64 / 71 variables - # Partitioned 5 equations into 1 components of size: - # [4] - # Elimination epoch completed... 0 eqns remain in ideal basis - # Partitioned 6 equations into 6 components of size: - # [1, 1, 1, 1, 1, 1] - # Computing appropriate NumberField... - # sage: f.certify_pentagons() # long time (~1.5s) - # Success!!! Found valid F-symbols for The Fusion Ring of Type C3 and level 1 with Integer Ring coefficients - # """ - # fvars_copy = deepcopy(self._fvars) - # self._fvars = {sextuple: float(rhs) for sextuple, rhs in self.get_fvars_in_alg_field().items()} - # if use_mp: - # pool = Pool() - # else: - # pool = None - # n_proc = pool._processes if pool is not None else 1 - # params = [(child_id,n_proc,verbose) for child_id in range(n_proc)] - # pe = self._map_triv_reduce('pent_verify',params,worker_pool=pool,chunksize=1,mp_thresh=0) - # if np.all(np.isclose(np.array(pe),0,atol=1e-7)): - # print("Success!!! Found valid F-symbols for {}".format(self._FR)) - # pe = None - # else: - # print("Ooops... something went wrong... These pentagons remain:") - # print(pe) - # self._fvars = fvars_copy - # return pe - - def fmats_are_orthogonal(self): +def fmats_are_orthogonal(self): r""" Verify that all F-matrices are orthogonal. @@ -2485,22 +2387,3 @@ def fmats_are_orthogonal(self): mat = self.fmatrix(a,b,c,d) is_orthog.append(mat.T * mat == matrix.identity(mat.nrows())) return all(is_orthog) - - # def fvars_are_real(self): - # r""" - # Test whether all F-symbols are real. - # - # EXAMPLES:: - # - # sage: f = FMatrix(FusionRing("A1", 3)) - # sage: f.find_orthogonal_solution(verbose=False) # long time - # sage: f.fvars_are_real() # long time - # True - # """ - # try: - # for k, v in self._fvars.items(): - # AA(self._qqbar_embedding(v)) - # except ValueError: - # print("the F-symbol {} (key {}) has a nonzero imaginary part!".format(v,k)) - # return False - # return True diff --git a/src/sage/algebras/fusion_rings/fusion_ring.py b/src/sage/algebras/fusion_rings/fusion_ring.py index 05b560e193a..75b28678c15 100644 --- a/src/sage/algebras/fusion_rings/fusion_ring.py +++ b/src/sage/algebras/fusion_rings/fusion_ring.py @@ -957,7 +957,7 @@ def r_matrix(self, i, j, k, base_coercion=True): True """ if self.Nk_ij(i, j, k) == 0: - return 0 + return self.field().zero() if i != j: ret = self.root_of_unity((k.twist(reduced=False) - i.twist(reduced=False) - j.twist(reduced=False)) / 2, base_coercion=False) else: From f13165f73ed6dfa338366ac4c179b682dbc771bf Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Fri, 1 Jul 2022 17:31:45 -0500 Subject: [PATCH 111/632] adding back fvars_real and certify_methods, not testing due to cypari issues in doctest framework --- src/sage/algebras/fusion_rings/f_matrix.py | 101 ++++++++++++++++++--- 1 file changed, 88 insertions(+), 13 deletions(-) diff --git a/src/sage/algebras/fusion_rings/f_matrix.py b/src/sage/algebras/fusion_rings/f_matrix.py index db3d1c7b7f2..49e814f20f9 100644 --- a/src/sage/algebras/fusion_rings/f_matrix.py +++ b/src/sage/algebras/fusion_rings/f_matrix.py @@ -2368,22 +2368,97 @@ def find_cyclotomic_solution(self, equations=None, algorithm="", verbose=True, o ### Verifications ### ##################### -def fmats_are_orthogonal(self): + def fmats_are_orthogonal(self): + r""" + Verify that all F-matrices are orthogonal. + + This method should always return ``True`` when called after running + :meth:`find_orthogonal_solution`. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("D4", 1)) + sage: f.find_orthogonal_solution(verbose=False) + sage: f.fmats_are_orthogonal() + True + """ + is_orthog = [] + for a,b,c,d in product(self._FR.basis(),repeat=4): + mat = self.fmatrix(a,b,c,d) + is_orthog.append(mat.T * mat == matrix.identity(mat.nrows())) + return all(is_orthog) + + def fvars_are_real(self): + r""" + Test whether all F-symbols are real. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("A1", 3)) + sage: f.find_orthogonal_solution(verbose=False) # long time + sage: f.fvars_are_real() # not tested (cypari issue in doctesting framework) + True + """ + try: + for k, v in self._fvars.items(): + AA(self._qqbar_embedding(v)) + except ValueError: + print("the F-symbol {} (key {}) has a nonzero imaginary part!".format(v,k)) + return False + return True + + def certify_pentagons(self,use_mp=True,verbose=False): r""" - Verify that all F-matrices are orthogonal. + Obtain a certificate of satisfaction for the pentagon equations, + up to floating-point error. + + This method converts the computed F-symbols (available through + :meth:`get_fvars`) to native Python floats and then checks whether + the pentagon equations are satisfied using floating point arithmetic. - This method should always return ``True`` when called after running - :meth:`find_orthogonal_solution`. + When ``self.FR().basis()`` has many elements, verifying satisfaction + of the pentagon relations exactly using :meth:`get_defining_equations` + with ``option="pentagons"`` may take a long time. This method is + faster, but it cannot provide mathematical guarantees. EXAMPLES:: - sage: f = FMatrix(FusionRing("D4", 1)) - sage: f.find_orthogonal_solution(verbose=False) - sage: f.fmats_are_orthogonal() - True + sage: f = FMatrix(FusionRing("C3", 1)) + sage: f.find_orthogonal_solution() # long time + Computing F-symbols for The Fusion Ring of Type C3 and level 1 with Integer Ring coefficients with 71 variables... + Set up 134 hex and orthogonality constraints... + Partitioned 134 equations into 17 components of size: + [12, 12, 6, 6, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1] + Elimination epoch completed... 10 eqns remain in ideal basis + Elimination epoch completed... 0 eqns remain in ideal basis + Hex elim step solved for 51 / 71 variables + Set up 121 reduced pentagons... + Elimination epoch completed... 18 eqns remain in ideal basis + Elimination epoch completed... 5 eqns remain in ideal basis + Pent elim step solved for 64 / 71 variables + Partitioned 5 equations into 1 components of size: + [4] + Elimination epoch completed... 0 eqns remain in ideal basis + Partitioned 6 equations into 6 components of size: + [1, 1, 1, 1, 1, 1] + Computing appropriate NumberField... + sage: f.certify_pentagons() # not tested (long time ~1.5s, cypari issue in doctesting framework) + Success!!! Found valid F-symbols for The Fusion Ring of Type C3 and level 1 with Integer Ring coefficients """ - is_orthog = [] - for a,b,c,d in product(self._FR.basis(),repeat=4): - mat = self.fmatrix(a,b,c,d) - is_orthog.append(mat.T * mat == matrix.identity(mat.nrows())) - return all(is_orthog) + fvars_copy = deepcopy(self._fvars) + self._fvars = {sextuple: float(rhs) for sextuple, rhs in self.get_fvars_in_alg_field().items()} + if use_mp: + pool = Pool() + else: + pool = None + n_proc = pool._processes if pool is not None else 1 + params = [(child_id,n_proc,verbose) for child_id in range(n_proc)] + pe = self._map_triv_reduce('pent_verify',params,worker_pool=pool,chunksize=1,mp_thresh=0) + if np.all(np.isclose(np.array(pe),0,atol=1e-7)): + print("Success!!! Found valid F-symbols for {}".format(self._FR)) + pe = None + else: + print("Ooops... something went wrong... These pentagons remain:") + print(pe) + self._fvars = fvars_copy + return pe From 8bb08fa7933cfc3d6e520a7233e55aaee9a5d1af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 3 Jul 2022 22:23:55 +0200 Subject: [PATCH 112/632] more usage of the category Rings().Commutative() --- src/sage/categories/schemes.py | 4 +- .../dynamics/arithmetic_dynamics/wehlerK3.py | 16 +++---- src/sage/geometry/polyhedron/parent.py | 3 +- src/sage/rings/ideal.py | 22 +++++++--- .../polynomial/polynomial_ring_constructor.py | 3 +- src/sage/rings/quotient_ring.py | 43 +++++++++---------- src/sage/schemes/affine/affine_point.py | 4 +- src/sage/schemes/generic/ambient_space.py | 13 +++--- src/sage/schemes/generic/homset.py | 15 ++++--- src/sage/schemes/generic/scheme.py | 11 +++-- src/sage/schemes/product_projective/space.py | 9 ++-- .../schemes/projective/projective_point.py | 9 ++-- .../schemes/projective/projective_space.py | 9 ++-- 13 files changed, 82 insertions(+), 79 deletions(-) diff --git a/src/sage/categories/schemes.py b/src/sage/categories/schemes.py index 73eef1d15d4..87dc5b9ef2f 100644 --- a/src/sage/categories/schemes.py +++ b/src/sage/categories/schemes.py @@ -138,11 +138,11 @@ def _call_(self, x): from sage.schemes.generic.morphism import is_SchemeMorphism if is_SchemeMorphism(x): return x - from sage.rings.ring import CommutativeRing + from sage.categories.commutative_rings import CommutativeRings from sage.schemes.generic.spec import Spec from sage.categories.map import Map from sage.categories.all import Rings - if isinstance(x, CommutativeRing): + if x in CommutativeRings(): return Spec(x) elif isinstance(x, Map) and x.category_for().is_subcategory(Rings()): # x is a morphism of Rings diff --git a/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py b/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py index 8f5f1f6a690..51cfd77460a 100644 --- a/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py +++ b/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py @@ -27,11 +27,11 @@ from sage.calculus.functions import jacobian from sage.categories.fields import Fields +from sage.categories.commutative_ring import CommutativeRings from sage.categories.number_fields import NumberFields from sage.misc.functional import sqrt from sage.misc.cachefunc import cached_method from sage.misc.mrange import xmrange -from sage.rings.all import CommutativeRing from sage.rings.finite_rings.finite_field_constructor import GF from sage.rings.finite_rings.finite_field_base import is_FiniteField from sage.rings.fraction_field import FractionField @@ -48,6 +48,7 @@ _NumberFields = NumberFields() _Fields = Fields() +_CommutativeRings = CommutativeRings() def WehlerK3Surface(polys): @@ -75,14 +76,12 @@ def WehlerK3Surface(polys): R = polys[0].parent().base_ring() if R in _Fields: - if is_FiniteField(R): + if R in _Fields.Finite(): return WehlerK3Surface_finite_field(polys) - else: - return WehlerK3Surface_field(polys) - elif isinstance(R, CommutativeRing): + return WehlerK3Surface_field(polys) + if R in _CommutativeRings: return WehlerK3Surface_ring(polys) - else: - raise TypeError("R (= %s) must be a commutative ring" % R) + raise TypeError("R (= %s) must be a commutative ring" % R) def random_WehlerK3Surface(PP): @@ -131,7 +130,7 @@ class WehlerK3Surface_ring(AlgebraicScheme_subscheme_product_projective): x*y*v^2 + z^2*u*w """ def __init__(self, polys): - if not isinstance(polys, (list,tuple)): + if not isinstance(polys, (list, tuple)): raise TypeError("polys must be a list or tuple of polynomials") R = polys[0].parent() vars = R.variable_names() @@ -2295,7 +2294,6 @@ def orbit_phi(self, P, N, **kwds): : 3992260691327218828582255586014718568398539828275296031491644987908/18550615454277582153932951051931712107449915856862264913424670784695 : 1 , -117756062505511/54767410965117 : -23134047983794359/37466994368025041 : 1)] """ - if not isinstance(N, (list, tuple)): N = [0, N] try: diff --git a/src/sage/geometry/polyhedron/parent.py b/src/sage/geometry/polyhedron/parent.py index 881c614238a..be12c5c5638 100644 --- a/src/sage/geometry/polyhedron/parent.py +++ b/src/sage/geometry/polyhedron/parent.py @@ -19,7 +19,6 @@ from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.rings.real_double import RDF -from sage.rings.ring import CommutativeRing from sage.categories.fields import Fields from sage.categories.rings import Rings from sage.categories.modules import Modules @@ -985,7 +984,7 @@ def _get_action_(self, other, op, self_is_left): extended_self._internal_coerce_map_from(self).__copy__()) return action - if op is operator.mul and isinstance(other, CommutativeRing): + if op is operator.mul and other in Rings().Commutative(): ring = self._coerce_base_ring(other) if ring is self.base_ring(): return ActedUponAction(other, self, not self_is_left) diff --git a/src/sage/rings/ideal.py b/src/sage/rings/ideal.py index 40653979145..c8982791348 100644 --- a/src/sage/rings/ideal.py +++ b/src/sage/rings/ideal.py @@ -11,7 +11,7 @@ If `R` is non-commutative, the former creates a left and the latter a right ideal, and ``R*[a,b,...]*R`` creates a two-sided ideal. """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2005 William Stein # # Distributed under the terms of the GNU General Public License (GPL) @@ -23,16 +23,23 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from types import GeneratorType -import sage.rings.ring +from sage.categories.rings import Rings +from sage.categories.fields import Fields from sage.structure.element import MonoidElement from sage.structure.richcmp import rich_to_bool, richcmp from sage.structure.sequence import Sequence + +# for efficiency +_Rings = Rings() +_Fields = Fields() + + def Ideal(*args, **kwds): r""" Create the ideal in ring with given generators. @@ -171,7 +178,7 @@ def Ideal(*args, **kwds): first = args[0] inferred_field = False - if not isinstance(first, sage.rings.ring.Ring): + if first not in _Rings: if isinstance(first, Ideal_generic) and len(args) == 1: R = first.ring() gens = first.gens() @@ -182,12 +189,12 @@ def Ideal(*args, **kwds): gens = args gens = Sequence(gens) R = gens.universe() - inferred_field = isinstance(R, sage.rings.ring.Field) + inferred_field = R in _Fields else: R = first gens = args[1:] - if not isinstance(R, sage.rings.ring.CommutativeRing): + if R not in _Rings.Commutative(): raise TypeError("R must be a commutative ring") I = R.ideal(*gens, **kwds) @@ -200,6 +207,7 @@ def Ideal(*args, **kwds): return I + def is_Ideal(x): r""" Return ``True`` if object is an ideal of a ring. diff --git a/src/sage/rings/polynomial/polynomial_ring_constructor.py b/src/sage/rings/polynomial/polynomial_ring_constructor.py index c07c93adb38..f10c66d8cf1 100644 --- a/src/sage/rings/polynomial/polynomial_ring_constructor.py +++ b/src/sage/rings/polynomial/polynomial_ring_constructor.py @@ -720,7 +720,7 @@ def _single_variate(base_ring, name, sparse=None, implementation=None, order=Non # Generic implementations if constructor is None: - if not isinstance(base_ring, ring.CommutativeRing): + if base_ring not in _CommutativeRings: constructor = polynomial_ring.PolynomialRing_general elif base_ring in _CompleteDiscreteValuationRings: constructor = polynomial_ring.PolynomialRing_cdvr @@ -928,7 +928,6 @@ def BooleanPolynomialRing_constructor(n=None, names=None, order="lex"): sage: x2 > x3 True """ - if isinstance(n, str): names = n n = -1 diff --git a/src/sage/rings/quotient_ring.py b/src/sage/rings/quotient_ring.py index 90f177f9694..5fbfd1c69d7 100644 --- a/src/sage/rings/quotient_ring.py +++ b/src/sage/rings/quotient_ring.py @@ -97,7 +97,7 @@ True """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2005 William Stein # # This program is free software: you can redistribute it and/or modify @@ -105,9 +105,7 @@ # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # http://www.gnu.org/licenses/ -#***************************************************************************** - - +# **************************************************************************** import sage.misc.latex as latex from . import ring, ideal, quotient_ring_element from sage.structure.category_object import normalize_names @@ -118,6 +116,10 @@ from sage.categories.commutative_rings import CommutativeRings +_Rings = Rings() +_CommRings = CommutativeRings() + + MPolynomialIdeal_quotient = None try: from sage.interfaces.singular import singular as singular_default, is_SingularElement @@ -276,19 +278,14 @@ def QuotientRing(R, I, names=None, **kwds): """ # 1. Not all rings inherit from the base class of rings. # 2. We want to support quotients of free algebras by homogeneous two-sided ideals. - #if not isinstance(R, commutative_ring.CommutativeRing): - # raise TypeError, "R must be a commutative ring." from sage.all import Integers, ZZ - if R not in Rings(): + if R not in _Rings: raise TypeError("R must be a ring.") - try: - is_commutative = R.is_commutative() - except (AttributeError, NotImplementedError): - is_commutative = False + is_commutative = R in _CommRings if names is None: try: names = tuple([x + 'bar' for x in R.variable_names()]) - except ValueError: # no names are assigned + except ValueError: # no names are assigned pass else: names = normalize_names(R.ngens(), names) @@ -320,10 +317,11 @@ def QuotientRing(R, I, names=None, **kwds): if S == ZZ: return Integers((I_lift+J).gen(), **kwds) return R.__class__(S, I_lift + J, names=names) - if isinstance(R, ring.CommutativeRing): + if R in _CommRings: return QuotientRing_generic(R, I, names, **kwds) return QuotientRing_nc(R, I, names, **kwds) + def is_QuotientRing(x): """ Tests whether or not ``x`` inherits from :class:`QuotientRing_nc`. @@ -348,14 +346,12 @@ def is_QuotientRing(x): True sage: is_QuotientRing(F) False - """ return isinstance(x, QuotientRing_nc) -_Rings = Rings() -_RingsQuotients = Rings().Quotients() -_CommutativeRingsQuotients = CommutativeRings().Quotients() +_RingsQuotients = _Rings.Quotients() +_CommutativeRingsQuotients = _CommRings.Quotients() from sage.structure.category_object import check_default_category @@ -511,8 +507,9 @@ def construction(self): names = self.cover_ring().variable_names() except ValueError: names = None - if self in CommutativeRings(): - return QuotientFunctor(self.__I, names=names, domain=CommutativeRings(), codomain=CommutativeRings(), as_field=isinstance(self, Field)), self.__R + if self in _CommRings: + return QuotientFunctor(self.__I, names=names, domain=_CommRings, + codomain=_CommRings, as_field=isinstance(self)), self.__R else: return QuotientFunctor(self.__I, names=names, as_field=isinstance(self, Field)), self.__R @@ -1289,6 +1286,7 @@ def term_order(self): """ return self.__R.term_order() + class QuotientRing_generic(QuotientRing_nc, ring.CommutativeRing): r""" Creates a quotient ring of a *commutative* ring `R` by the ideal `I`. @@ -1315,13 +1313,14 @@ def __init__(self, R, I, names, category=None): TESTS:: - sage: isinstance(ZZ.quo(2), sage.rings.ring.CommutativeRing) # indirect doctest + sage: ZZ.quo(2) in Rings().Commutative() # indirect doctest True """ - if not isinstance(R, ring.CommutativeRing): + if R not in _CommRings: raise TypeError("This class is for quotients of commutative rings only.\n For non-commutative rings, use ") if not self._is_category_initialized(): - category = check_default_category(_CommutativeRingsQuotients,category) + category = check_default_category(_CommutativeRingsQuotients, + category) QuotientRing_nc.__init__(self, R, I, names, category=category) def _macaulay2_init_(self, macaulay2=None): diff --git a/src/sage/schemes/affine/affine_point.py b/src/sage/schemes/affine/affine_point.py index bf86bc225c6..fb902a5596d 100644 --- a/src/sage/schemes/affine/affine_point.py +++ b/src/sage/schemes/affine/affine_point.py @@ -76,12 +76,12 @@ def __init__(self, X, v, check=True): """ SchemeMorphism.__init__(self, X) if check: - from sage.rings.ring import CommutativeRing + from sage.categories.commutative_rings import CommutativeRings if is_SchemeMorphism(v): v = list(v) else: try: - if isinstance(v.parent(), CommutativeRing): + if v.parent() in CommutativeRings(): v = [v] except AttributeError: pass diff --git a/src/sage/schemes/generic/ambient_space.py b/src/sage/schemes/generic/ambient_space.py index f6b5f91c686..70ed608bdcc 100644 --- a/src/sage/schemes/generic/ambient_space.py +++ b/src/sage/schemes/generic/ambient_space.py @@ -1,8 +1,7 @@ """ Ambient spaces """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2006 William Stein # # This program is free software: you can redistribute it and/or modify @@ -10,9 +9,10 @@ # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # http://www.gnu.org/licenses/ -#***************************************************************************** +# **************************************************************************** -from sage.rings.all import Integer, ZZ, CommutativeRing +from sage.rings.all import Integer, ZZ +from sage.categories.commutative_rings import CommutativeRings from sage.schemes.generic.scheme import Scheme @@ -39,7 +39,6 @@ class AmbientSpace(Scheme): INPUT: - - ``n`` - dimension - ``R`` - ring @@ -52,7 +51,7 @@ def __init__(self, n, R=ZZ): sage: A = AmbientSpace(5, ZZ) sage: TestSuite(A).run() # not tested (abstract scheme with no elements?) """ - if not isinstance(R, CommutativeRing): + if R not in CommutativeRings(): raise TypeError("R (={}) must be a commutative ring".format(R)) if n < 0: raise ValueError("n (={}) must be nonnegative".format(n)) @@ -233,7 +232,7 @@ def base_extend(self, R): ValueError: no natural map from the base ring (=Rational Field) to R (=Finite Field of size 5)! """ - if isinstance(R, CommutativeRing): + if R in CommutativeRings(): if self.base_ring() == R: return self if not R.has_coerce_map_from(self.base_ring()): diff --git a/src/sage/schemes/generic/homset.py b/src/sage/schemes/generic/homset.py index a1aa946614e..acc7d88bc15 100644 --- a/src/sage/schemes/generic/homset.py +++ b/src/sage/schemes/generic/homset.py @@ -41,7 +41,7 @@ from sage.structure.parent import Set_generic from sage.rings.integer_ring import ZZ -from sage.rings.ring import CommutativeRing +from sage.categories.commutative_rings import CommutativeRings from sage.schemes.generic.scheme import AffineScheme, is_AffineScheme from sage.schemes.generic.morphism import ( @@ -149,14 +149,15 @@ def create_key_and_extra_args(self, X, Y, category=None, base=ZZ, 'base_ring': Integer Ring, 'check': False} """ - if isinstance(X, CommutativeRing): + _CommRings = CommutativeRings() + if X in _CommRings: X = AffineScheme(X) - if isinstance(Y, CommutativeRing): + if Y in _CommRings: Y = AffineScheme(Y) if is_AffineScheme(base): base_spec = base base_ring = base.coordinate_ring() - elif isinstance(base, CommutativeRing): + elif base in _CommRings: base_spec = AffineScheme(base) base_ring = base else: @@ -556,9 +557,9 @@ def _coerce_map_from_(self, other): True """ target = self.codomain() - #ring elements can be coerced to a space if we're affine dimension 1 - #and the base rings are coercible - if isinstance(other, CommutativeRing): + # ring elements can be coerced to a space if we're affine dimension 1 + # and the base rings are coercible + if other in CommutativeRings(): try: from sage.schemes.affine.affine_space import is_AffineSpace if is_AffineSpace(target.ambient_space())\ diff --git a/src/sage/schemes/generic/scheme.py b/src/sage/schemes/generic/scheme.py index a0fa83ce65d..27dafd7bbea 100644 --- a/src/sage/schemes/generic/scheme.py +++ b/src/sage/schemes/generic/scheme.py @@ -21,12 +21,13 @@ from sage.structure.parent import Parent from sage.misc.cachefunc import cached_method -from sage.rings.all import (ZZ, CommutativeRing) +from sage.rings.all import ZZ +from sage.categories.commutative_rings import CommutativeRings from sage.rings.ideal import is_Ideal from sage.structure.unique_representation import UniqueRepresentation - from sage.schemes.generic.point import SchemeTopologicalPoint_prime_ideal + def is_Scheme(x): """ Test whether ``x`` is a scheme. @@ -108,7 +109,7 @@ def __init__(self, X=None, category=None): self._base_scheme = X elif is_SchemeMorphism(X): self._base_morphism = X - elif isinstance(X, CommutativeRing): + elif X in CommutativeRings(): self._base_ring = X elif isinstance(X, Map) and X.category_for().is_subcategory(Rings()): # X is a morphism of Rings @@ -252,7 +253,7 @@ def __call__(self, *args): if len(args) == 1: from sage.schemes.generic.morphism import SchemeMorphism_point S = args[0] - if isinstance(S, CommutativeRing): + if S in CommutativeRings(): return self.point_homset(S) elif is_Scheme(S): return S.Hom(self) @@ -865,7 +866,6 @@ def __init__(self, R, S=None, category=None): sage: type(S) """ - from sage.categories.commutative_rings import CommutativeRings if R not in CommutativeRings(): raise TypeError("R (={}) must be a commutative ring".format(R)) self.__R = R @@ -1141,7 +1141,6 @@ def base_extend(self, R): sage: Spec(ZZ['x']).base_extend(Spec(QQ)) Spectrum of Univariate Polynomial Ring in x over Rational Field """ - from sage.categories.commutative_rings import CommutativeRings if R in CommutativeRings(): return AffineScheme(self.coordinate_ring().base_extend(R), self.base_ring()) if not self.base_scheme() == R.base_scheme(): diff --git a/src/sage/schemes/product_projective/space.py b/src/sage/schemes/product_projective/space.py index a7eb86ce43f..b694ec6087b 100644 --- a/src/sage/schemes/product_projective/space.py +++ b/src/sage/schemes/product_projective/space.py @@ -42,9 +42,10 @@ from sage.misc.cachefunc import cached_method from sage.misc.misc_c import prod -from sage.rings.all import (PolynomialRing, QQ, Integer, CommutativeRing) +from sage.rings.all import (PolynomialRing, QQ, Integer) from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.categories.fields import Fields +from sage.categories.commutative_rings import CommutativeRings from sage.rings.polynomial.polydict import ETuple from sage.schemes.generic.algebraic_scheme import AlgebraicScheme_subscheme from sage.schemes.generic.ambient_space import AmbientSpace @@ -141,9 +142,9 @@ def ProductProjectiveSpaces(n, R=None, names='x'): X = ProductProjectiveSpaces_ring(N, R, names) X._components = n else: - if not isinstance(n,(list,tuple)): + if not isinstance(n,(list, tuple)): raise ValueError("need list or tuple of dimensions") - if not isinstance(R, CommutativeRing): + if R not in CommutativeRings(): raise ValueError("must be a commutative ring") from sage.structure.category_object import normalize_names n_vars = sum(d+1 for d in n) @@ -226,7 +227,7 @@ def __init__(self, N, R = QQ, names = None): """ assert isinstance(N, (tuple, list)) N = [Integer(n) for n in N] - assert isinstance(R, CommutativeRing) + assert R in CommutativeRings() if len(N) < 2: raise ValueError("must be at least two components for a product") AmbientSpace.__init__(self, sum(N), R) diff --git a/src/sage/schemes/projective/projective_point.py b/src/sage/schemes/projective/projective_point.py index 95547692e86..99cc0bff6d9 100644 --- a/src/sage/schemes/projective/projective_point.py +++ b/src/sage/schemes/projective/projective_point.py @@ -163,14 +163,13 @@ def __init__(self, X, v, check=True): SchemeMorphism.__init__(self, X) if check: from sage.schemes.elliptic_curves.ell_point import EllipticCurvePoint_field - from sage.rings.ring import CommutativeRing + from sage.categories.commutative_rings import CommutativeRings d = X.codomain().ambient_space().ngens() - if is_SchemeMorphism(v) or isinstance(v, EllipticCurvePoint_field): v = list(v) else: try: - if isinstance(v.parent(), CommutativeRing): + if v.parent() in CommutativeRings(): v = [v] except AttributeError: pass @@ -1104,13 +1103,13 @@ def __init__(self, X, v, check=True): SchemeMorphism.__init__(self, X) if check: from sage.schemes.elliptic_curves.ell_point import EllipticCurvePoint_field - from sage.rings.ring import CommutativeRing + from sage.categories.commutative_rings import CommutativeRings d = X.codomain().ambient_space().ngens() if is_SchemeMorphism(v) or isinstance(v, EllipticCurvePoint_field): v = list(v) else: try: - if isinstance(v.parent(), CommutativeRing): + if v.parent() in CommutativeRings(): v = [v] except AttributeError: pass diff --git a/src/sage/schemes/projective/projective_space.py b/src/sage/schemes/projective/projective_space.py index 1bd4540983e..76c4eb37954 100644 --- a/src/sage/schemes/projective/projective_space.py +++ b/src/sage/schemes/projective/projective_space.py @@ -84,13 +84,13 @@ from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ -from sage.rings.ring import CommutativeRing from sage.rings.rational_field import is_RationalField from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing from sage.rings.polynomial.polynomial_ring import is_PolynomialRing from sage.rings.finite_rings.finite_field_constructor import is_FiniteField from sage.categories.fields import Fields +from sage.categories.rings import Rings from sage.categories.number_fields import NumberFields from sage.categories.homset import Hom from sage.categories.map import Map @@ -122,6 +122,8 @@ # for better efficiency _Fields = Fields() +_Rings = Rings() +_CommRings = _Rings.Commutative() def is_ProjectiveSpace(x): @@ -259,10 +261,9 @@ def ProjectiveSpace(n, R=None, names=None): return ProjectiveSpace_rational_field(n, R, names) else: return ProjectiveSpace_field(n, R, names) - elif isinstance(R, CommutativeRing): + elif R in _CommRings: return ProjectiveSpace_ring(n, R, names) - else: - raise TypeError("R (=%s) must be a commutative ring" % R) + raise TypeError("R (=%s) must be a commutative ring" % R) class ProjectiveSpace_ring(UniqueRepresentation, AmbientSpace): From a51e01723f1eb0158641f0c043a68505c4236de1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 4 Jul 2022 08:48:38 +0200 Subject: [PATCH 113/632] fix typo --- src/sage/rings/quotient_ring.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/quotient_ring.py b/src/sage/rings/quotient_ring.py index 5fbfd1c69d7..a67054d657d 100644 --- a/src/sage/rings/quotient_ring.py +++ b/src/sage/rings/quotient_ring.py @@ -509,9 +509,11 @@ def construction(self): names = None if self in _CommRings: return QuotientFunctor(self.__I, names=names, domain=_CommRings, - codomain=_CommRings, as_field=isinstance(self)), self.__R + codomain=_CommRings, + as_field=isinstance(self, Field)), self.__R else: - return QuotientFunctor(self.__I, names=names, as_field=isinstance(self, Field)), self.__R + return QuotientFunctor(self.__I, names=names, + as_field=isinstance(self, Field)), self.__R def _repr_(self): """ From 00bd035e05f647057c05cb268a6d133aa69a7c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 4 Jul 2022 09:45:37 +0200 Subject: [PATCH 114/632] fix Wehler K3 --- src/sage/dynamics/arithmetic_dynamics/wehlerK3.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py b/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py index 51cfd77460a..b3397242e24 100644 --- a/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py +++ b/src/sage/dynamics/arithmetic_dynamics/wehlerK3.py @@ -27,13 +27,12 @@ from sage.calculus.functions import jacobian from sage.categories.fields import Fields -from sage.categories.commutative_ring import CommutativeRings +from sage.categories.commutative_rings import CommutativeRings from sage.categories.number_fields import NumberFields from sage.misc.functional import sqrt from sage.misc.cachefunc import cached_method from sage.misc.mrange import xmrange from sage.rings.finite_rings.finite_field_constructor import GF -from sage.rings.finite_rings.finite_field_base import is_FiniteField from sage.rings.fraction_field import FractionField from sage.rings.integer_ring import ZZ from sage.rings.number_field.order import is_NumberFieldOrder From 281ed9282d0efa97c7e76ac721427da313360fb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 4 Jul 2022 14:08:09 +0200 Subject: [PATCH 115/632] fix some failures --- .../polynomial/polynomial_ring_constructor.py | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_ring_constructor.py b/src/sage/rings/polynomial/polynomial_ring_constructor.py index f10c66d8cf1..00735d24bc2 100644 --- a/src/sage/rings/polynomial/polynomial_ring_constructor.py +++ b/src/sage/rings/polynomial/polynomial_ring_constructor.py @@ -11,37 +11,36 @@ rings but rather quotients of them (see module :mod:`sage.rings.polynomial.pbori` for more details). """ - -#***************************************************************************** +# **************************************************************************** # 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.category_object import normalize_names import sage.rings.ring as ring import sage.rings.padics.padic_base_leaves as padic_base_leaves import sage.rings.abc from sage.rings.integer import Integer -from sage.rings.finite_rings.finite_field_base import is_FiniteField from sage.misc.cachefunc import weak_cached_function +import sage.misc.weak_dict from sage.categories.fields import Fields -_Fields = Fields() from sage.categories.commutative_rings import CommutativeRings -_CommutativeRings = CommutativeRings() +from sage.categories.domains import Domains from sage.categories.complete_discrete_valuation import CompleteDiscreteValuationRings, CompleteDiscreteValuationFields + +_CommutativeRings = CommutativeRings() +_Fields = Fields() +_Domains = Domains() _CompleteDiscreteValuationRings = CompleteDiscreteValuationRings() _CompleteDiscreteValuationFields = CompleteDiscreteValuationFields() -import sage.misc.weak_dict _cache = sage.misc.weak_dict.WeakValueDictionary() @@ -659,6 +658,7 @@ def unpickle_PolynomialRing(base_ring, arg1=None, arg2=None, sparse=False): args = [arg for arg in (arg1, arg2) if arg is not None] return PolynomialRing(base_ring, *args, sparse=sparse) + from sage.misc.persist import register_unpickle_override register_unpickle_override('sage.rings.polynomial.polynomial_ring_constructor', 'PolynomialRing', unpickle_PolynomialRing) @@ -700,7 +700,7 @@ def _single_variate(base_ring, name, sparse=None, implementation=None, order=Non specialized = polynomial_ring.PolynomialRing_dense_mod_p elif n > 1: # Specialized code breaks for n == 1 specialized = polynomial_ring.PolynomialRing_dense_mod_n - elif is_FiniteField(base_ring): + elif base_ring in _Fields.Finite(): specialized = polynomial_ring.PolynomialRing_dense_finite_field elif isinstance(base_ring, padic_base_leaves.pAdicFieldCappedRelative): specialized = polynomial_ring.PolynomialRing_dense_padic_field_capped_relative @@ -726,9 +726,9 @@ def _single_variate(base_ring, name, sparse=None, implementation=None, order=Non constructor = polynomial_ring.PolynomialRing_cdvr elif base_ring in _CompleteDiscreteValuationFields: constructor = polynomial_ring.PolynomialRing_cdvf - elif base_ring.is_field(proof=False): + elif base_ring in _Fields: constructor = polynomial_ring.PolynomialRing_field - elif base_ring.is_integral_domain(proof=False): + elif base_ring in _Domains: constructor = polynomial_ring.PolynomialRing_integral_domain else: constructor = polynomial_ring.PolynomialRing_commutative @@ -737,8 +737,8 @@ def _single_variate(base_ring, name, sparse=None, implementation=None, order=Non # Only use names which are not supported by the specialized class. if specialized is not None: - implementation_names = [n for n in implementation_names if - specialized._implementation_names_impl(n, base_ring, sparse) is NotImplemented] + implementation_names = [n for n in implementation_names + if specialized._implementation_names_impl(n, base_ring, sparse) is NotImplemented] if implementation is not None: kwds["implementation"] = implementation From f329d7ab2f9deb962567992f43e2d1c1a11933d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 4 Jul 2022 16:37:19 +0200 Subject: [PATCH 116/632] fix broken line --- src/sage/rings/polynomial/polynomial_ring_constructor.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/polynomial_ring_constructor.py b/src/sage/rings/polynomial/polynomial_ring_constructor.py index 00735d24bc2..e9a9365bda0 100644 --- a/src/sage/rings/polynomial/polynomial_ring_constructor.py +++ b/src/sage/rings/polynomial/polynomial_ring_constructor.py @@ -26,6 +26,7 @@ import sage.rings.abc from sage.rings.integer import Integer +from sage.rings.finite_rings.finite_field_base import is_FiniteField from sage.misc.cachefunc import weak_cached_function import sage.misc.weak_dict @@ -700,7 +701,7 @@ def _single_variate(base_ring, name, sparse=None, implementation=None, order=Non specialized = polynomial_ring.PolynomialRing_dense_mod_p elif n > 1: # Specialized code breaks for n == 1 specialized = polynomial_ring.PolynomialRing_dense_mod_n - elif base_ring in _Fields.Finite(): + elif is_FiniteField(base_ring): specialized = polynomial_ring.PolynomialRing_dense_finite_field elif isinstance(base_ring, padic_base_leaves.pAdicFieldCappedRelative): specialized = polynomial_ring.PolynomialRing_dense_padic_field_capped_relative From 78bd4ab601d87b8c70da2834ffb546857136c79b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 4 Jul 2022 16:41:51 +0200 Subject: [PATCH 117/632] fix category for complex_double field --- src/sage/rings/complex_double.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/complex_double.pyx b/src/sage/rings/complex_double.pyx index 5cd5cb1910f..a1df8289b79 100644 --- a/src/sage/rings/complex_double.pyx +++ b/src/sage/rings/complex_double.pyx @@ -174,7 +174,7 @@ cdef class ComplexDoubleField_class(sage.rings.abc.ComplexDoubleField): (-1.0, -1.0 + 1.2246...e-16*I, False) """ from sage.categories.fields import Fields - ParentWithGens.__init__(self, self, ('I',), normalize=False, category=Fields().Metric().Complete()) + ParentWithGens.__init__(self, self, ('I',), normalize=False, category=Fields().Infinite().Metric().Complete()) self._populate_coercion_lists_() def __reduce__(self): From d1507e466cae8f23da7024c722d55b1c64e2c2d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 4 Jul 2022 21:50:27 +0200 Subject: [PATCH 118/632] undo change in homset --- src/sage/schemes/generic/homset.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/generic/homset.py b/src/sage/schemes/generic/homset.py index acc7d88bc15..e8a4eb03af7 100644 --- a/src/sage/schemes/generic/homset.py +++ b/src/sage/schemes/generic/homset.py @@ -41,6 +41,7 @@ from sage.structure.parent import Set_generic from sage.rings.integer_ring import ZZ +from sage.rings.ring import CommutativeRing from sage.categories.commutative_rings import CommutativeRings from sage.schemes.generic.scheme import AffineScheme, is_AffineScheme @@ -559,7 +560,7 @@ def _coerce_map_from_(self, other): target = self.codomain() # ring elements can be coerced to a space if we're affine dimension 1 # and the base rings are coercible - if other in CommutativeRings(): + if isinstance(other, CommutativeRing): try: from sage.schemes.affine.affine_space import is_AffineSpace if is_AffineSpace(target.ambient_space())\ @@ -567,7 +568,7 @@ def _coerce_map_from_(self, other): return target.base_ring().has_coerce_map_from(other) else: return False - except AttributeError: #no .ambient_space + except AttributeError: # no .ambient_space return False elif isinstance(other, SchemeHomset_points): #we are converting between scheme points From 26184075c7230dccb6afbf84f04aa3f49644b35c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 5 Jul 2022 08:24:15 +0200 Subject: [PATCH 119/632] undo change in scheme points --- src/sage/schemes/projective/projective_point.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/projective/projective_point.py b/src/sage/schemes/projective/projective_point.py index 99cc0bff6d9..0217a943d09 100644 --- a/src/sage/schemes/projective/projective_point.py +++ b/src/sage/schemes/projective/projective_point.py @@ -163,13 +163,13 @@ def __init__(self, X, v, check=True): SchemeMorphism.__init__(self, X) if check: from sage.schemes.elliptic_curves.ell_point import EllipticCurvePoint_field - from sage.categories.commutative_rings import CommutativeRings + from sage.rings.ring import CommutativeRing d = X.codomain().ambient_space().ngens() if is_SchemeMorphism(v) or isinstance(v, EllipticCurvePoint_field): v = list(v) else: try: - if v.parent() in CommutativeRings(): + if isinstance(v.parent(), CommutativeRing): v = [v] except AttributeError: pass @@ -1103,13 +1103,13 @@ def __init__(self, X, v, check=True): SchemeMorphism.__init__(self, X) if check: from sage.schemes.elliptic_curves.ell_point import EllipticCurvePoint_field - from sage.categories.commutative_rings import CommutativeRings + from sage.rings.ring import CommutativeRing d = X.codomain().ambient_space().ngens() if is_SchemeMorphism(v) or isinstance(v, EllipticCurvePoint_field): v = list(v) else: try: - if v.parent() in CommutativeRings(): + if isinstance(v.parent(), CommutativeRing): v = [v] except AttributeError: pass From 31b72817869d2ac418686b00f9c251b8758abd11 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 2 Jul 2022 16:53:14 -0700 Subject: [PATCH 120/632] build/pkgs/scipy: Update to 1.9.0rc1 --- build/pkgs/scipy/checksums.ini | 6 +++--- build/pkgs/scipy/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/scipy/checksums.ini b/build/pkgs/scipy/checksums.ini index 25b470f4624..11730409e42 100644 --- a/build/pkgs/scipy/checksums.ini +++ b/build/pkgs/scipy/checksums.ini @@ -1,5 +1,5 @@ tarball=scipy-VERSION.tar.gz -sha1=6dfee9fe5f021409b4d294b0a7d9da05b810d207 -md5=df5ce79288fc457238aeef18e8f70dfc -cksum=3909760197 +sha1=f59a225dc4e41f19e0c69bcb4722163f08eb7ce1 +md5=3fe21eea99de291a468c64d218dae0a4 +cksum=2309164171 upstream_url=https://pypi.io/packages/source/s/scipy/scipy-VERSION.tar.gz diff --git a/build/pkgs/scipy/package-version.txt b/build/pkgs/scipy/package-version.txt index a8fdfda1c78..6ed8c32eb58 100644 --- a/build/pkgs/scipy/package-version.txt +++ b/build/pkgs/scipy/package-version.txt @@ -1 +1 @@ -1.8.1 +1.9.0rc1 From dfbf360eb04ef3fc07ace91ace01ef2b52bd766c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 2 Jul 2022 17:02:31 -0700 Subject: [PATCH 121/632] build/pkgs/scipy/patches/boost_math_tools_config.patch: Remove (upstreamed) --- .../pkgs/scipy/patches/boost_math_tools_config.patch | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 build/pkgs/scipy/patches/boost_math_tools_config.patch diff --git a/build/pkgs/scipy/patches/boost_math_tools_config.patch b/build/pkgs/scipy/patches/boost_math_tools_config.patch deleted file mode 100644 index 6c13393c407..00000000000 --- a/build/pkgs/scipy/patches/boost_math_tools_config.patch +++ /dev/null @@ -1,11 +0,0 @@ ---- a/scipy/_lib/boost/boost/math/tools/config.hpp 2021-11-01 02:28:55 UTC -+++ b/scipy/_lib/boost/boost/math/tools/config.hpp -@@ -28,7 +28,7 @@ - - #include - --#if (defined(__CYGWIN__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__EMSCRIPTEN__)\ -+#if (defined(__NetBSD__) || defined(__EMSCRIPTEN__)\ - || (defined(__hppa) && !defined(__OpenBSD__)) || (defined(__NO_LONG_DOUBLE_MATH) && (DBL_MANT_DIG != LDBL_MANT_DIG))) \ - && !defined(BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS) - # define BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS From 9d384841b03355d4bc83238cf8a13bb98a589124 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 2 Jul 2022 17:06:15 -0700 Subject: [PATCH 122/632] build/pkgs/meson_python: New, scipy build dep --- build/pkgs/meson_python/SPKG.rst | 16 ++++++++++++++++ build/pkgs/meson_python/checksums.ini | 5 +++++ build/pkgs/meson_python/dependencies | 4 ++++ build/pkgs/meson_python/install-requires.txt | 1 + build/pkgs/meson_python/package-version.txt | 1 + build/pkgs/meson_python/spkg-install.in | 2 ++ build/pkgs/meson_python/type | 1 + build/pkgs/scipy/dependencies | 2 +- 8 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 build/pkgs/meson_python/SPKG.rst create mode 100644 build/pkgs/meson_python/checksums.ini create mode 100644 build/pkgs/meson_python/dependencies create mode 100644 build/pkgs/meson_python/install-requires.txt create mode 100644 build/pkgs/meson_python/package-version.txt create mode 100644 build/pkgs/meson_python/spkg-install.in create mode 100644 build/pkgs/meson_python/type diff --git a/build/pkgs/meson_python/SPKG.rst b/build/pkgs/meson_python/SPKG.rst new file mode 100644 index 00000000000..d467894289c --- /dev/null +++ b/build/pkgs/meson_python/SPKG.rst @@ -0,0 +1,16 @@ +meson_python: Meson Python build backend (PEP 517) +================================================== + +Description +----------- + +Meson Python build backend (PEP 517) + +License +------- + +Upstream Contact +---------------- + +https://pypi.org/project/meson-python/ + diff --git a/build/pkgs/meson_python/checksums.ini b/build/pkgs/meson_python/checksums.ini new file mode 100644 index 00000000000..e0c1b8ca4cc --- /dev/null +++ b/build/pkgs/meson_python/checksums.ini @@ -0,0 +1,5 @@ +tarball=meson_python-VERSION.tar.gz +sha1=5472571439c7e40b15fa3cc35913db4e01a34ef7 +md5=409ded258744d5251349d729e5a29047 +cksum=3031327791 +upstream_url=https://pypi.io/packages/source/m/meson_python/meson_python-VERSION.tar.gz diff --git a/build/pkgs/meson_python/dependencies b/build/pkgs/meson_python/dependencies new file mode 100644 index 00000000000..0738c2d7777 --- /dev/null +++ b/build/pkgs/meson_python/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/meson_python/install-requires.txt b/build/pkgs/meson_python/install-requires.txt new file mode 100644 index 00000000000..9705cab644e --- /dev/null +++ b/build/pkgs/meson_python/install-requires.txt @@ -0,0 +1 @@ +meson-python diff --git a/build/pkgs/meson_python/package-version.txt b/build/pkgs/meson_python/package-version.txt new file mode 100644 index 00000000000..a918a2aa18d --- /dev/null +++ b/build/pkgs/meson_python/package-version.txt @@ -0,0 +1 @@ +0.6.0 diff --git a/build/pkgs/meson_python/spkg-install.in b/build/pkgs/meson_python/spkg-install.in new file mode 100644 index 00000000000..37ac1a53437 --- /dev/null +++ b/build/pkgs/meson_python/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install . diff --git a/build/pkgs/meson_python/type b/build/pkgs/meson_python/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/meson_python/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/scipy/dependencies b/build/pkgs/scipy/dependencies index 5cd74c1c23a..943fa47e111 100644 --- a/build/pkgs/scipy/dependencies +++ b/build/pkgs/scipy/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) $(BLAS) gfortran numpy pybind11 pythran | $(PYTHON_TOOLCHAIN) +$(PYTHON) $(BLAS) gfortran numpy pybind11 pythran | $(PYTHON_TOOLCHAIN) meson_python ---------- All lines of this file are ignored except the first. From 2f70caefa2c29f53e66e13717632183a55b2800b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 2 Jul 2022 17:14:07 -0700 Subject: [PATCH 123/632] build/pkgs/meson: New --- build/pkgs/meson/SPKG.rst | 18 ++++++++++++++++++ build/pkgs/meson/checksums.ini | 5 +++++ build/pkgs/meson/dependencies | 4 ++++ build/pkgs/meson/install-requires.txt | 1 + build/pkgs/meson/package-version.txt | 1 + build/pkgs/meson/spkg-install.in | 2 ++ build/pkgs/meson/type | 1 + 7 files changed, 32 insertions(+) create mode 100644 build/pkgs/meson/SPKG.rst create mode 100644 build/pkgs/meson/checksums.ini create mode 100644 build/pkgs/meson/dependencies create mode 100644 build/pkgs/meson/install-requires.txt create mode 100644 build/pkgs/meson/package-version.txt create mode 100644 build/pkgs/meson/spkg-install.in create mode 100644 build/pkgs/meson/type diff --git a/build/pkgs/meson/SPKG.rst b/build/pkgs/meson/SPKG.rst new file mode 100644 index 00000000000..6a586f562b6 --- /dev/null +++ b/build/pkgs/meson/SPKG.rst @@ -0,0 +1,18 @@ +meson: A high performance build system +====================================== + +Description +----------- + +A high performance build system + +License +------- + +Apache License, Version 2.0 + +Upstream Contact +---------------- + +https://pypi.org/project/meson/ + diff --git a/build/pkgs/meson/checksums.ini b/build/pkgs/meson/checksums.ini new file mode 100644 index 00000000000..e3b435cc812 --- /dev/null +++ b/build/pkgs/meson/checksums.ini @@ -0,0 +1,5 @@ +tarball=meson-VERSION.tar.gz +sha1=25a82635f5b4e43d3b126ecaaccc99bfff575a92 +md5=e6521b43730901bdd67afa2adefa64a3 +cksum=1062989682 +upstream_url=https://pypi.io/packages/source/m/meson/meson-VERSION.tar.gz diff --git a/build/pkgs/meson/dependencies b/build/pkgs/meson/dependencies new file mode 100644 index 00000000000..0738c2d7777 --- /dev/null +++ b/build/pkgs/meson/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/meson/install-requires.txt b/build/pkgs/meson/install-requires.txt new file mode 100644 index 00000000000..0b9952eaa15 --- /dev/null +++ b/build/pkgs/meson/install-requires.txt @@ -0,0 +1 @@ +meson diff --git a/build/pkgs/meson/package-version.txt b/build/pkgs/meson/package-version.txt new file mode 100644 index 00000000000..92c648bd8c3 --- /dev/null +++ b/build/pkgs/meson/package-version.txt @@ -0,0 +1 @@ +0.62.2 diff --git a/build/pkgs/meson/spkg-install.in b/build/pkgs/meson/spkg-install.in new file mode 100644 index 00000000000..37ac1a53437 --- /dev/null +++ b/build/pkgs/meson/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install . diff --git a/build/pkgs/meson/type b/build/pkgs/meson/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/meson/type @@ -0,0 +1 @@ +standard From ade4413e48ae18bf34690e79bdaaa61bc0be0500 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 2 Jul 2022 17:14:57 -0700 Subject: [PATCH 124/632] build/pkgs/pyproject_metadata: New --- build/pkgs/pyproject_metadata/SPKG.rst | 18 ++++++++++++++++++ build/pkgs/pyproject_metadata/checksums.ini | 5 +++++ build/pkgs/pyproject_metadata/dependencies | 4 ++++ .../pyproject_metadata/install-requires.txt | 1 + .../pyproject_metadata/package-version.txt | 1 + build/pkgs/pyproject_metadata/spkg-install.in | 2 ++ build/pkgs/pyproject_metadata/type | 1 + 7 files changed, 32 insertions(+) create mode 100644 build/pkgs/pyproject_metadata/SPKG.rst create mode 100644 build/pkgs/pyproject_metadata/checksums.ini create mode 100644 build/pkgs/pyproject_metadata/dependencies create mode 100644 build/pkgs/pyproject_metadata/install-requires.txt create mode 100644 build/pkgs/pyproject_metadata/package-version.txt create mode 100644 build/pkgs/pyproject_metadata/spkg-install.in create mode 100644 build/pkgs/pyproject_metadata/type diff --git a/build/pkgs/pyproject_metadata/SPKG.rst b/build/pkgs/pyproject_metadata/SPKG.rst new file mode 100644 index 00000000000..6c0300e9a16 --- /dev/null +++ b/build/pkgs/pyproject_metadata/SPKG.rst @@ -0,0 +1,18 @@ +pyproject_metadata: PEP 621 metadata parsing +============================================ + +Description +----------- + +PEP 621 metadata parsing + +License +------- + +MIT + +Upstream Contact +---------------- + +https://pypi.org/project/pyproject-metadata/ + diff --git a/build/pkgs/pyproject_metadata/checksums.ini b/build/pkgs/pyproject_metadata/checksums.ini new file mode 100644 index 00000000000..da299c46588 --- /dev/null +++ b/build/pkgs/pyproject_metadata/checksums.ini @@ -0,0 +1,5 @@ +tarball=pyproject-metadata-VERSION.tar.gz +sha1=5421824aa29786bde43f510365c4d035a0614ba5 +md5=85fcbd5d777809ca2217a996e06fe2e0 +cksum=1327227039 +upstream_url=https://pypi.io/packages/source/p/pyproject_metadata/pyproject-metadata-VERSION.tar.gz diff --git a/build/pkgs/pyproject_metadata/dependencies b/build/pkgs/pyproject_metadata/dependencies new file mode 100644 index 00000000000..0738c2d7777 --- /dev/null +++ b/build/pkgs/pyproject_metadata/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/pyproject_metadata/install-requires.txt b/build/pkgs/pyproject_metadata/install-requires.txt new file mode 100644 index 00000000000..7ca7140f9d4 --- /dev/null +++ b/build/pkgs/pyproject_metadata/install-requires.txt @@ -0,0 +1 @@ +pyproject-metadata diff --git a/build/pkgs/pyproject_metadata/package-version.txt b/build/pkgs/pyproject_metadata/package-version.txt new file mode 100644 index 00000000000..8f0916f768f --- /dev/null +++ b/build/pkgs/pyproject_metadata/package-version.txt @@ -0,0 +1 @@ +0.5.0 diff --git a/build/pkgs/pyproject_metadata/spkg-install.in b/build/pkgs/pyproject_metadata/spkg-install.in new file mode 100644 index 00000000000..37ac1a53437 --- /dev/null +++ b/build/pkgs/pyproject_metadata/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install . diff --git a/build/pkgs/pyproject_metadata/type b/build/pkgs/pyproject_metadata/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/pyproject_metadata/type @@ -0,0 +1 @@ +standard From cafd1c30ed2c66a2cdee89b859be606592999a5c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 2 Jul 2022 17:20:36 -0700 Subject: [PATCH 125/632] build/pkgs/ninja_build/type: Change to standard --- build/pkgs/ninja_build/type | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/ninja_build/type b/build/pkgs/ninja_build/type index 134d9bc32d5..a6a7b9cd726 100644 --- a/build/pkgs/ninja_build/type +++ b/build/pkgs/ninja_build/type @@ -1 +1 @@ -optional +standard From 6ae3f1e01f5c7b6b8d663e161f825d191a28a110 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 2 Jul 2022 17:24:55 -0700 Subject: [PATCH 126/632] build/pkgs/pyproject_metadata/dependencies: Update --- build/pkgs/pyproject_metadata/dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/pyproject_metadata/dependencies b/build/pkgs/pyproject_metadata/dependencies index 0738c2d7777..6d5368db738 100644 --- a/build/pkgs/pyproject_metadata/dependencies +++ b/build/pkgs/pyproject_metadata/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | $(PYTHON_TOOLCHAIN) +$(PYTHON) packaging pyparsing | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. From fb2039f4366ec1635deb8979ffebcd007b668d59 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 2 Jul 2022 17:32:48 -0700 Subject: [PATCH 127/632] build/pkgs/meson_python: Update dependencies, spkg-install --- build/pkgs/meson_python/dependencies | 5 ++++- build/pkgs/meson_python/spkg-install.in | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/build/pkgs/meson_python/dependencies b/build/pkgs/meson_python/dependencies index 0738c2d7777..10426eb880a 100644 --- a/build/pkgs/meson_python/dependencies +++ b/build/pkgs/meson_python/dependencies @@ -1,4 +1,7 @@ -$(PYTHON) | $(PYTHON_TOOLCHAIN) +$(PYTHON) meson pyproject_metadata tomli ninja_build | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. + +meson_python actually declares a dependency on ninja, the Python distribution package. +But it only needs the ninja executable. diff --git a/build/pkgs/meson_python/spkg-install.in b/build/pkgs/meson_python/spkg-install.in index 37ac1a53437..b3bbe7b8f3e 100644 --- a/build/pkgs/meson_python/spkg-install.in +++ b/build/pkgs/meson_python/spkg-install.in @@ -1,2 +1,2 @@ cd src -sdh_pip_install . +sdh_pip_install --no-build-isolation --no-deps . From d9a0a2338d347815532c04e91de0e5e2c6a51914 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 2 Jul 2022 17:34:03 -0700 Subject: [PATCH 128/632] build/pkgs/scipy/spkg-install.in: Use --no-build-isolation to avoid hardcoded numpy version --- build/pkgs/scipy/spkg-install.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/scipy/spkg-install.in b/build/pkgs/scipy/spkg-install.in index 4f359e7363f..c9374839152 100644 --- a/build/pkgs/scipy/spkg-install.in +++ b/build/pkgs/scipy/spkg-install.in @@ -41,4 +41,4 @@ UMFPACK="None"; export UMFPACK cd src/ # Install: -sdh_pip_install . +sdh_pip_install --no-build-isolation . From 05d5109485193d057473d905fabc92a8a55e1344 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 2 Jul 2022 18:17:16 -0700 Subject: [PATCH 129/632] build/pkgs/scipy/dependencies: Add cython --- build/pkgs/scipy/dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/scipy/dependencies b/build/pkgs/scipy/dependencies index 943fa47e111..5d42789eb10 100644 --- a/build/pkgs/scipy/dependencies +++ b/build/pkgs/scipy/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) $(BLAS) gfortran numpy pybind11 pythran | $(PYTHON_TOOLCHAIN) meson_python +$(PYTHON) $(BLAS) gfortran numpy pybind11 cython pythran | $(PYTHON_TOOLCHAIN) meson_python ---------- All lines of this file are ignored except the first. From 463660d95210d6d014c24e1742ab31ed1b0016c4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 3 Jul 2022 10:53:25 -0700 Subject: [PATCH 130/632] build/pkgs/scipy/spkg-install.in: Remove all old compiler/linker settings, add workaround for https://github.com/scipy/scipy/issues/16536 --- build/pkgs/scipy/spkg-install.in | 52 +++++++------------------------- 1 file changed, 11 insertions(+), 41 deletions(-) diff --git a/build/pkgs/scipy/spkg-install.in b/build/pkgs/scipy/spkg-install.in index c9374839152..35c80c027b0 100644 --- a/build/pkgs/scipy/spkg-install.in +++ b/build/pkgs/scipy/spkg-install.in @@ -1,44 +1,14 @@ -# These flags confuse numpy's distutils. In particular, -# if they are set empty bad things happen. -unset CFLAGS CXXFLAGS SHAREDFLAGS -echo "Note: CFLAGS, CXXFLAGS and SHAREDFLAGS are taken from distutils," -echo " so their current settings are ignored." - -if [ "$UNAME" = "Darwin" ]; then - unset ATLAS - unset BLAS - unset LAPACK - export LDFLAGS="-bundle -undefined dynamic_lookup $LDFLAGS" - export CPPFLAGS="-D__ACCELERATE__ $CPPFLAGS" -else - export {ATLAS,PTATLAS,OPENBLAS,MKL,MKLROOT}=None - export LDFLAGS="-shared $LDFLAGS" -fi - -# Make sure that the fortran objects are compiled with -fPIC -export FFLAGS="$FFLAGS -fPIC" -export FCFLAGS="$FCFLAGS -fPIC" - -if [ "$UNAME" = "CYGWIN" ]; then - # Trac #30643 - export CPPFLAGS="${CPPFLAGS} -D_GNU_SOURCE" -fi - -if [ "$UNAME" = "CYGWIN" -a "$SAGE_DEBUG" = "yes" ]; then - # Needed for just one or two modules when compiling in debug mode - # Otherwise the debug symbols create too many sections in the binary - export CPPFLAGS="$CPPFLAGS -Wa,-mbig-obj" -fi - - -# This avoids problems on some systems -- until we officially -# support umfpack (which we will likely do, since cvxopt I think includes it): -UMFPACK="None"; export UMFPACK -# See http://projects.scipy.org/pipermail/scipy-user/2006-July/008661.html -# (Currently SWIG gets invoked by scipy when building the umfpack interface, -# which is bad.) +# https://github.com/scipy/scipy/issues/16536 - meson breaks when CXX="g++ -std=gnu++11" +export CXX=$(echo "$CXX" | sed 's/-std=[a-z0-9+]*//g') cd src/ -# Install: -sdh_pip_install --no-build-isolation . +# Install: "pip wheel" fails for this package with +# AttributeError: module 'mesonpy' has no attribute 'prepare_metadata_for_build_wheel'. +# We can use "build" instead. +# (However, according to https://github.com/FFY00/meson-python/issues/55 this is just a +# consequence of an earlier error.) +python3 -m build --wheel --no-isolation . +sdh_store_and_pip_install_wheel . + +#sdh_pip_install --no-build-isolation . From 0099213180402f0e5f6ad8b1aeb6372fe0967823 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 3 Jul 2022 11:14:33 -0700 Subject: [PATCH 131/632] build/pkgs/scipy/spkg-install.in: Relax version pinning of numpy in pyproject.toml --- build/pkgs/scipy/spkg-install.in | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/build/pkgs/scipy/spkg-install.in b/build/pkgs/scipy/spkg-install.in index 35c80c027b0..6b2df258d1b 100644 --- a/build/pkgs/scipy/spkg-install.in +++ b/build/pkgs/scipy/spkg-install.in @@ -3,6 +3,10 @@ export CXX=$(echo "$CXX" | sed 's/-std=[a-z0-9+]*//g') cd src/ +# mesonpy enforces the build-system requirements, including the strict version pins of numpy +# even when --no-isolation (--no-build-isolation) is in used. We patch it out. +sed -i.bak '/build-system/,/project/s/^ *"numpy.*/ "numpy",/' pyproject.toml + # Install: "pip wheel" fails for this package with # AttributeError: module 'mesonpy' has no attribute 'prepare_metadata_for_build_wheel'. # We can use "build" instead. From 1ca943f265207965de883fd8241a7ab7bae84126 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 3 Jul 2022 11:39:23 -0700 Subject: [PATCH 132/632] build/pkgs/meson_python/patches: Add https://github.com/FFY00/meson-python/pull/90 --- build/pkgs/meson_python/package-version.txt | 2 +- build/pkgs/meson_python/patches/90.patch | 31 +++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 build/pkgs/meson_python/patches/90.patch diff --git a/build/pkgs/meson_python/package-version.txt b/build/pkgs/meson_python/package-version.txt index a918a2aa18d..61d13b8d467 100644 --- a/build/pkgs/meson_python/package-version.txt +++ b/build/pkgs/meson_python/package-version.txt @@ -1 +1 @@ -0.6.0 +0.6.0.p1 diff --git a/build/pkgs/meson_python/patches/90.patch b/build/pkgs/meson_python/patches/90.patch new file mode 100644 index 00000000000..5f51ea9aa92 --- /dev/null +++ b/build/pkgs/meson_python/patches/90.patch @@ -0,0 +1,31 @@ +From 055d7b88b815106a2281320668f218c012eda1ad Mon Sep 17 00:00:00 2001 +From: Ralf Gommers +Date: Sun, 3 Jul 2022 13:34:13 +0200 +Subject: [PATCH] BUG: remove `-Dstrip=true` default, it doesn't work on macOS + +See https://github.com/scipy/scipy/issues/16446 for details +on how it fails on macOS and also for proof that it is not +needed because it does not change the wheel size on Linux. + +This was originally discussed in gh-27. It looks like I was +wrong there about the need for stripping. That info was based +on my experience with `numpy.distutils` builds; there it is +needed because of the hardcoded debug flags that cannot be +switched with a flag like `-Ddebug=false` like we use in +`meson-python`. +--- + mesonpy/__init__.py | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/mesonpy/__init__.py b/mesonpy/__init__.py +index 6594812..0896e34 100644 +--- a/mesonpy/__init__.py ++++ b/mesonpy/__init__.py +@@ -487,7 +487,6 @@ def _configure(self, reconfigure: bool = False) -> None: + f'--native-file={os.fspath(self._meson_native_file)}', + # TODO: Allow configuring these arguments + '-Ddebug=false', +- '-Dstrip=true', + '-Doptimization=2', + *setup_args, + ) From ddc40a893c71997d4817c14af3e15da789c268fd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 3 Jul 2022 12:02:51 -0700 Subject: [PATCH 133/632] build/pkgs/scipy/spkg-install.in: Back to sdh_pip_install --- build/pkgs/scipy/spkg-install.in | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/build/pkgs/scipy/spkg-install.in b/build/pkgs/scipy/spkg-install.in index 6b2df258d1b..37c4e972358 100644 --- a/build/pkgs/scipy/spkg-install.in +++ b/build/pkgs/scipy/spkg-install.in @@ -7,12 +7,4 @@ cd src/ # even when --no-isolation (--no-build-isolation) is in used. We patch it out. sed -i.bak '/build-system/,/project/s/^ *"numpy.*/ "numpy",/' pyproject.toml -# Install: "pip wheel" fails for this package with -# AttributeError: module 'mesonpy' has no attribute 'prepare_metadata_for_build_wheel'. -# We can use "build" instead. -# (However, according to https://github.com/FFY00/meson-python/issues/55 this is just a -# consequence of an earlier error.) -python3 -m build --wheel --no-isolation . -sdh_store_and_pip_install_wheel . - -#sdh_pip_install --no-build-isolation . +sdh_pip_install --no-build-isolation . From fd0eb69806454d30276b8d99ca88ea617eeaf96b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 8 Jul 2022 19:39:13 -0700 Subject: [PATCH 134/632] build/pkgs/scipy: Use 1.9.0rc2 --- build/pkgs/scipy/checksums.ini | 6 +++--- build/pkgs/scipy/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/scipy/checksums.ini b/build/pkgs/scipy/checksums.ini index 11730409e42..66188de7d3c 100644 --- a/build/pkgs/scipy/checksums.ini +++ b/build/pkgs/scipy/checksums.ini @@ -1,5 +1,5 @@ tarball=scipy-VERSION.tar.gz -sha1=f59a225dc4e41f19e0c69bcb4722163f08eb7ce1 -md5=3fe21eea99de291a468c64d218dae0a4 -cksum=2309164171 +sha1=9e4ea6e219a7141ce510651be2a8e63e6ea4f025 +md5=fed06fd35282e493d02585359e2e5541 +cksum=3088600366 upstream_url=https://pypi.io/packages/source/s/scipy/scipy-VERSION.tar.gz diff --git a/build/pkgs/scipy/package-version.txt b/build/pkgs/scipy/package-version.txt index 6ed8c32eb58..65194bf5ebe 100644 --- a/build/pkgs/scipy/package-version.txt +++ b/build/pkgs/scipy/package-version.txt @@ -1 +1 @@ -1.9.0rc1 +1.9.0rc2 From f517441bcf7ae32afd9be3ac3f56a81038e2d374 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 26 Jul 2022 11:39:03 -0700 Subject: [PATCH 135/632] build/pkgs/meson_python: Update to 0.8.0 --- build/pkgs/meson_python/checksums.ini | 6 +++--- build/pkgs/meson_python/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/meson_python/checksums.ini b/build/pkgs/meson_python/checksums.ini index e0c1b8ca4cc..4c1a13cb3f3 100644 --- a/build/pkgs/meson_python/checksums.ini +++ b/build/pkgs/meson_python/checksums.ini @@ -1,5 +1,5 @@ tarball=meson_python-VERSION.tar.gz -sha1=5472571439c7e40b15fa3cc35913db4e01a34ef7 -md5=409ded258744d5251349d729e5a29047 -cksum=3031327791 +sha1=87bc586a5fb4fd25af77c33312357ec421a83711 +md5=71c6dbcaf2b193e3c4d56be3df571b14 +cksum=2230438557 upstream_url=https://pypi.io/packages/source/m/meson_python/meson_python-VERSION.tar.gz diff --git a/build/pkgs/meson_python/package-version.txt b/build/pkgs/meson_python/package-version.txt index 61d13b8d467..a3df0a6959e 100644 --- a/build/pkgs/meson_python/package-version.txt +++ b/build/pkgs/meson_python/package-version.txt @@ -1 +1 @@ -0.6.0.p1 +0.8.0 From ee75343d4548f23ca6d97a8a95854526a49945e4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 26 Jul 2022 11:40:26 -0700 Subject: [PATCH 136/632] build/pkgs/scipy: Update to 1.9.0rc3 --- build/pkgs/scipy/checksums.ini | 6 +++--- build/pkgs/scipy/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/scipy/checksums.ini b/build/pkgs/scipy/checksums.ini index 66188de7d3c..ff78ec3a4e8 100644 --- a/build/pkgs/scipy/checksums.ini +++ b/build/pkgs/scipy/checksums.ini @@ -1,5 +1,5 @@ tarball=scipy-VERSION.tar.gz -sha1=9e4ea6e219a7141ce510651be2a8e63e6ea4f025 -md5=fed06fd35282e493d02585359e2e5541 -cksum=3088600366 +sha1=e1c790ad1d98990925f2bd623566c94f55d86c8d +md5=8f838833943af35aac2d90d9b94e5b34 +cksum=2905963215 upstream_url=https://pypi.io/packages/source/s/scipy/scipy-VERSION.tar.gz diff --git a/build/pkgs/scipy/package-version.txt b/build/pkgs/scipy/package-version.txt index 65194bf5ebe..806b16b355d 100644 --- a/build/pkgs/scipy/package-version.txt +++ b/build/pkgs/scipy/package-version.txt @@ -1 +1 @@ -1.9.0rc2 +1.9.0rc3 From bfd5ff7e02729f4389595f006d96e0367a57b049 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 26 Jul 2022 11:44:05 -0700 Subject: [PATCH 137/632] build/pkgs/meson_python/patches/90.patch: Remove (upstreamed) --- build/pkgs/meson_python/patches/90.patch | 31 ------------------------ 1 file changed, 31 deletions(-) delete mode 100644 build/pkgs/meson_python/patches/90.patch diff --git a/build/pkgs/meson_python/patches/90.patch b/build/pkgs/meson_python/patches/90.patch deleted file mode 100644 index 5f51ea9aa92..00000000000 --- a/build/pkgs/meson_python/patches/90.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 055d7b88b815106a2281320668f218c012eda1ad Mon Sep 17 00:00:00 2001 -From: Ralf Gommers -Date: Sun, 3 Jul 2022 13:34:13 +0200 -Subject: [PATCH] BUG: remove `-Dstrip=true` default, it doesn't work on macOS - -See https://github.com/scipy/scipy/issues/16446 for details -on how it fails on macOS and also for proof that it is not -needed because it does not change the wheel size on Linux. - -This was originally discussed in gh-27. It looks like I was -wrong there about the need for stripping. That info was based -on my experience with `numpy.distutils` builds; there it is -needed because of the hardcoded debug flags that cannot be -switched with a flag like `-Ddebug=false` like we use in -`meson-python`. ---- - mesonpy/__init__.py | 1 - - 1 file changed, 1 deletion(-) - -diff --git a/mesonpy/__init__.py b/mesonpy/__init__.py -index 6594812..0896e34 100644 ---- a/mesonpy/__init__.py -+++ b/mesonpy/__init__.py -@@ -487,7 +487,6 @@ def _configure(self, reconfigure: bool = False) -> None: - f'--native-file={os.fspath(self._meson_native_file)}', - # TODO: Allow configuring these arguments - '-Ddebug=false', -- '-Dstrip=true', - '-Doptimization=2', - *setup_args, - ) From 30c2c7916705146452987d115a010f673bb9ae73 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 26 Jul 2022 16:52:41 -0700 Subject: [PATCH 138/632] build/pkgs/patchelf: New (scipy/meson_py dependency) --- build/pkgs/meson_python/dependencies | 5 ++++- build/pkgs/patchelf/SPKG.rst | 18 ++++++++++++++++++ build/pkgs/patchelf/checksums.ini | 5 +++++ build/pkgs/patchelf/dependencies | 4 ++++ build/pkgs/patchelf/install-requires.txt | 1 + build/pkgs/patchelf/package-version.txt | 1 + build/pkgs/patchelf/spkg-install.in | 4 ++++ build/pkgs/patchelf/type | 1 + 8 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 build/pkgs/patchelf/SPKG.rst create mode 100644 build/pkgs/patchelf/checksums.ini create mode 100644 build/pkgs/patchelf/dependencies create mode 100644 build/pkgs/patchelf/install-requires.txt create mode 100644 build/pkgs/patchelf/package-version.txt create mode 100644 build/pkgs/patchelf/spkg-install.in create mode 100644 build/pkgs/patchelf/type diff --git a/build/pkgs/meson_python/dependencies b/build/pkgs/meson_python/dependencies index 10426eb880a..faf5360b3a5 100644 --- a/build/pkgs/meson_python/dependencies +++ b/build/pkgs/meson_python/dependencies @@ -1,7 +1,10 @@ -$(PYTHON) meson pyproject_metadata tomli ninja_build | $(PYTHON_TOOLCHAIN) +$(PYTHON) meson pyproject_metadata tomli ninja_build buildelf | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. meson_python actually declares a dependency on ninja, the Python distribution package. But it only needs the ninja executable. + +buildelf is needed by projects that use meson_python as their build system +for wheel building. diff --git a/build/pkgs/patchelf/SPKG.rst b/build/pkgs/patchelf/SPKG.rst new file mode 100644 index 00000000000..2c9861192e1 --- /dev/null +++ b/build/pkgs/patchelf/SPKG.rst @@ -0,0 +1,18 @@ +patchelf: A small utility to modify the dynamic linker and RPATH of ELF executables. +==================================================================================== + +Description +----------- + +A small utility to modify the dynamic linker and RPATH of ELF executables. + +License +------- + +GPL-3.0-or-later + +Upstream Contact +---------------- + +https://pypi.org/project/patchelf/ + diff --git a/build/pkgs/patchelf/checksums.ini b/build/pkgs/patchelf/checksums.ini new file mode 100644 index 00000000000..c67e77272d7 --- /dev/null +++ b/build/pkgs/patchelf/checksums.ini @@ -0,0 +1,5 @@ +tarball=patchelf-VERSION.tar.gz +sha1=5ee564ad25632ac44313e29d2cc2aec80f3b2556 +md5=4b90a7a8660289538473b7896229642d +cksum=1799259701 +upstream_url=https://pypi.io/packages/source/p/patchelf/patchelf-VERSION.tar.gz diff --git a/build/pkgs/patchelf/dependencies b/build/pkgs/patchelf/dependencies new file mode 100644 index 00000000000..0738c2d7777 --- /dev/null +++ b/build/pkgs/patchelf/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/patchelf/install-requires.txt b/build/pkgs/patchelf/install-requires.txt new file mode 100644 index 00000000000..fca4680084f --- /dev/null +++ b/build/pkgs/patchelf/install-requires.txt @@ -0,0 +1 @@ +patchelf diff --git a/build/pkgs/patchelf/package-version.txt b/build/pkgs/patchelf/package-version.txt new file mode 100644 index 00000000000..202f94b31ec --- /dev/null +++ b/build/pkgs/patchelf/package-version.txt @@ -0,0 +1 @@ +0.15.0.0 diff --git a/build/pkgs/patchelf/spkg-install.in b/build/pkgs/patchelf/spkg-install.in new file mode 100644 index 00000000000..cde9ca2d6ba --- /dev/null +++ b/build/pkgs/patchelf/spkg-install.in @@ -0,0 +1,4 @@ +cd src +if [ "$UNAME" = "Linux" ]; then + sdh_pip_install . +fi diff --git a/build/pkgs/patchelf/type b/build/pkgs/patchelf/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/patchelf/type @@ -0,0 +1 @@ +standard From 66d989a1096f3e119f1f2be67f95cc2cf7563a57 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 26 Jul 2022 17:15:29 -0700 Subject: [PATCH 139/632] build/pkgs/meson_python/dependencies: fixup --- build/pkgs/meson_python/dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/meson_python/dependencies b/build/pkgs/meson_python/dependencies index faf5360b3a5..160adbf36c9 100644 --- a/build/pkgs/meson_python/dependencies +++ b/build/pkgs/meson_python/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) meson pyproject_metadata tomli ninja_build buildelf | $(PYTHON_TOOLCHAIN) +$(PYTHON) meson pyproject_metadata tomli ninja_build patchelf | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. From b8e8ad71e0a560db6538b49daff35a8b5809c78a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 26 Jul 2022 18:17:30 -0700 Subject: [PATCH 140/632] build/pkgs/patchelf: Switch to using non-Python package --- build/pkgs/patchelf/SPKG.rst | 7 +++---- build/pkgs/patchelf/checksums.ini | 10 +++++----- build/pkgs/patchelf/dependencies | 2 +- build/pkgs/patchelf/install-requires.txt | 1 - build/pkgs/patchelf/package-version.txt | 2 +- build/pkgs/patchelf/spkg-install.in | 4 +++- 6 files changed, 13 insertions(+), 13 deletions(-) delete mode 100644 build/pkgs/patchelf/install-requires.txt diff --git a/build/pkgs/patchelf/SPKG.rst b/build/pkgs/patchelf/SPKG.rst index 2c9861192e1..a2d1a1c4ccf 100644 --- a/build/pkgs/patchelf/SPKG.rst +++ b/build/pkgs/patchelf/SPKG.rst @@ -1,5 +1,5 @@ -patchelf: A small utility to modify the dynamic linker and RPATH of ELF executables. -==================================================================================== +patchelf: A small utility to modify the dynamic linker and RPATH of ELF executables +=================================================================================== Description ----------- @@ -14,5 +14,4 @@ GPL-3.0-or-later Upstream Contact ---------------- -https://pypi.org/project/patchelf/ - +https://github.com/NixOS/patchelf diff --git a/build/pkgs/patchelf/checksums.ini b/build/pkgs/patchelf/checksums.ini index c67e77272d7..728e73fbccd 100644 --- a/build/pkgs/patchelf/checksums.ini +++ b/build/pkgs/patchelf/checksums.ini @@ -1,5 +1,5 @@ -tarball=patchelf-VERSION.tar.gz -sha1=5ee564ad25632ac44313e29d2cc2aec80f3b2556 -md5=4b90a7a8660289538473b7896229642d -cksum=1799259701 -upstream_url=https://pypi.io/packages/source/p/patchelf/patchelf-VERSION.tar.gz +tarball=patchelf-VERSION.tar.bz2 +sha1=b116615abd01d5093a6fe674cb4629c2be38cce9 +md5=0a707393c1b95291b19b9dd06a4dd546 +cksum=3263104798 +upstream_url=https://github.com/NixOS/patchelf/releases/download/VERSION/patchelf-VERSION.tar.bz2 diff --git a/build/pkgs/patchelf/dependencies b/build/pkgs/patchelf/dependencies index 0738c2d7777..c856a61a50b 100644 --- a/build/pkgs/patchelf/dependencies +++ b/build/pkgs/patchelf/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | $(PYTHON_TOOLCHAIN) +| bzip2 ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/patchelf/install-requires.txt b/build/pkgs/patchelf/install-requires.txt deleted file mode 100644 index fca4680084f..00000000000 --- a/build/pkgs/patchelf/install-requires.txt +++ /dev/null @@ -1 +0,0 @@ -patchelf diff --git a/build/pkgs/patchelf/package-version.txt b/build/pkgs/patchelf/package-version.txt index 202f94b31ec..a5510516948 100644 --- a/build/pkgs/patchelf/package-version.txt +++ b/build/pkgs/patchelf/package-version.txt @@ -1 +1 @@ -0.15.0.0 +0.15.0 diff --git a/build/pkgs/patchelf/spkg-install.in b/build/pkgs/patchelf/spkg-install.in index cde9ca2d6ba..b4a009197cd 100644 --- a/build/pkgs/patchelf/spkg-install.in +++ b/build/pkgs/patchelf/spkg-install.in @@ -1,4 +1,6 @@ cd src if [ "$UNAME" = "Linux" ]; then - sdh_pip_install . + sdh_configure + sdh_make + sdh_make_install fi From 270789e0b783792057c80634091ced4645e30dbb Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 27 Jul 2022 09:44:34 -0700 Subject: [PATCH 141/632] build/pkgs/patchelf: Downgrade to 0.13.1 --- build/pkgs/patchelf/checksums.ini | 6 +++--- build/pkgs/patchelf/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/patchelf/checksums.ini b/build/pkgs/patchelf/checksums.ini index 728e73fbccd..bd52a82dbc5 100644 --- a/build/pkgs/patchelf/checksums.ini +++ b/build/pkgs/patchelf/checksums.ini @@ -1,5 +1,5 @@ tarball=patchelf-VERSION.tar.bz2 -sha1=b116615abd01d5093a6fe674cb4629c2be38cce9 -md5=0a707393c1b95291b19b9dd06a4dd546 -cksum=3263104798 +sha1=5d9c1690c0fbe70c312f43d597e04b6c1eeffc60 +md5=04d243d3626a33201b0d6eef0e2c4317 +cksum=92812155 upstream_url=https://github.com/NixOS/patchelf/releases/download/VERSION/patchelf-VERSION.tar.bz2 diff --git a/build/pkgs/patchelf/package-version.txt b/build/pkgs/patchelf/package-version.txt index a5510516948..c317a91891f 100644 --- a/build/pkgs/patchelf/package-version.txt +++ b/build/pkgs/patchelf/package-version.txt @@ -1 +1 @@ -0.15.0 +0.13.1 From 1c53962dd6cfa678d4d809ceaa2288c224fa6b35 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 27 Jul 2022 15:20:43 -0700 Subject: [PATCH 142/632] build/pkgs/ninja_build: Update to 1.11.0 --- build/pkgs/ninja_build/checksums.ini | 7 ++++--- build/pkgs/ninja_build/package-version.txt | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/build/pkgs/ninja_build/checksums.ini b/build/pkgs/ninja_build/checksums.ini index cd1733990c7..d3914da794a 100644 --- a/build/pkgs/ninja_build/checksums.ini +++ b/build/pkgs/ninja_build/checksums.ini @@ -1,4 +1,5 @@ tarball=ninja_build-VERSION.tar.gz -sha1=17219deb34dd816363e37470f77ff7231509143a -md5=5fdb04461cc7f5d02536b3bfc0300166 -cksum=28253504 +sha1=f8c9279bdd4efc63b1a6be3b8c5a5031699af9ac +md5=7d1a1a2f5cdc06795b3054df5c17d5ef +cksum=3142198237 +upstream_url=https://github.com/ninja-build/ninja/archive/refs/tags/vVERSION.tar.gz diff --git a/build/pkgs/ninja_build/package-version.txt b/build/pkgs/ninja_build/package-version.txt index 53adb84c822..1cac385c6cb 100644 --- a/build/pkgs/ninja_build/package-version.txt +++ b/build/pkgs/ninja_build/package-version.txt @@ -1 +1 @@ -1.8.2 +1.11.0 From 4fb3a63847487c0b205a4a02eed24a069f84513c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 27 Jul 2022 15:24:16 -0700 Subject: [PATCH 143/632] build/pkgs/ninja_build/spkg-configure.m4: Set lower version bound to 1.8.2 --- build/pkgs/ninja_build/spkg-configure.m4 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/build/pkgs/ninja_build/spkg-configure.m4 b/build/pkgs/ninja_build/spkg-configure.m4 index d9661574a13..fb6c4f00985 100644 --- a/build/pkgs/ninja_build/spkg-configure.m4 +++ b/build/pkgs/ninja_build/spkg-configure.m4 @@ -1,11 +1,12 @@ SAGE_SPKG_CONFIGURE( [ninja_build], [ - AC_CACHE_CHECK([for ninja >= 1.7.2], [ac_cv_path_NINJA], [ + dnl meson_python needs 1.8.2 or later + AC_CACHE_CHECK([for ninja >= 1.8.2], [ac_cv_path_NINJA], [ AC_PATH_PROGS_FEATURE_CHECK([NINJA], [ninja], [ ninja_version=`$ac_path_NINJA --version 2>&1 \ | $SED -n -e 's/\([[0-9]]*\.[[0-9]]*\.[[0-9]]*\).*/\1/p'` AS_IF([test -n "$ninja_version"], [ - AX_COMPARE_VERSION([$ninja_version], [ge], [1.7.2], [ + AX_COMPARE_VERSION([$ninja_version], [ge], [1.8.2], [ ac_cv_path_NINJA="$ac_path_NINJA" ac_path_NINJA_found=: ]) From a4c382153030b8ef99eb2e35e0a5a493f38ba73b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 2 Aug 2022 16:30:37 -0700 Subject: [PATCH 144/632] build/pkgs/meson_python/patches: Add https://github.com/FFY00/meson-python/pull/126 --- build/pkgs/meson_python/patches/126.patch | 26 +++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 build/pkgs/meson_python/patches/126.patch diff --git a/build/pkgs/meson_python/patches/126.patch b/build/pkgs/meson_python/patches/126.patch new file mode 100644 index 00000000000..3f417b41a5e --- /dev/null +++ b/build/pkgs/meson_python/patches/126.patch @@ -0,0 +1,26 @@ +From 320ecea8996a062317ac7790eebe17f5f3f95551 Mon Sep 17 00:00:00 2001 +From: Matthias Koeppe +Date: Tue, 2 Aug 2022 16:20:50 -0700 +Subject: [PATCH] Project.platform_tag: Handle the case of 32bit Python on + x86_64 + +--- + mesonpy/__init__.py | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/mesonpy/__init__.py b/mesonpy/__init__.py +index aff3dd1..bdcda25 100644 +--- a/mesonpy/__init__.py ++++ b/mesonpy/__init__.py +@@ -684,6 +684,11 @@ def platform_tag(self) -> str: + # https://github.com/pypa/packaging/issues/578 + parts[1] += '.0' + platform_ = '-'.join(parts) ++ elif parts[0] == 'linux' and parts[1] == 'x86_64' and sys.maxsize == 2147483647: ++ # 32-bit Python running on an x86_64 host ++ # https://github.com/FFY00/meson-python/issues/123 ++ parts[1] = 'i686' ++ platform_ = '-'.join(parts) + return platform_.replace('-', '_').replace('.', '_') + + def _calculate_file_abi_tag_heuristic_windows(self, filename: str) -> Optional[mesonpy._tags.Tag]: From 12698ea05dcf048574dbac4650900f8dc77a775f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 3 Aug 2022 23:27:42 -0700 Subject: [PATCH 145/632] build/pkgs/scipy: Update to 1.9.0 --- build/pkgs/scipy/checksums.ini | 6 +++--- build/pkgs/scipy/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/scipy/checksums.ini b/build/pkgs/scipy/checksums.ini index ff78ec3a4e8..8090f084328 100644 --- a/build/pkgs/scipy/checksums.ini +++ b/build/pkgs/scipy/checksums.ini @@ -1,5 +1,5 @@ tarball=scipy-VERSION.tar.gz -sha1=e1c790ad1d98990925f2bd623566c94f55d86c8d -md5=8f838833943af35aac2d90d9b94e5b34 -cksum=2905963215 +sha1=22c0e73b933b938c272c6eb747cd6b70ab2f9b83 +md5=1f2e527930ddfa15a622b146dae42144 +cksum=994041683 upstream_url=https://pypi.io/packages/source/s/scipy/scipy-VERSION.tar.gz diff --git a/build/pkgs/scipy/package-version.txt b/build/pkgs/scipy/package-version.txt index 806b16b355d..f8e233b2733 100644 --- a/build/pkgs/scipy/package-version.txt +++ b/build/pkgs/scipy/package-version.txt @@ -1 +1 @@ -1.9.0rc3 +1.9.0 From f896ae19d78b17476dbe2079251832fa3b4f21cb Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 3 Aug 2022 23:37:49 -0700 Subject: [PATCH 146/632] build/pkgs/meson_python: Update to 0.8.1 --- build/pkgs/meson_python/checksums.ini | 6 +++--- build/pkgs/meson_python/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/meson_python/checksums.ini b/build/pkgs/meson_python/checksums.ini index 4c1a13cb3f3..e72f2a738cc 100644 --- a/build/pkgs/meson_python/checksums.ini +++ b/build/pkgs/meson_python/checksums.ini @@ -1,5 +1,5 @@ tarball=meson_python-VERSION.tar.gz -sha1=87bc586a5fb4fd25af77c33312357ec421a83711 -md5=71c6dbcaf2b193e3c4d56be3df571b14 -cksum=2230438557 +sha1=18c1742379f1a9f3905c67c348aeb2442b02c119 +md5=38cc212d532a55ba4a53c572656ecd70 +cksum=4204960713 upstream_url=https://pypi.io/packages/source/m/meson_python/meson_python-VERSION.tar.gz diff --git a/build/pkgs/meson_python/package-version.txt b/build/pkgs/meson_python/package-version.txt index a3df0a6959e..6f4eebdf6f6 100644 --- a/build/pkgs/meson_python/package-version.txt +++ b/build/pkgs/meson_python/package-version.txt @@ -1 +1 @@ -0.8.0 +0.8.1 From 9e64ba4905de7d5548e175dc6ee8a5f3a541a9aa Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 4 Aug 2022 00:14:54 -0700 Subject: [PATCH 147/632] build/pkgs/meson: Update to 0.63.0 --- build/pkgs/meson/checksums.ini | 6 +++--- build/pkgs/meson/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/meson/checksums.ini b/build/pkgs/meson/checksums.ini index e3b435cc812..c46bd7bb761 100644 --- a/build/pkgs/meson/checksums.ini +++ b/build/pkgs/meson/checksums.ini @@ -1,5 +1,5 @@ tarball=meson-VERSION.tar.gz -sha1=25a82635f5b4e43d3b126ecaaccc99bfff575a92 -md5=e6521b43730901bdd67afa2adefa64a3 -cksum=1062989682 +sha1=85b1c71b598686e40632b5a57ac7f2bdd1dcd926 +md5=d9e7d69f73f875004fbb3cc8bfe2a39b +cksum=339088378 upstream_url=https://pypi.io/packages/source/m/meson/meson-VERSION.tar.gz diff --git a/build/pkgs/meson/package-version.txt b/build/pkgs/meson/package-version.txt index 92c648bd8c3..70cd2261d5c 100644 --- a/build/pkgs/meson/package-version.txt +++ b/build/pkgs/meson/package-version.txt @@ -1 +1 @@ -0.62.2 +0.63.0 From 719fed550269b8c138f1cb323d6c8f31c8862fed Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Thu, 4 Aug 2022 19:27:56 -0700 Subject: [PATCH 148/632] fusion_ring r_matrix zero bugfix --- src/sage/algebras/fusion_rings/fusion_ring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/algebras/fusion_rings/fusion_ring.py b/src/sage/algebras/fusion_rings/fusion_ring.py index 75b28678c15..e4a52b4e35a 100644 --- a/src/sage/algebras/fusion_rings/fusion_ring.py +++ b/src/sage/algebras/fusion_rings/fusion_ring.py @@ -957,7 +957,7 @@ def r_matrix(self, i, j, k, base_coercion=True): True """ if self.Nk_ij(i, j, k) == 0: - return self.field().zero() + return self.field().zero() if (not base_coercion) or (self._basecoer is None) else self.fvars_field().zero() if i != j: ret = self.root_of_unity((k.twist(reduced=False) - i.twist(reduced=False) - j.twist(reduced=False)) / 2, base_coercion=False) else: From aa7ba6cedb8ae6620a5ef6f867f04dee26a13700 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 10 Aug 2022 16:12:24 +0900 Subject: [PATCH 149/632] Reviewer changes and doc fixes. --- src/sage/algebras/all.py | 1 + src/sage/algebras/fusion_rings/all.py | 2 +- src/sage/algebras/fusion_rings/f_matrix.py | 597 +++++++++--------- .../fast_parallel_fmats_methods.pxd | 1 + .../fast_parallel_fusion_ring_braid_repn.pxd | 1 + .../fast_parallel_fusion_ring_braid_repn.pyx | 8 +- .../algebras/fusion_rings/poly_tup_engine.pyx | 4 +- .../algebras/fusion_rings/shm_managers.pxd | 2 +- .../algebras/fusion_rings/shm_managers.pyx | 349 +++++----- src/sage/combinat/root_system/all.py | 2 +- 10 files changed, 511 insertions(+), 456 deletions(-) diff --git a/src/sage/algebras/all.py b/src/sage/algebras/all.py index ac06fa76ab8..cd71ece02d8 100644 --- a/src/sage/algebras/all.py +++ b/src/sage/algebras/all.py @@ -65,3 +65,4 @@ lazy_import('sage.algebras.cluster_algebra', 'ClusterAlgebra') lazy_import('sage.algebras.yangian', 'Yangian') + diff --git a/src/sage/algebras/fusion_rings/all.py b/src/sage/algebras/fusion_rings/all.py index 6742afd5d55..041ced4f28d 100644 --- a/src/sage/algebras/fusion_rings/all.py +++ b/src/sage/algebras/fusion_rings/all.py @@ -3,7 +3,7 @@ """ # **************************************************************************** -# Copyright (C) 2021 Travis Scrimshaw +# Copyright (C) 2022 Guillermo Aboumrad # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/src/sage/algebras/fusion_rings/f_matrix.py b/src/sage/algebras/fusion_rings/f_matrix.py index 49e814f20f9..3c24c942dd0 100644 --- a/src/sage/algebras/fusion_rings/f_matrix.py +++ b/src/sage/algebras/fusion_rings/f_matrix.py @@ -1,5 +1,5 @@ r""" -F-Matrix Factory for FusionRings +F-Matries of fusion rings """ # **************************************************************************** # Copyright (C) 2019 Daniel Bump @@ -43,239 +43,244 @@ from sage.matrix.constructor import matrix from sage.misc.misc import get_main_globals from sage.rings.ideal import Ideal -from sage.rings.polynomial.all import PolynomialRing +from sage.structure.sage_object import SageObject +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.polynomial.polydict import ETuple from sage.rings.qqbar import AA, QQbar, number_field_elements_from_algebraics -class FMatrix(): +class FMatrix(SageObject): + r""" + Return an F-Matrix factory for a :class:`FusionRing`. + + INPUT: + + - ``FR`` -- a :class:`FusionRing` + - ``fusion_label`` -- (optional) a string used to label basis elements + of the :class:`FusionRing` associated to ``self`` + (see :meth:`FusionRing.fusion_labels`) + - ``var_prefix`` -- (optional) a string indicating the desired prefix + for variables denoting F-symbols to be solved + - ``inject_variables`` -- (default: ``False``) a boolean indicating + whether to inject variables (:class:`FusionRing` basis element + labels and F-symbols) into the global namespace + + The :class:`FusionRing` or Verlinde algebra is the + Grothendieck ring of a modular tensor category [BaKi2001]_. + Such categories arise in conformal field theory or in the + representation theories of affine Lie algebras, or + quantum groups at roots of unity. They have applications + to low dimensional topology and knot theory, to conformal + field theory and to topological quantum computing. The + :class:`FusionRing` captures much information about a fusion + category, but to complete the picture, the F-matrices or + 6j-symbols are needed. For example these are required in + order to construct braid group representations. This + can be done using the :class:`FusionRing` method + :meth:`FusionRing.get_braid_generators`, which uses + the F-matrix. + + We only undertake to compute the F-matrix if the + :class:`FusionRing` is *multiplicity free* meaning that + the Fusion coefficients `N^{ij}_k` are bounded + by 1. For Cartan Types `X_r` and level `k`, + the multiplicity-free cases are given by the + following table. + + +------------------------+----------+ + | Cartan Type | `k` | + +========================+==========+ + | `A_1` | any | + +------------------------+----------+ + | `A_r, r\geq 2` | `\leq 2` | + +------------------------+----------+ + | `B_r, r\geq 2` | `\leq 2` | + +------------------------+----------+ + | `C_2` | `\leq 2` | + +------------------------+----------+ + | `C_r, r\geq 3` | `\leq 1` | + +------------------------+----------+ + | `D_r, r\geq 4` | `\leq 2` | + +------------------------+----------+ + | `G_2,F_4,E_6,E_7` | `\leq 2` | + +------------------------+----------+ + | `E_8` | `\leq 3` | + +------------------------+----------+ + + Beyond this limitation, computation of the F-matrix + can involve very large systems of equations. A + rule of thumb is that this code can compute the + F-matrix for systems with `\leq 14` simple objects + (primary fields) on a machine with 16 GB of memory. + (Larger examples can be quite time consuming.) + + The :class:`FusionRing` and its methods capture much + of the structure of the underlying tensor category. + But an important aspect that is not encoded in the + fusion ring is the associator, which is a homomorphism + `(A\otimes B)\otimes C\to A\otimes(B\otimes C)` that + requires an additional tool, the F-matrix or 6j-symbol. + To specify this, we fix a simple object `D` + and represent the transformation + + .. MATH:: + + \text{Hom}(D,(A\otimes B)\otimes C) + \to \text{Hom}(D,A\otimes(B\otimes C)) + + by a matrix `F^{ABC}_D`. This depends on a pair of + additional simple objects `X` and `Y`. Indeed, we can + get a basis for `\text{Hom}(D,(A\otimes B)\otimes C)` + indexed by simple objects `X` in which the corresponding + homomorphism factors through `X\otimes C`, and similarly + `\text{Hom}(D,A\otimes(B\otimes C))` has a basis indexed + by `Y`, in which the basis vector factors through `A\otimes Y`. + + See [TTWL2009]_ for an introduction to this topic, + [EGNO2015]_ Section 4.9 for a precise mathematical + definition, and [Bond2007]_ Section 2.5 for a discussion + of how to compute the F-matrix. In addition to + [Bond2007]_, worked out F-matrices may be found in + [RoStWa2009]_ and [CHW2015]_. + + The F-matrix is only determined up to a *gauge*. This + is a family of embeddings `C \to A\otimes B` for + simple objects `A,B,C` such that `\text{Hom}(C, A\otimes B)` + is nonzero. Changing the gauge changes the F-matrix though + not in a very essential way. By varying the gauge it is + possible to make the F-matrices unitary, or it is possible + to make them cyclotomic. + + Due to the large number of equations we may fail to find a + Groebner basis if there are too many variables. + + EXAMPLES:: + + sage: I = FusionRing("E8", 2, conjugate=True) + sage: I.fusion_labels(["i0","p","s"],inject_variables=True) + sage: f = FMatrix(I,inject_variables=True); f + creating variables fx1..fx14 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 + F-Matrix factory for The Fusion Ring of Type E8 and level 2 with Integer Ring coefficients + + We have injected two sets of variables to the global namespace. + We created three variables ``i0, p, s`` to represent the + primary fields (simple elements) of the :class:`FusionRing`. Creating + the :class:`FMatrix` factory also created variables + ``fx1, fx2, ..., fx14`` in order to solve the hexagon and pentagon + equations describing the F-matrix. Since we called :class:`FMatrix` + with the parameter ``inject_variables=True``, these have been injected + into the global namespace. This is not necessary for the code to work + but if you want to run the code experimentally you may want access + to these variables. + + EXAMPLES:: + + sage: f.fmatrix(s,s,s,s) + [fx10 fx11] + [fx12 fx13] + + The F-matrix has not been computed at this stage, so + the F-matrix `F^{sss}_s` is filled with variables + ``fx10``, ``fx11``, ``fx12``, ``fx13``. The task is + to solve for these. + + As explained above The F-matrix `(F^{ABC}_D)_{X,Y}` + two other variables `X` and `Y`. We have methods to + tell us (depending on `A,B,C,D`) what the possibilities + for these are. In this example with `A=B=C=D=s` + both `X` and `Y` are allowed to be `i_0` or `s`. + + :: + + sage: f.f_from(s,s,s,s), f.f_to(s,s,s,s) + ([i0, p], [i0, p]) + + The last two statments show that the possible values of + `X` and `Y` when `A = B = C = D = s` are `i_0` and `p`. + + The F-matrix is computed by solving the so-called + pentagon and hexagon equations. The *pentagon equations* + reflect the Mac Lane pentagon axiom in the definition + of a monoidal category. The hexagon relations + reflect the axioms of a *braided monoidal category*, + which are constraints on both the F-matrix and on + the R-matrix. Optionally, orthogonality constraints + may be imposed to obtain an orthogonal F-matrix. + + :: + + sage: sorted(f.get_defining_equations("pentagons"))[1:3] + [fx9*fx12 - fx2*fx13, fx4*fx11 - fx2*fx13] + sage: sorted(f.get_defining_equations("hexagons"))[1:3] + [fx6 - 1, fx2 + 1] + sage: sorted(f.get_orthogonality_constraints())[1:3] + [fx10*fx11 + fx12*fx13, fx10*fx11 + fx12*fx13] + + There are two methods available to compute an F-matrix. + The first, :meth:`find_cyclotomic_solution` uses only + the pentagon and hexagon relations. The second, + :meth:`find_orthogonal_solution` uses additionally + the orthogonality relations. There are some differences + that should be kept in mind. + + :meth:`find_cyclotomic_solution` currently works only with + smaller examples. For example the :class:`FusionRing` for `G_2` + at level 2 is too large. When it is available, this method + produces an F-matrix whose entries are in the same + cyclotomic field as the underlying :class:`FusionRing`. + + :: + + sage: f.find_cyclotomic_solution() + Setting up hexagons and pentagons... + Finding a Groebner basis... + Solving... + Fixing the gauge... + adding equation... fx1 - 1 + adding equation... fx11 - 1 + Done! + + We now have access to the values of the F-matrix using + the methods :meth:`fmatrix` and :meth:`fmat`:: + + sage: f.fmatrix(s,s,s,s) + [(-1/2*zeta128^48 + 1/2*zeta128^16) 1] + [ 1/2 (1/2*zeta128^48 - 1/2*zeta128^16)] + sage: f.fmat(s,s,s,s,p,p) + (1/2*zeta128^48 - 1/2*zeta128^16) + + :meth:`find_orthogonal_solution` is much more powerful + and is capable of handling large cases, sometimes + quickly but sometimes (in larger cases) after hours of + computation. Its F-matrices are not always in the + cyclotomic field that is the base ring of the underlying + :class:`FusionRing`, but sometimes in an extension field adjoining + some square roots. When this happens, the :class:`FusionRing` is + modified, adding an attribute ``_basecoer`` that is + a coercion from the cyclotomic field to the field + containing the F-matrix. The field containing the F-matrix + is available through :meth:`field`. + + :: + + sage: f = FMatrix(FusionRing("B3",2)) + sage: f.find_orthogonal_solution(verbose=False,checkpoint=True) # not tested (~100 s) + sage: all(v in CyclotomicField(56) for v in f.get_fvars().values()) # not tested + True + + sage: f = FMatrix(FusionRing("G2",2)) + sage: f.find_orthogonal_solution(verbose=False) # long time (~11 s) + sage: f.field() # long time + Algebraic Field + """ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variables=False): r""" - Return an F-Matrix factory for a :class:`FusionRing`. - - INPUT: - - - ``FR`` -- a :class:`FusionRing` - - - ``fusion_label`` -- (optional) a string used to label basis elements - of the :class:`FusionRing` associated to ``self`` - - See :meth:`FusionRing.fusion_labels` - - - ``var_prefix`` -- (optional) a string indicating the desired prefix - for variables denoting F-symbols to be solved - - - ``inject_variables`` -- (default: ``False``) a boolean indicating - whether to inject variables (:class:`FusionRing` basis element - labels and F-symbols) into the global namespace - - The :class:`FusionRing` or Verlinde algebra is the - Grothendieck ring of a modular tensor category [BaKi2001]_. - Such categories arise in conformal field theory or in the - representation theories of affine Lie algebras, or - quantum groups at roots of unity. They have applications - to low dimensional topology and knot theory, to conformal - field theory and to topological quantum computing. The - :class:`FusionRing` captures much information about a fusion - category, but to complete the picture, the F-matrices or - 6j-symbols are needed. For example these are required in - order to construct braid group representations. This - can be done using the :class:`FusionRing` method - :meth:`FusionRing.get_braid_generators`, which uses - the F-matrix. - - We only undertake to compute the F-matrix if the - :class:`FusionRing` is *multiplicity free* meaning that - the Fusion coefficients `N^{ij}_k` are bounded - by 1. For Cartan Types `X_r` and level `k`, - the multiplicity-free cases are given by the - following table. - - +------------------------+----------+ - | Cartan Type | `k` | - +========================+==========+ - | `A_1` | any | - +------------------------+----------+ - | `A_r, r\geq 2` | `\leq 2` | - +------------------------+----------+ - | `B_r, r\geq 2` | `\leq 2` | - +------------------------+----------+ - | `C_2` | `\leq 2` | - +------------------------+----------+ - | `C_r, r\geq 3` | `\leq 1` | - +------------------------+----------+ - | `D_r, r\geq 4` | `\leq 2` | - +------------------------+----------+ - | `G_2,F_4,E_6,E_7` | `\leq 2` | - +------------------------+----------+ - | `E_8` | `\leq 3` | - +------------------------+----------+ - - Beyond this limitation, computation of the F-matrix - can involve very large systems of equations. A - rule of thumb is that this code can compute the - F-matrix for systems with `\leq 14` simple objects - (primary fields) on a machine with 16 GB of memory. - (Larger examples can be quite time consuming.) - - The :class:`FusionRing` and its methods capture much - of the structure of the underlying tensor category. - But an important aspect that is not encoded in the - fusion ring is the associator, which is a homomorphism - `(A\otimes B)\otimes C\to A\otimes(B\otimes C)` that - requires an additional tool, the F-matrix or 6j-symbol. - To specify this, we fix a simple object `D` - and represent the transformation - - .. MATH:: - - \text{Hom}(D,(A\otimes B)\otimes C) - \to \text{Hom}(D,A\otimes(B\otimes C)) - - by a matrix `F^{ABC}_D`. This depends on a pair of - additional simple objects `X` and `Y`. Indeed, we can - get a basis for `\text{Hom}(D,(A\otimes B)\otimes C)` - indexed by simple objects `X` in which the corresponding - homomorphism factors through `X\otimes C`, and similarly - `\text{Hom}(D,A\otimes(B\otimes C))` has a basis indexed - by `Y`, in which the basis vector factors through `A\otimes Y`. - - See [TTWL2009]_ for an introduction to this topic, - [EGNO2015]_ Section 4.9 for a precise mathematical - definition, and [Bond2007]_ Section 2.5 for a discussion - of how to compute the F-matrix. In addition to - [Bond2007]_, worked out F-matrices may be found in - [RoStWa2009]_ and [CHW2015]_. - - The F-matrix is only determined up to a *gauge*. This - is a family of embeddings `C \to A\otimes B` for - simple objects `A,B,C` such that `\text{Hom}(C, A\otimes B)` - is nonzero. Changing the gauge changes the F-matrix though - not in a very essential way. By varying the gauge it is - possible to make the F-matrices unitary, or it is possible - to make them cyclotomic. - - Due to the large number of equations we may fail to find a - Groebner basis if there are too many variables. + Initialize ``self``. EXAMPLES:: - sage: I = FusionRing("E8",2,conjugate=True) - sage: I.fusion_labels(["i0","p","s"],inject_variables=True) - sage: f = FMatrix(I,inject_variables=True); f - creating variables fx1..fx14 - Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 - F-Matrix factory for The Fusion Ring of Type E8 and level 2 with Integer Ring coefficients - - We have injected two sets of variables to the global namespace. - We created three variables ``i0, p, s`` to represent the - primary fields (simple elements) of the :class:`FusionRing`. Creating - the :class:`FMatrix` factory also created variables - ``fx1, fx2, ..., fx14`` in order to solve the hexagon and pentagon - equations describing the F-matrix. Since we called :class:`FMatrix` - with the parameter ``inject_variables=True``, these have been injected - into the global namespace. This is not necessary for the code to work - but if you want to run the code experimentally you may want access - to these variables. - - EXAMPLES:: - - sage: f.fmatrix(s,s,s,s) - [fx10 fx11] - [fx12 fx13] - - The F-matrix has not been computed at this stage, so - the F-matrix `F^{sss}_s` is filled with variables - ``fx10``, ``fx11``, ``fx12``, ``fx13``. The task is - to solve for these. - - As explained above The F-matrix `(F^{ABC}_D)_{X,Y}` - two other variables `X` and `Y`. We have methods to - tell us (depending on `A,B,C,D`) what the possibilities - for these are. In this example with `A=B=C=D=s` - both `X` and `Y` are allowed to be `i_0` or `s`. - - :: - - sage: f.f_from(s,s,s,s), f.f_to(s,s,s,s) - ([i0, p], [i0, p]) - - The last two statments show that the possible values of - `X` and `Y` when `A = B = C = D = s` are `i_0` and `p`. - - The F-matrix is computed by solving the so-called - pentagon and hexagon equations. The *pentagon equations* - reflect the Mac Lane pentagon axiom in the definition - of a monoidal category. The hexagon relations - reflect the axioms of a *braided monoidal category*, - which are constraints on both the F-matrix and on - the R-matrix. Optionally, orthogonality constraints - may be imposed to obtain an orthogonal F-matrix. - - :: - - sage: sorted(f.get_defining_equations("pentagons"))[1:3] - [fx9*fx12 - fx2*fx13, fx4*fx11 - fx2*fx13] - sage: sorted(f.get_defining_equations("hexagons"))[1:3] - [fx6 - 1, fx2 + 1] - sage: sorted(f.get_orthogonality_constraints())[1:3] - [fx10*fx11 + fx12*fx13, fx10*fx11 + fx12*fx13] - - There are two methods available to compute an F-matrix. - The first, :meth:`find_cyclotomic_solution` uses only - the pentagon and hexagon relations. The second, - :meth:`find_orthogonal_solution` uses additionally - the orthogonality relations. There are some differences - that should be kept in mind. - - :meth:`find_cyclotomic_solution` currently works only with - smaller examples. For example the :class:`FusionRing` for `G_2` - at level 2 is too large. When it is available, this method - produces an F-matrix whose entries are in the same - cyclotomic field as the underlying :class:`FusionRing`. - - :: - - sage: f.find_cyclotomic_solution() - Setting up hexagons and pentagons... - Finding a Groebner basis... - Solving... - Fixing the gauge... - adding equation... fx1 - 1 - adding equation... fx11 - 1 - Done! - - We now have access to the values of the F-matrix using - the methods :meth:`fmatrix` and :meth:`fmat`:: - - sage: f.fmatrix(s,s,s,s) - [(-1/2*zeta128^48 + 1/2*zeta128^16) 1] - [ 1/2 (1/2*zeta128^48 - 1/2*zeta128^16)] - sage: f.fmat(s,s,s,s,p,p) - (1/2*zeta128^48 - 1/2*zeta128^16) - - :meth:`find_orthogonal_solution` is much more powerful - and is capable of handling large cases, sometimes - quickly but sometimes (in larger cases) after hours of - computation. Its F-matrices are not always in the - cyclotomic field that is the base ring of the underlying - :class:`FusionRing`, but sometimes in an extension field adjoining - some square roots. When this happens, the :class:`FusionRing` is - modified, adding an attribute ``_basecoer`` that is - a coercion from the cyclotomic field to the field - containing the F-matrix. The field containing the F-matrix - is available through :meth:`field`. - - :: - sage: f = FMatrix(FusionRing("B3",2)) - sage: f.find_orthogonal_solution(verbose=False,checkpoint=True) # not tested (~100 s) - sage: all(v in CyclotomicField(56) for v in f.get_fvars().values()) # not tested - True - - sage: f = FMatrix(FusionRing("G2",2)) - sage: f.find_orthogonal_solution(verbose=False) # long time (~11 s) - sage: f.field() # long time - Algebraic Field + sage: TestSuite(f).run() """ self._FR = fusion_ring if inject_variables and (self._FR._fusion_labels is None): @@ -306,8 +311,10 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab ### Class utilities ### ####################### - def __repr__(self): + def _repr_(self): """ + Return a string representation of ``self``. + EXAMPLES:: sage: FMatrix(FusionRing("B2", 1)) @@ -329,7 +336,7 @@ def clear_equations(self): sage: len(f.ideal_basis) == 0 True """ - self.ideal_basis = list() + self.ideal_basis = [] def clear_vars(self): r""" @@ -350,12 +357,14 @@ def clear_vars(self): fx0 """ self._fvars = {t: self._poly_ring.gen(idx) for idx, t in self._idx_to_sextuple.items()} - self._solved = [False]*self._poly_ring.ngens() + self._solved = [False] * self._poly_ring.ngens() def _reset_solver_state(self): r""" - Reset solver state and clear relevant cache. Used to ensure - state variables are the same for each orthogonal solver run. + Reset solver state and clear relevant cache. + + Used to ensure state variables are the same for each + orthogonal solver run. EXAMPLES:: @@ -380,7 +389,6 @@ def _reset_solver_state(self): True sage: for k, v in f._ks.items(): ....: k - ....: sage: len(f._nnz.nonzero_positions()) == 1 True sage: all(len(x.q_dimension.cache) == 0 for x in f.FR().basis()) @@ -392,14 +400,14 @@ def _reset_solver_state(self): """ self._FR._basecoer = None self._field = self._FR.field() - self._non_cyc_roots = list() + self._non_cyc_roots = [] self._poly_ring = self._poly_ring.change_ring(self._field) self._chkpt_status = -1 self.clear_vars() self.clear_equations() n = self._poly_ring.ngens() - self._var_degs = [0]*n - self._kp = dict() + self._var_degs = [0] * n + self._kp = {} self._ks = KSHandler(n,self._field) self._singles = self.get_fvars_by_size(1,indices=True) self._nnz = self._get_known_nonz() @@ -436,7 +444,8 @@ def fmat(self, a, b, c, d, x, y, data=True): (-zeta60^14 + zeta60^6 + zeta60^4 - 1), (zeta60^14 - zeta60^6 - zeta60^4 + 1)] """ - if self._FR.Nk_ij(a,b,x) == 0 or self._FR.Nk_ij(x,c,d) == 0 or self._FR.Nk_ij(b,c,y) == 0 or self._FR.Nk_ij(a,y,d) == 0: + if (self._FR.Nk_ij(a,b,x) == 0 or self._FR.Nk_ij(x,c,d) == 0 + or self._FR.Nk_ij(b,c,y) == 0 or self._FR.Nk_ij(a,y,d) == 0): return 0 #Some known zero F-symbols @@ -471,7 +480,7 @@ def fmatrix(self,a,b,c,d): INPUT: - - ``a,b,c,d`` -- basis elements of the associated :class:`FusionRing` + - ``a, b, c, d`` -- basis elements of the associated :class:`FusionRing` EXAMPLES:: @@ -601,11 +610,11 @@ def findcases(self,output=False): def f_from(self,a,b,c,d): r""" Return the possible `x` such that there are morphisms - `d \to x \otimes c \to (a\otimes b)\otimes c`. + `d \to x \otimes c \to (a \otimes b) \otimes c`. INPUT: - - ``a,b,c,d`` -- basis elements of the associated :class:`FusionRing` + - ``a, b, c, d`` -- basis elements of the associated :class:`FusionRing` EXAMPLES:: @@ -625,11 +634,11 @@ def f_from(self,a,b,c,d): def f_to(self,a,b,c,d): r""" Return the possible `y` such that there are morphisms - `d\to a\otimes y\to a\otimes(b\otimes c)`. + `d \to a \otimes y \to a \otimes (b \otimes c)`. INPUT: - - ``a,b,c,d`` -- basis elements of the associated :class:`FusionRing` + - ``a, b, c, d`` -- basis elements of the associated :class:`FusionRing` EXAMPLES:: @@ -677,7 +686,7 @@ def get_fvars(self): def get_poly_ring(self): r""" - Return the polynomial ring whose generators denote F-symbols we seek. + Return the polynomial ring whose generators denote the desired F-symbols. EXAMPLES:: @@ -1238,8 +1247,8 @@ def start_worker_pool(self, processes=None): INPUT: - ``processes`` -- an integer indicating the number of workers - in the pool. If left unspecified, the number of workers is - equals the number of processors available. + in the pool; if left unspecified, the number of workers is + equals the number of processors available OUTPUT: @@ -1319,7 +1328,7 @@ def init(fmats_id, solved_name, vd_name, ks_names, fvar_names, n_proc, pids_name return True def shutdown_worker_pool(self): - """ + r""" Shutdown the given worker pool and dispose of shared memory resources created when the pool was set up using :meth:`start_worker_pool`. @@ -1327,8 +1336,8 @@ def shutdown_worker_pool(self): Failure to call this method after using :meth:`start_worker_pool` to create a process pool may result in a memory - leak, since shared memory resources outlive the process that created - them. + leak, since shared memory resources outlive the process that + created them. EXAMPLES:: @@ -1354,7 +1363,7 @@ def _map_triv_reduce(self, mapper, input_iter, worker_pool=None, chunksize=None, INPUT: - -``mapper`` -- string specifying the name of a function defined in + - ``mapper`` -- string specifying the name of a function defined in the ``fast_parallel_fmats_methods`` module .. NOTE:: @@ -1839,7 +1848,9 @@ def _get_component_variety(self,var,eqns): ### Solution method ### ####################### - #TODO: this can probably be improved by constructing a set of defining polynomials and checking, one by one, if it's irreducible over the current field. If it is, we construct an extension. Perhaps it's best to go one by one here... + # TODO: this can probably be improved by constructing a set of defining polynomials + # and checking, one by one, if it's irreducible over the current field. + # If it is, we construct an extension. Perhaps it's best to go one by one here... def attempt_number_field_computation(self): r""" Based on the ``CartanType`` of ``self`` and data @@ -1895,6 +1906,8 @@ def attempt_number_field_computation(self): def _get_explicit_solution(self,eqns=None,verbose=True): r""" + Construct an explicit solution of ``self``. + When this method is called, the solution is already found in terms of Groeber basis. A few degrees of freedom remain. By specializing the free variables and back substituting, a @@ -2210,7 +2223,7 @@ def _fix_gauge(self, algorithm=""): adding equation... fx18 - 1 adding equation... fx21 - 1 """ - while sum(1 for v in self._solved if not v) > 0: + while not all(v for v in self._solved): #Get a variable that has not been fixed #In ascending index order, for consistent results for i, var in enumerate(self._poly_ring.gens()): @@ -2283,7 +2296,7 @@ def _update_equations(self): sage: f.ideal_basis {fx3} """ - special_values = {known : self._fvars[self._var_to_sextuple[known]] for known in self._solved if known} + special_values = {known: self._fvars[self._var_to_sextuple[known]] for known in self._solved if known} self.ideal_basis = set(eq.subs(special_values) for eq in self.ideal_basis) self.ideal_basis.discard(0) @@ -2369,45 +2382,45 @@ def find_cyclotomic_solution(self, equations=None, algorithm="", verbose=True, o ##################### def fmats_are_orthogonal(self): - r""" - Verify that all F-matrices are orthogonal. + r""" + Verify that all F-matrices are orthogonal. - This method should always return ``True`` when called after running - :meth:`find_orthogonal_solution`. + This method should always return ``True`` when called after running + :meth:`find_orthogonal_solution`. - EXAMPLES:: + EXAMPLES:: - sage: f = FMatrix(FusionRing("D4", 1)) - sage: f.find_orthogonal_solution(verbose=False) - sage: f.fmats_are_orthogonal() - True - """ - is_orthog = [] - for a,b,c,d in product(self._FR.basis(),repeat=4): - mat = self.fmatrix(a,b,c,d) - is_orthog.append(mat.T * mat == matrix.identity(mat.nrows())) - return all(is_orthog) + sage: f = FMatrix(FusionRing("D4", 1)) + sage: f.find_orthogonal_solution(verbose=False) + sage: f.fmats_are_orthogonal() + True + """ + is_orthog = [] + for a,b,c,d in product(self._FR.basis(),repeat=4): + mat = self.fmatrix(a,b,c,d) + is_orthog.append(mat.T * mat == matrix.identity(mat.nrows())) + return all(is_orthog) def fvars_are_real(self): - r""" - Test whether all F-symbols are real. - - EXAMPLES:: - - sage: f = FMatrix(FusionRing("A1", 3)) - sage: f.find_orthogonal_solution(verbose=False) # long time - sage: f.fvars_are_real() # not tested (cypari issue in doctesting framework) - True - """ - try: - for k, v in self._fvars.items(): - AA(self._qqbar_embedding(v)) - except ValueError: - print("the F-symbol {} (key {}) has a nonzero imaginary part!".format(v,k)) - return False - return True - - def certify_pentagons(self,use_mp=True,verbose=False): + r""" + Test whether all F-symbols are real. + + EXAMPLES:: + + sage: f = FMatrix(FusionRing("A1", 3)) + sage: f.find_orthogonal_solution(verbose=False) # long time + sage: f.fvars_are_real() # not tested (cypari issue in doctesting framework) + True + """ + try: + for k, v in self._fvars.items(): + AA(self._qqbar_embedding(v)) + except ValueError: + print("the F-symbol {} (key {}) has a nonzero imaginary part".format(v,k)) + return False + return True + + def certify_pentagons(self,use_mp=True, verbose=False): r""" Obtain a certificate of satisfaction for the pentagon equations, up to floating-point error. @@ -2442,8 +2455,8 @@ def certify_pentagons(self,use_mp=True,verbose=False): Partitioned 6 equations into 6 components of size: [1, 1, 1, 1, 1, 1] Computing appropriate NumberField... - sage: f.certify_pentagons() # not tested (long time ~1.5s, cypari issue in doctesting framework) - Success!!! Found valid F-symbols for The Fusion Ring of Type C3 and level 1 with Integer Ring coefficients + sage: f.certify_pentagons() is None # not tested (long time ~1.5s, cypari issue in doctesting framework) + True """ fvars_copy = deepcopy(self._fvars) self._fvars = {sextuple: float(rhs) for sextuple, rhs in self.get_fvars_in_alg_field().items()} @@ -2452,13 +2465,15 @@ def certify_pentagons(self,use_mp=True,verbose=False): else: pool = None n_proc = pool._processes if pool is not None else 1 - params = [(child_id,n_proc,verbose) for child_id in range(n_proc)] - pe = self._map_triv_reduce('pent_verify',params,worker_pool=pool,chunksize=1,mp_thresh=0) + params = [(child_id, n_proc, verbose) for child_id in range(n_proc)] + pe = self._map_triv_reduce('pent_verify', params, worker_pool=pool, chunksize=1, mp_thresh=0) if np.all(np.isclose(np.array(pe),0,atol=1e-7)): - print("Success!!! Found valid F-symbols for {}".format(self._FR)) + if verbose: + print("Found valid F-symbols for {}".format(self._FR)) pe = None else: - print("Ooops... something went wrong... These pentagons remain:") - print(pe) + if verbose: + print("Something went wrong. Pentagons remain.") self._fvars = fvars_copy return pe + diff --git a/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pxd b/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pxd index e0908ab5884..11dc0253b35 100644 --- a/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pxd +++ b/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pxd @@ -2,3 +2,4 @@ cdef _fmat(fvars, Nk_ij, one, a, b, c, d, x, y) cpdef _backward_subs(factory, bint flatten=*) cpdef executor(tuple params) cpdef _solve_for_linear_terms(factory, list eqns=*) + diff --git a/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pxd b/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pxd index a992f0339a4..b3eec73b15b 100644 --- a/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pxd +++ b/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pxd @@ -1,2 +1,3 @@ cpdef _unflatten_entries(factory, list entries) cpdef executor(tuple params) + diff --git a/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx b/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx index e82d36ce5d8..49af0008602 100644 --- a/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx +++ b/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx @@ -77,7 +77,7 @@ cdef odd_one_out_ij_cache = dict() cdef mid_sig_ij_cache = dict() cdef cached_mid_sig_ij(fusion_ring,row,col,a,b): - """ + r""" Cached version of :meth:`mid_sig_ij`. """ if (row,col,a,b) in mid_sig_ij_cache: @@ -87,7 +87,7 @@ cdef cached_mid_sig_ij(fusion_ring,row,col,a,b): return entry cdef cached_odd_one_out_ij(fusion_ring,xi,xj,a,b): - """ + r""" Cached version of :meth:`odd_one_out_ij`. """ if (xi,xj,a,b) in odd_one_out_ij_cache: @@ -178,7 +178,7 @@ cdef sig_2k(fusion_ring, tuple args): @cython.nonecheck(False) @cython.cdivision(True) cdef odd_one_out(fusion_ring, tuple args): - """ + r""" Compute entries of the rightmost braid generator, in case we have an odd number of strands. """ @@ -304,7 +304,7 @@ cpdef executor(tuple params): ###################################### cpdef _unflatten_entries(fusion_ring, list entries): - """ + r""" Restore cyclotomic coefficient object from its tuple of rational coefficients representation. diff --git a/src/sage/algebras/fusion_rings/poly_tup_engine.pyx b/src/sage/algebras/fusion_rings/poly_tup_engine.pyx index be804b4da8b..a34fa303d15 100644 --- a/src/sage/algebras/fusion_rings/poly_tup_engine.pyx +++ b/src/sage/algebras/fusion_rings/poly_tup_engine.pyx @@ -150,7 +150,7 @@ cdef inline int has_appropriate_linear_term(tuple eq_tup): ###################### cpdef inline tup_to_univ_poly(tuple eq_tup, univ_poly_ring): - """ + r""" Given a tuple of pairs representing a univariate polynomial and a univariate polynomial ring, return a univariate polynomial object. @@ -178,7 +178,7 @@ cpdef inline tup_to_univ_poly(tuple eq_tup, univ_poly_ring): return univ_poly_ring({exp._data[1] if exp._nonzero else 0: c for exp, c in eq_tup}) cpdef inline tuple resize(tuple eq_tup, dict idx_map, int nvars): - """ + r""" Return a tuple representing a polynomial in a ring with ``len(sorted_vars)`` generators. diff --git a/src/sage/algebras/fusion_rings/shm_managers.pxd b/src/sage/algebras/fusion_rings/shm_managers.pxd index 32dd0073091..342b533acae 100644 --- a/src/sage/algebras/fusion_rings/shm_managers.pxd +++ b/src/sage/algebras/fusion_rings/shm_managers.pxd @@ -1,5 +1,4 @@ cimport numpy as np -import numpy as np from sage.rings.number_field.number_field_base cimport NumberField from sage.rings.number_field.number_field_element cimport NumberFieldElement_absolute @@ -22,3 +21,4 @@ cdef class FvarsHandler: cdef object fvars_t, pid_list cdef Py_ssize_t child_id cdef public object shm + diff --git a/src/sage/algebras/fusion_rings/shm_managers.pyx b/src/sage/algebras/fusion_rings/shm_managers.pyx index 062a999fec4..21a9d0ea3eb 100644 --- a/src/sage/algebras/fusion_rings/shm_managers.pyx +++ b/src/sage/algebras/fusion_rings/shm_managers.pyx @@ -1,4 +1,4 @@ -""" +r""" Shared memory managers for F-symbol attributes. This module provides an implementation for shared dictionary like @@ -7,6 +7,7 @@ state attributes required by the orthogonal F-matrix solver. Currently, the attributes only work when the base field of the FMatrix factory is a cyclotomic field. """ + # **************************************************************************** # Copyright (C) 2021 Guillermo Aboumrad # @@ -15,8 +16,8 @@ factory is a cyclotomic field. # **************************************************************************** cimport cython -from cysignals.memory cimport sig_malloc cimport numpy as np +from cysignals.memory cimport sig_malloc from sage.algebras.fusion_rings.poly_tup_engine cimport poly_to_tup, tup_fixes_sq, _flatten_coeffs from sage.rings.integer cimport Integer from sage.rings.rational cimport Rational @@ -27,51 +28,80 @@ import numpy as np from os import getpid cdef class KSHandler: + r""" + A shared memory backed dict-like structure to manage the + ``_ks`` attribute of an F-matrix. + + This structure implements a representation of the known squares dictionary + using a structured NumPy array backed by a contiguous shared memory + object. + + The structure mimics a dictionary of ``(idx, known_sq)`` pairs. Each + integer index corresponds to a variable and each ``known_sq`` is an + element of the F-matrix factory's base cyclotomic field. + + Each cyclotomic coefficient is stored as a list of numerators and a + list of denominators representing the rational coefficients. The + structured array also maintains ``known`` attribute that indicates + whether the structure contains an entry corresponding to the given index. + + The parent process should construct this object without a + ``name`` attribute. Children processes use the ``name`` attribute, + accessed via ``self.shm.name`` to attach to the shared memory block. + + INPUT: + + - ``n_slots`` -- the total number of F-symbols + - ``field`` -- F-matrix's base cyclotomic field + - ``use_mp`` -- a boolean indicating whether to construct a shared + memory block to back ``self``. Requires Python 3.8+, since we + must import the ``multiprocessing.shared_memory`` module. Attempting + to initialize when ``multiprocessing.shared_memory`` is not available + results in an ``ImportError``. + - ``name`` -- the name of a shared memory object + (used by child processes for attaching) + - ``init_data`` -- a dictionary or :class:`KSHandler` object containing + known squares for initialization, e.g., from a solver checkpoint + + .. NOTE:: + + To properly dispose of shared memory resources, + ``self.shm.unlink()`` must be called before exiting. + + .. WARNING:: + + This structure *cannot* modify an entry that + has already been set. + + EXAMPLES:: + + sage: from sage.algebras.fusion_rings.shm_managers import KSHandler + sage: #Create shared data structure + sage: f = FMatrix(FusionRing("A1",2), inject_variables=True) + creating variables fx1..fx14 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 + sage: n = f._poly_ring.ngens() + sage: is_shared_memory_available = f.start_worker_pool() + sage: ks = KSHandler(n,f._field,use_mp=is_shared_memory_available) + sage: #In the same shell or in a different shell, attach to fvars + sage: name = ks.shm.name if is_shared_memory_available else None + sage: ks2 = KSHandler(n,f._field,name=name,use_mp=is_shared_memory_available) + sage: if not is_shared_memory_available: + ....: ks2 = ks + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup + sage: eqns = [fx1**2 - 4, fx3**2 + f._field.gen()**4 - 1/19*f._field.gen()**2] + sage: ks.update([poly_to_tup(p) for p in eqns]) + sage: for idx, sq in ks.items(): + ....: print("Index: {}, square: {}".format(idx, sq)) + ....: + Index: 1, square: 4 + Index: 3, square: -zeta32^4 + 1/19*zeta32^2 + sage: if is_shared_memory_available: ks.shm.unlink() + sage: f.shutdown_worker_pool() + """ def __init__(self, n_slots, field, use_mp=False, init_data={}, name=None): - """ - Return a shared memory backed dict-like structure to manage the - ``_ks`` attribute of an F-matrix factory object. - - This structure implements a representation of the known squares dictionary - using a structured NumPy array backed by a contiguous shared memory - object. - - The structure mimics a dictionary of ``(idx, known_sq)`` pairs. Each - integer index corresponds to a variable and each ``known_sq`` is an - element of the F-matrix factory's base cyclotomic field. - - Each cyclotomic coefficient is stored as a list of numerators and a - list of denominators representing the rational coefficients. The - structured array also maintains ``known`` attribute that indicates - whether the structure contains an entry corresponding to the given index. - - The parent process should construct this object without a - ``name`` attribute. Children processes use the ``name`` attribute, - accessed via ``self.shm.name`` to attach to the shared memory block. - - INPUT: - - - ``n_slots`` -- The total number of F-symbols. - - ``field`` -- F-matrix factory's base cyclotomic field. - - ``use_mp`` -- a boolean indicating whether to construct a shared - memory block to back ``self``. Requires Python 3.8+, since we - must import the ``multiprocessing.shared_memory`` module. Attempting - to initialize when ``multiprocessing.shared_memory`` is not available - results in an ``ImportError``. - - ``name`` -- the name of a shared memory object - (used by child processes for attaching). - - ``init_data`` -- a dictionary or :class:`KSHandler` object containing - known squares for initialization, e.g. from a solver checkpoint. - - .. NOTE:: - - To properly dispose of shared memory resources, - ``self.shm.unlink()`` must be called before exiting. - - .. WARNING:: - - This structure *cannot* modify an entry that - has already been set. + r""" + Initialize ``self``. EXAMPLES:: @@ -81,21 +111,9 @@ cdef class KSHandler: creating variables fx1..fx14 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 sage: n = f._poly_ring.ngens() - sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ + sage: is_shared_memory_available = f.start_worker_pool() sage: ks = KSHandler(n,f._field,use_mp=is_shared_memory_available) - sage: #In the same shell or in a different shell, attach to fvars - sage: name = ks.shm.name if is_shared_memory_available else None - sage: ks2 = KSHandler(n,f._field,name=name,use_mp=is_shared_memory_available) - sage: if not is_shared_memory_available: - ....: ks2 = ks - sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup - sage: eqns = [fx1**2 - 4, fx3**2 + f._field.gen()**4 - 1/19*f._field.gen()**2] - sage: ks.update([poly_to_tup(p) for p in eqns]) - sage: for idx, sq in ks.items(): - ....: print("Index: {}, square: {}".format(idx, sq)) - ....: - Index: 1, square: 4 - Index: 3, square: -zeta32^4 + 1/19*zeta32^2 + sage: TestSuite(ks).run() sage: if is_shared_memory_available: ks.shm.unlink() sage: f.shutdown_worker_pool() """ @@ -134,7 +152,7 @@ cdef class KSHandler: @cython.wraparound(False) @cython.boundscheck(False) cdef NumberFieldElement_absolute get(self, int idx): - """ + r""" Retrieve the known square corresponding to the given index, if it exists. """ @@ -259,14 +277,14 @@ cdef class KSHandler: denoms[i] = denom cdef bint contains(self, int idx): - """ + r""" Determine whether ``self`` contains entry corresponding to given ``idx``. """ return self.ks_dat[idx]['known'] def __eq__(self, KSHandler other): - """ + r""" Test for equality. TESTS:: @@ -276,7 +294,7 @@ cdef class KSHandler: sage: f.get_orthogonality_constraints(output=False) sage: from sage.algebras.fusion_rings.shm_managers import KSHandler sage: n = f._poly_ring.ngens() - sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ + sage: is_shared_memory_available = f.start_worker_pool() sage: ks = KSHandler(n,f._field,use_mp=is_shared_memory_available,init_data=f._ks) sage: #In the same shell or in a different one, attach to shared memory handler sage: name = ks.shm.name if is_shared_memory_available else None @@ -288,13 +306,10 @@ cdef class KSHandler: sage: if is_shared_memory_available: ks.shm.unlink() sage: f.shutdown_worker_pool() """ - ret = True - for idx, sq in self.items(): - ret &= other.get(idx) == sq - return ret + return all(other.get(idx) == sq for idx, sq in self.items()) def __reduce__(self): - """ + r""" Provide pickling / unpickling support for ``self.`` TESTS:: @@ -308,10 +323,10 @@ cdef class KSHandler: True """ d = {i: sq for i, sq in self.items()} - return make_KSHandler, (self.ks_dat.size,self.field,d) + return make_KSHandler, (self.ks_dat.size, self.field, d) def items(self): - """ + r""" Iterate through existing entries using Python dict-style syntax. EXAMPLES:: @@ -338,91 +353,122 @@ cdef class KSHandler: yield i, self.get(i) def make_KSHandler(n_slots,field,init_data): - """ + r""" Provide pickling / unpickling support for :class:`KSHandler`. TESTS:: sage: f = FMatrix(FusionRing("B4",1)) sage: f._reset_solver_state() - sage: loads(dumps(f._ks)) == f._ks #indirect doctest + sage: loads(dumps(f._ks)) == f._ks # indirect doctest True - sage: f.find_orthogonal_solution(verbose=False) #long time - sage: loads(dumps(f._ks)) == f._ks #indirect doctest + sage: f.find_orthogonal_solution(verbose=False) # long time + sage: loads(dumps(f._ks)) == f._ks # indirect doctest True """ - return KSHandler(n_slots,field,init_data=init_data) + return KSHandler(n_slots, field, init_data=init_data) cdef class FvarsHandler: - def __init__(self, n_slots, field, idx_to_sextuple, init_data={}, use_mp=0, - pids_name=None, name=None, max_terms=20, n_bytes=32): - """ - Return a shared memory backed dict-like structure to manage the - ``_fvars`` attribute of an F-matrix factory object. - - This structure implements a representation of the F-symbols dictionary - using a structured NumPy array backed by a contiguous shared memory - object. - - The monomial data is stored in the ``exp_data`` structure. Monomial - exponent data is stored contiguously and ``ticks`` are used to - indicate different monomials. - - Coefficient data is stored in the ``coeff_nums`` and ``coeff_denom`` - arrays. The ``coeff_denom`` array stores the value - ``d = coeff.denominator()`` for each cyclotomic coefficient. The - ``coeff_nums`` array stores the values - ``c.numerator() * d for c in coeff._coefficients()``, the abridged - list representation of the cyclotomic coefficient ``coeff``. - - Each entry also has a boolean ``modified`` attribute, indicating - whether it has been modified by the parent process. Entry retrieval - is cached in each process, so each process must check whether - entries have been modified before attempting retrieval. - - The parent process should construct this object without a - ``name`` attribute. Children processes use the ``name`` attribute, - accessed via ``self.shm.name`` to attach to the shared memory block. - - INPUT: - - - ``factory`` -- an F-matrix factory object - - ``name`` -- the name of a shared memory object - (used by child processes for attaching) - - ``max_terms`` -- maximum number of terms in each entry; since - we use contiguous C-style memory blocks, the size of the block - must be known in advance - - ``use_mp`` -- an integer indicating the number of child processes - used for multiprocessing; if running serially, use 0. - - Multiprocessing requires Python 3.8+, since we must import the - ``multiprocessing.shared_memory`` module. Attempting to initialize - when ``multiprocessing.shared_memory`` is not available results in - an ``ImportError``. - - - ``pids_name`` -- the name of a ``ShareableList`` contaning the - process ``pid``'s for every process in the pool (including the - parent process) - - ``n_bytes`` -- the number of bytes that should be allocated for - each numerator and each denominator stored by the structure + r""" + A shared memory backed dict-like structure to manage the + ``_fvars`` attribute of an F-matrix. - .. NOTE:: + This structure implements a representation of the F-symbols dictionary + using a structured NumPy array backed by a contiguous shared memory + object. - To properly dispose of shared memory resources, - ``self.shm.unlink()`` must be called before exiting. + The monomial data is stored in the ``exp_data`` structure. Monomial + exponent data is stored contiguously and ``ticks`` are used to + indicate different monomials. - .. NOTE:: + Coefficient data is stored in the ``coeff_nums`` and ``coeff_denom`` + arrays. The ``coeff_denom`` array stores the value + ``d = coeff.denominator()`` for each cyclotomic coefficient. The + ``coeff_nums`` array stores the values + ``c.numerator() * d for c in coeff._coefficients()``, the abridged + list representation of the cyclotomic coefficient ``coeff``. - If you ever encounter an ``OverflowError`` when running the - :meth:`FMatrix.find_orthogonal_solution`` solver, consider - increasing the parameter ``n_bytes``. + Each entry also has a boolean ``modified`` attribute, indicating + whether it has been modified by the parent process. Entry retrieval + is cached in each process, so each process must check whether + entries have been modified before attempting retrieval. - .. WARNING:: + The parent process should construct this object without a + ``name`` attribute. Children processes use the ``name`` attribute, + accessed via ``self.shm.name`` to attach to the shared memory block. + + INPUT: + + - ``factory`` -- an F-matrix + - ``name`` -- the name of a shared memory object + (used by child processes for attaching) + - ``max_terms`` -- maximum number of terms in each entry; since + we use contiguous C-style memory blocks, the size of the block + must be known in advance + - ``use_mp`` -- an integer indicating the number of child processes + used for multiprocessing; if running serially, use 0. + + Multiprocessing requires Python 3.8+, since we must import the + ``multiprocessing.shared_memory`` module. Attempting to initialize + when ``multiprocessing.shared_memory`` is not available results in + an ``ImportError``. + + - ``pids_name`` -- the name of a ``ShareableList`` contaning the + process ``pid``'s for every process in the pool (including the + parent process) + - ``n_bytes`` -- the number of bytes that should be allocated for + each numerator and each denominator stored by the structure + + .. NOTE:: + + To properly dispose of shared memory resources, + ``self.shm.unlink()`` must be called before exiting. + + .. NOTE:: + + If you ever encounter an ``OverflowError`` when running the + :meth:`FMatrix.find_orthogonal_solution` solver, consider + increasing the parameter ``n_bytes``. + + .. WARNING:: - The current data structure supports up to `2^16` entries, - with each monomial in each entry having at most 254 - nonzero terms. On average, each of the ``max_terms`` monomials - can have at most 30 terms. + The current data structure supports up to `2^16` entries, + with each monomial in each entry having at most 254 + nonzero terms. On average, each of the ``max_terms`` monomials + can have at most 30 terms. + + EXAMPLES:: + + sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler + sage: #Create shared data structure + sage: f = FMatrix(FusionRing("A2",1), inject_variables=True) + creating variables fx1..fx8 + Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 + sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ + sage: if is_shared_memory_available: + ....: n_proc = f.pool._processes + ....: pids_name = f._pid_list.shm.name + ....: else: + ....: n_proc = 0 + ....: pids_name = None + sage: fvars = FvarsHandler(8, f._field, f._idx_to_sextuple, use_mp=n_proc, pids_name=pids_name) + sage: #In the same shell or in a different shell, attach to fvars + sage: name = fvars.shm.name if is_shared_memory_available else None + sage: fvars2 = FvarsHandler(8, f._field, f._idx_to_sextuple, name=name ,use_mp=n_proc, pids_name=pids_name) + sage: if not is_shared_memory_available: + ....: fvars2 = fvars + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup + sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(fx5**5)) + sage: fvars[f2, f1, f2, f2, f0, f0] = rhs + sage: f._tup_to_fpoly(fvars2[f2, f1, f2, f2, f0, f0]) + fx5^5 + sage: if is_shared_memory_available: fvars.shm.unlink() + sage: f.shutdown_worker_pool() + """ + def __init__(self, n_slots, field, idx_to_sextuple, init_data={}, use_mp=0, + pids_name=None, name=None, max_terms=20, n_bytes=32): + r""" + Initialize ``self``. EXAMPLES:: @@ -439,16 +485,7 @@ cdef class FvarsHandler: ....: n_proc = 0 ....: pids_name = None sage: fvars = FvarsHandler(8,f._field,f._idx_to_sextuple,use_mp=n_proc,pids_name=pids_name) - sage: #In the same shell or in a different shell, attach to fvars - sage: name = fvars.shm.name if is_shared_memory_available else None - sage: fvars2 = FvarsHandler(8,f._field,f._idx_to_sextuple,name=name,use_mp=n_proc,pids_name=pids_name) - sage: if not is_shared_memory_available: - ....: fvars2 = fvars - sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup - sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(fx5**5)) - sage: fvars[f2, f1, f2, f2, f0, f0] = rhs - sage: f._tup_to_fpoly(fvars2[f2, f1, f2, f2, f0, f0]) - fx5^5 + sage: TestSuite(fvars).run() sage: if is_shared_memory_available: fvars.shm.unlink() sage: f.shutdown_worker_pool() """ @@ -611,7 +648,7 @@ cdef class FvarsHandler: @cython.nonecheck(False) @cython.wraparound(False) def __setitem__(self, sextuple, fvar): - """ + r""" Given a sextuple of labels and a tuple of ``(ETuple, cyc_coeff)`` pairs, create or overwrite an entry in the shared data structure corresponding to the given sextuple. @@ -623,7 +660,7 @@ cdef class FvarsHandler: sage: f = FMatrix(FusionRing("A3", 1), inject_variables=True) creating variables fx1..fx27 Defining fx0, ..., fx26 - sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ + sage: is_shared_memory_available = f.start_worker_pool() sage: if is_shared_memory_available: ....: n_proc = f.pool._processes ....: pids_name = f._pid_list.shm.name @@ -700,7 +737,7 @@ cdef class FvarsHandler: modified[:] = 1 def __reduce__(self): - """ + r""" Provide pickling / unpickling support for ``self.`` TESTS:: @@ -708,7 +745,7 @@ cdef class FvarsHandler: sage: f = FMatrix(FusionRing("F4",1)) sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() - sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ + sage: is_shared_memory_available = f.start_worker_pool() sage: if is_shared_memory_available: ....: n_proc = f.pool._processes ....: pids_name = f._pid_list.shm.name @@ -728,7 +765,7 @@ cdef class FvarsHandler: return make_FvarsHandler, (n,self.field,idx_map,d) def items(self): - """ + r""" Iterates through key-value pairs in the data structure as if it were a Python dict. @@ -752,7 +789,7 @@ cdef class FvarsHandler: yield sextuple, self[sextuple] def make_FvarsHandler(n,field,idx_map,init_data): - """ + r""" Provide pickling / unpickling support for :class:`FvarsHandler`. TESTS:: @@ -760,7 +797,7 @@ def make_FvarsHandler(n,field,idx_map,init_data): sage: f = FMatrix(FusionRing("G2",1)) sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() - sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ + sage: is_shared_memory_available = f.start_worker_pool() sage: if is_shared_memory_available: ....: n_proc = f.pool._processes ....: pids_name = f._pid_list.shm.name diff --git a/src/sage/combinat/root_system/all.py b/src/sage/combinat/root_system/all.py index ebcf08bf0c6..263a4d916d2 100644 --- a/src/sage/combinat/root_system/all.py +++ b/src/sage/combinat/root_system/all.py @@ -76,7 +76,7 @@ --------------------- - :ref:`sage.combinat.root_system.weyl_characters` -- :ref:`sage.combinat.root_system.fusion_ring` +- :ref:`sage.algebras.fusion_rings.fusion_ring` - :ref:`sage.combinat.root_system.integrable_representations` - :ref:`sage.combinat.root_system.branching_rules` - :ref:`sage.combinat.root_system.hecke_algebra_representation` From b340a2923991ce5495bdbea5ee3a2029583aacc4 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Sat, 13 Aug 2022 23:25:23 +0800 Subject: [PATCH 150/632] references for .integer_representation(), and use it if available as it's faster --- src/sage/rings/finite_rings/element_base.pyx | 10 ++++++++++ src/sage/rings/finite_rings/element_givaro.pyx | 4 ++++ src/sage/rings/finite_rings/element_ntl_gf2e.pyx | 4 ++++ 3 files changed, 18 insertions(+) diff --git a/src/sage/rings/finite_rings/element_base.pyx b/src/sage/rings/finite_rings/element_base.pyx index 4761183fa0c..3c5f18c5e05 100755 --- a/src/sage/rings/finite_rings/element_base.pyx +++ b/src/sage/rings/finite_rings/element_base.pyx @@ -993,6 +993,11 @@ cdef class FinitePolyExtElement(FiniteRingElement): Inverse of :meth:`sage.rings.finite_rings.finite_field_base.FiniteField.from_integer`. + .. SEEALSO:: + + - :meth:`sage.rings.finite_rings.element_givaro.FiniteField_givaroElement.integer_representation` + - :meth:`sage.rings.finite_rings.element_ntl_gf2e.FiniteField_ntl_gf2eElement.integer_representation` + EXAMPLES:: sage: F. = GF(7^5) @@ -1031,6 +1036,11 @@ cdef class FinitePolyExtElement(FiniteRingElement): sage: F.from_integer(n, rev).to_integer(rev) == n True """ + if not reverse: + try: + return self.integer_representation() + except AttributeError: + pass p = self.parent().characteristic() f = self.polynomial().change_ring(ZZ) if reverse: diff --git a/src/sage/rings/finite_rings/element_givaro.pyx b/src/sage/rings/finite_rings/element_givaro.pyx index 025e9fe80ce..481d0c7c7a9 100644 --- a/src/sage/rings/finite_rings/element_givaro.pyx +++ b/src/sage/rings/finite_rings/element_givaro.pyx @@ -1362,6 +1362,10 @@ cdef class FiniteField_givaroElement(FinitePolyExtElement): OUTPUT: A Python ``int``. + .. SEEALSO:: + + :meth:`sage.rings.finite_rings.element_base.FinitePolyExtElement.to_integer` + EXAMPLES:: sage: k. = GF(5^2); k diff --git a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx index beda02aea78..459b6a7d011 100644 --- a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx +++ b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx @@ -940,6 +940,10 @@ cdef class FiniteField_ntl_gf2eElement(FinitePolyExtElement): for `e \in \GF{p}[x]` with `e = a_0 + a_1 x + a_2 x^2 + \cdots`, `e` is represented as: `n = a_0 + a_1 p + a_2 p^2 + \cdots`. + .. SEEALSO:: + + :meth:`sage.rings.finite_rings.element_base.FinitePolyExtElement.to_integer` + EXAMPLES:: sage: k. = GF(2^20) From 6230ed7eb00079dcd760814ee0f1872cdbe86106 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Sat, 13 Aug 2022 23:27:41 +0800 Subject: [PATCH 151/632] correct file title for NTL binary-field elements --- src/sage/rings/finite_rings/element_ntl_gf2e.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx index 459b6a7d011..127c24a4267 100644 --- a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx +++ b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx @@ -5,7 +5,7 @@ # distutils: extra_link_args = NTL_LIBEXTRA # distutils: language = c++ r""" -Finite Fields of characteristic 2. +Elements of Finite Fields of Characteristic 2 This implementation uses NTL's GF2E class to perform the arithmetic and is the standard implementation for ``GF(2^n)`` for ``n >= 16``. From 3f9045d6e677267ab3c49bb91ddf9f1887f872bb Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 17 Aug 2022 11:31:02 +0900 Subject: [PATCH 152/632] Implementing infinite q-Pochhammer symbol as a lazy Laurent series. --- src/sage/rings/lazy_series.py | 52 +++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index c5034928ab6..5be58d12b32 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -1911,6 +1911,58 @@ def coeff(n, c): valuation=0) return f(self) + def q_pochhammer(self, q=None): + r""" + Return the infinite ``q``-Pochhammer symbol `(a; q)_{\infty}`, + where `a` is ``self``. + + This is also one version of the quantum dilogarithm (see + :wikipedia:`Quantum_dilogarithm`) or + the `q`-Exponential function (see :wikipedia:`Q-exponential`). + + INPUT: + + - ``q`` -- (default: `q \in \QQ(q)`) the parameter `q` + + EXAMPLES:: + + sage: q = ZZ['q'].fraction_field().gen() + sage: L. = LazyLaurentSeriesRing(q.parent()) + sage: qpoch = z.q_pochhammer(q) + sage: qpoch + 1 + + (-1/(-q + 1))*z + + (q/(q^3 - q^2 - q + 1))*z^2 + + (-q^3/(-q^6 + q^5 + q^4 - q^2 - q + 1))*z^3 + + (q^6/(q^10 - q^9 - q^8 + 2*q^5 - q^2 - q + 1))*z^4 + + (-q^10/(-q^15 + q^14 + q^13 - q^10 - q^9 - q^8 + q^7 + q^6 + q^5 - q^2 - q + 1))*z^5 + + (q^15/(q^21 - q^20 - q^19 + q^16 + 2*q^14 - q^12 - q^11 - q^10 - q^9 + 2*q^7 + q^5 - q^2 - q + 1))*z^6 + + O(z^7) + + We show that `(z; q)_n = \frac{(z; q)_{\infty}}{(q^n z; q)_{\infty}}`:: + + sage: qpoch / qpoch(q*z) + 1 - z + O(z^7) + sage: qpoch / qpoch(q^2*z) + 1 + (-q - 1)*z + q*z^2 + O(z^7) + sage: qpoch / qpoch(q^3*z) + 1 + (-q^2 - q - 1)*z + (q^3 + q^2 + q)*z^2 - q^3*z^3 + O(z^7) + sage: qpoch / qpoch(q^4*z) + 1 + (-q^3 - q^2 - q - 1)*z + (q^5 + q^4 + 2*q^3 + q^2 + q)*z^2 + + (-q^6 - q^5 - q^4 - q^3)*z^3 + q^6*z^4 + O(z^7) + """ + if q is None: + q = ZZ['q'].fraction_field().gen() + from .lazy_series_ring import LazyLaurentSeriesRing + from sage.arith.misc import binomial + qP = q.parent() + L = LazyLaurentSeriesRing(qP, "z", sparse=self.parent()._sparse) + one = qP.one() + def coeff(n): + return (-1)**n * q**binomial(n,2) / qP.prod(one - q**i for i in range(1, n+1)) + f = L(coeff, valuation=0) + return f(self) + # === powers === def __pow__(self, n): From 80bc4e5886e18fab4d5de332b8cc41bbb8eebe08 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Wed, 17 Aug 2022 12:07:46 +0900 Subject: [PATCH 153/632] Adding more documetnation and Euler's function. --- src/sage/rings/lazy_series.py | 67 ++++++++++++++++++++++++++++++++--- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 5be58d12b32..f105380f2ed 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -1911,14 +1911,15 @@ def coeff(n, c): valuation=0) return f(self) + # === named special functions === + def q_pochhammer(self, q=None): r""" Return the infinite ``q``-Pochhammer symbol `(a; q)_{\infty}`, where `a` is ``self``. - This is also one version of the quantum dilogarithm (see - :wikipedia:`Quantum_dilogarithm`) or - the `q`-Exponential function (see :wikipedia:`Q-exponential`). + This is also one version of the quantum dilogarithm or + the `q`-Exponential function. INPUT: @@ -1950,6 +1951,19 @@ def q_pochhammer(self, q=None): sage: qpoch / qpoch(q^4*z) 1 + (-q^3 - q^2 - q - 1)*z + (q^5 + q^4 + 2*q^3 + q^2 + q)*z^2 + (-q^6 - q^5 - q^4 - q^3)*z^3 + q^6*z^4 + O(z^7) + + We can also construct part of Euler's function:: + + sage: M. = LazyLaurentSeriesRing(QQ) + sage: phi = sum(qpoch[i](q=a)*a^i for i in range(10)) + sage: phi[:20] == a.euler()[:20] + True + + REFERENCES: + + - :wikipedia:`Q-Pochhammer_symbol` + - :wikipedia:`Quantum_dilogarithm` + - :wikipedia:`Q-exponential` """ if q is None: q = ZZ['q'].fraction_field().gen() @@ -1959,10 +1973,55 @@ def q_pochhammer(self, q=None): L = LazyLaurentSeriesRing(qP, "z", sparse=self.parent()._sparse) one = qP.one() def coeff(n): - return (-1)**n * q**binomial(n,2) / qP.prod(one - q**i for i in range(1, n+1)) + return (-1)**n * q**binomial(n, 2) / qP.prod(one - q**i for i in range(1, n+1)) f = L(coeff, valuation=0) return f(self) + def euler(self, q=None): + r""" + Return the Euler function evaluated at ``self``. + + The *Euler function* is defined as + + .. MATH:: + + \phi(z) = (z; z)_{\infty} + = \sum_{n=0}^{\infty} (-1)^n q^{(3n^2-n)/2}. + + INPUT: + + - ``q`` -- (default: `q \in \QQ(q)`) the parameter `q` + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: phi = q.euler() + sage: phi + 1 - q - q^2 + q^5 + O(q^7) + + We verify that `1 / phi` gives the generating function + for all partitions:: + + sage: P = 1 / phi; P + 1 + q + 2*q^2 + 3*q^3 + 5*q^4 + 7*q^5 + 11*q^6 + O(q^7) + sage: P[:20] == [Partitions(n).cardinality() for n in range(20)] + True + + REFERENCES: + + - :wikipedia:`Euler_function` + """ + from .lazy_series_ring import LazyLaurentSeriesRing + L = LazyLaurentSeriesRing(ZZ, "q", sparse=self.parent()._sparse) + def coeff(n): + k = ZZ(24 * n + 1) + m, rem = k.sqrtrem() + if rem: + return ZZ.zero() + return (-1) ** ((m + 1) // 6) + phi = L(coeff, valuation=0) + return phi(self) + # === powers === def __pow__(self, n): From 37a0c7ff2789bfacf767568e619b1de3c1109503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 20 Jun 2022 15:07:55 +0200 Subject: [PATCH 154/632] first sketch for FHM triangles --- src/doc/en/reference/combinat/module_list.rst | 1 + src/sage/combinat/posets/posets.py | 51 +- src/sage/combinat/triangles_FHM.py | 678 ++++++++++++++++++ src/sage/topology/simplicial_complex.py | 39 + 4 files changed, 767 insertions(+), 2 deletions(-) create mode 100644 src/sage/combinat/triangles_FHM.py diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index 7a97a40e30d..8264d7c4521 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -362,6 +362,7 @@ Comprehensive Module List sage/combinat/tamari_lattices sage/combinat/tiling sage/combinat/tools + sage/combinat/triangles_FHM sage/combinat/tuple sage/combinat/tutorial sage/combinat/vector_partition diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 2c236e4a4ae..25b8e110181 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -184,6 +184,7 @@ :meth:`~FinitePoset.flag_h_polynomial` | Return the flag h-polynomial of the poset. :meth:`~FinitePoset.order_polynomial` | Return the order polynomial of the poset. :meth:`~FinitePoset.zeta_polynomial` | Return the zeta polynomial of the poset. + :meth:`~FinitePoset.M_triangle` | Return the M-triangle of the poset. :meth:`~FinitePoset.kazhdan_lusztig_polynomial` | Return the Kazhdan-Lusztig polynomial of the poset. :meth:`~FinitePoset.coxeter_polynomial` | Return the characteristic polynomial of the Coxeter transformation. :meth:`~FinitePoset.degree_polynomial` | Return the generating polynomial of degrees of vertices in the Hasse diagram. @@ -7257,6 +7258,47 @@ def zeta_polynomial(self): f = g[n] + f / n return f + def M_triangle(self): + r""" + Return the M-triangle of the poset. + + The poset is expected to be graded. + + OUTPUT: + + an :class:`~sage.combinat.triangles_FHM.M_triangle` + + Thie M-triangle is the generating polynomial of the Möbius numbers + + .. MATH:: + + M(x, y)=\sum_{a \leq b} \mu(a,b) x^{|a|}y^{|b|} . + + EXAMPLES:: + + sage: P = posets.DiamondPoset(5) + sage: P.M_triangle() + x^2*y^2 - 3*x*y^2 + 3*x*y + 2*y^2 - 3*y + 1 + + TESTS:: + + sage: P = posets.PentagonPoset() + sage: P.M_triangle() + Traceback (most recent call last): + ... + ValueError: the poset is not graded + """ + from sage.combinat.triangles_FHM import M_triangle + hasse = self._hasse_diagram + rk = hasse.rank_function() + if rk is None: + raise ValueError('the poset is not graded') + x, y = polygen(ZZ, 'x,y') + p = sum(hasse.moebius_function(a, b) * x**rk(a) * y**rk(b) + for a in hasse + for b in hasse.principal_order_filter(a)) + return M_triangle(p) + def f_polynomial(self): r""" Return the `f`-polynomial of the poset. @@ -7294,12 +7336,17 @@ def f_polynomial(self): sage: P = Poset({2: []}) sage: P.f_polynomial() 1 + + sage: P = Poset({2:[1,3]}) + sage: P.f_polynomial() + Traceback (most recent call last): + ... + ValueError: the poset is not bounded """ q = polygen(ZZ, 'q') - one = q.parent().one() hasse = self._hasse_diagram if len(hasse) == 1: - return one + return q.parent().one() maxi = hasse.top() mini = hasse.bottom() if mini is None or maxi is None: diff --git a/src/sage/combinat/triangles_FHM.py b/src/sage/combinat/triangles_FHM.py new file mode 100644 index 00000000000..1f9f830ef9b --- /dev/null +++ b/src/sage/combinat/triangles_FHM.py @@ -0,0 +1,678 @@ +""" +Combinatorial triangles for posets and fans + +This provides several classes and methods to convert between them. +Elements of the classes are polynomials in two variables `x` and `y`, +possibly with other parameters. The conversion methods amount to specific +invertible rational change-of-variables involving `x` and `y`. + +The M-triangle class is motivated by the generating series of Möbius numbers +for graded posets. A typical example is:: + + sage: W = SymmetricGroup(4) + sage: posets.NoncrossingPartitions(W).M_triangle() + x^3*y^3 - 6*x^2*y^3 + 6*x^2*y^2 + 10*x*y^3 - 16*x*y^2 - 5*y^3 + + 6*x*y + 10*y^2 - 6*y + 1 + +The F-triangle class is motivated by the generating series of pure +simplicial complexes endowed with a distinguished facet. One can also +think about complete fans endowed with a distinguished maximal +cone. A typical example is:: + + sage: C = ClusterComplex(['A',3]) + sage: f = C.greedy_facet() + sage: C.F_triangle(f) + 5*x^3 + 5*x^2*y + 3*x*y^2 + y^3 + 10*x^2 + 8*x*y + 3*y^2 + 6*x + 3*y + 1 + +The H-triangles are related to the F-triangles by a relationship +similar to the classical link between the f-vector and the h-vector of a +simplicial complex. + +The Gamma-triangles are related to the H-triangles by an +analog of the relationship between gamma-vectors and h-vectors of flag +simplicial complexes. +""" +from sage.matrix.constructor import matrix +from sage.rings.integer_ring import ZZ +from sage.rings.polynomial.polynomial_ring import polygen +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.structure.sage_object import SageObject + + +def _matrix_display(self, variables=None): + """ + Return the 2-variables polynomial ``self`` as a matrix for display. + + INPUT: + + - ``variables`` -- optional choice of 2 variables + + OUPUT: + + matrix + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import _matrix_display + sage: x, y = PolynomialRing(QQ,['x', 'y']).gens() + sage: _matrix_display(x**2+x*y+y**3) + [1 0 0] + [0 0 0] + [0 1 0] + [0 0 1] + + With a specific choice of variables:: + + sage: x, y, z = PolynomialRing(QQ,['x','y','z']).gens() + sage: _matrix_display(x**2+z*x*y+z*y**3+z*x,[y,z]) + [ x x 0 1] + [x^2 0 0 0] + sage: _matrix_display(x**2+z*x*y+z*y**3+z*x,[x,z]) + [ y^3 y + 1 0] + [ 0 0 1] + """ + support = self.exponents() + if variables is None: + ring = self.parent().base_ring() + x, y = self.parent().gens() + ix = 0 + iy = 1 + else: + x, y = variables + ring = self.parent() + toutes_vars = x.parent().gens() + ix = toutes_vars.index(x) + iy = toutes_vars.index(y) + minx = min(u[ix] for u in support) + maxy = max(u[iy] for u in support) + deltax = max(u[ix] for u in support) - minx + 1 + deltay = maxy - min(u[iy] for u in support) + 1 + mat = matrix(ring, deltay, deltax) + for u in support: + ex = u[ix] + ey = u[iy] + mat[maxy - ey, ex - minx] = self.coefficient({x: ex, y: ey}) + return mat + + +class Triangle(SageObject): + """ + Common class for different kinds of triangles. + + This serves as a base class for F-triangles, H-triangles, M-triangles + and Gamma-triangles. + + The user should use these subclasses directly. + + The input is a polynomial in two variables. One can also give a + polynomial with more variables and specify two chosen variables. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import Triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: ht = Triangle(1+4*x+2*x*y) + sage: unicode_art(ht) + ⎛0 2⎞ + ⎝1 4⎠ + """ + def __init__(self, poly, variables=None): + """ + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import Triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: ht = Triangle(1+2*x*y) + sage: unicode_art(ht) + ⎛0 2⎞ + ⎝1 0⎠ + """ + if variables is None: + self._vars = poly.parent().gens() + else: + self._vars = variables + self._poly = poly + self._n = max(self._poly.degree(v) for v in self._vars) + + def _unicode_art_(self): + """ + Return the unicode representation (as a matrix). + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: ht = H_triangle(1+2*x*y) + sage: unicode_art(ht) + ⎛0 2⎞ + ⎝1 0⎠ + """ + return self.matrix()._unicode_art_() + + def _repr_(self) -> str: + """ + Return the string representation (as a polynomial). + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: ht = H_triangle(1+2*x*y) + sage: ht + 2*x*y + 1 + """ + return repr(self._poly) + + def __eq__(self, other) -> bool: + """ + Test for equality. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: h1 = H_triangle(1+2*x*y) + sage: h2 = H_triangle(1+3*x*y) + sage: h1 == h1 + True + sage: h1 == h2 + False + """ + if isinstance(other, Triangle): + return self._poly == other._poly + return self._poly == other + + def __ne__(self, other) -> bool: + """ + Test for unequality. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: h1 = H_triangle(1+2*x*y) + sage: h2 = H_triangle(1+3*x*y) + sage: h1 != h1 + False + sage: h1 != h2 + True + """ + return not self == other + + def __call__(self, *args): + """ + Return the evaluation (as a polynomial). + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: h = H_triangle(1+3*x*y) + sage: h(4,5) + 61 + """ + return self._poly(*args) + + def __getitem__(self, *args): + """ + Return some coefficient. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: h = H_triangle(1+2*x+3*x*y) + sage: h[1,1] + 3 + """ + return self._poly.__getitem__(*args) + + def __hash__(self): + """ + Return the hash value. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: h = H_triangle(1+2*x*y) + sage: g = H_triangle(1+2*x*y) + sage: hash(h) == hash(g) + True + """ + return hash(self._poly) + + def matrix(self): + """ + Return the associated matrix for display. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: h = H_triangle(1+2*x*y) + sage: h.matrix() + [0 2] + [1 0] + """ + return _matrix_display(self._poly, variables=self._vars) + + def truncate(self, d): + """ + Return the truncated triangle. + + INPUT: + + - ``d`` -- integer + + As a polynomial, this means that all monomials with a power + of either `x` or `y` greater than or equal to ``d`` are dismissed. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: h = H_triangle(1+2*x*y) + sage: h.truncate(2) + 2*x*y + 1 + """ + p = self._poly + for v in self._vars: + p = p.truncate(v, d) + return self.__class__(p, self._vars) + + +class M_triangle(Triangle): + """ + Class for the M-triangles. + + This is motivated by generating series of Möbius numbers of graded posets. + + EXAMPLES:: + + sage: x, y = polygens(ZZ, 'x,y') + sage: P = Poset({2:[1]}) + sage: P.M_triangle() + x*y - y + 1 + """ + def dual(self): + """ + Return the dual M-triangle. + + This is the M-triangle of the dual poset, hence an involution. + + When seen as a matrix, this performs a symmetry with respect to the + northwest-southeast diagonal. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import M_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: mt = M_triangle(x*y - y + 1) + sage: mt.dual() == mt + True + """ + x, y = self._vars + n = self._n + A = self._poly.parent() + + dict_dual = {(n - dy, n - dx): coeff + for (dx, dy), coeff in self._poly.dict().items()} + return M_triangle(A(dict_dual), variables=(x, y)) + + def transpose(self): + """ + Return the image of ``self`` by an involution. + + OUTPUT: + + another M-triangle + + The involution is defined by converting to an H-triangle, + transposing the matrix, and then converting back to an M-triangle. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import M_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: nc3 = x^2*y^2 - 3*x*y^2 + 3*x*y + 2*y^2 - 3*y + 1 + sage: m = M_triangle(nc3) + sage: m2 = m.transpose(); m2 + 2*x^2*y^2 - 3*x*y^2 + 2*x*y + y^2 - 2*y + 1 + sage: m2.transpose() == m + True + """ + return self.h().transpose().m() + + def h(self): + """ + Return the associated H-triangle. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import M_triangle + sage: x, y = polygens(ZZ,'x,y') + sage: M_triangle(1-y+x*y).h() + x*y + 1 + + TESTS:: + + sage: h = polygen(ZZ, 'h') + sage: x, y = polygens(h.parent(),'x,y') + sage: mt = x**2*y**2+(-2*h+2)*x*y**2+(2*h-2)*x*y+(2*h-3)*y**2+(-2*h+2)*y+1 + sage: M_triangle(mt, [x,y]).h() + x^2*y^2 + 2*x*y + (2*h - 4)*x + 1 + """ + x, y = self._vars + n = self._n + step = self._poly(x=y / (y - 1), y=(y - 1) * x / (1 + (y - 1) * x)) + step *= (1 + (y - 1) * x)**n + polyh = step.numerator() + return H_triangle(polyh, variables=(x, y)) + + def f(self): + """ + Return the associated F-triangle. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import M_triangle + sage: x, y = polygens(ZZ,'x,y') + sage: M_triangle(1-y+x*y).f() + x + y + 1 + + TESTS:: + + sage: h = polygen(ZZ, 'h') + sage: x, y = polygens(h.parent(),'x,y') + sage: mt = x**2*y**2+(-2*h+2)*x*y**2+(2*h-2)*x*y+(2*h-3)*y**2+(-2*h+2)*y+1 + sage: M_triangle(mt, [x,y]).f() + (2*h - 3)*x^2 + 2*x*y + y^2 + (2*h - 2)*x + 2*y + 1 + """ + return self.h().f() + + +class H_triangle(Triangle): + """ + Class for the H-triangles. + """ + def transpose(self): + """ + Return the transposed H-triangle. + + OUTPUT: + + another H-triangle + + This operation is an involution. When seen as a matrix, it + performs a symmetry with respect to the northwest-southeast + diagonal. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ,'x,y') + sage: H_triangle(1+x*y).transpose() + x*y + 1 + sage: H_triangle(x^2*y^2 + 2*x*y + x + 1).transpose() + x^2*y^2 + x^2*y + 2*x*y + 1 + """ + x, y = self._vars + n = self._n + A = self._poly.parent() + + dict_dual = {(n - dy, n - dx): coeff + for (dx, dy), coeff in self._poly.dict().items()} + return H_triangle(A(dict_dual), variables=(x, y)) + + def m(self): + """ + Return the associated M-triangle. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: h = polygen(ZZ, 'h') + sage: x, y = polygens(h.parent(),'x,y') + sage: ht = H_triangle(x^2*y^2 + 2*x*y + 2*x*h - 4*x + 1, variables=[x,y]) + sage: ht.m() + x^2*y^2 + (-2*h + 2)*x*y^2 + (2*h - 2)*x*y + (2*h - 3)*y^2 + + (-2*h + 2)*y + 1 + """ + x, y = self._vars + n = self._n + step = self._poly(x=(x - 1) * y / (1 - y), y=x / (x - 1)) * (1 - y)**n + polym = step.numerator() + return M_triangle(polym, variables=(x, y)) + + def f(self): + """ + Return the associated F-triangle. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ,'x,y') + sage: H_triangle(1+x*y).f() + x + y + 1 + sage: H_triangle(x^2*y^2 + 2*x*y + x + 1).f() + 2*x^2 + 2*x*y + y^2 + 3*x + 2*y + 1 + sage: flo = H_triangle(1+4*x+2*x**2+x*y*(4+8*x)+ + ....: x**2*y**2*(6+4*x)+4*(x*y)**3+(x*y)**4).f(); flo + 7*x^4 + 12*x^3*y + 10*x^2*y^2 + 4*x*y^3 + y^4 + 20*x^3 + 28*x^2*y + + 16*x*y^2 + 4*y^3 + 20*x^2 + 20*x*y + 6*y^2 + 8*x + 4*y + 1 + sage: flo(-1-x,-1-y) == flo + True + + TESTS:: + + sage: x,y,h = polygens(ZZ,'x,y,h') + sage: ht = x^2*y^2 + 2*x*y + 2*x*h - 4*x + 1 + sage: H_triangle(ht,[x,y]).f() + 2*x^2*h - 3*x^2 + 2*x*y + y^2 + 2*x*h - 2*x + 2*y + 1 + """ + x, y = self._vars + n = self._n + step1 = self._poly(x=x / (1 + x), y=y) * (x + 1)**n + step2 = step1(x=x, y=y / x) + polyf = step2.numerator() + return F_triangle(polyf, variables=(x, y)) + + def gamma(self): + """ + Return the associated Gamma-triangle. + + In some cases, this is a more condensed way to encode + the same amount of information. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygen(ZZ,'x,y') + sage: ht = x**2*y**2 + 2*x*y + x + 1 + sage: H_triangle(ht).gamma() + y^2 + x + + sage: W = SymmetricGroup(5) + sage: P = posets.NoncrossingPartitions(W) + sage: P.M_triangle().h().gamma() + y^4 + 3*x*y^2 + 2*x^2 + 2*x*y + x + """ + x, y = self._vars + n = self._n + remain = self._poly + gamma = x.parent().zero() + for k in range(n, -1, -1): + step = remain.coefficient({x: k}) + gamma += x**(n - k) * step + remain -= x**(n - k) * step.homogenize(x)(x=1 + x, y=1 + x * y) + return Gamma_triangle(gamma, variables=(x, y)) + + def vector(self): + """ + Return the h-vector as a polynomial in one variable. + + This is obtained by letting `y=1`. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygen(ZZ,'x,y') + sage: ht = x**2*y**2 + 2*x*y + x + 1 + sage: H_triangle(ht).vector() + x^2 + 3*x + 1 + """ + anneau = PolynomialRing(ZZ, 'x') + return anneau(self._poly(y=1)) + + +class F_triangle(Triangle): + """ + Class for the F-triangles. + """ + def h(self): + """ + Return the associated H-triangle. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import F_triangle + sage: x,y = polygens(ZZ,'x,y') + sage: ft = F_triangle(1+x+y) + sage: ft.h() + x*y + 1 + + TESTS:: + + sage: h = polygen(ZZ, 'h') + sage: x, y = polygens(h.parent(),'x,y') + sage: ft = 1+2*y+(2*h-2)*x+y**2+2*x*y+(2*h-3)*x**2 + sage: F_triangle(ft, [x,y]).h() + x^2*y^2 + 2*x*y + (2*h - 4)*x + 1 + """ + x, y = self._vars + n = self._n + step = (1 - x)**n * self._poly(x=x / (1 - x), y=x * y / (1 - x)) + polyh = step.numerator() + return H_triangle(polyh, variables=(x, y)) + + def m(self): + """ + Return the associated M-triangle. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ,'x,y') + sage: H_triangle(1+x*y).f() + x + y + 1 + sage: _.m() + x*y - y + 1 + + sage: H_triangle(x^2*y^2 + 2*x*y + x + 1).f() + 2*x^2 + 2*x*y + y^2 + 3*x + 2*y + 1 + sage: _.m() + x^2*y^2 - 3*x*y^2 + 3*x*y + 2*y^2 - 3*y + 1 + + TESTS:: + + sage: p = 1+4*x+2*x**2+x*y*(4+8*x) + sage: p += x**2*y**2*(6+4*x)+4*(x*y)**3+(x*y)**4 + sage: flo = H_triangle(p).f(); flo + 7*x^4 + 12*x^3*y + 10*x^2*y^2 + 4*x*y^3 + y^4 + 20*x^3 + + 28*x^2*y + 16*x*y^2 + 4*y^3 + 20*x^2 + 20*x*y + 6*y^2 + + 8*x + 4*y + 1 + sage: flo.m() + x^4*y^4 - 8*x^3*y^4 + 8*x^3*y^3 + 20*x^2*y^4 + - 36*x^2*y^3 - 20*x*y^4 + + 16*x^2*y^2 + 48*x*y^3 + 7*y^4 - 36*x*y^2 - 20*y^3 + 8*x*y + + 20*y^2 - 8*y + 1 + + sage: from sage.combinat.triangles_FHM import F_triangle + sage: h = polygen(ZZ, 'h') + sage: x, y = polygens(h.parent(),'x,y') + sage: ft = F_triangle(1+2*y+(2*h-2)*x+y**2+2*x*y+(2*h-3)*x**2,(x,y)) + sage: ft.m() + x^2*y^2 + (-2*h + 2)*x*y^2 + (2*h - 2)*x*y + (2*h - 3)*y^2 + + (-2*h + 2)*y + 1 + """ + x, y = self._vars + n = self._n + step = self._poly(x=y * (x - 1) / (1 - x * y), y=x * y / (1 - x * y)) + step *= (1 - x * y)**n + polym = step.numerator() + return M_triangle(polym, variables=(x, y)) + + def vector(self): + """ + Return the f-vector as a polynomial in one variable. + + This is obtained by letting `y=x`. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import F_triangle + sage: x, y = polygen(ZZ,'x,y') + sage: ft = 2*x^2 + 2*x*y + y^2 + 3*x + 2*y + 1 + sage: F_triangle(ft).vector() + 5*x^2 + 5*x + 1 + """ + anneau = PolynomialRing(ZZ, 'x') + x = anneau.gen() + return anneau(self._poly(y=x)) + + + +class Gamma_triangle(Triangle): + """ + Class for the Gamma-triangles. + """ + def h(self): + r""" + Return the associated H-triangle. + + The transition between Gamma-triangles and H-triangles is defined by + + .. MATH:: + + H(x,y) = (1+x)^d \sum_{0\leq i; 0\leq j \leq d-2i} gamma_{i,j} + (\frac{x}{(1+x)^2})^i (\frac{1+xy}{1+x})^j + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import Gamma_triangle + sage: x, y = polygen(ZZ,'x,y') + sage: g = y**2 + x + sage: Gamma_triangle(g).h() + x^2*y^2 + 2*x*y + x + 1 + + sage: a, b = polygen(ZZ, 'a, b') + sage: x, y = polygens(a.parent(),'x,y') + sage: g = Gamma_triangle(y**3+a*x*y+b*x,(x,y)) + sage: hh = g.h() + sage: hh.gamma() == g + True + """ + x, y = self._vars + n = self._n + resu = (1 + x)**n * self._poly(x=x / (1 + x)**2, + y=(1 + x * y) / (1 + x)) + polyh = resu.numerator() + return H_triangle(polyh, variables=(x, y)) + + def vector(self): + """ + Return the gamma-vector as a polynomial in one variable. + + This is obtained by letting `y=1`. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import Gamma_triangle + sage: x, y = polygen(ZZ,'x,y') + sage: gt = y**2 + x + sage: Gamma_triangle(gt).vector() + x + 1 + """ + anneau = PolynomialRing(ZZ, 'x') + return anneau(self._poly(y=1)) diff --git a/src/sage/topology/simplicial_complex.py b/src/sage/topology/simplicial_complex.py index 6c000a8625b..4b07f5a79f2 100644 --- a/src/sage/topology/simplicial_complex.py +++ b/src/sage/topology/simplicial_complex.py @@ -165,6 +165,7 @@ from sage.structure.parent import Parent from sage.rings.integer import Integer from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.polynomial.polynomial_ring import polygens from sage.sets.set import Set from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ @@ -1521,6 +1522,10 @@ def f_triangle(self): The `f`-triangle is given by `f_{i,j}` being the number of faces `F` of size `j` such that `i = \max_{G \subseteq F} |G|`. + .. SEEALSO:: + + Not to be confused with :meth:`F_triangle` . + EXAMPLES:: sage: X = SimplicialComplex([[1,2,3], [3,4,5], [1,4], [1,5], [2,4], [2,5]]) @@ -1577,6 +1582,40 @@ def h_triangle(self): for k in range(j+1)) return ret + def F_triangle(self, S): + """ + Return the F-triangle of ``self`` w.r.t one maximal simplex ``S``. + + This is the bivariate generating polynomial of all faces, + according to the number of elements in ``S`` and outside ``S``. + + OUTPUT: + + an :class:`~sage.combinat.triangles_FHM.F_triangle` + + .. SEEALSO:: + + Not to be confused with :meth:`f_triangle` . + + EXAMPLES:: + + sage: cs = simplicial_complexes.Torus() + sage: cs.F_triangle(cs.facets()[0]) + x^3 + 9*x^2*y + 3*x*y^2 + y^3 + 6*x^2 + 12*x*y + 3*y^2 + 4*x + 3*y + 1 + """ + x, y = polygens(ZZ, 'x, y') + from sage.combinat.triangles_FHM import F_triangle + + def nega(f): + return sum(1 for v in f if v in S) + + def posi(f): + return f.dimension() + 1 - nega(f) + + poly = sum(x**posi(fa) * y**nega(fa) + for fa in self.face_iterator()) + return F_triangle(poly) + def flip_graph(self): """ If ``self`` is pure, then it returns the flip graph of ``self``, From 252220c9afb6cf1a0fd8402c727a8ef66b2167bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 18 Aug 2022 09:55:03 +0200 Subject: [PATCH 155/632] trying Travis suggestions --- src/sage/rings/lazy_series.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index c5034928ab6..f96a6d4317a 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -87,7 +87,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.structure.element import Element, parent +from sage.structure.element import Element, parent, ModuleElement, RingElement from sage.structure.richcmp import op_EQ, op_NE from sage.functions.other import factorial from sage.arith.power import generic_power @@ -114,7 +114,8 @@ Stream_dirichlet_invert ) -class LazyModuleElement(Element): + +class LazyModuleElement(ModuleElement): r""" A lazy sequence with a module structure given by term-wise addition and scalar multiplication. @@ -858,7 +859,6 @@ def _unicode_art_(self): return UnicodeArt('Uninitialized Lazy Laurent Series') return self._format_series(unicode_art, True) - def change_ring(self, ring): r""" Return ``self`` with coefficients converted to elements of ``ring``. @@ -1971,10 +1971,10 @@ def sqrt(self): sage: f*f - Z O(1/(8^s)) """ - return self ** (1/ZZ(2)) + return self ** (~ZZ(2)) -class LazyCauchyProductSeries(LazyModuleElement): +class LazyCauchyProductSeries(RingElement, LazyModuleElement): r""" A class for series where multiplication is the Cauchy product. From ab3df11b4c43a5e4855e13193b4eb803936a746d Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 20 Aug 2022 16:26:43 +0200 Subject: [PATCH 156/632] trac #34397: clean src/sage/graphs/generic_graph.py - part 9 --- src/sage/graphs/generic_graph.py | 122 ++++++++++++++++++------------- 1 file changed, 73 insertions(+), 49 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index f2c94041c51..fe877bdf359 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -460,7 +460,8 @@ class GenericGraph(GenericGraph_pyx): """ # Nice defaults for plotting arrays of graphs (see sage.misc.functional.show) - graphics_array_defaults = {'layout': 'circular', 'vertex_size':50, 'vertex_labels':False, 'graph_border':True} + graphics_array_defaults = {'layout': 'circular', 'vertex_size': 50, + 'vertex_labels': False, 'graph_border': True} def __init__(self): r""" @@ -601,16 +602,16 @@ def __eq__(self, other): if not isinstance(other, GenericGraph): return False from sage.graphs.graph import Graph - g1_is_graph = isinstance(self, Graph) # otherwise, DiGraph - g2_is_graph = isinstance(other, Graph) # otherwise, DiGraph + g1_is_graph = isinstance(self, Graph) # otherwise, DiGraph + g2_is_graph = isinstance(other, Graph) # otherwise, DiGraph # Fast checks if (g1_is_graph != g2_is_graph or - self.allows_multiple_edges() != other.allows_multiple_edges() or - self.allows_loops() != other.allows_loops() or - self.order() != other.order() or - self.size() != other.size() or - self.weighted() != other.weighted()): - return False + self.allows_multiple_edges() != other.allows_multiple_edges() or + self.allows_loops() != other.allows_loops() or + self.order() != other.order() or + self.size() != other.size() or + self.weighted() != other.weighted()): + return False return self._backend.is_subgraph(other._backend, self, ignore_labels=not self.weighted()) @@ -809,11 +810,18 @@ def _bit_vector(self): n = self.order() if self._directed: total_length = n * n - bit = lambda x, y: x * n + y + + def bit(x, y): + return x * n + y else: total_length = (n * (n - 1)) // 2 - n_ch_2 = lambda b: int(b * (b - 1)) // 2 - bit = lambda x, y: n_ch_2(max(x, y)) + min(x, y) + + def n_ch_2(b): + return int(b * (b - 1)) // 2 + + def bit(x, y): + return n_ch_2(max(x, y)) + min(x, y) + bit_vector = set() try: @@ -821,13 +829,13 @@ def _bit_vector(self): except TypeError: V = self v_to_int = {v: i for i, v in enumerate(V)} - for u,v,_ in self.edge_iterator(): + for u, v in self.edge_iterator(labels=False): bit_vector.add(bit(v_to_int[u], v_to_int[v])) bit_vector = sorted(bit_vector) s = [] j = 0 for i in bit_vector: - s.append( '0' * (i - j) + '1' ) + s.append('0' * (i - j) + '1') j = i + 1 s = "".join(s) s += '0' * (total_length - len(s)) @@ -921,7 +929,7 @@ def _repr_(self): name += "multi-" if self._directed: name += "di" - name += "graph on %d vert"%self.order() + name += "graph on %d vert" % self.order() if self.order() == 1: name += "ex" else: @@ -945,7 +953,7 @@ def is_immutable(self): """ return getattr(self, '_immutable', False) - ### Formats + # Formats def copy(self, weighted=None, data_structure=None, sparse=None, immutable=None): """ @@ -1164,7 +1172,7 @@ def copy(self, weighted=None, data_structure=None, sparse=None, immutable=None): (weighted is None or self._weighted == weighted)): from sage.graphs.base.static_sparse_backend import StaticSparseBackend if (isinstance(self._backend, StaticSparseBackend) and - (data_structure=='static_sparse' or data_structure is None)): + (data_structure == 'static_sparse' or data_structure is None)): return self if data_structure is None: @@ -1306,7 +1314,7 @@ def export_to_file(self, filename, format=None, **kwds): if format not in formats: raise ValueError("format '{}' unknown".format(format)) - formats[format](self.networkx_graph(),filename,**kwds) + formats[format](self.networkx_graph(), filename, **kwds) def _scream_if_not_simple(self, allow_loops=False, allow_multiple_edges=False): r""" @@ -1931,20 +1939,20 @@ def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds i = new_indices[u] j = new_indices[v] if multiple_edges and (i, j) in D: - D[i,j] += 1 + D[i, j] += 1 if not directed and i != j: - D[j,i] += 1 + D[j, i] += 1 else: - D[i,j] = 1 + D[i, j] = 1 if not directed and i != j: - D[j,i] = 1 + D[j, i] = 1 from sage.matrix.constructor import matrix if base_ring is None: base_ring = ZZ M = matrix(base_ring, n, n, D, sparse=sparse, **kwds) return M - am = adjacency_matrix # shorter call makes life easier + am = adjacency_matrix # shorter call makes life easier def incidence_matrix(self, oriented=None, sparse=True, vertices=None, edges=None, *, base_ring=None, **kwds): @@ -2277,13 +2285,13 @@ def distance_matrix(self, vertices=None, *, base_ring=None, **kwds): from sage.matrix.constructor import matrix if ((self.is_directed() and not self.is_strongly_connected()) or - (not self.is_directed() and not self.is_connected())): + (not self.is_directed() and not self.is_connected())): raise ValueError("input (di)graph must be (strongly) connected") if vertices is None: vertices = self.vertices(sort=True) elif (len(vertices) != self.order() or - set(vertices) != set(self.vertex_iterator())): + set(vertices) != set(self.vertex_iterator())): raise ValueError("``vertices`` must be a permutation of the vertices") # We extract from **kwds the arguments for distance_all_pairs @@ -2396,20 +2404,20 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None, *, base_ring=Non set(vertices) != set(self.vertex_iterator())): raise ValueError("``vertices`` must be a permutation of the vertices") - new_indices = {v: i for i,v in enumerate(vertices)} + new_indices = {v: i for i, v in enumerate(vertices)} D = {} if self._directed: for u, v, l in self.edge_iterator(): i = new_indices[u] j = new_indices[v] - D[i,j] = l + D[i, j] = l else: for u, v, l in self.edge_iterator(): i = new_indices[u] j = new_indices[v] - D[i,j] = l - D[j,i] = l + D[i, j] = l + D[j, i] = l from sage.matrix.constructor import matrix if base_ring is None: M = matrix(self.num_verts(), D, sparse=sparse, **kwds) @@ -2572,29 +2580,28 @@ def kirchhoff_matrix(self, weighted=None, indegree=True, normalized=False, signl if M.is_sparse(): row_sums = {} if indegree: - for (i,j), entry in M.dict().items(): + for (i, j), entry in M.dict().items(): row_sums[j] = row_sums.get(j, 0) + entry else: - for (i,j), entry in M.dict().items(): + for (i, j), entry in M.dict().items(): row_sums[i] = row_sums.get(i, 0) + entry - for i in range(M.nrows()): - D[i,i] += row_sums.get(i, 0) + D[i, i] += row_sums.get(i, 0) else: if indegree: col_sums = [sum(v) for v in M.columns()] for i in range(M.nrows()): - D[i,i] += col_sums[i] + D[i, i] += col_sums[i] else: - row_sums=[sum(v) for v in M.rows()] + row_sums = [sum(v) for v in M.rows()] for i in range(M.nrows()): - D[i,i] += row_sums[i] + D[i, i] += row_sums[i] if normalized: from sage.misc.functional import sqrt - Dsqrt = diagonal_matrix([1 / sqrt(D[i,i]) if D[i,i] else 1 \ + Dsqrt = diagonal_matrix([1 / sqrt(D[i, i]) if D[i, i] else 1 for i in range(D.nrows())]) if signless: ret = Dsqrt * (D + M) * Dsqrt @@ -2612,7 +2619,7 @@ def kirchhoff_matrix(self, weighted=None, indegree=True, normalized=False, signl laplacian_matrix = kirchhoff_matrix - ### Attributes + # Attributes def set_embedding(self, embedding): """ @@ -2639,8 +2646,14 @@ def set_embedding(self, embedding): EXAMPLES:: sage: G = graphs.PetersenGraph() - sage: G.set_embedding({0: [1, 5, 4], 1: [0, 2, 6], 2: [1, 3, 7], 3: [8, 2, 4], 4: [0, 9, 3], 5: [0, 8, 7], 6: [8, 1, 9], 7: [9, 2, 5], 8: [3, 5, 6], 9: [4, 6, 7]}) - sage: G.set_embedding({'s': [1, 5, 4], 1: [0, 2, 6], 2: [1, 3, 7], 3: [8, 2, 4], 4: [0, 9, 3], 5: [0, 8, 7], 6: [8, 1, 9], 7: [9, 2, 5], 8: [3, 5, 6], 9: [4, 6, 7]}) + sage: G.set_embedding({0: [1, 5, 4], 1: [0, 2, 6], 2: [1, 3, 7], + ....: 3: [8, 2, 4], 4: [0, 9, 3], 5: [0, 8, 7], + ....: 6: [8, 1, 9], 7: [9, 2, 5], 8: [3, 5, 6], + ....: 9: [4, 6, 7]}) + sage: G.set_embedding({'s': [1, 5, 4], 1: [0, 2, 6], 2: [1, 3, 7], + ....: 3: [8, 2, 4], 4: [0, 9, 3], 5: [0, 8, 7], + ....: 6: [8, 1, 9], 7: [9, 2, 5], 8: [3, 5, 6], + ....: 9: [4, 6, 7]}) Traceback (most recent call last): ... ValueError: vertices in ['s'] from the embedding do not belong to the graph @@ -2677,7 +2690,9 @@ def get_embedding(self): sage: G.genus() 1 sage: G.get_embedding() - {0: [1, 4, 5], 1: [0, 2, 6], 2: [1, 3, 7], 3: [2, 4, 8], 4: [0, 3, 9], 5: [0, 7, 8], 6: [1, 9, 8], 7: [2, 5, 9], 8: [3, 6, 5], 9: [4, 6, 7]} + {0: [1, 4, 5], 1: [0, 2, 6], 2: [1, 3, 7], 3: [2, 4, 8], + 4: [0, 3, 9], 5: [0, 7, 8], 6: [1, 9, 8], 7: [2, 5, 9], + 8: [3, 6, 5], 9: [4, 6, 7]} Note that the embeddings gets properly modified on vertex or edge deletion:: @@ -2746,7 +2761,9 @@ def _check_embedding_validity(self, embedding=None, boolean=True): EXAMPLES:: - sage: d = {0: [1, 5, 4], 1: [0, 2, 6], 2: [1, 3, 7], 3: [8, 2, 4], 4: [0, 9, 3], 5: [0, 8, 7], 6: [8, 1, 9], 7: [9, 2, 5], 8: [3, 5, 6], 9: [4, 6, 7]} + sage: d = {0: [1, 5, 4], 1: [0, 2, 6], 2: [1, 3, 7], 3: [8, 2, 4], + ....: 4: [0, 9, 3], 5: [0, 8, 7], 6: [8, 1, 9], 7: [9, 2, 5], + ....: 8: [3, 5, 6], 9: [4, 6, 7]} sage: G = graphs.PetersenGraph() sage: G._check_embedding_validity(d) True @@ -2794,28 +2811,36 @@ def _check_embedding_validity(self, embedding=None, boolean=True): if boolean: return False if set(embedding).difference(self): - raise ValueError("vertices in {} from the embedding do not belong to the graph".format(list(set(embedding).difference(self)))) + raise ValueError("vertices in {} from the embedding do not " + "belong to the graph".format(list(set(embedding).difference(self)))) else: - raise ValueError("vertices in {} have no corresponding entry in the embedding".format(list(set(self).difference(embedding)))) + raise ValueError("vertices in {} have no corresponding entry " + "in the embedding".format(list(set(self).difference(embedding)))) if self._directed: - connected = lambda u, v: self.has_edge(u, v) or self.has_edge(v, u) + def connected(u, v): + return self.has_edge(u, v) or self.has_edge(v, u) else: connected = self.has_edge for v in embedding: if len(embedding[v]) != self.degree(v): if boolean: return False - raise ValueError("the list associated with vertex {} has length {} but d({})={}".format(v, len(embedding[v]), v, self.degree(v))) + raise ValueError("the list associated with vertex {} has " + "length {} but d({})={}".format(v, len(embedding[v]), + v, self.degree(v))) if len(embedding[v]) != len(set(embedding[v])): if boolean: return False - raise ValueError("the list associated with vertex {} contains >1 occurrences of {}".format(v, [x for x in set(embedding[v]) if embedding[v].count(x) > 1])) + raise ValueError("the list associated with vertex {} contains >1 " + "occurrences of {}".format(v, [x for x in set(embedding[v]) + if embedding[v].count(x) > 1])) for u in embedding[v]: if not connected(v, u): if boolean: return False - raise ValueError("{} and {} are not neighbors but {} is in the list associated with {}".format(u, v, u, v)) + raise ValueError("{} and {} are not neighbors but {} is in " + "the list associated with {}".format(u, v, u, v)) return True def has_loops(self): @@ -23125,7 +23150,6 @@ def is_hamiltonian(self, solver=None, constraint_generation=None, except EmptySetError: return False - def is_isomorphic(self, other, certificate=False, verbosity=0, edge_labels=False): r""" Tests for isomorphism between self and other. From 70486e6108fb53b35c7b094e43e4b71e67806300 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 22 Aug 2022 16:54:35 +0900 Subject: [PATCH 157/632] Refactoring the implementation of q_pochhammer and euler. --- src/sage/rings/lazy_series.py | 87 +++++------------------ src/sage/rings/lazy_series_ring.py | 106 +++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 70 deletions(-) diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index c9d377ea596..5c268b33ae5 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -1995,6 +1995,10 @@ def q_pochhammer(self, q=None): This is also one version of the quantum dilogarithm or the `q`-Exponential function. + .. SEEALSO:: + + :meth:`sage.rings.lazy_series_ring.LazyLaurentSeriesRing.q_pochhammer` + INPUT: - ``q`` -- (default: `q \in \QQ(q)`) the parameter `q` @@ -2003,55 +2007,16 @@ def q_pochhammer(self, q=None): sage: q = ZZ['q'].fraction_field().gen() sage: L. = LazyLaurentSeriesRing(q.parent()) - sage: qpoch = z.q_pochhammer(q) - sage: qpoch - 1 - + (-1/(-q + 1))*z - + (q/(q^3 - q^2 - q + 1))*z^2 - + (-q^3/(-q^6 + q^5 + q^4 - q^2 - q + 1))*z^3 - + (q^6/(q^10 - q^9 - q^8 + 2*q^5 - q^2 - q + 1))*z^4 - + (-q^10/(-q^15 + q^14 + q^13 - q^10 - q^9 - q^8 + q^7 + q^6 + q^5 - q^2 - q + 1))*z^5 - + (q^15/(q^21 - q^20 - q^19 + q^16 + 2*q^14 - q^12 - q^11 - q^10 - q^9 + 2*q^7 + q^5 - q^2 - q + 1))*z^6 - + O(z^7) - - We show that `(z; q)_n = \frac{(z; q)_{\infty}}{(q^n z; q)_{\infty}}`:: - - sage: qpoch / qpoch(q*z) - 1 - z + O(z^7) - sage: qpoch / qpoch(q^2*z) - 1 + (-q - 1)*z + q*z^2 + O(z^7) - sage: qpoch / qpoch(q^3*z) - 1 + (-q^2 - q - 1)*z + (q^3 + q^2 + q)*z^2 - q^3*z^3 + O(z^7) - sage: qpoch / qpoch(q^4*z) - 1 + (-q^3 - q^2 - q - 1)*z + (q^5 + q^4 + 2*q^3 + q^2 + q)*z^2 - + (-q^6 - q^5 - q^4 - q^3)*z^3 + q^6*z^4 + O(z^7) - - We can also construct part of Euler's function:: - - sage: M. = LazyLaurentSeriesRing(QQ) - sage: phi = sum(qpoch[i](q=a)*a^i for i in range(10)) - sage: phi[:20] == a.euler()[:20] - True - - REFERENCES: - - - :wikipedia:`Q-Pochhammer_symbol` - - :wikipedia:`Quantum_dilogarithm` - - :wikipedia:`Q-exponential` + sage: qp = L.q_pochhammer(q) + sage: (z + z^2).q_pochhammer(q) - qp(z + z^2) + O(z^7) """ - if q is None: - q = ZZ['q'].fraction_field().gen() from .lazy_series_ring import LazyLaurentSeriesRing - from sage.arith.misc import binomial - qP = q.parent() - L = LazyLaurentSeriesRing(qP, "z", sparse=self.parent()._sparse) - one = qP.one() - def coeff(n): - return (-1)**n * q**binomial(n, 2) / qP.prod(one - q**i for i in range(1, n+1)) - f = L(coeff, valuation=0) + P = LazyLaurentSeriesRing(self.base_ring(), "z", sparse=self.parent()._sparse) + f = P.q_pochhammer(q) return f(self) - def euler(self, q=None): + def euler(self): r""" Return the Euler function evaluated at ``self``. @@ -2062,38 +2027,20 @@ def euler(self, q=None): \phi(z) = (z; z)_{\infty} = \sum_{n=0}^{\infty} (-1)^n q^{(3n^2-n)/2}. - INPUT: + .. SEEALSO:: - - ``q`` -- (default: `q \in \QQ(q)`) the parameter `q` + :meth:`sage.rings.lazy_series_ring.LazyLaurentSeriesRing.euler` EXAMPLES:: sage: L. = LazyLaurentSeriesRing(ZZ) - sage: phi = q.euler() - sage: phi - 1 - q - q^2 + q^5 + O(q^7) - - We verify that `1 / phi` gives the generating function - for all partitions:: - - sage: P = 1 / phi; P - 1 + q + 2*q^2 + 3*q^3 + 5*q^4 + 7*q^5 + 11*q^6 + O(q^7) - sage: P[:20] == [Partitions(n).cardinality() for n in range(20)] - True - - REFERENCES: - - - :wikipedia:`Euler_function` + sage: phi = L.euler() + sage: (q + q^2).euler() - phi(q + q^2) + O(q^7) """ from .lazy_series_ring import LazyLaurentSeriesRing - L = LazyLaurentSeriesRing(ZZ, "q", sparse=self.parent()._sparse) - def coeff(n): - k = ZZ(24 * n + 1) - m, rem = k.sqrtrem() - if rem: - return ZZ.zero() - return (-1) ** ((m + 1) // 6) - phi = L(coeff, valuation=0) + P = LazyLaurentSeriesRing(self.base_ring(), "z", sparse=self.parent()._sparse) + phi = P.euler() return phi(self) # === powers === diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 2626384097c..52840fdf30b 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -1112,6 +1112,112 @@ def _monomial(self, c, n): """ return self._laurent_poly_ring(c).shift(n) + # === special functions === + + + def q_pochhammer(self, q=None): + r""" + Return the infinite ``q``-Pochhammer symbol `(a; q)_{\infty}`, + where `a` is the variable of ``self``. + + This is also one version of the quantum dilogarithm or + the `q`-Exponential function. + + INPUT: + + - ``q`` -- (default: `q \in \QQ(q)`) the parameter `q` + + EXAMPLES:: + + sage: q = ZZ['q'].fraction_field().gen() + sage: L. = LazyLaurentSeriesRing(q.parent()) + sage: qpoch = L.q_pochhammer(q) + sage: qpoch + 1 + + (-1/(-q + 1))*z + + (q/(q^3 - q^2 - q + 1))*z^2 + + (-q^3/(-q^6 + q^5 + q^4 - q^2 - q + 1))*z^3 + + (q^6/(q^10 - q^9 - q^8 + 2*q^5 - q^2 - q + 1))*z^4 + + (-q^10/(-q^15 + q^14 + q^13 - q^10 - q^9 - q^8 + q^7 + q^6 + q^5 - q^2 - q + 1))*z^5 + + (q^15/(q^21 - q^20 - q^19 + q^16 + 2*q^14 - q^12 - q^11 - q^10 - q^9 + 2*q^7 + q^5 - q^2 - q + 1))*z^6 + + O(z^7) + + We show that `(z; q)_n = \frac{(z; q)_{\infty}}{(q^n z; q)_{\infty}}`:: + + sage: qpoch / qpoch(q*z) + 1 - z + O(z^7) + sage: qpoch / qpoch(q^2*z) + 1 + (-q - 1)*z + q*z^2 + O(z^7) + sage: qpoch / qpoch(q^3*z) + 1 + (-q^2 - q - 1)*z + (q^3 + q^2 + q)*z^2 - q^3*z^3 + O(z^7) + sage: qpoch / qpoch(q^4*z) + 1 + (-q^3 - q^2 - q - 1)*z + (q^5 + q^4 + 2*q^3 + q^2 + q)*z^2 + + (-q^6 - q^5 - q^4 - q^3)*z^3 + q^6*z^4 + O(z^7) + + We can also construct part of Euler's function:: + + sage: M. = LazyLaurentSeriesRing(QQ) + sage: phi = sum(qpoch[i](q=a)*a^i for i in range(10)) + sage: phi[:20] == M.euler()[:20] + True + + REFERENCES: + + - :wikipedia:`Q-Pochhammer_symbol` + - :wikipedia:`Quantum_dilogarithm` + - :wikipedia:`Q-exponential` + """ + if q is None: + q = ZZ['q'].fraction_field().gen() + if q not in self.base_ring(): + raise ValueError("q must be in the base ring") + from .lazy_series_ring import LazyLaurentSeriesRing + from sage.arith.misc import binomial + qP = q.parent() + one = qP.one() + def coeff(n): + return (-1)**n * q**binomial(n, 2) / qP.prod(one - q**i for i in range(1, n+1)) + return self(coeff, valuation=0) + + def euler(self): + r""" + Return the Euler function as an element of ``self``. + + The *Euler function* is defined as + + .. MATH:: + + \phi(z) = (z; z)_{\infty} + = \sum_{n=0}^{\infty} (-1)^n q^{(3n^2-n)/2}. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(ZZ) + sage: phi = q.euler() + sage: phi + 1 - q - q^2 + q^5 + O(q^7) + + We verify that `1 / phi` gives the generating function + for all partitions:: + + sage: P = 1 / phi; P + 1 + q + 2*q^2 + 3*q^3 + 5*q^4 + 7*q^5 + 11*q^6 + O(q^7) + sage: P[:20] == [Partitions(n).cardinality() for n in range(20)] + True + + REFERENCES: + + - :wikipedia:`Euler_function` + """ + from .lazy_series_ring import LazyLaurentSeriesRing + def coeff(n): + k = ZZ(24 * n + 1) + m, rem = k.sqrtrem() + if rem: + return ZZ.zero() + return (-1) ** ((m + 1) // 6) + return self(coeff, valuation=0) + ###################################################################### class LazyTaylorSeriesRing(LazySeriesRing): From 7fb2924620ec1253b32d55952e48eaed7b5ec058 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Tue, 23 Aug 2022 17:18:05 -0400 Subject: [PATCH 158/632] correcting shm_managers INPUT and TestSuite --- src/sage/algebras/fusion_rings/f_matrix.py | 3 +- .../algebras/fusion_rings/shm_managers.pyx | 34 +++++++++++-------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/src/sage/algebras/fusion_rings/f_matrix.py b/src/sage/algebras/fusion_rings/f_matrix.py index 3c24c942dd0..f2c2ab7656a 100644 --- a/src/sage/algebras/fusion_rings/f_matrix.py +++ b/src/sage/algebras/fusion_rings/f_matrix.py @@ -280,7 +280,7 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab EXAMPLES:: sage: f = FMatrix(FusionRing("B3",2)) - sage: TestSuite(f).run() + sage: TestSuite(f).run(skip="_test_pickling") """ self._FR = fusion_ring if inject_variables and (self._FR._fusion_labels is None): @@ -2476,4 +2476,3 @@ def certify_pentagons(self,use_mp=True, verbose=False): print("Something went wrong. Pentagons remain.") self._fvars = fvars_copy return pe - diff --git a/src/sage/algebras/fusion_rings/shm_managers.pyx b/src/sage/algebras/fusion_rings/shm_managers.pyx index 21a9d0ea3eb..0106adbb59d 100644 --- a/src/sage/algebras/fusion_rings/shm_managers.pyx +++ b/src/sage/algebras/fusion_rings/shm_managers.pyx @@ -58,10 +58,10 @@ cdef class KSHandler: must import the ``multiprocessing.shared_memory`` module. Attempting to initialize when ``multiprocessing.shared_memory`` is not available results in an ``ImportError``. - - ``name`` -- the name of a shared memory object - (used by child processes for attaching) - ``init_data`` -- a dictionary or :class:`KSHandler` object containing known squares for initialization, e.g., from a solver checkpoint + - ``name`` -- the name of a shared memory object (used by child processes + for attaching) .. NOTE:: @@ -397,25 +397,29 @@ cdef class FvarsHandler: ``name`` attribute. Children processes use the ``name`` attribute, accessed via ``self.shm.name`` to attach to the shared memory block. + Multiprocessing requires Python 3.8+, since we must import the + ``multiprocessing.shared_memory`` module. Attempting to initialize + when ``multiprocessing.shared_memory`` is not available results in + an ``ImportError``. + INPUT: - - ``factory`` -- an F-matrix - - ``name`` -- the name of a shared memory object - (used by child processes for attaching) - - ``max_terms`` -- maximum number of terms in each entry; since - we use contiguous C-style memory blocks, the size of the block - must be known in advance + - ``n_slots`` -- number of generators of the underlying polynomial ring + - ``field`` -- base field for polynomial ring + - ``idx_to_sextuple`` -- map relating a single integer index to a sextuple + of ``FusionRing`` elements + - ``init_data`` -- a dictionary or :class:`FvarsHandler` object containing + known squares for initialization, e.g., from a solver checkpoint - ``use_mp`` -- an integer indicating the number of child processes used for multiprocessing; if running serially, use 0. - - Multiprocessing requires Python 3.8+, since we must import the - ``multiprocessing.shared_memory`` module. Attempting to initialize - when ``multiprocessing.shared_memory`` is not available results in - an ``ImportError``. - - ``pids_name`` -- the name of a ``ShareableList`` contaning the process ``pid``'s for every process in the pool (including the parent process) + - ``name`` -- the name of a shared memory object + (used by child processes for attaching) + - ``max_terms`` -- maximum number of terms in each entry; since + we use contiguous C-style memory blocks, the size of the block + must be known in advance - ``n_bytes`` -- the number of bytes that should be allocated for each numerator and each denominator stored by the structure @@ -485,7 +489,7 @@ cdef class FvarsHandler: ....: n_proc = 0 ....: pids_name = None sage: fvars = FvarsHandler(8,f._field,f._idx_to_sextuple,use_mp=n_proc,pids_name=pids_name) - sage: TestSuite(fvars).run() + sage: TestSuite(fvars).run(skip="_test_pickling") sage: if is_shared_memory_available: fvars.shm.unlink() sage: f.shutdown_worker_pool() """ From 61d3a666861bc9518f161aa461fe30be90496c47 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Thu, 25 Aug 2022 22:06:50 -0400 Subject: [PATCH 159/632] removing 3.8 mentions in code --- src/sage/algebras/fusion_rings/f_matrix.py | 26 +++++-------------- src/sage/algebras/fusion_rings/fusion_ring.py | 4 +-- .../algebras/fusion_rings/shm_managers.pyx | 11 +------- 3 files changed, 8 insertions(+), 33 deletions(-) diff --git a/src/sage/algebras/fusion_rings/f_matrix.py b/src/sage/algebras/fusion_rings/f_matrix.py index f2c2ab7656a..fccb79d4880 100644 --- a/src/sage/algebras/fusion_rings/f_matrix.py +++ b/src/sage/algebras/fusion_rings/f_matrix.py @@ -12,17 +12,13 @@ # **************************************************************************** -#Import pickle for checkpointing and loading -try: - import cPickle as pickle -except: - import pickle from copy import deepcopy from ctypes import cast, py_object from itertools import product, zip_longest -from multiprocessing import Pool, cpu_count, set_start_method +from multiprocessing import Pool, cpu_count, set_start_method, shared_memory import numpy as np from os import getpid, remove +import pickle from sage.algebras.fusion_rings.fast_parallel_fmats_methods import ( _backward_subs, _solve_for_linear_terms, @@ -35,8 +31,7 @@ poly_to_tup, _tup_to_poly, tup_to_univ_poly, _unflatten_coeffs, poly_tup_sortkey, - resize, - # tup_fixes_sq + resize ) from sage.algebras.fusion_rings.shm_managers import KSHandler, FvarsHandler from sage.graphs.graph import Graph @@ -1258,7 +1253,7 @@ def start_worker_pool(self, processes=None): EXAMPLES:: sage: f = FMatrix(FusionRing("G2", 1)) - sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ + sage: f.start_worker_pool() sage: he = f.get_defining_equations('hexagons') sage: sorted(he) [fx0 - 1, @@ -1282,12 +1277,6 @@ class methods. leak, since shared memory resources outlive the process that created them. """ - #Try to import module requiring Python 3.8+ locally - try: - from multiprocessing import shared_memory - except ImportError: - self.pool = None - return False try: set_start_method('fork') except RuntimeError: @@ -1382,11 +1371,8 @@ def _map_triv_reduce(self, mapper, input_iter, worker_pool=None, chunksize=None, sage: f._reset_solver_state() sage: len(f._map_triv_reduce('get_reduced_hexagons',[(0,1,False)])) 11 - sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ - sage: if is_shared_memory_available: - ....: mp_params = [(i,f.pool._processes,True) for i in range(f.pool._processes)] - ....: else: - ....: mp_params = [(0,1,True)] + sage: f.start_worker_pool() + sage: mp_params = [(i,f.pool._processes,True) for i in range(f.pool._processes)] sage: len(f._map_triv_reduce('get_reduced_pentagons',mp_params,worker_pool=f.pool,chunksize=1,mp_thresh=0)) 33 sage: f.shutdown_worker_pool() diff --git a/src/sage/algebras/fusion_rings/fusion_ring.py b/src/sage/algebras/fusion_rings/fusion_ring.py index e4a52b4e35a..bd010614493 100644 --- a/src/sage/algebras/fusion_rings/fusion_ring.py +++ b/src/sage/algebras/fusion_rings/fusion_ring.py @@ -1263,9 +1263,7 @@ def get_braid_generators(self, we don't run the solver again. - ``use_mp`` -- (default: ``True``) a boolean indicating whether to use multiprocessing to speed up the computation; this is - highly recommended. Python 3.8+ is required. This method will - raise an error if it cannot import the necessary components - (``shared_memory`` sub-module of ``multiprocessing``). + highly recommended. Python 3.8+ is required. - ``verbose`` -- (default: ``True``) boolean indicating whether to be verbose with the computation diff --git a/src/sage/algebras/fusion_rings/shm_managers.pyx b/src/sage/algebras/fusion_rings/shm_managers.pyx index 0106adbb59d..5a3eb29694a 100644 --- a/src/sage/algebras/fusion_rings/shm_managers.pyx +++ b/src/sage/algebras/fusion_rings/shm_managers.pyx @@ -18,6 +18,7 @@ factory is a cyclotomic field. cimport cython cimport numpy as np from cysignals.memory cimport sig_malloc +from multiprocessing import shared_memory from sage.algebras.fusion_rings.poly_tup_engine cimport poly_to_tup, tup_fixes_sq, _flatten_coeffs from sage.rings.integer cimport Integer from sage.rings.rational cimport Rational @@ -128,11 +129,6 @@ cdef class KSHandler: ]) self.obj_cache = [None]*n if use_mp: - #Try to import module requiring Python 3.8+ locally - try: - from multiprocessing import shared_memory - except ImportError: - raise ImportError("failed to import shared_memory module; requires Python 3.8+") if name is None: self.shm = shared_memory.SharedMemory(create=True, size=n*ks_t.itemsize) else: @@ -509,11 +505,6 @@ cdef class FvarsHandler: self.sext_to_idx = {s: i for i, s in idx_to_sextuple.items()} self.ngens = n_slots if use_mp: - #Try to import module requiring Python 3.8+ locally - try: - from multiprocessing import shared_memory - except ImportError: - raise ImportError("failed to import shared_memory module; requires Python 3.8+") if name is None: self.shm = shared_memory.SharedMemory(create=True, size=self.ngens*self.fvars_t.itemsize) else: From ff7f5dbd41ef20107f3a3e594ce350ed02f31915 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Thu, 25 Aug 2022 22:49:27 -0400 Subject: [PATCH 160/632] updated doctests to remove back-compatibility with Python 3.8- --- src/sage/algebras/fusion_rings/f_matrix.py | 35 +++--- .../algebras/fusion_rings/shm_managers.pyx | 114 ++++++------------ 2 files changed, 54 insertions(+), 95 deletions(-) diff --git a/src/sage/algebras/fusion_rings/f_matrix.py b/src/sage/algebras/fusion_rings/f_matrix.py index fccb79d4880..61053cc62af 100644 --- a/src/sage/algebras/fusion_rings/f_matrix.py +++ b/src/sage/algebras/fusion_rings/f_matrix.py @@ -1235,9 +1235,7 @@ def start_worker_pool(self, processes=None): .. NOTE:: Python 3.8+ is required, since the ``multiprocessing.shared_memory`` - module must be imported. If we fail to import the ``shared_memory`` - module, ``self.pool`` is set to ``None``, no worker pool is created, - and all subsequent calculations will run serially. + module must be imported. INPUT: @@ -1314,7 +1312,7 @@ def init(fmats_id, solved_name, vd_name, ks_names, fvar_names, n_proc, pids_name self._pid_list[0] = getpid() for i, p in enumerate(self.pool._pool): self._pid_list[i+1] = p.pid - return True + # return True def shutdown_worker_pool(self): r""" @@ -1331,7 +1329,7 @@ def shutdown_worker_pool(self): EXAMPLES:: sage: f = FMatrix(FusionRing("A1", 3)) - sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ + sage: f.start_worker_pool() sage: he = f.get_defining_equations('hexagons') sage: f.shutdown_worker_pool() """ @@ -1532,7 +1530,7 @@ def _tup_to_fpoly(self, eq_tup): sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup sage: f = FMatrix(FusionRing("C3", 1)) - sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ + sage: f.start_worker_pool() sage: he = f.get_defining_equations('hexagons') sage: all(f._tup_to_fpoly(poly_to_tup(h)) for h in he) True @@ -1549,17 +1547,13 @@ def _update_reduction_params(self, eqns=None): sage: f = FMatrix(FusionRing("A1", 3)) sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) - sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ + sage: f.start_worker_pool() sage: f.get_defining_equations('hexagons', output=False) sage: f.ideal_basis = f._par_graph_gb(verbose=False) sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey, poly_to_tup sage: f.ideal_basis.sort(key=poly_tup_sortkey) sage: f.mp_thresh = 0 - sage: if is_shared_memory_available: - ....: f._fvars = f._shared_fvars - ....: else: - ....: from sage.algebras.fusion_rings.shm_managers import FvarsHandler - ....: f._fvars = FvarsHandler(f._poly_ring.ngens(),f._field,f._idx_to_sextuple,init_data=f._fvars) + sage: f._fvars = f._shared_fvars sage: f._triangular_elim(verbose=False) # indirect doctest sage: f.ideal_basis [] @@ -1760,7 +1754,7 @@ def _par_graph_gb(self, eqns=None, term_order="degrevlex", largest_comp=45, verb sage: f = FMatrix(FusionRing("F4",1)) sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) - sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ + sage: f.start_worker_pool() sage: f.get_defining_equations('hexagons',output=False) sage: gb = f._par_graph_gb() Partitioned 10 equations into 2 components of size: @@ -1803,7 +1797,7 @@ def _get_component_variety(self,var,eqns): EXAMPLES:: sage: f = FMatrix(FusionRing("G2", 2)) - sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ + sage: f.start_worker_pool() sage: f.get_defining_equations('hexagons', output=False) # long time sage: f.shutdown_worker_pool() sage: partition = f._partition_eqns() # long time @@ -2045,10 +2039,7 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start - ``use_mp`` -- (default: ``True``) a boolean indicating whether to use multiprocessing to speed up calculation. The default value ``True`` is highly recommended, since parallel processing yields - results much more quickly. Python 3.8+ is required, since the - ``multiprocessing.shared_memory`` module must be imported. If we - fail to import the ``shared_memory`` module, the solver runs - serially. + results much more quickly. - ``verbose`` -- (default: ``True``) a boolean indicating whether the solver should print out intermediate progress reports. @@ -2114,9 +2105,11 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start #Loading from a pickle with solved F-symbols if self._chkpt_status > 5: return - loads_shared_memory = False + # loads_shared_memory = False + # if use_mp: + # loads_shared_memory = self.start_worker_pool() if use_mp: - loads_shared_memory = self.start_worker_pool() + self.start_worker_pool() if verbose: print("Computing F-symbols for {} with {} variables...".format(self._FR, self._poly_ring.ngens())) @@ -2129,7 +2122,7 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start print("Set up {} hex and orthogonality constraints...".format(len(self.ideal_basis))) #Unzip _fvars and link to shared_memory structure if using multiprocessing - if use_mp and loads_shared_memory: + if use_mp:# and loads_shared_memory: self._fvars = self._shared_fvars else: n = self._poly_ring.ngens() diff --git a/src/sage/algebras/fusion_rings/shm_managers.pyx b/src/sage/algebras/fusion_rings/shm_managers.pyx index 5a3eb29694a..fd5fe8d0637 100644 --- a/src/sage/algebras/fusion_rings/shm_managers.pyx +++ b/src/sage/algebras/fusion_rings/shm_managers.pyx @@ -56,9 +56,7 @@ cdef class KSHandler: - ``field`` -- F-matrix's base cyclotomic field - ``use_mp`` -- a boolean indicating whether to construct a shared memory block to back ``self``. Requires Python 3.8+, since we - must import the ``multiprocessing.shared_memory`` module. Attempting - to initialize when ``multiprocessing.shared_memory`` is not available - results in an ``ImportError``. + must import the ``multiprocessing.shared_memory`` module. - ``init_data`` -- a dictionary or :class:`KSHandler` object containing known squares for initialization, e.g., from a solver checkpoint - ``name`` -- the name of a shared memory object (used by child processes @@ -82,13 +80,11 @@ cdef class KSHandler: creating variables fx1..fx14 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 sage: n = f._poly_ring.ngens() - sage: is_shared_memory_available = f.start_worker_pool() - sage: ks = KSHandler(n,f._field,use_mp=is_shared_memory_available) + sage: f.start_worker_pool() + sage: ks = KSHandler(n,f._field,use_mp=True) sage: #In the same shell or in a different shell, attach to fvars - sage: name = ks.shm.name if is_shared_memory_available else None - sage: ks2 = KSHandler(n,f._field,name=name,use_mp=is_shared_memory_available) - sage: if not is_shared_memory_available: - ....: ks2 = ks + sage: name = ks.shm.name + sage: ks2 = KSHandler(n,f._field,name=name,use_mp=True) sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup sage: eqns = [fx1**2 - 4, fx3**2 + f._field.gen()**4 - 1/19*f._field.gen()**2] sage: ks.update([poly_to_tup(p) for p in eqns]) @@ -97,7 +93,7 @@ cdef class KSHandler: ....: Index: 1, square: 4 Index: 3, square: -zeta32^4 + 1/19*zeta32^2 - sage: if is_shared_memory_available: ks.shm.unlink() + sage: ks.shm.unlink() sage: f.shutdown_worker_pool() """ def __init__(self, n_slots, field, use_mp=False, init_data={}, name=None): @@ -112,10 +108,10 @@ cdef class KSHandler: creating variables fx1..fx14 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 sage: n = f._poly_ring.ngens() - sage: is_shared_memory_available = f.start_worker_pool() - sage: ks = KSHandler(n,f._field,use_mp=is_shared_memory_available) + sage: f.start_worker_pool() + sage: ks = KSHandler(n,f._field,use_mp=True) sage: TestSuite(ks).run() - sage: if is_shared_memory_available: ks.shm.unlink() + sage: ks.shm.unlink() sage: f.shutdown_worker_pool() """ cdef int n, d @@ -290,16 +286,14 @@ cdef class KSHandler: sage: f.get_orthogonality_constraints(output=False) sage: from sage.algebras.fusion_rings.shm_managers import KSHandler sage: n = f._poly_ring.ngens() - sage: is_shared_memory_available = f.start_worker_pool() - sage: ks = KSHandler(n,f._field,use_mp=is_shared_memory_available,init_data=f._ks) + sage: f.start_worker_pool() + sage: ks = KSHandler(n,f._field,use_mp=True,init_data=f._ks) sage: #In the same shell or in a different one, attach to shared memory handler - sage: name = ks.shm.name if is_shared_memory_available else None - sage: k2 = KSHandler(n,f._field,name=name,use_mp=is_shared_memory_available) - sage: if not is_shared_memory_available: - ....: k2 = ks + sage: name = ks.shm.name + sage: k2 = KSHandler(n,f._field,name=name,use_mp=True) sage: ks == k2 True - sage: if is_shared_memory_available: ks.shm.unlink() + sage: ks.shm.unlink() sage: f.shutdown_worker_pool() """ return all(other.get(idx) == sq for idx, sq in self.items()) @@ -394,9 +388,7 @@ cdef class FvarsHandler: accessed via ``self.shm.name`` to attach to the shared memory block. Multiprocessing requires Python 3.8+, since we must import the - ``multiprocessing.shared_memory`` module. Attempting to initialize - when ``multiprocessing.shared_memory`` is not available results in - an ``ImportError``. + ``multiprocessing.shared_memory`` module. INPUT: @@ -444,25 +436,19 @@ cdef class FvarsHandler: sage: f = FMatrix(FusionRing("A2",1), inject_variables=True) creating variables fx1..fx8 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 - sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ - sage: if is_shared_memory_available: - ....: n_proc = f.pool._processes - ....: pids_name = f._pid_list.shm.name - ....: else: - ....: n_proc = 0 - ....: pids_name = None + sage: f.start_worker_pool() + sage: n_proc = f.pool._processes + sage: pids_name = f._pid_list.shm.name sage: fvars = FvarsHandler(8, f._field, f._idx_to_sextuple, use_mp=n_proc, pids_name=pids_name) sage: #In the same shell or in a different shell, attach to fvars - sage: name = fvars.shm.name if is_shared_memory_available else None + sage: name = fvars.shm.name sage: fvars2 = FvarsHandler(8, f._field, f._idx_to_sextuple, name=name ,use_mp=n_proc, pids_name=pids_name) - sage: if not is_shared_memory_available: - ....: fvars2 = fvars sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(fx5**5)) sage: fvars[f2, f1, f2, f2, f0, f0] = rhs sage: f._tup_to_fpoly(fvars2[f2, f1, f2, f2, f0, f0]) fx5^5 - sage: if is_shared_memory_available: fvars.shm.unlink() + sage: fvars.shm.unlink() sage: f.shutdown_worker_pool() """ def __init__(self, n_slots, field, idx_to_sextuple, init_data={}, use_mp=0, @@ -477,16 +463,12 @@ cdef class FvarsHandler: sage: f = FMatrix(FusionRing("A2",1), inject_variables=True) creating variables fx1..fx8 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 - sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ - sage: if is_shared_memory_available: - ....: n_proc = f.pool._processes - ....: pids_name = f._pid_list.shm.name - ....: else: - ....: n_proc = 0 - ....: pids_name = None + sage: f.start_worker_pool() + sage: n_proc = f.pool._processes + sage: pids_name = f._pid_list.shm.name sage: fvars = FvarsHandler(8,f._field,f._idx_to_sextuple,use_mp=n_proc,pids_name=pids_name) sage: TestSuite(fvars).run(skip="_test_pickling") - sage: if is_shared_memory_available: fvars.shm.unlink() + sage: fvars.shm.unlink() sage: f.shutdown_worker_pool() """ self.field = field @@ -549,13 +531,9 @@ cdef class FvarsHandler: sage: f = FMatrix(FusionRing("B7", 1), inject_variables=True) creating variables fx1..fx14 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 - sage: is_shared_memory_available = f.start_worker_pool() # Requires Python 3.8+ - sage: if is_shared_memory_available: - ....: n_proc = f.pool._processes - ....: pids_name = f._pid_list.shm.name - ....: else: - ....: n_proc = 0 - ....: pids_name = None + sage: f.start_worker_pool() + sage: n_proc = f.pool._processes + sage: pids_name = f._pid_list.shm.name sage: fvars = FvarsHandler(14,f._field,f._idx_to_sextuple,use_mp=n_proc,pids_name=pids_name) sage: rhs = tuple((exp, tuple(c._coefficients())) ....: for exp, c in poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx13**3 - 799/2881*fx1*fx2**5*fx10)) @@ -571,7 +549,7 @@ cdef class FvarsHandler: True sage: f._tup_to_fpoly(fvars[r]) == -1/19 True - sage: if is_shared_memory_available: fvars.shm.unlink() + sage: fvars.shm.unlink() sage: f.shutdown_worker_pool() .. NOTE:: @@ -655,13 +633,9 @@ cdef class FvarsHandler: sage: f = FMatrix(FusionRing("A3", 1), inject_variables=True) creating variables fx1..fx27 Defining fx0, ..., fx26 - sage: is_shared_memory_available = f.start_worker_pool() - sage: if is_shared_memory_available: - ....: n_proc = f.pool._processes - ....: pids_name = f._pid_list.shm.name - ....: else: - ....: n_proc = 0 - ....: pids_name = None + sage: f.start_worker_pool() + sage: n_proc = f.pool._processes + sage: pids_name = f._pid_list.shm.name sage: fvars = FvarsHandler(27,f._field,f._idx_to_sextuple,use_mp=n_proc,pids_name=pids_name) sage: rhs = tuple((exp, tuple(c._coefficients())) ....: for exp, c in poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx21**3 - 799/2881*fx1*fx2**5*fx10)) @@ -677,7 +651,7 @@ cdef class FvarsHandler: True sage: f._tup_to_fpoly(fvars[r]) == -1/19 True - sage: if is_shared_memory_available: fvars.shm.unlink() + sage: fvars.shm.unlink() sage: f.shutdown_worker_pool() """ cdef ETuple exp @@ -740,18 +714,14 @@ cdef class FvarsHandler: sage: f = FMatrix(FusionRing("F4",1)) sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() - sage: is_shared_memory_available = f.start_worker_pool() - sage: if is_shared_memory_available: - ....: n_proc = f.pool._processes - ....: pids_name = f._pid_list.shm.name - ....: else: - ....: n_proc = 0 - ....: pids_name = None + sage: f.start_worker_pool() + sage: n_proc = f.pool._processes + sage: pids_name = f._pid_list.shm.name sage: fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars,use_mp=n_proc,pids_name=pids_name) sage: for s, fvar in loads(dumps(fvars)).items(): ....: assert f._fvars[s] == f._tup_to_fpoly(fvar) ....: - sage: if is_shared_memory_available: fvars.shm.unlink() + sage: fvars.shm.unlink() sage: f.shutdown_worker_pool() """ n = self.fvars.size @@ -792,18 +762,14 @@ def make_FvarsHandler(n,field,idx_map,init_data): sage: f = FMatrix(FusionRing("G2",1)) sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() - sage: is_shared_memory_available = f.start_worker_pool() - sage: if is_shared_memory_available: - ....: n_proc = f.pool._processes - ....: pids_name = f._pid_list.shm.name - ....: else: - ....: n_proc = 0 - ....: pids_name = None + sage: f.start_worker_pool() + sage: n_proc = f.pool._processes + sage: pids_name = f._pid_list.shm.name sage: fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars,use_mp=n_proc,pids_name=pids_name) sage: for s, fvar in loads(dumps(fvars)).items(): # indirect doctest ....: assert f._fvars[s] == f._tup_to_fpoly(fvar) ....: - sage: if is_shared_memory_available: fvars.shm.unlink() + sage: fvars.shm.unlink() sage: f.shutdown_worker_pool() """ return FvarsHandler(n, field, idx_map, init_data=init_data) From 32c29c9232e990f9a94d81a72b37b262849df26d Mon Sep 17 00:00:00 2001 From: Miguel Marco Date: Sat, 27 Aug 2022 13:49:51 +0200 Subject: [PATCH 161/632] First implementation --- .../polynomial/multi_polynomial_ideal.py | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 10c0db501af..4ad17b06a47 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -546,6 +546,49 @@ def _groebner_basis_libsingular(self, algorithm="groebner", *args, **kwds): raise NameError("Algorithm '%s' unknown"%algorithm) return S + @libsingular_gb_standard_options + def _groebner_cover(self): + r""" + Compute the Groebner cover of the ideal. + + The Groebner cover is a partition of the space of parmeters, + such that the groebner basis is constant for each of the parts. + + The parts are given by two set of equations: the parameters + in the part are the ones that satosfy the first, but don't + satisfy the second ones. + + EXAMPLES:: + + sage: from sage.libs.singular.function import singular_function, lib + sage: F = PolynomialRing(QQ,'a').fraction_field() + sage: F.inject_variables() + Defining a + sage: R. = F[] + sage: I = R.ideal([-x+3*y+z-5,2*x+a*z+4,4*x-3*z-a-1]) + sage: I._groebner_cover() + [[[z, y, x], + [(2*a + 3)*z + (a + 9), (12*a + 18)*y + (-a^2 - 23*a - 36), (4*a + 6)*x + (-a^2 - a + 12)], + [[[0], [[(2*a + 3)]]]]], + [[1], [1], [[[(2*a + 3)], [[1]]]]]] + + """ + from sage.rings.fraction_field import FractionField_generic + F = self.base_ring() + if not isinstance(F, FractionField_generic): + raise TypeError("The base ring must be a field with parameters") + from sage.rings.polynomial.multi_polynomial_ring_base import is_MPolynomialRing + from sage.rings.polynomial.polynomial_ring import is_PolynomialRing + if not is_MPolynomialRing(F.ring()) and not is_PolynomialRing (F.ring()): + raise TypeError("The base ring must be a field with parameters") + from sage.libs.singular.function import singular_function, lib + from sage.arith.functions import lcm + lib("grobcov.lib") + grobcov = singular_function("grobcov") + polynomials = [] + for f in self.gens(): + polynomials.append(f * lcm([c.denominator() for c in f.coefficients()])) + return grobcov(self.ring().ideal(polynomials)) class MPolynomialIdeal_singular_repr( MPolynomialIdeal_singular_base_repr): @@ -4434,6 +4477,52 @@ def groebner_basis(self, algorithm='', deg_bound=None, mult_bound=None, prot=Fal gb = PolynomialSequence(self.ring(), gb, immutable=True) return gb + def groebner_cover(self): + r""" + Compute the Groebner cover of the ideal, over a field with parameters. + + The Groebner cover is a partition of the space of parameters, such that the + Groebner basis in each part is given by the same expression. + + EXAMPLES:: + + sage: F = PolynomialRing(QQ,'a').fraction_field() + sage: F.inject_variables() + Defining a + sage: R. = F[] + sage: I = R.ideal([-x+3*y+z-5,2*x+a*z+4,4*x-3*z-1/a]) + sage: I.groebner_cover() + {Quasi-affine subscheme X - Y of Affine Space of dimension 1 over Rational Field, where X is defined by: + 0 + and Y is defined by: + 2*a^2 + 3*a: [(2*a^2 + 3*a)*z + (8*a + 1), (12*a^2 + 18*a)*y + (-20*a^2 - 35*a - 2), (4*a + 6)*x + 11], + Quasi-affine subscheme X - Y of Affine Space of dimension 1 over Rational Field, where X is defined by: + a + and Y is defined by: + 1: [1], + Quasi-affine subscheme X - Y of Affine Space of dimension 1 over Rational Field, where X is defined by: + 2*a + 3 + and Y is defined by: + 1: [1]} + + + """ + gc= self._groebner_cover() + from sage.schemes.affine import affine_subscheme + from sage.schemes.affine.affine_space import AffineSpace + F = self.base_ring() + A = AffineSpace(F.base_ring(), F.ngens(), list(F.gens_dict().keys())) + result = {} + for segment in gc: + for piece in segment[2]: + X = A.subscheme([F.ring()(c) for c in piece[0]]) + Y = A.subscheme([F.ring()(c) for c in piece[1][0]]) + for pol in piece[1][1:]: + Y = Y.union(A.subscheme([F.ring()(c) for c in pol])) + result[Y.complement(X)] = segment[1] + return result + + def change_ring(self, P): r""" Return the ideal ``I`` in ``P`` spanned by From c041897a3fb0ad63df2acb39f446e719234bdfa7 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 29 Aug 2022 10:07:31 +0900 Subject: [PATCH 162/632] Trac #34381: Fixing pyflakes issues. --- src/sage/rings/lazy_series_ring.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 52840fdf30b..08ccb377660 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -1171,7 +1171,6 @@ def q_pochhammer(self, q=None): q = ZZ['q'].fraction_field().gen() if q not in self.base_ring(): raise ValueError("q must be in the base ring") - from .lazy_series_ring import LazyLaurentSeriesRing from sage.arith.misc import binomial qP = q.parent() one = qP.one() @@ -1209,7 +1208,6 @@ def euler(self): - :wikipedia:`Euler_function` """ - from .lazy_series_ring import LazyLaurentSeriesRing def coeff(n): k = ZZ(24 * n + 1) m, rem = k.sqrtrem() From 1b3695a1ea128e77b7a6404dd6afa6252a3ae1e4 Mon Sep 17 00:00:00 2001 From: Miguel Marco Date: Wed, 31 Aug 2022 17:51:46 +0200 Subject: [PATCH 163/632] Address reviewer's comments --- .../polynomial/multi_polynomial_ideal.py | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 4ad17b06a47..43acd037cf9 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -549,14 +549,22 @@ def _groebner_basis_libsingular(self, algorithm="groebner", *args, **kwds): @libsingular_gb_standard_options def _groebner_cover(self): r""" - Compute the Groebner cover of the ideal. + Compute the Gröbner cover of the ideal. - The Groebner cover is a partition of the space of parmeters, - such that the groebner basis is constant for each of the parts. + The Gröbner cover is a partition of the space of parmeters, + such that the Gröbner basis is constant for each of the parts. + + OUTPUT: + + A list of parts. Each element of this list contains: + + - The leading monomials of the Gröbner basis in the part + - The Gröbner basis in the part + - A list of components of the part. Each component is + has two lists of equations. The parameters in the + part are those that satisfy the first list of + equations, but do not satisfy the second one. - The parts are given by two set of equations: the parameters - in the part are the ones that satosfy the first, but don't - satisfy the second ones. EXAMPLES:: @@ -575,11 +583,10 @@ def _groebner_cover(self): """ from sage.rings.fraction_field import FractionField_generic F = self.base_ring() - if not isinstance(F, FractionField_generic): - raise TypeError("The base ring must be a field with parameters") from sage.rings.polynomial.multi_polynomial_ring_base import is_MPolynomialRing from sage.rings.polynomial.polynomial_ring import is_PolynomialRing - if not is_MPolynomialRing(F.ring()) and not is_PolynomialRing (F.ring()): + if (not isinstance(F, FractionField_generic) or + (not is_MPolynomialRing(F.ring()) and not is_PolynomialRing (F.ring()))): raise TypeError("The base ring must be a field with parameters") from sage.libs.singular.function import singular_function, lib from sage.arith.functions import lcm @@ -4479,7 +4486,7 @@ def groebner_basis(self, algorithm='', deg_bound=None, mult_bound=None, prot=Fal def groebner_cover(self): r""" - Compute the Groebner cover of the ideal, over a field with parameters. + Compute the Gröbner cover of the ideal, over a field with parameters. The Groebner cover is a partition of the space of parameters, such that the Groebner basis in each part is given by the same expression. @@ -4507,18 +4514,19 @@ def groebner_cover(self): """ - gc= self._groebner_cover() + gc = self._groebner_cover() from sage.schemes.affine import affine_subscheme from sage.schemes.affine.affine_space import AffineSpace F = self.base_ring() A = AffineSpace(F.base_ring(), F.ngens(), list(F.gens_dict().keys())) result = {} + ring = F.ring() for segment in gc: for piece in segment[2]: - X = A.subscheme([F.ring()(c) for c in piece[0]]) - Y = A.subscheme([F.ring()(c) for c in piece[1][0]]) + X = A.subscheme([ring(c) for c in piece[0]]) + Y = A.subscheme([ring(c) for c in piece[1][0]]) for pol in piece[1][1:]: - Y = Y.union(A.subscheme([F.ring()(c) for c in pol])) + Y = Y.union(A.subscheme([ring(c) for c in pol])) result[Y.complement(X)] = segment[1] return result From 66e1f10a4c31be55935f09f517ea4f06e094b0ca Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 2 Sep 2022 12:03:41 -0700 Subject: [PATCH 164/632] build/pkgs/scipy: Update to 1.9.1 --- build/pkgs/scipy/checksums.ini | 6 +++--- build/pkgs/scipy/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/scipy/checksums.ini b/build/pkgs/scipy/checksums.ini index 8090f084328..83f9a385fca 100644 --- a/build/pkgs/scipy/checksums.ini +++ b/build/pkgs/scipy/checksums.ini @@ -1,5 +1,5 @@ tarball=scipy-VERSION.tar.gz -sha1=22c0e73b933b938c272c6eb747cd6b70ab2f9b83 -md5=1f2e527930ddfa15a622b146dae42144 -cksum=994041683 +sha1=2f5a7bb5f992dd6766a3a0e088180be427518903 +md5=e6e70a9014dba74b4ef16686d23fd3ad +cksum=39501961 upstream_url=https://pypi.io/packages/source/s/scipy/scipy-VERSION.tar.gz diff --git a/build/pkgs/scipy/package-version.txt b/build/pkgs/scipy/package-version.txt index f8e233b2733..9ab8337f396 100644 --- a/build/pkgs/scipy/package-version.txt +++ b/build/pkgs/scipy/package-version.txt @@ -1 +1 @@ -1.9.0 +1.9.1 From b85c2d690f6f1e9fce7e91d8d3a44128938d2c9f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 2 Sep 2022 12:04:42 -0700 Subject: [PATCH 165/632] build/pkgs/meson: Update to 0.63.1 --- build/pkgs/meson/checksums.ini | 6 +++--- build/pkgs/meson/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/meson/checksums.ini b/build/pkgs/meson/checksums.ini index c46bd7bb761..c14db7a0114 100644 --- a/build/pkgs/meson/checksums.ini +++ b/build/pkgs/meson/checksums.ini @@ -1,5 +1,5 @@ tarball=meson-VERSION.tar.gz -sha1=85b1c71b598686e40632b5a57ac7f2bdd1dcd926 -md5=d9e7d69f73f875004fbb3cc8bfe2a39b -cksum=339088378 +sha1=e81d2915173324476693e585a785abed8fd1bbad +md5=078e59d11a72b74c3bd78cb8205e9ed7 +cksum=4240866935 upstream_url=https://pypi.io/packages/source/m/meson/meson-VERSION.tar.gz diff --git a/build/pkgs/meson/package-version.txt b/build/pkgs/meson/package-version.txt index 70cd2261d5c..630f2e0ce67 100644 --- a/build/pkgs/meson/package-version.txt +++ b/build/pkgs/meson/package-version.txt @@ -1 +1 @@ -0.63.0 +0.63.1 From a2d8f2f35f529fe708aeafc41b3b73625277eeed Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 3 Sep 2022 12:39:12 -0700 Subject: [PATCH 166/632] build/pkgs/scipy/spkg-check.in: New --- build/pkgs/scipy/spkg-check.in | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 build/pkgs/scipy/spkg-check.in diff --git a/build/pkgs/scipy/spkg-check.in b/build/pkgs/scipy/spkg-check.in new file mode 100644 index 00000000000..0787989fa5e --- /dev/null +++ b/build/pkgs/scipy/spkg-check.in @@ -0,0 +1,3 @@ +python3 -c 'import scipy' +cd src +python3 runtests.py --no-build From aff3fcaf34e789eeef49b887576286dc66676464 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 4 Sep 2022 06:49:31 -0700 Subject: [PATCH 167/632] .github/workflows/docker.yml: Lowercase for DOCKER_PUSH_REPOSITORY --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 23cab40539d..f6456b536c4 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -171,7 +171,7 @@ jobs: TOKEN="${{ secrets.GITHUB_TOKEN }}" fi if echo "$TOKEN" | docker login ghcr.io -u ${{ github.actor }} --password-stdin; then - echo "DOCKER_PUSH_REPOSITORY=${{ inputs.docker_push_repository }}" >> $GITHUB_ENV + echo "DOCKER_PUSH_REPOSITORY=$(echo ${{ inputs.docker_push_repository }} | tr "[:upper:]" "[:lower:]")" >> $GITHUB_ENV echo "DOCKER_CONFIG_FILE=$HOME/.docker/config.json" >> $GITHUB_ENV fi # From the docker documentation via .ci/update-env.sh: From a10bd2965e4dfdcbdb4f25d77932330ac981a80a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 4 Sep 2022 11:03:44 -0700 Subject: [PATCH 168/632] build/pkgs/scipy/spkg-check.in: Use scipy.test() --- build/pkgs/scipy/spkg-check.in | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/build/pkgs/scipy/spkg-check.in b/build/pkgs/scipy/spkg-check.in index 0787989fa5e..8ab92278cf2 100644 --- a/build/pkgs/scipy/spkg-check.in +++ b/build/pkgs/scipy/spkg-check.in @@ -1,3 +1 @@ -python3 -c 'import scipy' -cd src -python3 runtests.py --no-build +python3 -c 'import scipy; scipy.test()' From 074984448b625aaab4b334732a481d52bdcf3c0f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 4 Sep 2022 11:43:00 -0700 Subject: [PATCH 169/632] build/pkgs/scipy/dependencies_check: New - need pytest --- build/pkgs/scipy/dependencies_check | 1 + 1 file changed, 1 insertion(+) create mode 100644 build/pkgs/scipy/dependencies_check diff --git a/build/pkgs/scipy/dependencies_check b/build/pkgs/scipy/dependencies_check new file mode 100644 index 00000000000..e079f8a6038 --- /dev/null +++ b/build/pkgs/scipy/dependencies_check @@ -0,0 +1 @@ +pytest From a81e4d5ffac9263a015930c417f55c86d9402070 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 9 Sep 2022 22:22:37 +0200 Subject: [PATCH 170/632] implement factorization of symmetric functions --- src/sage/combinat/sf/sfa.py | 52 +++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index afeaf70a3e0..914ae2bcb37 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -225,6 +225,7 @@ from sage.categories.tensor import tensor from sage.combinat.free_module import CombinatorialFreeModule from sage.matrix.constructor import matrix +from sage.structure.factorization import Factorization from sage.misc.misc_c import prod from sage.data_structures.blas_dict import convert_remove_zeroes, linear_combination from copy import copy @@ -2992,6 +2993,57 @@ class SymmetricFunctionAlgebra_generic_Element(CombinatorialFreeModule.Element): m[1, 1, 1] + m[2, 1] + m[3] sage: m.set_print_style('lex') """ + def factor(self, proof=None): + """ + Return the factorization of this symmetric function. + + EXAMPLES:: + + sage: e = SymmetricFunctions(QQ).e() + sage: factor((5*e[3] + e[2,1] + e[1])*(7*e[2] + e[5,1])) + (e[1] + e[2, 1] + 5*e[3]) * (7*e[2] + e[5, 1]) + + sage: R. = QQ[] + sage: s = SymmetricFunctions(R.fraction_field()).s() + sage: factor((s[3] + x*s[2,1] + 1)*(3*y*s[2] + s[4,1] + x*y)) + (-s[] + (-x)*s[2, 1] - s[3]) * ((-x*y)*s[] + (-3*y)*s[2] - s[4, 1]) + + TESTS:: + + sage: p = SymmetricFunctions(QQ).p() + sage: factor((p[3] + p[2,1])*(p[2] + p[4,1])) + (p[2, 1] + p[3]) * (p[2] + p[4, 1]) + + sage: s = SymmetricFunctions(QQ).s() + sage: factor((s[3] + s[2,1])*(s[2] + s[4,1])) + (-1) * s[1] * s[2] * (-s[2] - s[4, 1]) + + sage: s = SymmetricFunctions(ZZ).s() + sage: factor((s[3] + s[2,1])*(s[2] + s[4,1])) + (-1) * s[1] * s[2] * (-s[2] - s[4, 1]) + + """ + from sage.combinat.sf.multiplicative import SymmetricFunctionAlgebra_multiplicative + L = self.parent() + if isinstance(L, SymmetricFunctionAlgebra_multiplicative): + M = L + else: + M = L.realization_of().h() + self = M(self) + + n = max((part[0] for part in self.support() if part), default=0) + R = PolynomialRing(self.base_ring(), + ["v%s" % a for a in range(1, n + 1)]) + poly = R({tuple(part.to_exp(n)): c for part, c in self}) + factors = poly.factor(proof=proof) + unit = factors.unit() + factors = [(M.sum_of_terms((_Partitions.from_exp(e), c) + for e, c in factor.iterator_exp_coeff(False)), + exponent) + for factor, exponent in factors] + if not isinstance(L, SymmetricFunctionAlgebra_multiplicative): + factors = [(L(factor), exponent) for factor, exponent in factors] + return Factorization(factors, unit=unit) def plethysm(self, x, include=None, exclude=None): r""" From c3ded2798260fe7a297d788ffd84d7416666f2a4 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 12 Sep 2022 13:08:44 +0200 Subject: [PATCH 171/632] add categories --- src/sage/combinat/sf/sf.py | 5 +++++ src/sage/combinat/sf/sfa.py | 35 +++++++++++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/sf/sf.py b/src/sage/combinat/sf/sf.py index f18f6fb75a2..46c9a905799 100644 --- a/src/sage/combinat/sf/sf.py +++ b/src/sage/combinat/sf/sf.py @@ -22,6 +22,8 @@ from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.categories.graded_hopf_algebras import GradedHopfAlgebras +from sage.categories.principal_ideal_domains import PrincipalIdealDomains +from sage.categories.unique_factorization_domains import UniqueFactorizationDomains from sage.categories.fields import Fields from sage.categories.rings import Rings from sage.combinat.partition import Partitions @@ -70,6 +72,7 @@ class SymmetricFunctions(UniqueRepresentation, Parent): sage: Sym.category() Join of Category of hopf algebras over Rational Field + and Category of unique factorization domains and Category of graded algebras over Rational Field and Category of commutative algebras over Rational Field and Category of monoids with realizations @@ -864,6 +867,8 @@ def __init__(self, R): assert(R in Fields() or R in Rings()) # side effect of this statement assures MRO exists for R self._base = R # Won't be needed when CategoryObject won't override anymore base_ring cat = GradedHopfAlgebras(R).Commutative().Cocommutative() + if R in PrincipalIdealDomains(): + cat &= UniqueFactorizationDomains() Parent.__init__(self, category=cat.WithRealizations()) def a_realization(self): diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index 914ae2bcb37..da1ff44d6cd 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -222,6 +222,8 @@ from sage.combinat.partition import _Partitions, Partitions, Partitions_n, Partition from sage.categories.hopf_algebras import HopfAlgebras from sage.categories.hopf_algebras_with_basis import HopfAlgebrasWithBasis +from sage.categories.principal_ideal_domains import PrincipalIdealDomains +from sage.categories.unique_factorization_domains import UniqueFactorizationDomains from sage.categories.tensor import tensor from sage.combinat.free_module import CombinatorialFreeModule from sage.matrix.constructor import matrix @@ -356,15 +358,33 @@ def super_categories(self): Category of commutative hopf algebras with basis over Rational Field, Join of Category of realizations of hopf algebras over Rational Field and Category of graded algebras over Rational Field - and Category of graded coalgebras over Rational Field] + and Category of graded coalgebras over Rational Field, + Category of unique factorization domains] + + sage: Sym = SymmetricFunctions(ZZ["x"]) + sage: bases = SymmetricFunctionsBases(Sym) + sage: bases.super_categories() + [Category of realizations of Symmetric Functions over Univariate Polynomial Ring in x over Integer Ring, + Category of commutative hopf algebras with basis over Univariate Polynomial Ring in x over Integer Ring, + Join of Category of realizations of hopf algebras over Univariate Polynomial Ring in x over Integer Ring + and Category of graded algebras over Univariate Polynomial Ring in x over Integer Ring + and Category of graded coalgebras over Univariate Polynomial Ring in x over Integer Ring] """ # FIXME: The last one should also be commutative, but this triggers a # KeyError when doing the C3 algorithm!!! - cat = HopfAlgebras(self.base().base_ring()) + R = self.base().base_ring() + cat = HopfAlgebras(R) + if R in PrincipalIdealDomains: + return [self.base().Realizations(), + cat.Commutative().WithBasis(), + cat.Graded().Realizations(), + UniqueFactorizationDomains()] + return [self.base().Realizations(), cat.Commutative().WithBasis(), cat.Graded().Realizations()] + class ParentMethods: def is_integral_domain(self, proof=True): @@ -3022,6 +3042,12 @@ def factor(self, proof=None): sage: factor((s[3] + s[2,1])*(s[2] + s[4,1])) (-1) * s[1] * s[2] * (-s[2] - s[4, 1]) + sage: R. = QQ[] + sage: JP = SymmetricFunctions(FractionField(R)).jack(t).P() + sage: f = (JP[2,1]*t + JP[1,1,1])^2 + sage: f.factor() + (1/(t^2 + 4*t + 4)) * ((-t-2)*JackP[1, 1, 1] + (-t^2-2*t)*JackP[2, 1])^2 + """ from sage.combinat.sf.multiplicative import SymmetricFunctionAlgebra_multiplicative L = self.parent() @@ -3037,10 +3063,11 @@ def factor(self, proof=None): poly = R({tuple(part.to_exp(n)): c for part, c in self}) factors = poly.factor(proof=proof) unit = factors.unit() - factors = [(M.sum_of_terms((_Partitions.from_exp(e), c) - for e, c in factor.iterator_exp_coeff(False)), + factors = [(M.element_class(M, {_Partitions.from_exp(e): c + for e, c in factor.iterator_exp_coeff(False)}), exponent) for factor, exponent in factors] + if not isinstance(L, SymmetricFunctionAlgebra_multiplicative): factors = [(L(factor), exponent) for factor, exponent in factors] return Factorization(factors, unit=unit) From c501119c1d2eea747247d9bcfee3f81e87625cbf Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 22 Sep 2022 23:33:23 +0900 Subject: [PATCH 172/632] Making the result not depend on the input. --- src/sage/combinat/sf/sfa.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index fe04e1d574d..51da315cc76 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -3165,11 +3165,12 @@ def plethysm(self, x, include=None, exclude=None): """ from sage.structure.element import parent as get_parent Px = get_parent(x) - if not self: - return Px(0) - parent = self.parent() R = parent.base_ring() + + if not self: + return R(0) + tHA = HopfAlgebrasWithBasis(R).TensorProducts() tensorflag = Px in tHA if not is_SymmetricFunction(x): From bad6489f58748f042916bbf70bf0f763613a2f63 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 23 Sep 2022 20:05:15 +0900 Subject: [PATCH 173/632] Adding some doctests and documentation on the plethysm output. --- src/sage/combinat/sf/sfa.py | 40 ++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index 51da315cc76..3687615857f 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -3031,28 +3031,48 @@ def plethysm(self, x, include=None, exclude=None): - ``x`` -- a symmetric function over the same base ring as ``self`` - - ``include`` -- a list of variables to be treated as degree one elements instead of the default degree one elements - - ``exclude`` -- a list of variables to be excluded from the default degree one elements + OUTPUT: + + An element in the parent of ``x`` or the base ring `R` of ``self`` + when ``x`` is in `R`. + EXAMPLES:: sage: Sym = SymmetricFunctions(QQ) sage: s = Sym.s() sage: h = Sym.h() - sage: s(h[3](h[2])) + sage: h3h2 = h[3](h[2]); h3h2 + h[2, 2, 2] - 2*h[3, 2, 1] + h[3, 3] + h[4, 1, 1] - h[5, 1] + h[6] + sage: s(h3h2) s[2, 2, 2] + s[4, 2] + s[6] sage: p = Sym.p() - sage: p(p[3](s[2,1])) + sage: p3s21 = p[3](s[2,1]); p3s21 + s[2, 2, 2, 1, 1, 1] - s[2, 2, 2, 2, 1] - s[3, 2, 1, 1, 1, 1] + + s[3, 2, 2, 2] + s[3, 3, 1, 1, 1] - s[3, 3, 2, 1] + 2*s[3, 3, 3] + + s[4, 1, 1, 1, 1, 1] - s[4, 3, 2] + s[4, 4, 1] - s[5, 1, 1, 1, 1] + + s[5, 2, 2] - s[5, 4] + s[6, 1, 1, 1] - s[6, 2, 1] + s[6, 3] + sage: p(p3s21) 1/3*p[3, 3, 3] - 1/3*p[9] sage: e = Sym.e() sage: e[3](e[2]) e[3, 3] + e[4, 1, 1] - 2*e[4, 2] - e[5, 1] + e[6] - :: + Note that the output is in the basis of the input ``x``:: + + sage: s[2,1](h[3]) + h[4, 3, 2] - h[4, 4, 1] - h[5, 2, 2] + h[5, 3, 1] + h[5, 4] + + h[6, 2, 1] - 2*h[6, 3] - h[7, 1, 1] + h[7, 2] + h[8, 1] - h[9] + + sage: h[2,1](s[3]) + s[4, 3, 2] + s[4, 4, 1] + s[5, 2, 2] + s[5, 3, 1] + s[5, 4] + + s[6, 2, 1] + 2*s[6, 3] + 2*s[7, 2] + s[8, 1] + s[9] + + Examples over a polynomial ring:: sage: R. = QQ[] sage: s = SymmetricFunctions(R).s() @@ -3069,6 +3089,12 @@ def plethysm(self, x, include=None, exclude=None): sage: s(1).plethysm(s(0)) s[] + When ``x`` is a constant, then it is returned as an element + of the base ring:: + + sage: s[3](2).parent() is R + True + Sage also handles plethysm of tensor products of symmetric functions:: sage: s = SymmetricFunctions(QQ).s() @@ -3080,8 +3106,8 @@ def plethysm(self, x, include=None, exclude=None): s[1, 1, 1] # s[3] + s[2, 1] # s[2, 1] + s[3] # s[1, 1, 1] One can use this to work with symmetric functions in two sets of - commuting variables. For example, we verify the Cauchy identities (in - degree 5):: + commuting variables. For example, we verify the Cauchy identities + (in degree 5):: sage: m = SymmetricFunctions(QQ).m() sage: P5 = Partitions(5) From c0690f019e4e493d2d8f750d2bc5e7a7d2559d43 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 28 Sep 2022 10:35:41 -0700 Subject: [PATCH 174/632] .github/workflows/docker.yml: Increase fetch_depth --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index ebc1a528fe6..6ccd9a07f62 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -135,7 +135,7 @@ jobs: with: repository: ${{ inputs.sage_repo }} ref: ${{ inputs.sage_ref }} - fetch-depth: 2000 + fetch-depth: 10000 - name: fetch tags run: git fetch --depth=1 origin +refs/tags/*:refs/tags/* - name: free disk space From 433b2e9658be0a42e3382a905d79a225cb7f968d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 28 Sep 2022 10:45:56 -0700 Subject: [PATCH 175/632] build/pkgs/meson_python: Add spkg-check.in, add dependencies_check packages: gitpython, pytest_mock --- build/pkgs/gitpython/SPKG.rst | 18 ++++++++++++++++++ build/pkgs/gitpython/dependencies | 4 ++++ build/pkgs/gitpython/requirements.txt | 1 + build/pkgs/gitpython/type | 1 + build/pkgs/meson_python/dependencies_check | 1 + build/pkgs/meson_python/spkg-check.in | 2 ++ build/pkgs/pytest_mock/SPKG.rst | 18 ++++++++++++++++++ build/pkgs/pytest_mock/dependencies | 4 ++++ build/pkgs/pytest_mock/requirements.txt | 1 + build/pkgs/pytest_mock/type | 1 + 10 files changed, 51 insertions(+) create mode 100644 build/pkgs/gitpython/SPKG.rst create mode 100644 build/pkgs/gitpython/dependencies create mode 100644 build/pkgs/gitpython/requirements.txt create mode 100644 build/pkgs/gitpython/type create mode 100644 build/pkgs/meson_python/dependencies_check create mode 100644 build/pkgs/meson_python/spkg-check.in create mode 100644 build/pkgs/pytest_mock/SPKG.rst create mode 100644 build/pkgs/pytest_mock/dependencies create mode 100644 build/pkgs/pytest_mock/requirements.txt create mode 100644 build/pkgs/pytest_mock/type diff --git a/build/pkgs/gitpython/SPKG.rst b/build/pkgs/gitpython/SPKG.rst new file mode 100644 index 00000000000..8928ececb8d --- /dev/null +++ b/build/pkgs/gitpython/SPKG.rst @@ -0,0 +1,18 @@ +gitpython: GitPython is a python library used to interact with Git repositories +=============================================================================== + +Description +----------- + +GitPython is a python library used to interact with Git repositories + +License +------- + +BSD + +Upstream Contact +---------------- + +https://pypi.org/project/GitPython/ + diff --git a/build/pkgs/gitpython/dependencies b/build/pkgs/gitpython/dependencies new file mode 100644 index 00000000000..0738c2d7777 --- /dev/null +++ b/build/pkgs/gitpython/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/gitpython/requirements.txt b/build/pkgs/gitpython/requirements.txt new file mode 100644 index 00000000000..64b1adaeeb4 --- /dev/null +++ b/build/pkgs/gitpython/requirements.txt @@ -0,0 +1 @@ +GitPython diff --git a/build/pkgs/gitpython/type b/build/pkgs/gitpython/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/gitpython/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/meson_python/dependencies_check b/build/pkgs/meson_python/dependencies_check new file mode 100644 index 00000000000..7aa85eab873 --- /dev/null +++ b/build/pkgs/meson_python/dependencies_check @@ -0,0 +1 @@ +pytest pytest_mock gitpython cython auditwheel_or_delocate diff --git a/build/pkgs/meson_python/spkg-check.in b/build/pkgs/meson_python/spkg-check.in new file mode 100644 index 00000000000..8dd1124505a --- /dev/null +++ b/build/pkgs/meson_python/spkg-check.in @@ -0,0 +1,2 @@ +cd src +pytest diff --git a/build/pkgs/pytest_mock/SPKG.rst b/build/pkgs/pytest_mock/SPKG.rst new file mode 100644 index 00000000000..59692594971 --- /dev/null +++ b/build/pkgs/pytest_mock/SPKG.rst @@ -0,0 +1,18 @@ +pytest_mock: Thin-wrapper around the mock package for easier use with pytest +============================================================================ + +Description +----------- + +Thin-wrapper around the mock package for easier use with pytest + +License +------- + +MIT + +Upstream Contact +---------------- + +https://pypi.org/project/pytest-mock/ + diff --git a/build/pkgs/pytest_mock/dependencies b/build/pkgs/pytest_mock/dependencies new file mode 100644 index 00000000000..4f0297e40f8 --- /dev/null +++ b/build/pkgs/pytest_mock/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) pytest iniconfig packaging attrs pluggy tomli py pyparsing | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/pytest_mock/requirements.txt b/build/pkgs/pytest_mock/requirements.txt new file mode 100644 index 00000000000..4ef37316e6a --- /dev/null +++ b/build/pkgs/pytest_mock/requirements.txt @@ -0,0 +1 @@ +pytest-mock diff --git a/build/pkgs/pytest_mock/type b/build/pkgs/pytest_mock/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/pytest_mock/type @@ -0,0 +1 @@ +optional From bce99d207802265cdfd4ad9570856e2aaf22bf32 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 28 Sep 2022 11:31:17 -0700 Subject: [PATCH 176/632] build/pkgs/pytest_mock/dependencies: Remove iniconfig added by mistake --- build/pkgs/pytest_mock/dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/pytest_mock/dependencies b/build/pkgs/pytest_mock/dependencies index 4f0297e40f8..37ea60eb442 100644 --- a/build/pkgs/pytest_mock/dependencies +++ b/build/pkgs/pytest_mock/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) pytest iniconfig packaging attrs pluggy tomli py pyparsing | $(PYTHON_TOOLCHAIN) +$(PYTHON) pytest packaging attrs pluggy tomli py pyparsing | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. From 9a566098a112372e028c5aa1a633469fe714b115 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 28 Sep 2022 12:24:49 -0700 Subject: [PATCH 177/632] tox.ini: Add packages factor 'develop' --- tox.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 0106e693891..504111ea4c2 100644 --- a/tox.ini +++ b/tox.ini @@ -181,6 +181,7 @@ setenv = # What system packages should be installed. Default: All standard packages with spkg-configure. SAGE_PACKAGE_LIST_ARGS=--has-file=spkg-configure.m4 :standard: recommended: EXTRA_SAGE_PACKAGES_3=_recommended $(head -n 1 build/pkgs/_recommended/dependencies) + develop: EXTRA_SAGE_PACKAGES_4=_develop $(head -n 1 build/pkgs/_develop/dependencies) minimal: SAGE_PACKAGE_LIST_ARGS=_prereq maximal: SAGE_PACKAGE_LIST_ARGS=:standard: :optional: conda-environment: SAGE_PACKAGE_LIST_ARGS=_prereq @@ -570,7 +571,7 @@ setenv = # # Resulting EXTRA_SAGE_PACKAGES # - ALL_EXTRA_SAGE_PACKAGES={env:EXTRA_SAGE_PACKAGES_0:} {env:EXTRA_SAGE_PACKAGES_1:} {env:EXTRA_SAGE_PACKAGES_2:} {env:EXTRA_SAGE_PACKAGES_3:} {env:EXTRA_SAGE_PACKAGES:} + ALL_EXTRA_SAGE_PACKAGES={env:EXTRA_SAGE_PACKAGES_0:} {env:EXTRA_SAGE_PACKAGES_1:} {env:EXTRA_SAGE_PACKAGES_2:} {env:EXTRA_SAGE_PACKAGES_3:} {env:EXTRA_SAGE_PACKAGES_4:} {env:EXTRA_SAGE_PACKAGES:} # environment will be skipped if regular expression does not match against the sys.platform string platform = From 7a4c6690d499609137b6ea881f35a8471a87e27e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 28 Sep 2022 13:04:37 -0700 Subject: [PATCH 178/632] build/pkgs/git/distros/arch.txt: New --- build/pkgs/git/distros/arch.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 build/pkgs/git/distros/arch.txt diff --git a/build/pkgs/git/distros/arch.txt b/build/pkgs/git/distros/arch.txt new file mode 100644 index 00000000000..5664e303b5d --- /dev/null +++ b/build/pkgs/git/distros/arch.txt @@ -0,0 +1 @@ +git From 004197d2eb3fec2104e25053b2b90045c61e95d3 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 28 Sep 2022 13:31:14 -0700 Subject: [PATCH 179/632] build/pkgs/pyproject_metadata: Update to 0.6.1 --- build/pkgs/pyproject_metadata/checksums.ini | 6 +++--- build/pkgs/pyproject_metadata/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/pyproject_metadata/checksums.ini b/build/pkgs/pyproject_metadata/checksums.ini index da299c46588..4fcc0ec49c2 100644 --- a/build/pkgs/pyproject_metadata/checksums.ini +++ b/build/pkgs/pyproject_metadata/checksums.ini @@ -1,5 +1,5 @@ tarball=pyproject-metadata-VERSION.tar.gz -sha1=5421824aa29786bde43f510365c4d035a0614ba5 -md5=85fcbd5d777809ca2217a996e06fe2e0 -cksum=1327227039 +sha1=c2b7679b1e56a341aa00c186c0d1a6bbd7bd5c2c +md5=e13b11cb723da96f8397addddca963cc +cksum=2246727402 upstream_url=https://pypi.io/packages/source/p/pyproject_metadata/pyproject-metadata-VERSION.tar.gz diff --git a/build/pkgs/pyproject_metadata/package-version.txt b/build/pkgs/pyproject_metadata/package-version.txt index 8f0916f768f..ee6cdce3c29 100644 --- a/build/pkgs/pyproject_metadata/package-version.txt +++ b/build/pkgs/pyproject_metadata/package-version.txt @@ -1 +1 @@ -0.5.0 +0.6.1 From 80d1e58693a633bec4564acd048c2d3020bccfd4 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 29 Sep 2022 12:02:21 +0200 Subject: [PATCH 180/632] use that exp is holonomic --- src/sage/rings/lazy_series.py | 56 +++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 0b1eba2a6a7..e764411d159 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -3129,6 +3129,62 @@ def _floordiv_(self, other): raise TypeError("must be an integral domain") return P(self / other) + # === fast special functions === + + def exp(self): + r""" + Return the exponential series of ``self``. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(QQ) + sage: exp(z) + 1 + z + 1/2*z^2 + 1/6*z^3 + 1/24*z^4 + 1/120*z^5 + 1/720*z^6 + O(z^7) + sage: exp(z + z^2) + 1 + z + 3/2*z^2 + 7/6*z^3 + 25/24*z^4 + 27/40*z^5 + 331/720*z^6 + O(z^7) + sage: exp(0) + 1 + sage: exp(1 + z) + Traceback (most recent call last): + ... + ValueError: can only compose with a positive valuation series + + sage: L. = LazyPowerSeriesRing(QQ) + sage: exp(x+y)[4].factor() + (1/24) * (x + y)^4 + sage: exp(x/(1-y)).polynomial(3) + 1/6*x^3 + x^2*y + x*y^2 + 1/2*x^2 + x*y + x + 1 + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(QQ); x = var("x") + sage: exp(z)[0:6] == exp(x).series(x, 6).coefficients(sparse=False) + True + + Check the exponential when the base ring is a lazy ring:: + + sage: L. = LazyPowerSeriesRing(QQ) + sage: M. = LazyPowerSeriesRing(L) + sage: exp(x) + 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) + """ + P = self.parent() + R = self.base_ring() + coeff_stream = self._coeff_stream + if any(coeff_stream[i] for i in range(coeff_stream._approximate_order, 1)): + raise ValueError("can only compose with a positive valuation series") + # WARNING: d_self need not be a proper element of P, e.g. for + # multivariate power series + d_self = Stream_function(lambda n: (n + 1) * coeff_stream[n + 1], + P.is_sparse(), 0) + f = P.undefined(valuation=0) + d_self_f = Stream_cauchy_mul(d_self, f._coeff_stream) + int_d_self_f = Stream_function(lambda n: d_self_f[n-1] / R(n) if n else R.one(), + P.is_sparse(), 0) + f._coeff_stream._target = int_d_self_f + return f + + class LazyLaurentSeries(LazyCauchyProductSeries): r""" A Laurent series where the coefficients are computed lazily. From 08ca5a3b8b9dac49beacd14efa49883f197d6212 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 29 Sep 2022 16:02:18 +0200 Subject: [PATCH 181/632] provide fast logarithm --- src/sage/rings/lazy_series.py | 113 +++++++++++++++++++--------------- 1 file changed, 65 insertions(+), 48 deletions(-) diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index e764411d159..a880039b90e 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -1809,36 +1809,10 @@ def exp(self): EXAMPLES:: - sage: L. = LazyLaurentSeriesRing(QQ) - sage: exp(z) - 1 + z + 1/2*z^2 + 1/6*z^3 + 1/24*z^4 + 1/120*z^5 + 1/720*z^6 + O(z^7) - sage: exp(z + z^2) - 1 + z + 3/2*z^2 + 7/6*z^3 + 25/24*z^4 + 27/40*z^5 + 331/720*z^6 + O(z^7) - sage: exp(0) - 1 - sage: exp(1 + z) - Traceback (most recent call last): - ... - ValueError: can only compose with a positive valuation series - - sage: L. = LazyPowerSeriesRing(QQ) - sage: exp(x+y)[4].factor() - (1/24) * (x + y)^4 - sage: exp(x/(1-y)).polynomial(3) - 1/6*x^3 + x^2*y + x*y^2 + 1/2*x^2 + x*y + x + 1 - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(QQ); x = var("x") - sage: exp(z)[0:6] == exp(x).series(x, 6).coefficients(sparse=False) - True - - Check the exponential when the base ring is a lazy ring:: - - sage: L. = LazyPowerSeriesRing(QQ) - sage: M. = LazyPowerSeriesRing(L) - sage: exp(x) - 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: L = LazyDirichletSeriesRing(QQ, "s") + sage: Z = L(constant=1, valuation=2) + sage: exp(Z) + 1 + 1/(2^s) + 1/(3^s) + 3/2/4^s + 1/(5^s) + 2/6^s + 1/(7^s) + O(1/(8^s)) """ from .lazy_series_ring import LazyLaurentSeriesRing P = LazyLaurentSeriesRing(self.base_ring(), "z", sparse=self.parent()._sparse) @@ -1851,24 +1825,10 @@ def log(self): EXAMPLES:: - sage: L. = LazyLaurentSeriesRing(QQ) - sage: log(1/(1-z)) - z + 1/2*z^2 + 1/3*z^3 + 1/4*z^4 + 1/5*z^5 + 1/6*z^6 + 1/7*z^7 + O(z^8) - - sage: L. = LazyPowerSeriesRing(QQ) - sage: log((1 + x/(1-y))).polynomial(3) - 1/3*x^3 - x^2*y + x*y^2 - 1/2*x^2 + x*y + x - - TESTS:: - - sage: L. = LazyLaurentSeriesRing(QQ); x = var("x") - sage: log(1+z)[0:6] == log(1+x).series(x, 6).coefficients(sparse=False) - True - - sage: log(z) - Traceback (most recent call last): - ... - ValueError: can only compose with a positive valuation series + sage: L = LazyDirichletSeriesRing(QQ, "s") + sage: Z = L(constant=1) + sage: log(Z) + 1/(2^s) + 1/(3^s) + 1/2/4^s + 1/(5^s) + 1/(7^s) + O(1/(8^s)) """ from .lazy_series_ring import LazyLaurentSeriesRing P = LazyLaurentSeriesRing(self.base_ring(), "z", sparse=self.parent()._sparse) @@ -3135,6 +3095,12 @@ def exp(self): r""" Return the exponential series of ``self``. + We use the identity + + .. MATH:: + + \exp(s) = 1 + \int s' \exp(s). + EXAMPLES:: sage: L. = LazyLaurentSeriesRing(QQ) @@ -3171,6 +3137,8 @@ def exp(self): P = self.parent() R = self.base_ring() coeff_stream = self._coeff_stream + # TODO: coefficients should not be checked here, it prevents + # us from using self.define in some cases! if any(coeff_stream[i] for i in range(coeff_stream._approximate_order, 1)): raise ValueError("can only compose with a positive valuation series") # WARNING: d_self need not be a proper element of P, e.g. for @@ -3184,6 +3152,55 @@ def exp(self): f._coeff_stream._target = int_d_self_f return f + def log(self): + r""" + Return the series for the natural logarithm of ``self``. + + We use the identity + + .. MATH:: + + \log(s) = \int s' / s. + + EXAMPLES:: + + sage: L. = LazyLaurentSeriesRing(QQ) + sage: log(1/(1-z)) + z + 1/2*z^2 + 1/3*z^3 + 1/4*z^4 + 1/5*z^5 + 1/6*z^6 + 1/7*z^7 + O(z^8) + + sage: L. = LazyPowerSeriesRing(QQ) + sage: log((1 + x/(1-y))).polynomial(3) + 1/3*x^3 - x^2*y + x*y^2 - 1/2*x^2 + x*y + x + + TESTS:: + + sage: L. = LazyLaurentSeriesRing(QQ); x = var("x") + sage: log(1+z)[0:6] == log(1+x).series(x, 6).coefficients(sparse=False) + True + + sage: log(z) + Traceback (most recent call last): + ... + ValueError: can only compose with a positive valuation series + """ + P = self.parent() + R = self.base_ring() + coeff_stream = self._coeff_stream + # TODO: coefficients should not be checked here, it prevents + # us from using self.define in some cases! + if (any(coeff_stream[i] for i in range(coeff_stream._approximate_order, 0)) + or coeff_stream[0] != R.one()): + raise ValueError("can only compose with a positive valuation series") + # WARNING: d_self need not be a proper element of P, e.g. for + # multivariate power series + d_self = Stream_function(lambda n: (n + 1) * coeff_stream[n + 1], + P.is_sparse(), 0) + d_self_quo_self = Stream_cauchy_mul(d_self, + Stream_cauchy_invert(coeff_stream)) + int_d_self_quo_self = Stream_function(lambda n: d_self_quo_self[n-1] / R(n), + P.is_sparse(), 1) + return P.element_class(P, int_d_self_quo_self) + class LazyLaurentSeries(LazyCauchyProductSeries): r""" From d2faec419ae8db9d21f13aca431e37c9d710d0d7 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 29 Sep 2022 18:16:01 -0700 Subject: [PATCH 182/632] build/pkgs/meson_python: Update to 0.9.0 --- build/pkgs/meson_python/checksums.ini | 6 +++--- build/pkgs/meson_python/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/meson_python/checksums.ini b/build/pkgs/meson_python/checksums.ini index e72f2a738cc..cc19a05eee3 100644 --- a/build/pkgs/meson_python/checksums.ini +++ b/build/pkgs/meson_python/checksums.ini @@ -1,5 +1,5 @@ tarball=meson_python-VERSION.tar.gz -sha1=18c1742379f1a9f3905c67c348aeb2442b02c119 -md5=38cc212d532a55ba4a53c572656ecd70 -cksum=4204960713 +sha1=87b086c28d626182056d020d8e4eeafa8bb8ccee +md5=4774f7348d5fc6769e62716600d3773a +cksum=3965133995 upstream_url=https://pypi.io/packages/source/m/meson_python/meson_python-VERSION.tar.gz diff --git a/build/pkgs/meson_python/package-version.txt b/build/pkgs/meson_python/package-version.txt index 6f4eebdf6f6..ac39a106c48 100644 --- a/build/pkgs/meson_python/package-version.txt +++ b/build/pkgs/meson_python/package-version.txt @@ -1 +1 @@ -0.8.1 +0.9.0 From 99de8018b1bc3f599b10bea2480aed7417dedc3f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 29 Sep 2022 18:18:16 -0700 Subject: [PATCH 183/632] build/pkgs/meson_python/patches/126.patch: Remove (upstreamed) --- build/pkgs/meson_python/patches/126.patch | 26 ----------------------- 1 file changed, 26 deletions(-) delete mode 100644 build/pkgs/meson_python/patches/126.patch diff --git a/build/pkgs/meson_python/patches/126.patch b/build/pkgs/meson_python/patches/126.patch deleted file mode 100644 index 3f417b41a5e..00000000000 --- a/build/pkgs/meson_python/patches/126.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 320ecea8996a062317ac7790eebe17f5f3f95551 Mon Sep 17 00:00:00 2001 -From: Matthias Koeppe -Date: Tue, 2 Aug 2022 16:20:50 -0700 -Subject: [PATCH] Project.platform_tag: Handle the case of 32bit Python on - x86_64 - ---- - mesonpy/__init__.py | 5 +++++ - 1 file changed, 5 insertions(+) - -diff --git a/mesonpy/__init__.py b/mesonpy/__init__.py -index aff3dd1..bdcda25 100644 ---- a/mesonpy/__init__.py -+++ b/mesonpy/__init__.py -@@ -684,6 +684,11 @@ def platform_tag(self) -> str: - # https://github.com/pypa/packaging/issues/578 - parts[1] += '.0' - platform_ = '-'.join(parts) -+ elif parts[0] == 'linux' and parts[1] == 'x86_64' and sys.maxsize == 2147483647: -+ # 32-bit Python running on an x86_64 host -+ # https://github.com/FFY00/meson-python/issues/123 -+ parts[1] = 'i686' -+ platform_ = '-'.join(parts) - return platform_.replace('-', '_').replace('.', '_') - - def _calculate_file_abi_tag_heuristic_windows(self, filename: str) -> Optional[mesonpy._tags.Tag]: From 37f6f4ca0c116c99de3660dbc0b93160307ceea1 Mon Sep 17 00:00:00 2001 From: Miguel Marco Date: Fri, 30 Sep 2022 11:27:07 +0200 Subject: [PATCH 184/632] Eliminate restriction on orderings --- src/sage/algebras/commutative_dga.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/algebras/commutative_dga.py b/src/sage/algebras/commutative_dga.py index a05ede46e52..1f32129b6e2 100644 --- a/src/sage/algebras/commutative_dga.py +++ b/src/sage/algebras/commutative_dga.py @@ -968,12 +968,12 @@ def __classcall__(cls, base, names=None, degrees=None, R=None, I=None, category= # The entries of degrees are not iterables, so # treat as singly-graded. pass - if multigrade: - if sorted(map(sum, degrees)) != list(map(sum, degrees)): - raise ValueError("the generators should be ordered in increased total degree") - else: - if sorted(degrees) != list(degrees): - raise ValueError("the generators should be ordered in increasing degree") + #if multigrade: + #if sorted(map(sum, degrees)) != list(map(sum, degrees)): + #raise ValueError("the generators should be ordered in increased total degree") + #else: + #if sorted(degrees) != list(degrees): + #raise ValueError("the generators should be ordered in increasing degree") degrees = tuple(degrees) if not R or not I: if n > 1: From 994dd6b40e9eccc9137907bee340410512230928 Mon Sep 17 00:00:00 2001 From: Miguel Marco Date: Fri, 30 Sep 2022 12:03:44 +0200 Subject: [PATCH 185/632] Implement `cdg_algebra` to create `DifferentialGCAlgebra` from an existing one --- src/sage/algebras/commutative_dga.py | 50 ++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 6 deletions(-) diff --git a/src/sage/algebras/commutative_dga.py b/src/sage/algebras/commutative_dga.py index 1f32129b6e2..cc06e907887 100644 --- a/src/sage/algebras/commutative_dga.py +++ b/src/sage/algebras/commutative_dga.py @@ -968,12 +968,7 @@ def __classcall__(cls, base, names=None, degrees=None, R=None, I=None, category= # The entries of degrees are not iterables, so # treat as singly-graded. pass - #if multigrade: - #if sorted(map(sum, degrees)) != list(map(sum, degrees)): - #raise ValueError("the generators should be ordered in increased total degree") - #else: - #if sorted(degrees) != list(degrees): - #raise ValueError("the generators should be ordered in increasing degree") + degrees = tuple(degrees) if not R or not I: if n > 1: @@ -2013,6 +2008,49 @@ def __init__(self, A, differential): self._minimalmodels = {} self._numerical_invariants = {} + def cdg_algebra(self, differential): + r""" + Construct a differential graded commutative algebra from the underlying + graded commutative algebra by specifying a differential. This may be used + to get a new differential over the same algebra structure. + + INPUT: + + - ``differential`` -- a dictionary defining a differential or + a map defining a valid differential + + The keys of the dictionary are generators of the algebra, and + the associated values are their targets under the + differential. Any generators which are not specified are + assumed to have zero differential. Alternatively, the + differential can be defined using the :meth:`differential` + method; see below for an example. + + .. SEEALSO:: + + :meth:`differential` + + EXAMPLES:: + + sage: A. = GradedCommutativeAlgebra(GF(5), degrees=(2, 3, 2, 4)) + sage: B = A.quotient(A.ideal(x^3-z*t)) + sage: C = B.cdg_algebra({y:t}) + sage: C + Commutative Differential Graded Algebra with generators ('x', 'y', 'z', 't') in degrees (2, 3, 2, 4) with relations [x^3 - z*t] over Finite Field of size 5 with differential: + x --> 0 + y --> t + z --> 0 + t --> 0 + sage: C.cdg_algebra({}) + Commutative Differential Graded Algebra with generators ('x', 'y', 'z', 't') in degrees (2, 3, 2, 4) with relations [x^3 - z*t] over Finite Field of size 5 with differential: + x --> 0 + y --> 0 + z --> 0 + t --> 0 + + """ + return self.graded_commutative_algebra().cdg_algebra(differential) + def graded_commutative_algebra(self): """ Return the base graded commutative algebra of ``self``. From bd3e3ebf0d7b707b9b03ab42ee7e7c71a28df782 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 7 Oct 2022 18:26:37 +0200 Subject: [PATCH 186/632] make sparsity a decision of the user --- src/sage/data_structures/stream.py | 323 ++++++++++++++--------------- src/sage/rings/lazy_series.py | 51 ++--- src/sage/rings/lazy_series_ring.py | 51 +++-- 3 files changed, 202 insertions(+), 223 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index d8d780dbca4..2bbb2f99106 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -27,33 +27,33 @@ sage: g = Stream_function(lambda n: 1, True, 0) sage: [g[i] for i in range(10)] [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] - sage: h = Stream_add(f, g) + sage: h = Stream_add(f, g, True) sage: [h[i] for i in range(10)] [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] We can subtract one stream from another:: - sage: h = Stream_sub(f, g) + sage: h = Stream_sub(f, g, True) sage: [h[i] for i in range(10)] [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8] There is a Cauchy product on streams:: - sage: h = Stream_cauchy_mul(f, g) + sage: h = Stream_cauchy_mul(f, g, True) sage: [h[i] for i in range(10)] [0, 1, 3, 6, 10, 15, 21, 28, 36, 45] We can compute the inverse corresponding to the Cauchy product:: sage: ginv = Stream_cauchy_invert(g) - sage: h = Stream_cauchy_mul(f, ginv) + sage: h = Stream_cauchy_mul(f, ginv, True) sage: [h[i] for i in range(10)] [0, 1, 1, 1, 1, 1, 1, 1, 1, 1] Two streams can be composed:: sage: g = Stream_function(lambda n: n, True, 1) - sage: h = Stream_cauchy_compose(f, g) + sage: h = Stream_cauchy_compose(f, g, True) sage: [h[i] for i in range(10)] [0, 1, 4, 14, 46, 145, 444, 1331, 3926, 11434] @@ -153,7 +153,7 @@ def _approximate_order(self): EXAMPLES:: sage: from sage.data_structures.stream import Stream_exact - sage: f = Stream_exact([0,3], True) + sage: f = Stream_exact([0,3]) sage: f._approximate_order 1 """ @@ -286,9 +286,9 @@ def __getstate__(self): sage: from sage.data_structures.stream import Stream_exact sage: from sage.data_structures.stream import Stream_cauchy_mul - sage: h = Stream_exact([1], True) - sage: g = Stream_exact([1, -1, -1], True) - sage: u = Stream_cauchy_mul(h, g) + sage: h = Stream_exact([1]) + sage: g = Stream_exact([1, -1, -1]) + sage: u = Stream_cauchy_mul(h, g, True) sage: [u[i] for i in range(10)] [1, -1, -1, 0, 0, 0, 0, 0, 0, 0] sage: u._cache @@ -299,9 +299,9 @@ def __getstate__(self): sage: [m[i] for i in range(10)] [1, -1, -1, 0, 0, 0, 0, 0, 0, 0] - sage: h = Stream_exact([1], False) - sage: g = Stream_exact([1, -1, -1], False) - sage: u = Stream_cauchy_mul(h, g) + sage: h = Stream_exact([1]) + sage: g = Stream_exact([1, -1, -1]) + sage: u = Stream_cauchy_mul(h, g, False) sage: [u[i] for i in range(10)] [1, -1, -1, 0, 0, 0, 0, 0, 0, 0] sage: u._cache @@ -331,10 +331,10 @@ def __setstate__(self, d): EXAMPLES:: sage: from sage.data_structures.stream import Stream_exact - sage: h = Stream_exact([-1], True) - sage: g = Stream_exact([1, -1], True) + sage: h = Stream_exact([-1]) + sage: g = Stream_exact([1, -1]) sage: from sage.data_structures.stream import Stream_cauchy_mul - sage: u = Stream_cauchy_mul(h, g) + sage: u = Stream_cauchy_mul(h, g, True) sage: [u[i] for i in range(10)] [-1, 1, 0, 0, 0, 0, 0, 0, 0, 0] sage: loads(dumps(u)) == u @@ -406,7 +406,7 @@ def iterate_coefficients(self): sage: from sage.data_structures.stream import Stream_function, Stream_cauchy_compose sage: f = Stream_function(lambda n: 1, False, 1) sage: g = Stream_function(lambda n: n^3, False, 1) - sage: h = Stream_cauchy_compose(f, g) + sage: h = Stream_cauchy_compose(f, g, True) sage: n = h.iterate_coefficients() sage: [next(n) for i in range(10)] [1, 9, 44, 207, 991, 4752, 22769, 109089, 522676, 2504295] @@ -585,51 +585,51 @@ class Stream_exact(Stream): the input is shifted to have the prescribed order. """ - def __init__(self, initial_coefficients, is_sparse, constant=None, degree=None, order=None): + def __init__(self, initial_coefficients, constant=None, degree=None, order=None): """ Initialize a stream with eventually constant coefficients. TESTS:: sage: from sage.data_structures.stream import Stream_exact - sage: Stream_exact([], False) + sage: Stream_exact([]) Traceback (most recent call last): ... AssertionError: Stream_exact should only be used for non-zero streams - sage: s = Stream_exact([0, 0, 1, 0, 0], False) + sage: s = Stream_exact([0, 0, 1, 0, 0]) sage: s._initial_coefficients, s._approximate_order, s._degree, s._true_order ((1,), 2, 3, True) - sage: s = Stream_exact([0, 0, 1, 0, 0], False, constant=0) + sage: s = Stream_exact([0, 0, 1, 0, 0], constant=0) sage: s._initial_coefficients, s._approximate_order, s._degree, s._true_order ((1,), 2, 3, True) - sage: s = Stream_exact([0, 0, 1, 0, 0], False, constant=0, degree=10) + sage: s = Stream_exact([0, 0, 1, 0, 0], constant=0, degree=10) sage: s._initial_coefficients, s._approximate_order, s._degree, s._true_order ((1,), 2, 3, True) - sage: s = Stream_exact([0, 0, 1, 0, 0], False, constant=1) + sage: s = Stream_exact([0, 0, 1, 0, 0], constant=1) sage: s._initial_coefficients, s._approximate_order, s._degree, s._true_order ((1,), 2, 5, True) - sage: s = Stream_exact([0, 0, 1, 0, 1], False, constant=1, degree=10) + sage: s = Stream_exact([0, 0, 1, 0, 1], constant=1, degree=10) sage: s._initial_coefficients, s._approximate_order, s._degree, s._true_order ((1, 0, 1), 2, 10, True) - sage: s = Stream_exact([0, 0, 1, 0, 1], False, constant=1, degree=5) + sage: s = Stream_exact([0, 0, 1, 0, 1], constant=1, degree=5) sage: s._initial_coefficients, s._approximate_order, s._degree, s._true_order ((1,), 2, 4, True) - sage: s = Stream_exact([0, 0, 1, 2, 0, 1], False, constant=1) + sage: s = Stream_exact([0, 0, 1, 2, 0, 1], constant=1) sage: s._initial_coefficients, s._approximate_order, s._degree, s._true_order ((1, 2), 2, 5, True) - sage: s = Stream_exact([0, 0, 1, 2, 1, 1], False, constant=1) + sage: s = Stream_exact([0, 0, 1, 2, 1, 1], constant=1) sage: s._initial_coefficients, s._approximate_order, s._degree, s._true_order ((1, 2), 2, 4, True) - sage: s = Stream_exact([0, 0, 1, 2, 1, 1], False, constant=1, order=-2) + sage: s = Stream_exact([0, 0, 1, 2, 1, 1], constant=1, order=-2) sage: s._initial_coefficients, s._approximate_order, s._degree, s._true_order ((1, 2), 0, 2, True) """ @@ -680,7 +680,7 @@ def __init__(self, initial_coefficients, is_sparse, constant=None, degree=None, assert self._initial_coefficients or self._constant, "Stream_exact should only be used for non-zero streams" - super().__init__(is_sparse, True) + super().__init__(None, True) self._approximate_order = order def __getitem__(self, n): @@ -694,35 +694,35 @@ def __getitem__(self, n): EXAMPLES:: sage: from sage.data_structures.stream import Stream_exact - sage: s = Stream_exact([1], False) + sage: s = Stream_exact([1]) sage: [s[i] for i in range(-2, 5)] [0, 0, 1, 0, 0, 0, 0] - sage: s = Stream_exact([], False, constant=1) + sage: s = Stream_exact([], constant=1) sage: [s[i] for i in range(-2, 5)] [0, 0, 1, 1, 1, 1, 1] - sage: s = Stream_exact([2], False, constant=1) + sage: s = Stream_exact([2], constant=1) sage: [s[i] for i in range(-2, 5)] [0, 0, 2, 1, 1, 1, 1] - sage: s = Stream_exact([2], False, order=-1, constant=1) + sage: s = Stream_exact([2], order=-1, constant=1) sage: [s[i] for i in range(-2, 5)] [0, 2, 1, 1, 1, 1, 1] - sage: s = Stream_exact([2], False, order=-1, degree=2, constant=1) + sage: s = Stream_exact([2], order=-1, degree=2, constant=1) sage: [s[i] for i in range(-2, 5)] [0, 2, 0, 0, 1, 1, 1] - sage: t = Stream_exact([0, 2, 0], False, order=-2, degree=2, constant=1) + sage: t = Stream_exact([0, 2, 0], order=-2, degree=2, constant=1) sage: t == s True - sage: s = Stream_exact([0,1,2,1,0,0,1,1], False, constant=1) + sage: s = Stream_exact([0,1,2,1,0,0,1,1], constant=1) sage: [s[i] for i in range(10)] [0, 1, 2, 1, 0, 0, 1, 1, 1, 1] - sage: t = Stream_exact([0,1,2,1,0,0], False, constant=1) + sage: t = Stream_exact([0,1,2,1,0,0], constant=1) sage: s == t True """ @@ -741,7 +741,7 @@ def order(self): EXAMPLES:: sage: from sage.data_structures.stream import Stream_exact - sage: s = Stream_exact([1], False) + sage: s = Stream_exact([1]) sage: s.order() 0 @@ -755,7 +755,7 @@ def __hash__(self): EXAMPLES:: sage: from sage.data_structures.stream import Stream_exact - sage: s = Stream_exact([1], False) + sage: s = Stream_exact([1]) sage: hash(s) == hash(s) True """ @@ -772,16 +772,16 @@ def __eq__(self, other): EXAMPLES:: sage: from sage.data_structures.stream import Stream_exact - sage: s = Stream_exact([2], False, order=-1, degree=2, constant=1) - sage: t = Stream_exact([0, 2, 0], False, 1, 2, -2) + sage: s = Stream_exact([2], order=-1, degree=2, constant=1) + sage: t = Stream_exact([0, 2, 0], 1, 2, -2) sage: [s[i] for i in range(10)] [0, 0, 1, 1, 1, 1, 1, 1, 1, 1] sage: [t[i] for i in range(10)] [0, 0, 1, 1, 1, 1, 1, 1, 1, 1] sage: s == t True - sage: s = Stream_exact([2], False, constant=1) - sage: t = Stream_exact([2], False, order=-1, constant=1) + sage: s = Stream_exact([2], constant=1) + sage: t = Stream_exact([2], order=-1, constant=1) sage: [s[i] for i in range(10)] [2, 1, 1, 1, 1, 1, 1, 1, 1, 1] sage: [t[i] for i in range(10)] @@ -791,8 +791,8 @@ def __eq__(self, other): sage: t == t True - sage: s = Stream_exact([2], False, order=0, degree=5, constant=1) - sage: t = Stream_exact([2], False, order=-1, degree=5, constant=1) + sage: s = Stream_exact([2], order=0, degree=5, constant=1) + sage: t = Stream_exact([2], order=-1, degree=5, constant=1) sage: s == t False """ @@ -814,12 +814,12 @@ def __ne__(self, other): EXAMPLES:: sage: from sage.data_structures.stream import Stream_exact - sage: s = Stream_exact([2], False, order=-1, degree=2, constant=1) - sage: t = Stream_exact([0, 2, 0], False, 1, 2, -2) + sage: s = Stream_exact([2], order=-1, degree=2, constant=1) + sage: t = Stream_exact([0, 2, 0], 1, 2, -2) sage: s != t False - sage: s = Stream_exact([2], False, constant=1) - sage: t = Stream_exact([2], False, order=-1, constant=1) + sage: s = Stream_exact([2], constant=1) + sage: t = Stream_exact([2], order=-1, constant=1) sage: s != t True @@ -866,7 +866,7 @@ def is_nonzero(self): EXAMPLES:: sage: from sage.data_structures.stream import Stream_exact - sage: s = Stream_exact([2], False, order=-1, degree=2, constant=1) + sage: s = Stream_exact([2], order=-1, degree=2, constant=1) sage: s.is_nonzero() True """ @@ -879,7 +879,7 @@ def _polynomial_part(self, R): EXAMPLES:: sage: from sage.data_structures.stream import Stream_exact - sage: s = Stream_exact([2], False, order=-1, degree=2, constant=1) + sage: s = Stream_exact([2], order=-1, degree=2, constant=1) sage: L. = LazyLaurentSeriesRing(ZZ) sage: s._polynomial_part(L._laurent_poly_ring) 2*z^-1 @@ -987,7 +987,7 @@ class Stream_uninitialized(Stream_inexact): sage: from sage.data_structures.stream import Stream_uninitialized sage: from sage.data_structures.stream import Stream_exact - sage: one = Stream_exact([1], True) + sage: one = Stream_exact([1]) sage: C = Stream_uninitialized(True, 0) sage: C._target sage: C._target = one @@ -1022,7 +1022,7 @@ def get_coefficient(self, n): sage: from sage.data_structures.stream import Stream_uninitialized sage: from sage.data_structures.stream import Stream_exact - sage: one = Stream_exact([1], True) + sage: one = Stream_exact([1]) sage: C = Stream_uninitialized(True, 0) sage: C._target sage: C._target = one @@ -1039,7 +1039,7 @@ def iterate_coefficients(self): sage: from sage.data_structures.stream import Stream_uninitialized sage: from sage.data_structures.stream import Stream_exact - sage: z = Stream_exact([1], True, order=1) + sage: z = Stream_exact([1], order=1) sage: C = Stream_uninitialized(True, 0) sage: C._target sage: C._target = z @@ -1081,7 +1081,7 @@ def __init__(self, series, is_sparse): sage: from sage.data_structures.stream import Stream_unary sage: from sage.data_structures.stream import (Stream_cauchy_invert, Stream_exact) - sage: f = Stream_exact([1, -1], False) + sage: f = Stream_exact([1, -1]) sage: g = Stream_cauchy_invert(f) sage: isinstance(g, Stream_unary) True @@ -1143,10 +1143,10 @@ class Stream_binary(Stream_inexact): sage: from sage.data_structures.stream import (Stream_function, Stream_add, Stream_sub) sage: f = Stream_function(lambda n: 2*n, True, 0) sage: g = Stream_function(lambda n: n, True, 1) - sage: h = Stream_add(f, g) + sage: h = Stream_add(f, g, True) sage: [h[i] for i in range(10)] [0, 3, 6, 9, 12, 15, 18, 21, 24, 27] - sage: h = Stream_sub(f, g) + sage: h = Stream_sub(f, g, True) sage: [h[i] for i in range(10)] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] """ @@ -1159,11 +1159,11 @@ def __init__(self, left, right, is_sparse): sage: from sage.data_structures.stream import Stream_binary sage: from sage.data_structures.stream import (Stream_add, Stream_cauchy_invert, Stream_exact) - sage: f1 = Stream_exact([1, -1], False) + sage: f1 = Stream_exact([1, -1]) sage: g1 = Stream_cauchy_invert(f1) - sage: f2 = Stream_exact([1, 1], False) + sage: f2 = Stream_exact([1, 1]) sage: g2 = Stream_cauchy_invert(f2) - sage: O = Stream_add(g1, g2) + sage: O = Stream_add(g1, g2, True) sage: isinstance(O, Stream_binary) True sage: TestSuite(O).run() @@ -1202,9 +1202,9 @@ def __eq__(self, other): sage: f = Stream_function(lambda n: 2*n, False, 1) sage: g = Stream_function(lambda n: n, False, 1) sage: h = Stream_function(lambda n: 1, False, 1) - sage: t = Stream_cauchy_mul(f, g) - sage: u = Stream_cauchy_mul(g, h) - sage: v = Stream_cauchy_mul(h, f) + sage: t = Stream_cauchy_mul(f, g, True) + sage: u = Stream_cauchy_mul(g, h, True) + sage: v = Stream_cauchy_mul(h, f, True) sage: t == u False sage: t == t @@ -1226,10 +1226,10 @@ class Stream_binaryCommutative(Stream_binary): sage: from sage.data_structures.stream import (Stream_function, Stream_add) sage: f = Stream_function(lambda n: 2*n, True, 0) sage: g = Stream_function(lambda n: n, True, 1) - sage: h = Stream_add(f, g) + sage: h = Stream_add(f, g, True) sage: [h[i] for i in range(10)] [0, 3, 6, 9, 12, 15, 18, 21, 24, 27] - sage: u = Stream_add(g, f) + sage: u = Stream_add(g, f, True) sage: [u[i] for i in range(10)] [0, 3, 6, 9, 12, 15, 18, 21, 24, 27] sage: h == u @@ -1244,8 +1244,8 @@ def __hash__(self): sage: from sage.data_structures.stream import (Stream_function, Stream_add) sage: f = Stream_function(lambda n: 2*n, True, 0) sage: g = Stream_function(lambda n: n, True, 1) - sage: h = Stream_add(f, g) - sage: u = Stream_add(g, f) + sage: h = Stream_add(f, g, True) + sage: u = Stream_add(g, f, True) sage: hash(h) == hash(u) True """ @@ -1264,10 +1264,10 @@ def __eq__(self, other): sage: from sage.data_structures.stream import (Stream_function, Stream_add) sage: f = Stream_function(lambda n: 2*n, True, 0) sage: g = Stream_function(lambda n: n, True, 1) - sage: h = Stream_add(f, g) + sage: h = Stream_add(f, g, True) sage: [h[i] for i in range(10)] [0, 3, 6, 9, 12, 15, 18, 21, 24, 27] - sage: u = Stream_add(g, f) + sage: u = Stream_add(g, f, True) sage: [u[i] for i in range(10)] [0, 3, 6, 9, 12, 15, 18, 21, 24, 27] sage: h == u @@ -1396,14 +1396,14 @@ class Stream_add(Stream_binaryCommutative): sage: from sage.data_structures.stream import (Stream_add, Stream_function) sage: f = Stream_function(lambda n: n, True, 0) sage: g = Stream_function(lambda n: 1, True, 0) - sage: h = Stream_add(f, g) + sage: h = Stream_add(f, g, True) sage: [h[i] for i in range(10)] [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - sage: u = Stream_add(g, f) + sage: u = Stream_add(g, f, True) sage: [u[i] for i in range(10)] [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] """ - def __init__(self, left, right): + def __init__(self, left, right, is_sparse): """ Initialize ``self``. @@ -1412,11 +1412,9 @@ def __init__(self, left, right): sage: from sage.data_structures.stream import (Stream_function, Stream_add) sage: f = Stream_function(lambda n: 1, True, 0) sage: g = Stream_function(lambda n: n^2, True, 0) - sage: h = Stream_add(f, g) + sage: h = Stream_add(f, g, True) """ - if left._is_sparse != right._is_sparse: - raise NotImplementedError - super().__init__(left, right, left._is_sparse) + super().__init__(left, right, is_sparse) @lazy_attribute def _approximate_order(self): @@ -1426,7 +1424,7 @@ def _approximate_order(self): EXAMPLES:: sage: from sage.data_structures.stream import Stream_exact - sage: h = Stream_exact([0,3], True) + sage: h = Stream_exact([0,3]) sage: h._approximate_order 1 """ @@ -1446,7 +1444,7 @@ def get_coefficient(self, n): sage: from sage.data_structures.stream import (Stream_function, Stream_add) sage: f = Stream_function(lambda n: n, True, 0) sage: g = Stream_function(lambda n: n^2, True, 0) - sage: h = Stream_add(f, g) + sage: h = Stream_add(f, g, True) sage: h.get_coefficient(5) 30 sage: [h.get_coefficient(i) for i in range(10)] @@ -1469,14 +1467,14 @@ class Stream_sub(Stream_binary): sage: from sage.data_structures.stream import (Stream_sub, Stream_function) sage: f = Stream_function(lambda n: n, True, 0) sage: g = Stream_function(lambda n: 1, True, 0) - sage: h = Stream_sub(f, g) + sage: h = Stream_sub(f, g, True) sage: [h[i] for i in range(10)] [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8] - sage: u = Stream_sub(g, f) + sage: u = Stream_sub(g, f, True) sage: [u[i] for i in range(10)] [1, 0, -1, -2, -3, -4, -5, -6, -7, -8] """ - def __init__(self, left, right): + def __init__(self, left, right, is_sparse): """ initialize ``self``. @@ -1485,11 +1483,9 @@ def __init__(self, left, right): sage: from sage.data_structures.stream import (Stream_function, Stream_sub) sage: f = Stream_function(lambda n: 1, True, 0) sage: g = Stream_function(lambda n: n^2, True, 0) - sage: h = Stream_sub(f, g) + sage: h = Stream_sub(f, g, True) """ - if left._is_sparse != right._is_sparse: - raise NotImplementedError - super().__init__(left, right, left._is_sparse) + super().__init__(left, right, is_sparse) @lazy_attribute def _approximate_order(self): @@ -1499,9 +1495,9 @@ def _approximate_order(self): EXAMPLES:: sage: from sage.data_structures.stream import Stream_exact, Stream_function, Stream_add - sage: f = Stream_exact([0,3], True) + sage: f = Stream_exact([0,3]) sage: g = Stream_function(lambda n: -3*n, True, 1) - sage: h = Stream_add(f, g) + sage: h = Stream_add(f, g, True) sage: h._approximate_order 1 sage: [h[i] for i in range(5)] @@ -1523,7 +1519,7 @@ def get_coefficient(self, n): sage: from sage.data_structures.stream import (Stream_function, Stream_sub) sage: f = Stream_function(lambda n: n, True, 0) sage: g = Stream_function(lambda n: n^2, True, 0) - sage: h = Stream_sub(f, g) + sage: h = Stream_sub(f, g, True) sage: h.get_coefficient(5) -20 sage: [h.get_coefficient(i) for i in range(10)] @@ -1550,14 +1546,14 @@ class Stream_cauchy_mul(Stream_binary): sage: from sage.data_structures.stream import (Stream_cauchy_mul, Stream_function) sage: f = Stream_function(lambda n: n, True, 0) sage: g = Stream_function(lambda n: 1, True, 0) - sage: h = Stream_cauchy_mul(f, g) + sage: h = Stream_cauchy_mul(f, g, True) sage: [h[i] for i in range(10)] [0, 1, 3, 6, 10, 15, 21, 28, 36, 45] - sage: u = Stream_cauchy_mul(g, f) + sage: u = Stream_cauchy_mul(g, f, True) sage: [u[i] for i in range(10)] [0, 1, 3, 6, 10, 15, 21, 28, 36, 45] """ - def __init__(self, left, right): + def __init__(self, left, right, is_sparse): """ initialize ``self``. @@ -1566,11 +1562,9 @@ def __init__(self, left, right): sage: from sage.data_structures.stream import (Stream_function, Stream_cauchy_mul) sage: f = Stream_function(lambda n: 1, True, 0) sage: g = Stream_function(lambda n: n^2, True, 0) - sage: h = Stream_cauchy_mul(f, g) + sage: h = Stream_cauchy_mul(f, g, True) """ - if left._is_sparse != right._is_sparse: - raise NotImplementedError - super().__init__(left, right, left._is_sparse) + super().__init__(left, right, is_sparse) @lazy_attribute def _approximate_order(self): @@ -1580,9 +1574,9 @@ def _approximate_order(self): EXAMPLES:: sage: from sage.data_structures.stream import Stream_exact, Stream_function, Stream_cauchy_mul - sage: f = Stream_exact([0, Zmod(6)(2)], True) + sage: f = Stream_exact([0, Zmod(6)(2)]) sage: g = Stream_function(lambda n: Zmod(6)(3*n), True, 1) - sage: h = Stream_cauchy_mul(f, g) + sage: h = Stream_cauchy_mul(f, g, True) sage: h._approximate_order 2 sage: [h[i] for i in range(5)] @@ -1604,7 +1598,7 @@ def get_coefficient(self, n): sage: from sage.data_structures.stream import (Stream_function, Stream_cauchy_mul) sage: f = Stream_function(lambda n: n, True, 0) sage: g = Stream_function(lambda n: n^2, True, 0) - sage: h = Stream_cauchy_mul(f, g) + sage: h = Stream_cauchy_mul(f, g, True) sage: h.get_coefficient(5) 50 sage: [h.get_coefficient(i) for i in range(10)] @@ -1628,11 +1622,11 @@ def is_nonzero(self): sage: from sage.data_structures.stream import (Stream_function, ....: Stream_cauchy_mul, Stream_cauchy_invert) sage: f = Stream_function(lambda n: n, True, 1) - sage: g = Stream_cauchy_mul(f, f) + sage: g = Stream_cauchy_mul(f, f, True) sage: g.is_nonzero() False sage: fi = Stream_cauchy_invert(f) - sage: h = Stream_cauchy_mul(fi, fi) + sage: h = Stream_cauchy_mul(fi, fi, True) sage: h.is_nonzero() True """ @@ -1655,38 +1649,36 @@ class Stream_dirichlet_convolve(Stream_binary): sage: from sage.data_structures.stream import (Stream_dirichlet_convolve, Stream_function, Stream_exact) sage: f = Stream_function(lambda n: n, True, 1) - sage: g = Stream_exact([0], True, constant=1) - sage: h = Stream_dirichlet_convolve(f, g) + sage: g = Stream_exact([0], constant=1) + sage: h = Stream_dirichlet_convolve(f, g, True) sage: [h[i] for i in range(1, 10)] [1, 3, 4, 7, 6, 12, 8, 15, 13] sage: [sigma(n) for n in range(1, 10)] [1, 3, 4, 7, 6, 12, 8, 15, 13] - sage: u = Stream_dirichlet_convolve(g, f) + sage: u = Stream_dirichlet_convolve(g, f, True) sage: [u[i] for i in range(1, 10)] [1, 3, 4, 7, 6, 12, 8, 15, 13] """ - def __init__(self, left, right): + def __init__(self, left, right, is_sparse): """ Initialize ``self``. sage: from sage.data_structures.stream import (Stream_dirichlet_convolve, Stream_function, Stream_exact) sage: f = Stream_function(lambda n: n, True, 1) - sage: g = Stream_exact([1], True, constant=0) - sage: h = Stream_dirichlet_convolve(f, g) + sage: g = Stream_exact([1], constant=0) + sage: h = Stream_dirichlet_convolve(f, g, True) sage: h[1] Traceback (most recent call last): ... ValueError: Dirichlet convolution is only defined for coefficient streams with minimal index of nonzero coefficient at least 1 - sage: h = Stream_dirichlet_convolve(g, f) + sage: h = Stream_dirichlet_convolve(g, f, True) sage: h[1] Traceback (most recent call last): ... ValueError: Dirichlet convolution is only defined for coefficient streams with minimal index of nonzero coefficient at least 1 """ - if left._is_sparse != right._is_sparse: - raise NotImplementedError - super().__init__(left, right, left._is_sparse) + super().__init__(left, right, is_sparse) @lazy_attribute def _approximate_order(self): @@ -1696,9 +1688,9 @@ def _approximate_order(self): EXAMPLES:: sage: from sage.data_structures.stream import Stream_exact, Stream_function, Stream_dirichlet_convolve - sage: f = Stream_exact([0, 2], True) + sage: f = Stream_exact([0, 2]) sage: g = Stream_function(lambda n: 3*n, True, 1) - sage: h = Stream_dirichlet_convolve(f, g) + sage: h = Stream_dirichlet_convolve(f, g, True) sage: h._approximate_order 1 sage: [h[i] for i in range(5)] @@ -1724,8 +1716,8 @@ def get_coefficient(self, n): sage: from sage.data_structures.stream import (Stream_dirichlet_convolve, Stream_function, Stream_exact) sage: f = Stream_function(lambda n: n, True, 1) - sage: g = Stream_exact([0], True, constant=1) - sage: h = Stream_dirichlet_convolve(f, g) + sage: g = Stream_exact([0], constant=1) + sage: h = Stream_dirichlet_convolve(f, g, True) sage: h.get_coefficient(7) 8 sage: [h[i] for i in range(1, 10)] @@ -1766,7 +1758,7 @@ def __init__(self, series): TESTS:: sage: from sage.data_structures.stream import (Stream_exact, Stream_dirichlet_invert) - sage: f = Stream_exact([0, 0], True, constant=1) + sage: f = Stream_exact([0, 0], constant=1) sage: g = Stream_dirichlet_invert(f) sage: g[1] Traceback (most recent call last): @@ -1806,12 +1798,12 @@ def _ainv(self): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_exact, Stream_dirichlet_invert) - sage: f = Stream_exact([0, 3], True, constant=2) + sage: f = Stream_exact([0, 3], constant=2) sage: g = Stream_dirichlet_invert(f) sage: g._ainv 1/3 - sage: f = Stream_exact([Zmod(6)(5)], False, constant=2, order=1) + sage: f = Stream_exact([Zmod(6)(5)], constant=2, order=1) sage: g = Stream_dirichlet_invert(f) sage: g._ainv 5 @@ -1832,7 +1824,7 @@ def get_coefficient(self, n): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_exact, Stream_dirichlet_invert) - sage: f = Stream_exact([0, 3], True, constant=2) + sage: f = Stream_exact([0, 3], constant=2) sage: g = Stream_dirichlet_invert(f) sage: g.get_coefficient(6) 2/27 @@ -1866,14 +1858,14 @@ class Stream_cauchy_compose(Stream_binary): sage: from sage.data_structures.stream import Stream_cauchy_compose, Stream_function sage: f = Stream_function(lambda n: n, True, 1) sage: g = Stream_function(lambda n: 1, True, 1) - sage: h = Stream_cauchy_compose(f, g) + sage: h = Stream_cauchy_compose(f, g, True) sage: [h[i] for i in range(10)] [0, 1, 3, 8, 20, 48, 112, 256, 576, 1280] - sage: u = Stream_cauchy_compose(g, f) + sage: u = Stream_cauchy_compose(g, f, True) sage: [u[i] for i in range(10)] [0, 1, 3, 8, 21, 55, 144, 377, 987, 2584] """ - def __init__(self, f, g): + def __init__(self, f, g, is_sparse): """ Initialize ``self``. @@ -1882,11 +1874,11 @@ def __init__(self, f, g): sage: from sage.data_structures.stream import Stream_function, Stream_cauchy_compose sage: f = Stream_function(lambda n: 1, True, 1) sage: g = Stream_function(lambda n: n^2, True, 1) - sage: h = Stream_cauchy_compose(f, g) + sage: h = Stream_cauchy_compose(f, g, True) """ if g._true_order and g._approximate_order <= 0: raise ValueError("can only compose with a series of positive valuation") - super().__init__(f, g, f._is_sparse) + super().__init__(f, g, is_sparse) @lazy_attribute def _approximate_order(self): @@ -1898,7 +1890,7 @@ def _approximate_order(self): sage: from sage.data_structures.stream import Stream_function, Stream_cauchy_compose sage: f = Stream_function(lambda n: n, True, 1) sage: g = Stream_function(lambda n: n^2, True, 1) - sage: h = Stream_cauchy_compose(f, g) + sage: h = Stream_cauchy_compose(f, g, True) sage: h._approximate_order 1 sage: [h[i] for i in range(5)] @@ -1918,7 +1910,8 @@ def _approximate_order(self): # We need this for the case so self._neg_powers[0][n] => 0. self._neg_powers = [Stream_zero(self._left._is_sparse), ginv] for i in range(1, -self._left._approximate_order): - self._neg_powers.append(Stream_cauchy_mul(self._neg_powers[-1], ginv)) + # TODO: possibly we always want a dense cache here? + self._neg_powers.append(Stream_cauchy_mul(self._neg_powers[-1], ginv, self._is_sparse)) # placeholder None to make this 1-based. self._pos_powers = [None, self._right] @@ -1937,7 +1930,7 @@ def get_coefficient(self, n): sage: from sage.data_structures.stream import Stream_function, Stream_cauchy_compose sage: f = Stream_function(lambda n: n, True, 1) sage: g = Stream_function(lambda n: n^2, True, 1) - sage: h = Stream_cauchy_compose(f, g) + sage: h = Stream_cauchy_compose(f, g, True) sage: h[5] # indirect doctest 527 sage: [h[i] for i in range(10)] # indirect doctest @@ -1950,7 +1943,8 @@ def get_coefficient(self, n): for i in range(fv, n // gv + 1)) # n > 0 while len(self._pos_powers) <= n // gv: - self._pos_powers.append(Stream_cauchy_mul(self._pos_powers[-1], self._right)) + # TODO: possibly we always want a dense cache here? + self._pos_powers.append(Stream_cauchy_mul(self._pos_powers[-1], self._right, self._is_sparse)) ret = sum(self._left[i] * self._neg_powers[-i][n] for i in range(fv, 0)) if n == 0: ret += self._left[0] @@ -1984,14 +1978,14 @@ class Stream_plethysm(Stream_binary): sage: p = SymmetricFunctions(QQ).p() sage: f = Stream_function(lambda n: s[n], True, 1) sage: g = Stream_function(lambda n: s[[1]*n], True, 1) - sage: h = Stream_plethysm(f, g, p, s) + sage: h = Stream_plethysm(f, g, True, p, s) sage: [h[i] for i in range(5)] [0, s[1], s[1, 1] + s[2], 2*s[1, 1, 1] + s[2, 1] + s[3], 3*s[1, 1, 1, 1] + 2*s[2, 1, 1] + s[2, 2] + s[3, 1] + s[4]] - sage: u = Stream_plethysm(g, f, p, s) + sage: u = Stream_plethysm(g, f, True, p, s) sage: [u[i] for i in range(5)] [0, s[1], @@ -2003,9 +1997,9 @@ class Stream_plethysm(Stream_binary): stream of order `0`:: sage: from sage.data_structures.stream import Stream_exact - sage: f = Stream_exact([s[1]], True, order=1) + sage: f = Stream_exact([s[1]], order=1) sage: g = Stream_function(lambda n: s[n], True, 0) - sage: r = Stream_plethysm(f, g, p, s) + sage: r = Stream_plethysm(f, g, True, p, s) sage: [r[n] for n in range(3)] [s[], s[1], s[2]] @@ -2013,15 +2007,15 @@ class Stream_plethysm(Stream_binary): Check corner cases:: - sage: f0 = Stream_exact([p([])], True) - sage: f1 = Stream_exact([p[1]], True, order=1) - sage: f2 = Stream_exact([p[2]], True, order=2 ) - sage: f11 = Stream_exact([p[1,1]], True, order=2 ) - sage: r = Stream_plethysm(f0, f1, p); [r[n] for n in range(3)] + sage: f0 = Stream_exact([p([])]) + sage: f1 = Stream_exact([p[1]], order=1) + sage: f2 = Stream_exact([p[2]], order=2 ) + sage: f11 = Stream_exact([p[1,1]], order=2 ) + sage: r = Stream_plethysm(f0, f1, True, p); [r[n] for n in range(3)] [p[], 0, 0] - sage: r = Stream_plethysm(f0, f2, p); [r[n] for n in range(3)] + sage: r = Stream_plethysm(f0, f2, True, p); [r[n] for n in range(3)] [p[], 0, 0] - sage: r = Stream_plethysm(f0, f11, p); [r[n] for n in range(3)] + sage: r = Stream_plethysm(f0, f11, True, p); [r[n] for n in range(3)] [p[], 0, 0] Check that degree one elements are treated in the correct way:: @@ -2030,21 +2024,21 @@ class Stream_plethysm(Stream_binary): sage: f_s = a1*p[1] + a2*p[2] + a11*p[1,1] sage: g_s = b1*p[1] + b21*p[2,1] + b111*p[1,1,1] sage: r_s = f_s(g_s) - sage: f = Stream_exact([f_s.restrict_degree(k) for k in range(f_s.degree()+1)], True) - sage: g = Stream_exact([g_s.restrict_degree(k) for k in range(g_s.degree()+1)], True) - sage: r = Stream_plethysm(f, g, p) + sage: f = Stream_exact([f_s.restrict_degree(k) for k in range(f_s.degree()+1)]) + sage: g = Stream_exact([g_s.restrict_degree(k) for k in range(g_s.degree()+1)]) + sage: r = Stream_plethysm(f, g, True, p) sage: r_s == sum(r[n] for n in range(2*(r_s.degree()+1))) True sage: r_s - f_s(g_s, include=[]) (a2*b1^2-a2*b1)*p[2] + (a2*b111^2-a2*b111)*p[2, 2, 2] + (a2*b21^2-a2*b21)*p[4, 2] - sage: r2 = Stream_plethysm(f, g, p, include=[]) + sage: r2 = Stream_plethysm(f, g, True, p, include=[]) sage: r_s - sum(r2[n] for n in range(2*(r_s.degree()+1))) (a2*b1^2-a2*b1)*p[2] + (a2*b111^2-a2*b111)*p[2, 2, 2] + (a2*b21^2-a2*b21)*p[4, 2] """ - def __init__(self, f, g, p, ring=None, include=None, exclude=None): + def __init__(self, f, g, is_sparse, p, ring=None, include=None, exclude=None): r""" Initialize ``self``. @@ -2055,7 +2049,7 @@ def __init__(self, f, g, p, ring=None, include=None, exclude=None): sage: p = SymmetricFunctions(QQ).p() sage: f = Stream_function(lambda n: s[n], True, 1) sage: g = Stream_function(lambda n: s[n-1,1], True, 2) - sage: h = Stream_plethysm(f, g, p) + sage: h = Stream_plethysm(f, g, True, p) """ if isinstance(f, Stream_exact): self._degree_f = f._degree @@ -2094,7 +2088,7 @@ def _approximate_order(self): sage: from sage.data_structures.stream import Stream_function, Stream_plethysm sage: p = SymmetricFunctions(QQ).p() sage: f = Stream_function(lambda n: p[n], True, 1) - sage: h = Stream_plethysm(f, f, p) + sage: h = Stream_plethysm(f, f, True, p) sage: h._approximate_order 1 sage: [h[i] for i in range(5)] @@ -2123,7 +2117,7 @@ def get_coefficient(self, n): sage: p = SymmetricFunctions(QQ).p() sage: f = Stream_function(lambda n: s[n], True, 1) sage: g = Stream_function(lambda n: s[[1]*n], True, 1) - sage: h = Stream_plethysm(f, g, p) + sage: h = Stream_plethysm(f, g, True, p) sage: s(h.get_coefficient(5)) 4*s[1, 1, 1, 1, 1] + 4*s[2, 1, 1, 1] + 2*s[2, 2, 1] + 2*s[3, 1, 1] + s[3, 2] + s[4, 1] + s[5] sage: [s(h.get_coefficient(i)) for i in range(6)] @@ -2160,9 +2154,9 @@ def compute_product(self, n, la): sage: from sage.data_structures.stream import Stream_plethysm, Stream_exact, Stream_function, Stream_zero sage: s = SymmetricFunctions(QQ).s() sage: p = SymmetricFunctions(QQ).p() - sage: f = Stream_exact([1], False) # irrelevant for this test - sage: g = Stream_exact([s[2], s[3]], False, 0, 4, 2) - sage: h = Stream_plethysm(f, g, p) + sage: f = Stream_exact([1]) # irrelevant for this test + sage: g = Stream_exact([s[2], s[3]], 0, 4, 2) + sage: h = Stream_plethysm(f, g, True, p) sage: A = h.compute_product(7, Partition([2, 1])); A 1/12*p[2, 2, 1, 1, 1] + 1/4*p[2, 2, 2, 1] + 1/6*p[3, 2, 2] + 1/12*p[4, 1, 1, 1] + 1/4*p[4, 2, 1] + 1/6*p[4, 3] @@ -2170,18 +2164,18 @@ def compute_product(self, n, la): True sage: p2 = tensor([p, p]) - sage: f = Stream_exact([1], True) # irrelevant for this test + sage: f = Stream_exact([1]) # irrelevant for this test sage: g = Stream_function(lambda n: sum(tensor([p[k], p[n-k]]) for k in range(n+1)), True, 1) - sage: h = Stream_plethysm(f, g, p2) + sage: h = Stream_plethysm(f, g, True, p2) sage: A = h.compute_product(7, Partition([2, 1])) sage: B = p[2, 1](sum(g[n] for n in range(7))) sage: B = p2.element_class(p2, {m: c for m, c in B if sum(mu.size() for mu in m) == 7}) sage: A == B True - sage: f = Stream_exact([1], True) # irrelevant for this test + sage: f = Stream_exact([1]) # irrelevant for this test sage: g = Stream_function(lambda n: s[n], True, 0) - sage: h = Stream_plethysm(f, g, p) + sage: h = Stream_plethysm(f, g, True, p) sage: B = p[2, 2, 1](sum(s[i] for i in range(7))) sage: all(h.compute_product(k, Partition([2, 2, 1])) == B.restrict_degree(k) for k in range(7)) True @@ -2220,17 +2214,17 @@ def stretched_power_restrict_degree(self, i, m, d): sage: from sage.data_structures.stream import Stream_plethysm, Stream_exact, Stream_function, Stream_zero sage: s = SymmetricFunctions(QQ).s() sage: p = SymmetricFunctions(QQ).p() - sage: f = Stream_exact([1], False) # irrelevant for this test - sage: g = Stream_exact([s[2], s[3]], False, 0, 4, 2) - sage: h = Stream_plethysm(f, g, p) + sage: f = Stream_exact([1]) # irrelevant for this test + sage: g = Stream_exact([s[2], s[3]], 0, 4, 2) + sage: h = Stream_plethysm(f, g, True, p) sage: A = h.stretched_power_restrict_degree(2, 3, 6) sage: A == p[2,2,2](s[2] + s[3]).homogeneous_component(12) True sage: p2 = tensor([p, p]) - sage: f = Stream_exact([1], True) # irrelevant for this test + sage: f = Stream_exact([1]) # irrelevant for this test sage: g = Stream_function(lambda n: sum(tensor([p[k], p[n-k]]) for k in range(n+1)), True, 1) - sage: h = Stream_plethysm(f, g, p2) + sage: h = Stream_plethysm(f, g, True, p2) sage: A = h.stretched_power_restrict_degree(2, 3, 6) sage: B = p[2,2,2](sum(g[n] for n in range(7))) # long time sage: B = p2.element_class(p2, {m: c for m, c in B if sum(mu.size() for mu in m) == 12}) # long time @@ -2238,7 +2232,8 @@ def stretched_power_restrict_degree(self, i, m, d): True """ while len(self._powers) < m: - self._powers.append(Stream_cauchy_mul(self._powers[-1], self._powers[0])) + # TODO: possibly we always want a dense cache here? + self._powers.append(Stream_cauchy_mul(self._powers[-1], self._powers[0], self._is_sparse)) power_d = self._powers[m-1][d] # we have to check power_d for zero because it might be an # integer and not a symmetric function @@ -2566,7 +2561,7 @@ def __init__(self, series, approximate_order=None): TESTS:: sage: from sage.data_structures.stream import (Stream_cauchy_invert, Stream_exact) - sage: f = Stream_exact([1, -1], False) + sage: f = Stream_exact([1, -1]) sage: g = Stream_cauchy_invert(f) """ super().__init__(series, series._is_sparse) @@ -2604,12 +2599,12 @@ def _ainv(self): EXAMPLES:: sage: from sage.data_structures.stream import (Stream_cauchy_invert, Stream_exact) - sage: f = Stream_exact([2, -3], False) + sage: f = Stream_exact([2, -3]) sage: g = Stream_cauchy_invert(f) sage: g._ainv 1/2 - sage: f = Stream_exact([Zmod(6)(5)], False, constant=2) + sage: f = Stream_exact([Zmod(6)(5)], constant=2) sage: g = Stream_cauchy_invert(f) sage: g._ainv 5 @@ -2844,7 +2839,7 @@ def __init__(self, series, shift): sage: from sage.data_structures.stream import Stream_shift sage: from sage.data_structures.stream import Stream_exact - sage: h = Stream_exact([1], False, constant=3) + sage: h = Stream_exact([1], constant=3) sage: M = Stream_shift(h, 2) sage: TestSuite(M).run(skip="_test_pickling") """ @@ -2959,7 +2954,7 @@ def __init__(self, series, shift): EXAMPLES:: sage: from sage.data_structures.stream import Stream_exact, Stream_derivative - sage: f = Stream_exact([1,2,3], False) + sage: f = Stream_exact([1,2,3]) sage: f2 = Stream_derivative(f, 2) sage: TestSuite(f2).run() """ @@ -3062,7 +3057,7 @@ def is_nonzero(self): EXAMPLES:: sage: from sage.data_structures.stream import Stream_exact, Stream_derivative - sage: f = Stream_exact([1,2], False) + sage: f = Stream_exact([1,2]) sage: Stream_derivative(f, 1).is_nonzero() True sage: Stream_derivative(f, 2).is_nonzero() # it might be nice if this gave False diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 48af601a570..a4d8005167f 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -574,7 +574,6 @@ def map_coefficients(self, f): if not any(initial_coefficients) and not c: return P.zero() coeff_stream = Stream_exact(initial_coefficients, - self._coeff_stream._is_sparse, order=coeff_stream._approximate_order, degree=coeff_stream._degree, constant=P.base_ring()(c)) @@ -626,8 +625,7 @@ def truncate(self, d): coeff_stream = self._coeff_stream v = coeff_stream._approximate_order initial_coefficients = [coeff_stream[i] for i in range(v, d)] - return P.element_class(P, Stream_exact(initial_coefficients, P._sparse, - order=v)) + return P.element_class(P, Stream_exact(initial_coefficients, order=v)) def shift(self, n): r""" @@ -687,7 +685,7 @@ def shift(self, n): init_coeff = self._coeff_stream._initial_coefficients degree = self._coeff_stream._degree + n valuation = self._coeff_stream._approximate_order + n - coeff_stream = Stream_exact(init_coeff, self._coeff_stream._is_sparse, + coeff_stream = Stream_exact(init_coeff, constant=self._coeff_stream._constant, order=valuation, degree=degree) else: @@ -945,7 +943,7 @@ def __bool__(self): return False if isinstance(self._coeff_stream._target, Stream_exact): return True - if self.parent()._sparse: + if self._coeff_stream._is_sparse: cache = self._coeff_stream._cache if any(cache[a] for a in cache): return True @@ -1506,13 +1504,13 @@ def _add_(self, other): if not any(initial_coefficients) and not constant: return P.zero() coeff_stream = Stream_exact(initial_coefficients, - P._sparse, constant=constant, degree=degree, order=approximate_order) return P.element_class(P, coeff_stream) return P.element_class(P, Stream_add(self._coeff_stream, - other._coeff_stream)) + other._coeff_stream, + P.is_sparse())) def _sub_(self, other): """ @@ -1584,14 +1582,15 @@ def _sub_(self, other): if not any(initial_coefficients) and not constant: return P.zero() coeff_stream = Stream_exact(initial_coefficients, - P._sparse, constant=constant, degree=degree, order=approximate_order) return P.element_class(P, coeff_stream) if left == right: return P.zero() - return P.element_class(P, Stream_sub(self._coeff_stream, other._coeff_stream)) + return P.element_class(P, Stream_sub(self._coeff_stream, + other._coeff_stream, + P.is_sparse())) def _acted_upon_(self, scalar, self_on_left): r""" @@ -1729,7 +1728,6 @@ def _acted_upon_(self, scalar, self_on_left): c = scalar * coeff_stream._constant initial_coefficients = [scalar * val for val in init_coeffs] return P.element_class(P, Stream_exact(initial_coefficients, - P._sparse, order=v, constant=c, degree=coeff_stream._degree)) @@ -1791,7 +1789,6 @@ def _neg_(self): initial_coefficients = [-v for v in coeff_stream._initial_coefficients] constant = -coeff_stream._constant coeff_stream = Stream_exact(initial_coefficients, - P._sparse, constant=constant, degree=coeff_stream._degree, order=coeff_stream.order()) @@ -2713,12 +2710,11 @@ def _mul_(self, other): else: c = left._constant # this is zero coeff_stream = Stream_exact(initial_coefficients, - P._sparse, order=lv + rv, constant=c) return P.element_class(P, coeff_stream) - return P.element_class(P, Stream_cauchy_mul(left, right)) + return P.element_class(P, Stream_cauchy_mul(left, right, P.is_sparse())) def __pow__(self, n): r""" @@ -2794,7 +2790,6 @@ def __pow__(self, n): deg = ret.degree() + 1 initial_coefficients = [ret[i] for i in range(val, deg)] return P.element_class(P, Stream_exact(initial_coefficients, - P._sparse, constant=cs._constant, degree=deg, order=val)) @@ -2876,7 +2871,6 @@ def __invert__(self): v = -coeff_stream.order() c = P._internal_poly_ring.base_ring().zero() coeff_stream = Stream_exact((i, -i), - P._sparse, order=v, constant=c) return P.element_class(P, coeff_stream) @@ -2885,7 +2879,6 @@ def __invert__(self): v = -coeff_stream.order() c = P._internal_poly_ring.base_ring().zero() coeff_stream = Stream_exact((i,), - P._sparse, order=v, constant=c) return P.element_class(P, coeff_stream) @@ -2895,7 +2888,6 @@ def __invert__(self): v = -coeff_stream.order() c = ~initial_coefficients[0] coeff_stream = Stream_exact((), - P._sparse, order=v, constant=c) return P.element_class(P, coeff_stream) @@ -3055,7 +3047,6 @@ def _div_(self, other): initial_coefficients = [num[i] / d for i in range(v, num.degree() + 1)] order = v - den.valuation() return P.element_class(P, Stream_exact(initial_coefficients, - P._sparse, order=order, constant=0)) @@ -3081,12 +3072,10 @@ def _div_(self, other): order = 0 initial_coefficients = [quo[i] for i in range(order, quo.degree() + 1)] return P.element_class(P, Stream_exact(initial_coefficients, - P._sparse, order=order, degree=v, constant=constant)) return P.element_class(P, Stream_exact([], - P._sparse, order=v, degree=v, constant=constant)) @@ -3095,7 +3084,7 @@ def _div_(self, other): # P._minimal_valuation is zero, because we allow division by # series of positive valuation right_inverse = Stream_cauchy_invert(right) - return P.element_class(P, Stream_cauchy_mul(left, right_inverse)) + return P.element_class(P, Stream_cauchy_mul(left, right_inverse, P.is_sparse())) def _floordiv_(self, other): @@ -3583,7 +3572,6 @@ def __call__(self, g, *, check=True): deg = ret.degree() + 1 initial_coefficients = [ret[i] for i in range(val, deg)] coeff_stream = Stream_exact(initial_coefficients, - self._coeff_stream._is_sparse, constant=P.base_ring().zero(), degree=deg, order=val) return P.element_class(P, coeff_stream) @@ -3652,7 +3640,9 @@ def coefficient(n): coeff_stream = Stream_function(coefficient, P._sparse, 1) return P.element_class(P, coeff_stream) - coeff_stream = Stream_cauchy_compose(self._coeff_stream, g._coeff_stream) + coeff_stream = Stream_cauchy_compose(self._coeff_stream, + g._coeff_stream, + P.is_sparse()) return P.element_class(P, coeff_stream) compose = __call__ @@ -3807,7 +3797,6 @@ def revert(self): a = coeff_stream[0] b = coeff_stream[1] coeff_stream = Stream_exact((-a/b, 1/b), - coeff_stream._is_sparse, order=0) return P.element_class(P, coeff_stream) @@ -3910,7 +3899,6 @@ def derivative(self, *args): for i, c in enumerate(coeff_stream._initial_coefficients, coeff_stream._approximate_order)] coeff_stream = Stream_exact(coeffs, - self._coeff_stream._is_sparse, order=coeff_stream._approximate_order - order, constant=coeff_stream._constant) return P.element_class(P, coeff_stream) @@ -4462,7 +4450,9 @@ def coefficient(n): coeff_stream = Stream_function(coefficient, P._sparse, 1) return P.element_class(P, coeff_stream) - coeff_stream = Stream_cauchy_compose(self._coeff_stream, g0._coeff_stream) + coeff_stream = Stream_cauchy_compose(self._coeff_stream, + g0._coeff_stream, + P.is_sparse()) return P.element_class(P, coeff_stream) # The arity is at least 2 @@ -4606,7 +4596,6 @@ def revert(self): a = coeff_stream[0] b = coeff_stream[1] coeff_stream = Stream_exact((-a/b, 1/b), - coeff_stream._is_sparse, order=0) return P.element_class(P, coeff_stream) @@ -4707,7 +4696,6 @@ def derivative(self, *args): for i, c in enumerate(coeff_stream._initial_coefficients, coeff_stream._approximate_order)] coeff_stream = Stream_exact(coeffs, - self._coeff_stream._is_sparse, order=coeff_stream._approximate_order - order, constant=coeff_stream._constant) return P.element_class(P, coeff_stream) @@ -5337,7 +5325,7 @@ def __call__(self, *args, check=True): else: ps = tensor([R._sets[0].realization_of().p()]*P._arity) coeff_stream = Stream_plethysm(self._coeff_stream, g._coeff_stream, - ps, R) + P.is_sparse(), ps, R) return P.element_class(P, coeff_stream) else: @@ -5438,7 +5426,6 @@ def revert(self): b = coeff_stream[1][Partition([1])] X = R(Partition([1])) coeff_stream = Stream_exact((-a/b, 1/b * X), - coeff_stream._is_sparse, order=0) return P.element_class(P, coeff_stream) @@ -6132,7 +6119,7 @@ def _mul_(self, other): and right._initial_coefficients == (P._internal_poly_ring.base_ring().one(),) and right.order() == 1): return self # other == 1 - coeff = Stream_dirichlet_convolve(left, right) + coeff = Stream_dirichlet_convolve(left, right, P.is_sparse()) # Performing exact arithmetic is slow because the series grow large # very quickly as we are multiplying the degree #if (isinstance(left, Stream_exact) and not left._constant @@ -6142,7 +6129,7 @@ def _mul_(self, other): # deg = (left._degree - 1) * (right._degree - 1) + 1 # order = left._approximate_order * right._approximate_order # coeff_vals = [coeff[i] for i in range(order, deg)] - # return P.element_class(P, Stream_exact(coeff_vals, coeff._is_sparse, + # return P.element_class(P, Stream_exact(coeff_vals, # constant=left._constant, order=order, degree=deg)) return P.element_class(P, coeff) diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index c0e0d536562..fb7b3177e17 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -473,18 +473,18 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No raise ValueError("you must specify the degree for the polynomial 0") degree = valuation if x == R.zero(): - coeff_stream = Stream_exact([], self._sparse, order=degree, constant=constant) + coeff_stream = Stream_exact([], order=degree, constant=constant) return self.element_class(self, coeff_stream) initial_coefficients = [x[i] for i in range(x.valuation(), x.degree() + 1)] - coeff_stream = Stream_exact(initial_coefficients, self._sparse, + coeff_stream = Stream_exact(initial_coefficients, order=x.valuation(), degree=degree, constant=constant) return self.element_class(self, coeff_stream) # Handle when it is a lazy series if isinstance(x, self.Element): - if x._coeff_stream._is_sparse is not self._sparse: + # if x._coeff_stream._is_sparse is not self._sparse: # TODO: Implement a way to make a self._sparse copy - raise NotImplementedError("cannot convert between sparse and dense") + # raise NotImplementedError("cannot convert between sparse and dense") # If x is known to be 0 if isinstance(x._coeff_stream, Stream_zero): @@ -496,8 +496,7 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No if valuation is None: raise ValueError("you must specify the degree for the polynomial 0") degree = valuation - coeff_stream = Stream_exact([], self._sparse, order=degree, - constant=constant) + coeff_stream = Stream_exact([], order=degree, constant=constant) return self.element_class(self, coeff_stream) # Make the result exact @@ -513,7 +512,7 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No # We learned some stuff about x; pass it along x._coeff_stream._approximate_order += len(initial_coefficients) initial_coefficients = [] - coeff_stream = Stream_exact(initial_coefficients, self._sparse, + coeff_stream = Stream_exact(initial_coefficients, order=valuation, degree=degree, constant=constant) return self.element_class(self, coeff_stream) @@ -577,8 +576,7 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No p = [BR(c) for c, _ in zip(_skip_leading_zeros(x), range(valuation, degree))] if not any(p) and not constant: return self.zero() - coeff_stream = Stream_exact(p, self._sparse, order=valuation, - constant=constant, degree=degree) + coeff_stream = Stream_exact(p, order=valuation, constant=constant, degree=degree) return self.element_class(self, coeff_stream) raise ValueError(f"unable to convert {x} into {self}") @@ -700,7 +698,7 @@ def one(self): """ R = self.base_ring() - coeff_stream = Stream_exact([R.one()], self._sparse, constant=R.zero(), order=0) + coeff_stream = Stream_exact([R.one()], constant=R.zero(), order=0) return self.element_class(self, coeff_stream) @cached_method @@ -1269,8 +1267,7 @@ def gen(self, n=0): if n != 0: raise IndexError("there is only one generator") R = self.base_ring() - coeff_stream = Stream_exact([R.one()], self._sparse, - constant=R.zero(), order=1) + coeff_stream = Stream_exact([R.one()], constant=R.zero(), order=1) return self.element_class(self, coeff_stream) def ngens(self): @@ -1430,7 +1427,7 @@ def series(self, coefficient, valuation, degree=None, constant=None): constant = self.base_ring().zero() if degree is None: degree = valuation + len(coefficient) - coeff_stream = Stream_exact(coefficient, self._sparse, order=valuation, + coeff_stream = Stream_exact(coefficient, order=valuation, constant=constant, degree=degree) return self.element_class(self, coeff_stream) @@ -1678,9 +1675,9 @@ def gen(self, n=0): R = self._laurent_poly_ring BR = self.base_ring() if len(self.variable_names()) == 1: - coeff_stream = Stream_exact([BR.one()], self._sparse, constant=BR.zero(), order=1) + coeff_stream = Stream_exact([BR.one()], constant=BR.zero(), order=1) else: - coeff_stream = Stream_exact([R.gen(n)], self._sparse, constant=BR.zero(), order=1) + coeff_stream = Stream_exact([R.gen(n)], constant=BR.zero(), order=1) return self.element_class(self, coeff_stream) def ngens(self): @@ -1853,7 +1850,7 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No coeff_stream = Stream_zero(self._sparse) else: if not x: - coeff_stream = Stream_exact([], self._sparse, + coeff_stream = Stream_exact([], order=valuation, degree=degree, constant=constant) @@ -1871,14 +1868,14 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No d = max(p_dict.keys()) p_list = [p_dict.get(i, 0) for i in range(v, d + 1)] - coeff_stream = Stream_exact(p_list, self._sparse, + coeff_stream = Stream_exact(p_list, order=v, constant=constant, degree=degree) return self.element_class(self, coeff_stream) if isinstance(x, LazyPowerSeries): - if x._coeff_stream._is_sparse is self._sparse: + # if x._coeff_stream._is_sparse is self._sparse: stream = x._coeff_stream if isinstance(stream, Stream_exact): if self._arity == 1: @@ -1901,7 +1898,7 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No valuation=valuation) return self.element_class(self, stream) # TODO: Implement a way to make a self._sparse copy - raise NotImplementedError("cannot convert between sparse and dense") + # raise NotImplementedError("cannot convert between sparse and dense") if callable(x) or isinstance(x, (GeneratorType, map, filter)): if valuation is None: @@ -1920,7 +1917,7 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No if not all(e.is_homogeneous() and e.degree() == i for i, e in enumerate(p, valuation)): raise ValueError("coefficients must be homogeneous polynomials of the correct degree") - coeff_stream = Stream_exact(p, self._sparse, + coeff_stream = Stream_exact(p, order=valuation, constant=constant, degree=degree) @@ -2336,17 +2333,17 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No d = max(p_dict) p_list = [p_dict.get(i, 0) for i in range(v, d + 1)] - coeff_stream = Stream_exact(p_list, self._sparse, + coeff_stream = Stream_exact(p_list, order=v, constant=0, degree=degree) return self.element_class(self, coeff_stream) if isinstance(x, self.Element): - if x._coeff_stream._is_sparse is self._sparse: + # if x._coeff_stream._is_sparse is self._sparse: return self.element_class(self, x._coeff_stream) # TODO: Implement a way to make a self._sparse copy - raise NotImplementedError("cannot convert between sparse and dense") + # raise NotImplementedError("cannot convert between sparse and dense") if self._arity == 1: def check_homogeneous_of_degree(f, d): @@ -2383,7 +2380,7 @@ def check_homogeneous_of_degree(f, d): p = [R(e) for e in x] for i, e in enumerate(p, valuation): check_homogeneous_of_degree(e, i) - coeff_stream = Stream_exact(p, self._sparse, + coeff_stream = Stream_exact(p, order=valuation, constant=0, degree=degree) @@ -2393,7 +2390,7 @@ def check_homogeneous_of_degree(f, d): p = [R(x(i)) for i in range(valuation, degree)] for i, e in enumerate(p, valuation): check_homogeneous_of_degree(e, i) - coeff_stream = Stream_exact(p, self._sparse, + coeff_stream = Stream_exact(p, order=valuation, constant=0, degree=degree) @@ -2622,7 +2619,7 @@ def one(self): 1 + O(1/(8^z)) """ R = self.base_ring() - coeff_stream = Stream_exact([R.one()], self._sparse, constant=R.zero(), order=1) + coeff_stream = Stream_exact([R.one()], constant=R.zero(), order=1) return self.element_class(self, coeff_stream) def _coerce_map_from_(self, S): @@ -2766,7 +2763,7 @@ def _an_element_(self): 1/(4^z) + 1/(5^z) + 1/(6^z) + O(1/(7^z)) """ c = self.base_ring().an_element() - return self.element_class(self, Stream_exact([], self._sparse, constant=c, order=4)) + return self.element_class(self, Stream_exact([], constant=c, order=4)) def some_elements(self): """ From d823d95aba118930147401ec20b120b2dff8ae4d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 7 Oct 2022 20:30:52 -0700 Subject: [PATCH 187/632] build/pkgs/meson_python: Update to 0.10.0 --- build/pkgs/meson_python/checksums.ini | 6 +++--- build/pkgs/meson_python/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/meson_python/checksums.ini b/build/pkgs/meson_python/checksums.ini index cc19a05eee3..6c2854e30a4 100644 --- a/build/pkgs/meson_python/checksums.ini +++ b/build/pkgs/meson_python/checksums.ini @@ -1,5 +1,5 @@ tarball=meson_python-VERSION.tar.gz -sha1=87b086c28d626182056d020d8e4eeafa8bb8ccee -md5=4774f7348d5fc6769e62716600d3773a -cksum=3965133995 +sha1=6ff574d17feb98165a2678c78a429d3ff32aaa7b +md5=afd824bf61e79cd5eeab2a6dc7863eaf +cksum=3939516041 upstream_url=https://pypi.io/packages/source/m/meson_python/meson_python-VERSION.tar.gz diff --git a/build/pkgs/meson_python/package-version.txt b/build/pkgs/meson_python/package-version.txt index ac39a106c48..78bc1abd14f 100644 --- a/build/pkgs/meson_python/package-version.txt +++ b/build/pkgs/meson_python/package-version.txt @@ -1 +1 @@ -0.9.0 +0.10.0 From 790a684e10ee9b0d632fb8448ac77d44dc5a42c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 9 Oct 2022 13:22:56 +0200 Subject: [PATCH 188/632] refresh the file categories/rings.py --- src/sage/categories/rings.py | 197 ++++++++++++++++------------------- 1 file changed, 88 insertions(+), 109 deletions(-) diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index 88ce6ef5bc0..d43dc15bbb1 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -1,22 +1,22 @@ r""" Rings """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2005 David Kohel # William Stein # 2008 Teresa Gomez-Diaz (CNRS) # 2008-2011 Nicolas M. Thiery # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#****************************************************************************** +# https://www.gnu.org/licenses/ +# ***************************************************************************** +from functools import reduce from sage.misc.cachefunc import cached_method from sage.misc.lazy_import import LazyImport from sage.categories.category_with_axiom import CategoryWithAxiom from sage.categories.rngs import Rngs from sage.structure.element import Element -from functools import reduce class Rings(CategoryWithAxiom): @@ -49,19 +49,18 @@ class Rings(CategoryWithAxiom): .. TODO:: - (see: http://trac.sagemath.org/sage_trac/wiki/CategoriesRoadMap) + (see: https://trac.sagemath.org/sage_trac/wiki/CategoriesRoadMap) - Make Rings() into a subcategory or alias of Algebras(ZZ); - A parent P in the category ``Rings()`` should automatically be in the category ``Algebras(P)``. """ - _base_category_class_and_axiom = (Rngs, "Unital") class MorphismMethods: @cached_method - def is_injective(self): + def is_injective(self) -> bool: """ Return whether or not this morphism is injective. @@ -186,7 +185,7 @@ def is_injective(self): raise NotImplementedError - def _is_nonzero(self): + def _is_nonzero(self) -> bool: r""" Return whether this is not the zero morphism. @@ -255,7 +254,6 @@ def extend_to_fraction_field(self): parent = domain.Hom(codomain) # category = category=self.category_for() ??? return RingHomomorphism_from_fraction_field(parent, self) - class SubcategoryMethods: def NoZeroDivisors(self): @@ -273,11 +271,6 @@ def NoZeroDivisors(self): sage: Rings().NoZeroDivisors() Category of domains - .. NOTE:: - - This could be generalized to - :class:`MagmasAndAdditiveMagmas.Distributive.AdditiveUnital`. - TESTS:: sage: TestSuite(Rings().NoZeroDivisors()).run() @@ -293,11 +286,6 @@ def Division(self): A ring satisfies the *division axiom* if all non-zero elements have multiplicative inverses. - .. NOTE:: - - This could be generalized to - :class:`MagmasAndAdditiveMagmas.Distributive.AdditiveUnital`. - EXAMPLES:: sage: Rings().Division() @@ -313,15 +301,14 @@ def Division(self): """ return self._with_axiom('Division') - NoZeroDivisors = LazyImport('sage.categories.domains', 'Domains', at_startup=True) - Division = LazyImport('sage.categories.division_rings', 'DivisionRings', at_startup=True) - Commutative = LazyImport('sage.categories.commutative_rings', 'CommutativeRings', at_startup=True) + Division = LazyImport('sage.categories.division_rings', 'DivisionRings', at_startup=True) + Commutative = LazyImport('sage.categories.commutative_rings', 'CommutativeRings', at_startup=True) class ParentMethods: - def is_ring(self): + def is_ring(self) -> bool: """ - Return True, since this in an object of the category of rings. + Return ``True``, since this in an object of the category of rings. EXAMPLES:: @@ -330,7 +317,7 @@ def is_ring(self): """ return True - def is_zero(self): + def is_zero(self) -> bool: """ Return ``True`` if this is the zero ring. @@ -355,11 +342,11 @@ def is_zero(self): def bracket(self, x, y): """ - Returns the Lie bracket `[x, y] = x y - y x` of `x` and `y`. + Return the Lie bracket `[x, y] = x y - y x` of `x` and `y`. INPUT: - - ``x``, ``y`` -- elements of ``self`` + - ``x``, ``y`` -- elements of ``self`` EXAMPLES:: @@ -377,11 +364,11 @@ def bracket(self, x, y): sage: F.bracket( F.bracket(a,b), c) + F.bracket(F.bracket(b,c),a) + F.bracket(F.bracket(c,a),b) 0 """ - return x*y - y*x + return x * y - y * x def _Hom_(self, Y, category): r""" - Returns the homset from ``self`` to ``Y`` in the category ``category`` + Return the homset from ``self`` to ``Y`` in the category ``category``. INPUT: @@ -415,9 +402,9 @@ def _Hom_(self, Y, category): sage: TestSuite(Hom(QQ, QQ, category = Rings())).run() # indirect doctest """ if category is not None and not category.is_subcategory(Rings()): - raise TypeError("%s is not a subcategory of Rings()"%category) + raise TypeError(f"{category} is not a subcategory of Rings()") if Y not in Rings(): - raise TypeError("%s is not a ring" % Y) + raise TypeError(f"{Y} is not a ring") from sage.rings.homset import RingHomset return RingHomset(self, Y, category=category) @@ -428,16 +415,15 @@ def _mul_(self, x, switch_sides=False): """ Multiplication of rings with, e.g., lists. - NOTE: + .. NOTE:: - This method is used to create ideals. It is - the same as the multiplication method for - :class:`~sage.rings.ring.Ring`. However, not - all parents that belong to the category of - rings also inherits from the base class of - rings. Therefore, we implemented a ``__mul__`` - method for parents, that calls a ``_mul_`` - method implemented here. See :trac:`7797`. + This method is used to create ideals. It is the same + as the multiplication method for + :class:`~sage.rings.ring.Ring`. However, not all + parents that belong to the category of rings also + inherits from the base class of rings. Therefore, we + implemented a ``__mul__`` method for parents, that + calls a ``_mul_`` method implemented here. See :trac:`7797`. INPUT: @@ -480,12 +466,11 @@ def _mul_(self, x, switch_sides=False): AUTHOR: - Simon King (2011-03-22) - """ try: if self.is_commutative(): return self.ideal(x) - except (AttributeError,NotImplementedError): + except (AttributeError, NotImplementedError): pass try: side = x.side() @@ -495,19 +480,19 @@ def _mul_(self, x, switch_sides=False): try: x = x.gens() except (AttributeError, NotImplementedError): - pass # ... not an ideal + pass # ... not an ideal if switch_sides: - if side in ['right','twosided']: - return self.ideal(x,side=side) - elif side=='left': - return self.ideal(x,side='twosided') + if side in ['right', 'twosided']: + return self.ideal(x, side=side) + elif side == 'left': + return self.ideal(x, side='twosided') else: - if side in ['left','twosided']: - return self.ideal(x,side=side) - elif side=='right': - return self.ideal(x,side='twosided') + if side in ['left', 'twosided']: + return self.ideal(x, side=side) + elif side == 'right': + return self.ideal(x, side='twosided') # duck typing failed - raise TypeError("Don't know how to transform %s into an ideal of %s"%(x,self)) + raise TypeError("do not know how to transform %s into an ideal of %s" % (x, self)) def __pow__(self, n): """ @@ -537,12 +522,12 @@ def ideal_monoid(self): """ The monoid of the ideals of this ring. - NOTE: + .. NOTE:: - The code is copied from the base class of rings. - This is since there are rings that do not inherit - from that class, such as matrix algebras. See - :trac:`7797`. + The code is copied from the base class of rings. + This is since there are rings that do not inherit + from that class, such as matrix algebras. See + :trac:`7797`. EXAMPLES:: @@ -559,7 +544,6 @@ def ideal_monoid(self): sage: MS.ideal_monoid() is MS.ideal_monoid() True - """ try: from sage.rings.ideal_monoid import IdealMonoid @@ -602,7 +586,8 @@ def _test_characteristic(self, **options): try: characteristic = self.characteristic() except AttributeError: - return # raised when self.one() does not have a additive_order() + # raised when self.one() does not have a additive_order() + return except NotImplementedError: return @@ -614,13 +599,13 @@ def ideal(self, *args, **kwds): """ Create an ideal of this ring. - NOTE: + .. NOTE:: - The code is copied from the base class - :class:`~sage.rings.ring.Ring`. This is - because there are rings that do not inherit - from that class, such as matrix algebras. - See :trac:`7797`. + The code is copied from the base class + :class:`~sage.rings.ring.Ring`. This is + because there are rings that do not inherit + from that class, such as matrix algebras. + See :trac:`7797`. INPUT: @@ -656,7 +641,6 @@ def ideal(self, *args, **kwds): [0 0] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field - """ if 'coerce' in kwds: coerce = kwds['coerce'] @@ -690,12 +674,12 @@ def ideal(self, *args, **kwds): else: try: if self.has_coerce_map_from(first): - gens = first.gens() # we have a ring as argument + gens = first.gens() # we have a ring as argument elif isinstance(first, Element): gens = [first] else: - raise ArithmeticError("There is no coercion from %s to %s"%(first,self)) - except TypeError: # first may be a ring element + raise ArithmeticError("there is no coercion from %s to %s" % (first, self)) + except TypeError: # first may be a ring element pass break if coerce: @@ -706,7 +690,7 @@ def ideal(self, *args, **kwds): g = gens[0] if len(gens) == 1: try: - g = g.gcd(g) # note: we set g = gcd(g, g) to "canonicalize" the generator: make polynomials monic, etc. + g = g.gcd(g) # note: we set g = gcd(g, g) to "canonicalize" the generator: make polynomials monic, etc. except (AttributeError, NotImplementedError): pass else: @@ -722,15 +706,15 @@ def ideal(self, *args, **kwds): gens = gens[0] return C(self, gens, **kwds) - def _ideal_class_(self,n=0): + def _ideal_class_(self, n=0): """ Return the class that is used to implement ideals of this ring. - NOTE: + .. NOTE:: - We copy the code from :class:`~sage.rings.ring.Ring`. This is - necessary because not all rings inherit from that class, such - as matrix algebras. + We copy the code from :class:`~sage.rings.ring.Ring`. This is + necessary because not all rings inherit from that class, such + as matrix algebras. INPUT: @@ -742,10 +726,10 @@ def _ideal_class_(self,n=0): The class that is used to implement ideals of this ring with ``n`` generators. - NOTE: + .. NOTE:: - Often principal ideals (``n==1``) are implemented via a different - class. + Often principal ideals (``n==1``) are implemented via + a different class. EXAMPLES:: @@ -753,7 +737,7 @@ def _ideal_class_(self,n=0): sage: MS._ideal_class_() - We don't know of a commutative ring in Sage that does not inherit + We do not know of a commutative ring in Sage that does not inherit from the base class of rings. So, we need to cheat in the next example:: @@ -765,19 +749,17 @@ def _ideal_class_(self,n=0): sage: super(Ring,QQ)._ideal_class_(2) - """ from sage.rings.noncommutative_ideals import Ideal_nc try: if not self.is_commutative(): return Ideal_nc - except (NotImplementedError,AttributeError): + except (NotImplementedError, AttributeError): return Ideal_nc from sage.rings.ideal import Ideal_generic, Ideal_principal if n == 1: return Ideal_principal - else: - return Ideal_generic + return Ideal_generic ## # Quotient rings @@ -848,9 +830,9 @@ def quo(self, I, names=None, **kwds): """ Quotient of a ring by a two-sided ideal. - NOTE: + .. NOTE:: - This is a synonym for :meth:`quotient`. + This is a synonym for :meth:`quotient`. EXAMPLES:: @@ -894,15 +876,15 @@ def quo(self, I, names=None, **kwds): sage: a == b False """ - return self.quotient(I,names=names,**kwds) + return self.quotient(I, names=names, **kwds) def quotient_ring(self, I, names=None, **kwds): """ Quotient of a ring by a two-sided ideal. - NOTE: + .. NOTE:: - This is a synonym for :meth:`quotient`. + This is a synonym for :meth:`quotient`. INPUT: @@ -980,14 +962,14 @@ def __truediv__(self, I): sage: MS/I Traceback (most recent call last): ... - TypeError: Use self.quo(I) or self.quotient(I) to construct the quotient ring. + TypeError: use self.quotient(I) to construct the quotient ring sage: QQ['x'] / ZZ Traceback (most recent call last): ... - TypeError: Use self.quo(I) or self.quotient(I) to construct the quotient ring. + TypeError: use self.quotient(I) to construct the quotient ring """ - raise TypeError("Use self.quo(I) or self.quotient(I) to construct the quotient ring.") + raise TypeError("use self.quotient(I) to construct the quotient ring") def __getitem__(self, arg): """ @@ -1153,13 +1135,12 @@ def __getitem__(self, arg): """ def normalize_arg(arg): if isinstance(arg, (tuple, list)): - # Allowing arbitrary iterables would create confusion, but we - # may want to support a few more. + # Allowing arbitrary iterables would create confusion, + # but we may want to support a few more. return tuple(arg) - elif isinstance(arg, str): + if isinstance(arg, str): return tuple(arg.split(',')) - else: - return (arg,) + return (arg,) # 1. If arg is a list, try to return a power series ring. @@ -1221,7 +1202,7 @@ def normalize_arg(arg): # right ordered ring structure. from sage.rings.real_lazy import CLF, RLF if (iv.imag().is_zero() or iv.imag().contains_zero() - and elt.imag().is_zero()): + and elt.imag().is_zero()): emb = RLF(elt) else: emb = CLF(elt) @@ -1289,11 +1270,11 @@ def free_module(self, base=None, basis=None, map=True): if basis is not None: if isinstance(basis, (list, tuple)): if len(basis) != 1: - raise ValueError("Basis must have length 1") + raise ValueError("basis must have length 1") basis = basis[0] basis = self(basis) if not basis.is_unit(): - raise ValueError("Basis element must be a unit") + raise ValueError("basis element must be a unit") from sage.modules.free_module_morphism import BaseIsomorphism1D_from_FM, BaseIsomorphism1D_to_FM Hfrom = V.Hom(self) Hto = self.Hom(V) @@ -1306,7 +1287,7 @@ def free_module(self, base=None, basis=None, map=True): raise NotImplementedError class ElementMethods: - def is_unit(self): + def is_unit(self) -> bool: r""" Return whether this element is a unit in the ring. @@ -1314,7 +1295,7 @@ def is_unit(self): This is a generic implementation for (non-commutative) rings which only works for the one element, its additive inverse, and - the zero element. Most rings should provide a more specialized + the zero element. Most rings should provide a more specialized implementation. EXAMPLES:: @@ -1329,7 +1310,7 @@ def is_unit(self): """ if self.is_one() or (-self).is_one(): return True - if self.is_zero(): # now 0 != 1 + if self.is_zero(): # now 0 != 1 return False raise NotImplementedError @@ -1369,7 +1350,6 @@ def inverse_of_unit(self): Rational Field sage: (1/a).parent() Rational Field - """ try: if not self.is_unit(): @@ -1399,8 +1379,6 @@ def _divide_if_possible(self, y): sage: _.parent() Integer Ring - :: - sage: 4._divide_if_possible(3) Traceback (most recent call last): ... @@ -1408,9 +1386,10 @@ def _divide_if_possible(self, y): """ q, r = self.quo_rem(y) if r != 0: - raise ValueError("%s is not divisible by %s"%(self, y)) + raise ValueError("%s is not divisible by %s" % (self, y)) return q + def _gen_names(elts): r""" Used to find a name for a generator when rings are created using the @@ -1419,9 +1398,9 @@ def _gen_names(elts): EXAMPLES:: sage: from sage.categories.rings import _gen_names - sage: list(_gen_names([sqrt(5)])) # optional - sage.symbolic + sage: list(_gen_names([sqrt(5)])) # optional - sage.symbolic ['sqrt5'] - sage: list(_gen_names([sqrt(-17), 2^(1/3)])) # optional - sage.symbolic + sage: list(_gen_names([sqrt(-17), 2^(1/3)])) # optional - sage.symbolic ['a', 'b'] sage: list(_gen_names((1..27)))[-1] 'aa' @@ -1430,7 +1409,7 @@ def _gen_names(elts): from sage.structure.category_object import certify_names from sage.combinat.words.words import Words it = iter(Words("abcdefghijklmnopqrstuvwxyz", infinite=False)) - next(it) # skip empty word + next(it) # skip empty word for x in elts: name = str(x) m = re.match(r'^sqrt\((\d+)\)$', name) From 854064e92ce76ad3e8c1c0ae04aa65b6bcc4e8fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 9 Oct 2022 16:22:36 +0200 Subject: [PATCH 189/632] fix doctests --- src/sage/misc/sageinspect.py | 12 ++++----- src/sage/modules/free_module_morphism.py | 34 ++++++++++++------------ 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index fbca2defc20..92fc9304807 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -2144,11 +2144,11 @@ def sage_getsource(obj): - William Stein - extensions by Nick Alexander """ - #First we should check if the object has a _sage_src_ - #method. If it does, we just return the output from - #that. This is useful for getting pexpect interface - #elements to behave similar to regular Python objects - #with respect to introspection. + # First we should check if the object has a _sage_src_ + # method. If it does, we just return the output from + # that. This is useful for getting pexpect interface + # elements to behave similar to regular Python objects + # with respect to introspection. try: return obj._sage_src_() except (AttributeError, TypeError): @@ -2174,7 +2174,7 @@ def _sage_getsourcelines_name_with_dot(obj): sage: print(sage_getsource(C.parent_class)) #indirect doctest class ParentMethods: ... - Returns the Lie bracket `[x, y] = x y - y x` of `x` and `y`. + Return the Lie bracket `[x, y] = x y - y x` of `x` and `y`. ... TESTS: diff --git a/src/sage/modules/free_module_morphism.py b/src/sage/modules/free_module_morphism.py index 6a112282fa0..f7508ebeb97 100644 --- a/src/sage/modules/free_module_morphism.py +++ b/src/sage/modules/free_module_morphism.py @@ -61,6 +61,7 @@ def is_FreeModuleMorphism(x): """ return isinstance(x, FreeModuleMorphism) + class FreeModuleMorphism(matrix_morphism.MatrixMorphism): def __init__(self, parent, A, side="left"): """ @@ -80,7 +81,7 @@ def __init__(self, parent, A, side="left"): """ if not free_module_homspace.is_FreeModuleHomspace(parent): - raise TypeError("parent (=%s) must be a free module hom space"%parent) + raise TypeError("parent (=%s) must be a free module hom space" % parent) if isinstance(A, matrix_morphism.MatrixMorphism): A = A.matrix() A = parent._matrix_space(side)(A) @@ -162,15 +163,15 @@ def _repr_(self): The representation displays which side of the vectors the matrix is acting:: - sage: V = ZZ^3 - sage: h = V.hom([V.1, V.2, V.0]); h + sage: V = ZZ^3 + sage: h = V.hom([V.1, V.2, V.0]); h Free module morphism defined by the matrix [0 1 0] [0 0 1] [1 0 0] Domain: Ambient free module of rank 3 over the principal ideal domain Integer Ring Codomain: Ambient free module of rank 3 over the principal ideal domain Integer Ring - sage: h2 = V.hom([V.1, V.2, V.0], side="right"); h2 + sage: h2 = V.hom([V.1, V.2, V.0], side="right"); h2 Free module morphism defined as left-multiplication by the matrix [0 0 1] [1 0 0] @@ -551,18 +552,18 @@ def eigenvectors(self,extend=True): ], 1), (2, [ (0, 1, 0, 17/7) ], 2)] - + :: - sage: V = QQ^2 - sage: m = matrix(2, [1, 1, 0, 1]) - sage: V.hom(m, side="right").eigenvectors() + sage: V = QQ^2 + sage: m = matrix(2, [1, 1, 0, 1]) + sage: V.hom(m, side="right").eigenvectors() [(1, [ (1, 0) ], 2)] - sage: V.hom(m).eigenvectors() + sage: V.hom(m).eigenvectors() [(1, [ (0, 1) @@ -638,16 +639,16 @@ def eigenspaces(self,extend=True): Basis matrix: [0 1 0] [0 0 1])] - + :: - sage: V = QQ^2; m = matrix(2, [1, 1, 0, 1]) - sage: V.hom(m, side="right").eigenspaces() + sage: V = QQ^2; m = matrix(2, [1, 1, 0, 1]) + sage: V.hom(m, side="right").eigenspaces() [(1, Vector space of degree 2 and dimension 1 over Rational Field Basis matrix: [1 0])] - sage: V.hom(m).eigenspaces() + sage: V.hom(m).eigenspaces() [(1, Vector space of degree 2 and dimension 1 over Rational Field Basis matrix: @@ -796,7 +797,7 @@ class BaseIsomorphism1D_to_FM(BaseIsomorphism1D): sage: W, from_W, to_W = R.free_module(R, basis=4) Traceback (most recent call last): ... - ValueError: Basis element must be a unit + ValueError: basis element must be a unit """ def __init__(self, parent, basis=None): """ @@ -852,7 +853,7 @@ class BaseIsomorphism1D_from_FM(BaseIsomorphism1D): sage: W, from_W, to_W = R.free_module(R, basis=x) Traceback (most recent call last): ... - ValueError: Basis element must be a unit + ValueError: basis element must be a unit """ def __init__(self, parent, basis=None): """ @@ -878,5 +879,4 @@ def _call_(self, x): """ if self._basis is None: return x[0] - else: - return self.codomain()(x[0] / self._basis) + return self.codomain()(x[0] / self._basis) From 68990ba48ede32ed838a1c6e2567e5eaf1d7abaa Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 9 Oct 2022 11:09:44 -0700 Subject: [PATCH 190/632] build/pkgs/scipy: Update to 1.9.2 --- build/pkgs/scipy/checksums.ini | 6 +++--- build/pkgs/scipy/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/scipy/checksums.ini b/build/pkgs/scipy/checksums.ini index 83f9a385fca..086694d7214 100644 --- a/build/pkgs/scipy/checksums.ini +++ b/build/pkgs/scipy/checksums.ini @@ -1,5 +1,5 @@ tarball=scipy-VERSION.tar.gz -sha1=2f5a7bb5f992dd6766a3a0e088180be427518903 -md5=e6e70a9014dba74b4ef16686d23fd3ad -cksum=39501961 +sha1=f93163946802657fcd1c10821a1330497045ef6b +md5=ee6db269d03b2d47d04e876d38515d0d +cksum=3886027417 upstream_url=https://pypi.io/packages/source/s/scipy/scipy-VERSION.tar.gz diff --git a/build/pkgs/scipy/package-version.txt b/build/pkgs/scipy/package-version.txt index 9ab8337f396..8fdcf386946 100644 --- a/build/pkgs/scipy/package-version.txt +++ b/build/pkgs/scipy/package-version.txt @@ -1 +1 @@ -1.9.1 +1.9.2 From 4df3e8c32c5e07447b57d7c03a55059d38cd8fc9 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 9 Oct 2022 20:14:51 -0700 Subject: [PATCH 191/632] build/pkgs/meson: Update to 0.63.3 --- build/pkgs/meson/checksums.ini | 6 +++--- build/pkgs/meson/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/meson/checksums.ini b/build/pkgs/meson/checksums.ini index c14db7a0114..39f0a42c527 100644 --- a/build/pkgs/meson/checksums.ini +++ b/build/pkgs/meson/checksums.ini @@ -1,5 +1,5 @@ tarball=meson-VERSION.tar.gz -sha1=e81d2915173324476693e585a785abed8fd1bbad -md5=078e59d11a72b74c3bd78cb8205e9ed7 -cksum=4240866935 +sha1=3bce963302f547547c82fda35f84838ebc608e8a +md5=b2f2757b5dd84cc754b9df53ce37a175 +cksum=2257545181 upstream_url=https://pypi.io/packages/source/m/meson/meson-VERSION.tar.gz diff --git a/build/pkgs/meson/package-version.txt b/build/pkgs/meson/package-version.txt index 630f2e0ce67..068337d8307 100644 --- a/build/pkgs/meson/package-version.txt +++ b/build/pkgs/meson/package-version.txt @@ -1 +1 @@ -0.63.1 +0.63.3 From 77e77c77e73df867bfb25ef3b220a50af4e3f0ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 10 Oct 2022 17:26:10 +0200 Subject: [PATCH 192/632] tiny details, use transmute --- src/sage/combinat/triangles_FHM.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/triangles_FHM.py b/src/sage/combinat/triangles_FHM.py index 1f9f830ef9b..35eccbd0046 100644 --- a/src/sage/combinat/triangles_FHM.py +++ b/src/sage/combinat/triangles_FHM.py @@ -34,7 +34,6 @@ """ from sage.matrix.constructor import matrix from sage.rings.integer_ring import ZZ -from sage.rings.polynomial.polynomial_ring import polygen from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.structure.sage_object import SageObject @@ -320,7 +319,7 @@ def dual(self): for (dx, dy), coeff in self._poly.dict().items()} return M_triangle(A(dict_dual), variables=(x, y)) - def transpose(self): + def transmute(self): """ Return the image of ``self`` by an involution. @@ -337,9 +336,9 @@ def transpose(self): sage: x, y = polygens(ZZ, 'x,y') sage: nc3 = x^2*y^2 - 3*x*y^2 + 3*x*y + 2*y^2 - 3*y + 1 sage: m = M_triangle(nc3) - sage: m2 = m.transpose(); m2 + sage: m2 = m.transmute(); m2 2*x^2*y^2 - 3*x*y^2 + 2*x*y + y^2 - 2*y + 1 - sage: m2.transpose() == m + sage: m2.transmute() == m True """ return self.h().transpose().m() @@ -622,7 +621,6 @@ def vector(self): return anneau(self._poly(y=x)) - class Gamma_triangle(Triangle): """ Class for the Gamma-triangles. From 518b0bf598809626e13eef35fbc58da6e004979c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 11 Oct 2022 08:50:34 +0200 Subject: [PATCH 193/632] some suggestions --- src/sage/combinat/triangles_FHM.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/triangles_FHM.py b/src/sage/combinat/triangles_FHM.py index 35eccbd0046..396f299c46f 100644 --- a/src/sage/combinat/triangles_FHM.py +++ b/src/sage/combinat/triangles_FHM.py @@ -13,6 +13,11 @@ sage: posets.NoncrossingPartitions(W).M_triangle() x^3*y^3 - 6*x^2*y^3 + 6*x^2*y^2 + 10*x*y^3 - 16*x*y^2 - 5*y^3 + 6*x*y + 10*y^2 - 6*y + 1 + sage: unicode_art(_) + ⎛ -5 10 -6 1⎞ + ⎜ 10 -16 6 0⎟ + ⎜ -6 6 0 0⎟ + ⎝ 1 0 0 0⎠ The F-triangle class is motivated by the generating series of pure simplicial complexes endowed with a distinguished facet. One can also @@ -23,6 +28,11 @@ sage: f = C.greedy_facet() sage: C.F_triangle(f) 5*x^3 + 5*x^2*y + 3*x*y^2 + y^3 + 10*x^2 + 8*x*y + 3*y^2 + 6*x + 3*y + 1 + sage: unicode_art(_) + ⎛ 1 0 0 0⎞ + ⎜ 3 3 0 0⎟ + ⎜ 3 8 5 0⎟ + ⎝ 1 6 10 5⎠ The H-triangles are related to the F-triangles by a relationship similar to the classical link between the f-vector and the h-vector of a @@ -40,7 +50,7 @@ def _matrix_display(self, variables=None): """ - Return the 2-variables polynomial ``self`` as a matrix for display. + Return the 2-variable polynomial ``self`` as a matrix for display. INPUT: @@ -79,9 +89,9 @@ def _matrix_display(self, variables=None): else: x, y = variables ring = self.parent() - toutes_vars = x.parent().gens() - ix = toutes_vars.index(x) - iy = toutes_vars.index(y) + all_vars = x.parent().gens() + ix = all_vars.index(x) + iy = all_vars.index(y) minx = min(u[ix] for u in support) maxy = max(u[iy] for u in support) deltax = max(u[ix] for u in support) - minx + 1 From 2fbf8489733d8c7c052cd5c1a59ee029c8c2bb17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 11 Oct 2022 08:52:36 +0200 Subject: [PATCH 194/632] one more detail --- src/sage/topology/simplicial_complex.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/topology/simplicial_complex.py b/src/sage/topology/simplicial_complex.py index 6b07c7aef5d..7d194024016 100644 --- a/src/sage/topology/simplicial_complex.py +++ b/src/sage/topology/simplicial_complex.py @@ -1584,7 +1584,8 @@ def h_triangle(self): def F_triangle(self, S): """ - Return the F-triangle of ``self`` w.r.t one maximal simplex ``S``. + Return the F-triangle of ``self`` with respect + to one maximal simplex ``S``. This is the bivariate generating polynomial of all faces, according to the number of elements in ``S`` and outside ``S``. From 0de22b101de7f9a790e8cb043350760624c1a936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 11 Oct 2022 10:38:03 +0200 Subject: [PATCH 195/632] fix the doc (using trac role) --- src/sage/categories/rings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index d43dc15bbb1..6a7589bb109 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -49,7 +49,7 @@ class Rings(CategoryWithAxiom): .. TODO:: - (see: https://trac.sagemath.org/sage_trac/wiki/CategoriesRoadMap) + (see :trac:`sage_trac/wiki/CategoriesRoadMap`) - Make Rings() into a subcategory or alias of Algebras(ZZ); From 35e42a1994f8dff36815e01949fd20bf0bb133bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 11 Oct 2022 11:21:47 +0200 Subject: [PATCH 196/632] changing the repr --- src/sage/combinat/posets/posets.py | 2 +- src/sage/combinat/triangles_FHM.py | 86 ++++++++++++++----------- src/sage/topology/simplicial_complex.py | 3 +- 3 files changed, 50 insertions(+), 41 deletions(-) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 025c34bdcf9..48eadee2fa2 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -7280,7 +7280,7 @@ def M_triangle(self): sage: P = posets.DiamondPoset(5) sage: P.M_triangle() - x^2*y^2 - 3*x*y^2 + 3*x*y + 2*y^2 - 3*y + 1 + M: x^2*y^2 - 3*x*y^2 + 3*x*y + 2*y^2 - 3*y + 1 TESTS:: diff --git a/src/sage/combinat/triangles_FHM.py b/src/sage/combinat/triangles_FHM.py index 396f299c46f..6e962476433 100644 --- a/src/sage/combinat/triangles_FHM.py +++ b/src/sage/combinat/triangles_FHM.py @@ -11,8 +11,8 @@ sage: W = SymmetricGroup(4) sage: posets.NoncrossingPartitions(W).M_triangle() - x^3*y^3 - 6*x^2*y^3 + 6*x^2*y^2 + 10*x*y^3 - 16*x*y^2 - 5*y^3 - + 6*x*y + 10*y^2 - 6*y + 1 + M: x^3*y^3 - 6*x^2*y^3 + 6*x^2*y^2 + 10*x*y^3 - 16*x*y^2 + - 5*y^3 + 6*x*y + 10*y^2 - 6*y + 1 sage: unicode_art(_) ⎛ -5 10 -6 1⎞ ⎜ 10 -16 6 0⎟ @@ -27,7 +27,7 @@ sage: C = ClusterComplex(['A',3]) sage: f = C.greedy_facet() sage: C.F_triangle(f) - 5*x^3 + 5*x^2*y + 3*x*y^2 + y^3 + 10*x^2 + 8*x*y + 3*y^2 + 6*x + 3*y + 1 + F: 5*x^3 + 5*x^2*y + 3*x*y^2 + y^3 + 10*x^2 + 8*x*y + 3*y^2 + 6*x + 3*y + 1 sage: unicode_art(_) ⎛ 1 0 0 0⎞ ⎜ 3 3 0 0⎟ @@ -168,9 +168,9 @@ def _repr_(self) -> str: sage: x, y = polygens(ZZ, 'x,y') sage: ht = H_triangle(1+2*x*y) sage: ht - 2*x*y + 1 + H: 2*x*y + 1 """ - return repr(self._poly) + return self._prefix + repr(self._poly) def __eq__(self, other) -> bool: """ @@ -283,7 +283,7 @@ def truncate(self, d): sage: x, y = polygens(ZZ, 'x,y') sage: h = H_triangle(1+2*x*y) sage: h.truncate(2) - 2*x*y + 1 + H: 2*x*y + 1 """ p = self._poly for v in self._vars: @@ -302,8 +302,10 @@ class M_triangle(Triangle): sage: x, y = polygens(ZZ, 'x,y') sage: P = Poset({2:[1]}) sage: P.M_triangle() - x*y - y + 1 + M: x*y - y + 1 """ + _prefix = 'M: ' + def dual(self): """ Return the dual M-triangle. @@ -347,7 +349,7 @@ def transmute(self): sage: nc3 = x^2*y^2 - 3*x*y^2 + 3*x*y + 2*y^2 - 3*y + 1 sage: m = M_triangle(nc3) sage: m2 = m.transmute(); m2 - 2*x^2*y^2 - 3*x*y^2 + 2*x*y + y^2 - 2*y + 1 + M: 2*x^2*y^2 - 3*x*y^2 + 2*x*y + y^2 - 2*y + 1 sage: m2.transmute() == m True """ @@ -362,7 +364,7 @@ def h(self): sage: from sage.combinat.triangles_FHM import M_triangle sage: x, y = polygens(ZZ,'x,y') sage: M_triangle(1-y+x*y).h() - x*y + 1 + H: x*y + 1 TESTS:: @@ -370,7 +372,7 @@ def h(self): sage: x, y = polygens(h.parent(),'x,y') sage: mt = x**2*y**2+(-2*h+2)*x*y**2+(2*h-2)*x*y+(2*h-3)*y**2+(-2*h+2)*y+1 sage: M_triangle(mt, [x,y]).h() - x^2*y^2 + 2*x*y + (2*h - 4)*x + 1 + H: x^2*y^2 + 2*x*y + (2*h - 4)*x + 1 """ x, y = self._vars n = self._n @@ -388,7 +390,7 @@ def f(self): sage: from sage.combinat.triangles_FHM import M_triangle sage: x, y = polygens(ZZ,'x,y') sage: M_triangle(1-y+x*y).f() - x + y + 1 + F: x + y + 1 TESTS:: @@ -396,7 +398,7 @@ def f(self): sage: x, y = polygens(h.parent(),'x,y') sage: mt = x**2*y**2+(-2*h+2)*x*y**2+(2*h-2)*x*y+(2*h-3)*y**2+(-2*h+2)*y+1 sage: M_triangle(mt, [x,y]).f() - (2*h - 3)*x^2 + 2*x*y + y^2 + (2*h - 2)*x + 2*y + 1 + F: (2*h - 3)*x^2 + 2*x*y + y^2 + (2*h - 2)*x + 2*y + 1 """ return self.h().f() @@ -405,6 +407,8 @@ class H_triangle(Triangle): """ Class for the H-triangles. """ + _prefix = 'H: ' + def transpose(self): """ Return the transposed H-triangle. @@ -422,9 +426,9 @@ def transpose(self): sage: from sage.combinat.triangles_FHM import H_triangle sage: x, y = polygens(ZZ,'x,y') sage: H_triangle(1+x*y).transpose() - x*y + 1 + H: x*y + 1 sage: H_triangle(x^2*y^2 + 2*x*y + x + 1).transpose() - x^2*y^2 + x^2*y + 2*x*y + 1 + H: x^2*y^2 + x^2*y + 2*x*y + 1 """ x, y = self._vars n = self._n @@ -445,8 +449,8 @@ def m(self): sage: x, y = polygens(h.parent(),'x,y') sage: ht = H_triangle(x^2*y^2 + 2*x*y + 2*x*h - 4*x + 1, variables=[x,y]) sage: ht.m() - x^2*y^2 + (-2*h + 2)*x*y^2 + (2*h - 2)*x*y + (2*h - 3)*y^2 + - (-2*h + 2)*y + 1 + M: x^2*y^2 + (-2*h + 2)*x*y^2 + (2*h - 2)*x*y + + (2*h - 3)*y^2 + (-2*h + 2)*y + 1 """ x, y = self._vars n = self._n @@ -463,13 +467,14 @@ def f(self): sage: from sage.combinat.triangles_FHM import H_triangle sage: x, y = polygens(ZZ,'x,y') sage: H_triangle(1+x*y).f() - x + y + 1 + F: x + y + 1 sage: H_triangle(x^2*y^2 + 2*x*y + x + 1).f() - 2*x^2 + 2*x*y + y^2 + 3*x + 2*y + 1 + F: 2*x^2 + 2*x*y + y^2 + 3*x + 2*y + 1 sage: flo = H_triangle(1+4*x+2*x**2+x*y*(4+8*x)+ ....: x**2*y**2*(6+4*x)+4*(x*y)**3+(x*y)**4).f(); flo - 7*x^4 + 12*x^3*y + 10*x^2*y^2 + 4*x*y^3 + y^4 + 20*x^3 + 28*x^2*y - + 16*x*y^2 + 4*y^3 + 20*x^2 + 20*x*y + 6*y^2 + 8*x + 4*y + 1 + F: 7*x^4 + 12*x^3*y + 10*x^2*y^2 + 4*x*y^3 + y^4 + 20*x^3 + + 28*x^2*y + 16*x*y^2 + 4*y^3 + 20*x^2 + 20*x*y + + 6*y^2 + 8*x + 4*y + 1 sage: flo(-1-x,-1-y) == flo True @@ -478,7 +483,7 @@ def f(self): sage: x,y,h = polygens(ZZ,'x,y,h') sage: ht = x^2*y^2 + 2*x*y + 2*x*h - 4*x + 1 sage: H_triangle(ht,[x,y]).f() - 2*x^2*h - 3*x^2 + 2*x*y + y^2 + 2*x*h - 2*x + 2*y + 1 + F: 2*x^2*h - 3*x^2 + 2*x*y + y^2 + 2*x*h - 2*x + 2*y + 1 """ x, y = self._vars n = self._n @@ -500,12 +505,12 @@ def gamma(self): sage: x, y = polygen(ZZ,'x,y') sage: ht = x**2*y**2 + 2*x*y + x + 1 sage: H_triangle(ht).gamma() - y^2 + x + Γ: y^2 + x sage: W = SymmetricGroup(5) sage: P = posets.NoncrossingPartitions(W) sage: P.M_triangle().h().gamma() - y^4 + 3*x*y^2 + 2*x^2 + 2*x*y + x + Γ: y^4 + 3*x*y^2 + 2*x^2 + 2*x*y + x """ x, y = self._vars n = self._n @@ -539,6 +544,8 @@ class F_triangle(Triangle): """ Class for the F-triangles. """ + _prefix = 'F: ' + def h(self): """ Return the associated H-triangle. @@ -549,7 +556,7 @@ def h(self): sage: x,y = polygens(ZZ,'x,y') sage: ft = F_triangle(1+x+y) sage: ft.h() - x*y + 1 + H: x*y + 1 TESTS:: @@ -557,7 +564,7 @@ def h(self): sage: x, y = polygens(h.parent(),'x,y') sage: ft = 1+2*y+(2*h-2)*x+y**2+2*x*y+(2*h-3)*x**2 sage: F_triangle(ft, [x,y]).h() - x^2*y^2 + 2*x*y + (2*h - 4)*x + 1 + H: x^2*y^2 + 2*x*y + (2*h - 4)*x + 1 """ x, y = self._vars n = self._n @@ -574,36 +581,35 @@ def m(self): sage: from sage.combinat.triangles_FHM import H_triangle sage: x, y = polygens(ZZ,'x,y') sage: H_triangle(1+x*y).f() - x + y + 1 + F: x + y + 1 sage: _.m() - x*y - y + 1 + M: x*y - y + 1 sage: H_triangle(x^2*y^2 + 2*x*y + x + 1).f() - 2*x^2 + 2*x*y + y^2 + 3*x + 2*y + 1 + F: 2*x^2 + 2*x*y + y^2 + 3*x + 2*y + 1 sage: _.m() - x^2*y^2 - 3*x*y^2 + 3*x*y + 2*y^2 - 3*y + 1 + M: x^2*y^2 - 3*x*y^2 + 3*x*y + 2*y^2 - 3*y + 1 TESTS:: sage: p = 1+4*x+2*x**2+x*y*(4+8*x) sage: p += x**2*y**2*(6+4*x)+4*(x*y)**3+(x*y)**4 sage: flo = H_triangle(p).f(); flo - 7*x^4 + 12*x^3*y + 10*x^2*y^2 + 4*x*y^3 + y^4 + 20*x^3 - + 28*x^2*y + 16*x*y^2 + 4*y^3 + 20*x^2 + 20*x*y + 6*y^2 - + 8*x + 4*y + 1 + F: 7*x^4 + 12*x^3*y + 10*x^2*y^2 + 4*x*y^3 + y^4 + 20*x^3 + + 28*x^2*y + 16*x*y^2 + 4*y^3 + 20*x^2 + 20*x*y + + 6*y^2 + 8*x + 4*y + 1 sage: flo.m() - x^4*y^4 - 8*x^3*y^4 + 8*x^3*y^3 + 20*x^2*y^4 - - 36*x^2*y^3 - 20*x*y^4 - + 16*x^2*y^2 + 48*x*y^3 + 7*y^4 - 36*x*y^2 - 20*y^3 + 8*x*y - + 20*y^2 - 8*y + 1 + M: x^4*y^4 - 8*x^3*y^4 + 8*x^3*y^3 + 20*x^2*y^4 - 36*x^2*y^3 + - 20*x*y^4 + 16*x^2*y^2 + 48*x*y^3 + 7*y^4 - 36*x*y^2 - 20*y^3 + + 8*x*y + 20*y^2 - 8*y + 1 sage: from sage.combinat.triangles_FHM import F_triangle sage: h = polygen(ZZ, 'h') sage: x, y = polygens(h.parent(),'x,y') sage: ft = F_triangle(1+2*y+(2*h-2)*x+y**2+2*x*y+(2*h-3)*x**2,(x,y)) sage: ft.m() - x^2*y^2 + (-2*h + 2)*x*y^2 + (2*h - 2)*x*y + (2*h - 3)*y^2 - + (-2*h + 2)*y + 1 + M: x^2*y^2 + (-2*h + 2)*x*y^2 + (2*h - 2)*x*y + + (2*h - 3)*y^2 + (-2*h + 2)*y + 1 """ x, y = self._vars n = self._n @@ -635,6 +641,8 @@ class Gamma_triangle(Triangle): """ Class for the Gamma-triangles. """ + _prefix = 'Γ: ' + def h(self): r""" Return the associated H-triangle. @@ -652,7 +660,7 @@ def h(self): sage: x, y = polygen(ZZ,'x,y') sage: g = y**2 + x sage: Gamma_triangle(g).h() - x^2*y^2 + 2*x*y + x + 1 + H: x^2*y^2 + 2*x*y + x + 1 sage: a, b = polygen(ZZ, 'a, b') sage: x, y = polygens(a.parent(),'x,y') diff --git a/src/sage/topology/simplicial_complex.py b/src/sage/topology/simplicial_complex.py index 7d194024016..84026ddb17e 100644 --- a/src/sage/topology/simplicial_complex.py +++ b/src/sage/topology/simplicial_complex.py @@ -1602,7 +1602,8 @@ def F_triangle(self, S): sage: cs = simplicial_complexes.Torus() sage: cs.F_triangle(cs.facets()[0]) - x^3 + 9*x^2*y + 3*x*y^2 + y^3 + 6*x^2 + 12*x*y + 3*y^2 + 4*x + 3*y + 1 + F: x^3 + 9*x^2*y + 3*x*y^2 + y^3 + 6*x^2 + 12*x*y + + 3*y^2 + 4*x + 3*y + 1 """ x, y = polygens(ZZ, 'x, y') from sage.combinat.triangles_FHM import F_triangle From 8b063278be4a12ef00444fa10563919128c83152 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 11 Oct 2022 11:26:13 +0200 Subject: [PATCH 197/632] fix typo --- src/sage/combinat/posets/posets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 48eadee2fa2..2836d59d960 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -7270,7 +7270,7 @@ def M_triangle(self): an :class:`~sage.combinat.triangles_FHM.M_triangle` - Thie M-triangle is the generating polynomial of the Möbius numbers + The M-triangle is the generating polynomial of the Möbius numbers .. MATH:: From 814aa7cd53e8ca569ddfd87ba1cfa498c564531d Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 11 Oct 2022 12:11:27 +0200 Subject: [PATCH 198/632] remove Stream_cauchy_invert.get_coefficient, make sparse a mandatory argument, move _is_sparse an attribute of Stream_inexact --- src/sage/data_structures/stream.py | 261 +++++++++++++---------------- src/sage/rings/lazy_series.py | 40 +++-- src/sage/rings/lazy_series_ring.py | 6 +- 3 files changed, 144 insertions(+), 163 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 2bbb2f99106..a8d9f8f05d9 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -59,19 +59,19 @@ There is a unary negation operator:: - sage: h = Stream_neg(f) + sage: h = Stream_neg(f, True) sage: [h[i] for i in range(10)] [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] More generally, we can multiply by a scalar:: - sage: h = Stream_lmul(f, 2) + sage: h = Stream_lmul(f, 2, True) sage: [h[i] for i in range(10)] [0, 2, 4, 6, 8, 10, 12, 14, 16, 18] Finally, we can apply an arbitrary functions to the elements of a stream:: - sage: h = Stream_map_coefficients(f, lambda n: n^2) + sage: h = Stream_map_coefficients(f, lambda n: n^2, True) sage: [h[i] for i in range(10)] [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] @@ -110,7 +110,6 @@ class Stream(): INPUT: - - ``sparse`` -- boolean; whether the implementation of the stream is sparse - ``true_order`` -- boolean; if the approximate order is the actual order .. NOTE:: @@ -133,16 +132,15 @@ class Stream(): value before it is accessed. """ - def __init__(self, sparse, true_order): + def __init__(self, true_order): """ Initialize ``self``. EXAMPLES:: sage: from sage.data_structures.stream import Stream - sage: CS = Stream(True, 1) + sage: CS = Stream(1) """ - self._is_sparse = sparse self._true_order = true_order @lazy_attribute @@ -169,10 +167,10 @@ def __ne__(self, other): EXAMPLES:: sage: from sage.data_structures.stream import Stream - sage: CS = Stream(True, 1) + sage: CS = Stream(1) sage: CS != CS False - sage: CS != Stream(False, -2) + sage: CS != Stream(-2) False """ return False @@ -187,7 +185,7 @@ def is_nonzero(self): EXAMPLES:: sage: from sage.data_structures.stream import Stream - sage: CS = Stream(True, 1) + sage: CS = Stream(1) sage: CS.is_nonzero() False """ @@ -199,11 +197,12 @@ class Stream_inexact(Stream): An abstract base class for the stream when we do not know it is eventually constant. + In particular, a cache is provided. + INPUT: - - ``sparse`` -- boolean; whether the implementation of the stream is sparse - - ``approximate_order`` -- integer; a lower bound for the order - of the stream + - ``is_sparse`` -- boolean; whether the implementation of the stream is sparse + - ``true_order`` -- boolean; if the approximate order is the actual order .. TODO:: @@ -228,7 +227,8 @@ def __init__(self, is_sparse, true_order): True """ - super().__init__(is_sparse, true_order) + super().__init__(true_order) + self._is_sparse = is_sparse if self._is_sparse: self._cache = dict() # cache of known coefficients else: @@ -680,7 +680,7 @@ def __init__(self, initial_coefficients, constant=None, degree=None, order=None) assert self._initial_coefficients or self._constant, "Stream_exact should only be used for non-zero streams" - super().__init__(None, True) + super().__init__(True) self._approximate_order = order def __getitem__(self, n): @@ -1068,7 +1068,7 @@ class Stream_unary(Stream_inexact): sage: g = Stream_cauchy_invert(f) sage: [g[i] for i in range(10)] [-1, 1/2, 0, 0, 0, 0, 0, 0, 0, 0] - sage: g = Stream_lmul(f, 2) + sage: g = Stream_lmul(f, 2, True) sage: [g[i] for i in range(10)] [0, 4, 8, 12, 16, 20, 24, 28, 32, 36] """ @@ -1117,8 +1117,8 @@ def __eq__(self, other): sage: from sage.data_structures.stream import (Stream_function, Stream_rmul) sage: f = Stream_function(lambda n: 2*n, False, 1) sage: g = Stream_function(lambda n: n, False, 1) - sage: h = Stream_rmul(f, 2) - sage: n = Stream_rmul(g, 2) + sage: h = Stream_rmul(f, 2, True) + sage: n = Stream_rmul(g, 2, True) sage: h == n False sage: n == n @@ -1286,34 +1286,26 @@ class Stream_zero(Stream): """ A coefficient stream that is exactly equal to zero. - INPUT: - - - ``sparse`` -- boolean; whether the coefficient stream is sparse or not - EXAMPLES:: sage: from sage.data_structures.stream import Stream_zero - sage: s = Stream_zero(True) + sage: s = Stream_zero() sage: s[5] 0 """ - def __init__(self, is_sparse): + def __init__(self): """ Initialize ``self``. TESTS:: sage: from sage.data_structures.stream import Stream_zero - sage: s = Stream_zero(False) + sage: s = Stream_zero() sage: TestSuite(s).run() - .. TODO:: - - Having ``is_sparse`` as argument here does not really - make sense, since this stream does not cache values. """ - super().__init__(is_sparse, True) + super().__init__(True) self._approximate_order = infinity def __getitem__(self, n): @@ -1327,7 +1319,7 @@ def __getitem__(self, n): EXAMPLES:: sage: from sage.data_structures.stream import Stream_zero - sage: s = Stream_zero(True) + sage: s = Stream_zero() sage: s[1] 0 sage: sum([s[i] for i in range(10)]) @@ -1342,7 +1334,7 @@ def order(self): EXAMPLES:: sage: from sage.data_structures.stream import Stream_zero - sage: s = Stream_zero(True) + sage: s = Stream_zero() sage: s.order() +Infinity """ @@ -1350,12 +1342,12 @@ def order(self): def __eq__(self, other): """ - Check equality of ``self`` and ``other`` ignoring sparsity. + Check equality of ``self`` and ``other``. EXAMPLES:: sage: from sage.data_structures.stream import Stream_zero - sage: Stream_zero(True) == Stream_zero(False) + sage: Stream_zero() == Stream_zero() True """ return self is other or isinstance(other, Stream_zero) @@ -1367,14 +1359,9 @@ def __hash__(self): EXAMPLES:: sage: from sage.data_structures.stream import Stream_zero - sage: s = Stream_zero(False) - sage: a = hash(s); a + sage: s = Stream_zero() + sage: hash(s) 0 - sage: t = Stream_zero(False) - sage: b = hash(t); b - 0 - sage: b == a - True """ return 0 @@ -1745,13 +1732,13 @@ class Stream_dirichlet_invert(Stream_unary): sage: from sage.data_structures.stream import (Stream_dirichlet_invert, Stream_function) sage: f = Stream_function(lambda n: 1, True, 1) - sage: g = Stream_dirichlet_invert(f) + sage: g = Stream_dirichlet_invert(f, True) sage: [g[i] for i in range(10)] [0, 1, -1, -1, 0, -1, 1, -1, 0, 0] sage: [moebius(i) for i in range(10)] [0, 1, -1, -1, 0, -1, 1, -1, 0, 0] """ - def __init__(self, series): + def __init__(self, series, is_sparse): """ Initialize. @@ -1759,13 +1746,13 @@ def __init__(self, series): sage: from sage.data_structures.stream import (Stream_exact, Stream_dirichlet_invert) sage: f = Stream_exact([0, 0], constant=1) - sage: g = Stream_dirichlet_invert(f) + sage: g = Stream_dirichlet_invert(f, True) sage: g[1] Traceback (most recent call last): ... ZeroDivisionError: the Dirichlet inverse only exists if the coefficient with index 1 is non-zero """ - super().__init__(series, series._is_sparse) + super().__init__(series, is_sparse) self._zero = ZZ.zero() @lazy_attribute @@ -1777,7 +1764,7 @@ def _approximate_order(self): sage: from sage.data_structures.stream import Stream_function, Stream_dirichlet_invert sage: f = Stream_function(lambda n: n, True, 1) - sage: h = Stream_dirichlet_invert(f) + sage: h = Stream_dirichlet_invert(f, True) sage: h._approximate_order 1 sage: [h[i] for i in range(5)] @@ -1799,12 +1786,12 @@ def _ainv(self): sage: from sage.data_structures.stream import (Stream_exact, Stream_dirichlet_invert) sage: f = Stream_exact([0, 3], constant=2) - sage: g = Stream_dirichlet_invert(f) + sage: g = Stream_dirichlet_invert(f, True) sage: g._ainv 1/3 sage: f = Stream_exact([Zmod(6)(5)], constant=2, order=1) - sage: g = Stream_dirichlet_invert(f) + sage: g = Stream_dirichlet_invert(f, True) sage: g._ainv 5 """ @@ -1825,7 +1812,7 @@ def get_coefficient(self, n): sage: from sage.data_structures.stream import (Stream_exact, Stream_dirichlet_invert) sage: f = Stream_exact([0, 3], constant=2) - sage: g = Stream_dirichlet_invert(f) + sage: g = Stream_dirichlet_invert(f, True) sage: g.get_coefficient(6) 2/27 sage: [g[i] for i in range(8)] @@ -1908,7 +1895,7 @@ def _approximate_order(self): ginv = Stream_cauchy_invert(self._right) # The constant part makes no contribution to the negative. # We need this for the case so self._neg_powers[0][n] => 0. - self._neg_powers = [Stream_zero(self._left._is_sparse), ginv] + self._neg_powers = [Stream_zero(), ginv] for i in range(1, -self._left._approximate_order): # TODO: possibly we always want a dense cache here? self._neg_powers.append(Stream_cauchy_mul(self._neg_powers[-1], ginv, self._is_sparse)) @@ -2064,7 +2051,7 @@ def __init__(self, f, g, is_sparse, p, ring=None, include=None, exclude=None): else: self._basis = ring self._p = p - g = Stream_map_coefficients(g, lambda x: p(x)) + g = Stream_map_coefficients(g, lambda x: p(x), is_sparse) self._powers = [g] # a cache for the powers of g R = self._basis.base_ring() self._degree_one = _variables_recursive(R, include=include, exclude=exclude) @@ -2072,11 +2059,11 @@ def __init__(self, f, g, is_sparse, p, ring=None, include=None, exclude=None): if HopfAlgebrasWithBasis(R).TensorProducts() in p.categories(): self._tensor_power = len(p._sets) p_f = p._sets[0] - f = Stream_map_coefficients(f, lambda x: p_f(x)) + f = Stream_map_coefficients(f, lambda x: p_f(x), is_sparse) else: self._tensor_power = None - f = Stream_map_coefficients(f, lambda x: p(x)) - super().__init__(f, g, f._is_sparse) + f = Stream_map_coefficients(f, lambda x: p(x), is_sparse) + super().__init__(f, g, is_sparse) @lazy_attribute def _approximate_order(self): @@ -2266,7 +2253,7 @@ class Stream_scalar(Stream_inexact): :meth:`Stream_unary.__eq__`. Would this be any better? """ - def __init__(self, series, scalar): + def __init__(self, series, scalar, is_sparse): """ Initialize ``self``. @@ -2274,12 +2261,12 @@ def __init__(self, series, scalar): sage: from sage.data_structures.stream import (Stream_rmul, Stream_function) sage: f = Stream_function(lambda n: -1, True, 0) - sage: g = Stream_rmul(f, 3) + sage: g = Stream_rmul(f, 3, True) """ self._series = series self._scalar = scalar assert scalar, "the scalar must not be equal to 0" - super().__init__(series._is_sparse, series._true_order) + super().__init__(is_sparse, series._true_order) @lazy_attribute def _approximate_order(self): @@ -2290,7 +2277,7 @@ def _approximate_order(self): sage: from sage.data_structures.stream import Stream_function, Stream_rmul sage: f = Stream_function(lambda n: Zmod(6)(n), True, 2) - sage: h = Stream_rmul(f, 3) # indirect doctest + sage: h = Stream_rmul(f, 3, True) # indirect doctest sage: h._approximate_order 2 sage: [h[i] for i in range(5)] @@ -2308,7 +2295,7 @@ def __hash__(self): sage: from sage.data_structures.stream import Stream_function sage: from sage.data_structures.stream import Stream_rmul sage: a = Stream_function(lambda n: 2*n, False, 1) - sage: f = Stream_rmul(a, 2) + sage: f = Stream_rmul(a, 2, True) sage: hash(f) == hash(f) True """ @@ -2328,14 +2315,14 @@ def __eq__(self, other): sage: from sage.data_structures.stream import Stream_rmul, Stream_lmul sage: a = Stream_function(lambda n: 2*n, False, 1) sage: b = Stream_function(lambda n: n, False, 1) - sage: f = Stream_rmul(a, 2) - sage: f == Stream_rmul(b, 2) + sage: f = Stream_rmul(a, 2, True) + sage: f == Stream_rmul(b, 2, True) False - sage: f == Stream_rmul(a, 2) + sage: f == Stream_rmul(a, 2, False) True - sage: f == Stream_rmul(a, 3) + sage: f == Stream_rmul(a, 3, True) False - sage: f == Stream_lmul(a, 3) + sage: f == Stream_lmul(a, 3, True) False """ return (isinstance(other, type(self)) and self._series == other._series @@ -2350,13 +2337,13 @@ def is_nonzero(self): sage: from sage.data_structures.stream import (Stream_rmul, Stream_function) sage: f = Stream_function(lambda n: n, True, 1) - sage: g = Stream_rmul(f, 2) + sage: g = Stream_rmul(f, 2, True) sage: g.is_nonzero() False sage: from sage.data_structures.stream import Stream_cauchy_invert sage: fi = Stream_cauchy_invert(f) - sage: g = Stream_rmul(fi, 2) + sage: g = Stream_rmul(fi, 2, True) sage: g.is_nonzero() True """ @@ -2379,7 +2366,7 @@ class Stream_rmul(Stream_scalar): sage: W = algebras.DifferentialWeyl(QQ, names=('x',)) sage: x, dx = W.gens() sage: f = Stream_function(lambda n: x^n, True, 1) - sage: g = Stream_rmul(f, dx) + sage: g = Stream_rmul(f, dx, True) sage: [g[i] for i in range(5)] [0, x*dx + 1, x^2*dx + 2*x, x^3*dx + 3*x^2, x^4*dx + 4*x^3] """ @@ -2395,7 +2382,7 @@ def get_coefficient(self, n): sage: from sage.data_structures.stream import (Stream_rmul, Stream_function) sage: f = Stream_function(lambda n: n, True, 1) - sage: g = Stream_rmul(f, 3) + sage: g = Stream_rmul(f, 3, True) sage: g.get_coefficient(5) 15 sage: [g.get_coefficient(i) for i in range(10)] @@ -2420,7 +2407,7 @@ class Stream_lmul(Stream_scalar): sage: W = algebras.DifferentialWeyl(QQ, names=('x',)) sage: x, dx = W.gens() sage: f = Stream_function(lambda n: x^n, True, 1) - sage: g = Stream_lmul(f, dx) + sage: g = Stream_lmul(f, dx, True) sage: [g[i] for i in range(5)] [0, x*dx, x^2*dx, x^3*dx, x^4*dx] """ @@ -2436,7 +2423,7 @@ def get_coefficient(self, n): sage: from sage.data_structures.stream import (Stream_lmul, Stream_function) sage: f = Stream_function(lambda n: n, True, 1) - sage: g = Stream_lmul(f, 3) + sage: g = Stream_lmul(f, 3, True) sage: g.get_coefficient(5) 15 sage: [g.get_coefficient(i) for i in range(10)] @@ -2457,11 +2444,11 @@ class Stream_neg(Stream_unary): sage: from sage.data_structures.stream import (Stream_neg, Stream_function) sage: f = Stream_function(lambda n: 1, True, 1) - sage: g = Stream_neg(f) + sage: g = Stream_neg(f, True) sage: [g[i] for i in range(10)] [0, -1, -1, -1, -1, -1, -1, -1, -1, -1] """ - def __init__(self, series): + def __init__(self, series, is_sparse): """ Initialize ``self``. @@ -2469,9 +2456,9 @@ def __init__(self, series): sage: from sage.data_structures.stream import (Stream_neg, Stream_function) sage: f = Stream_function(lambda n: -1, True, 0) - sage: g = Stream_neg(f) + sage: g = Stream_neg(f, True) """ - super().__init__(series, series._is_sparse) + super().__init__(series, is_sparse) self._true_order = self._series._true_order @lazy_attribute @@ -2483,13 +2470,13 @@ def _approximate_order(self): sage: from sage.data_structures.stream import Stream_function, Stream_neg sage: f = Stream_function(lambda n: Zmod(6)(n), True, 2) - sage: h = Stream_neg(f) + sage: h = Stream_neg(f, True) sage: h._approximate_order 2 sage: [h[i] for i in range(5)] [0, 0, 4, 3, 2] """ - # this is the true order + # this is the true order, if self._series._true_order return self._series._approximate_order def get_coefficient(self, n): @@ -2504,7 +2491,7 @@ def get_coefficient(self, n): sage: from sage.data_structures.stream import (Stream_neg, Stream_function) sage: f = Stream_function(lambda n: n, True, 1) - sage: g = Stream_neg(f) + sage: g = Stream_neg(f, True) sage: g.get_coefficient(5) -5 sage: [g.get_coefficient(i) for i in range(10)] @@ -2521,13 +2508,13 @@ def is_nonzero(self): sage: from sage.data_structures.stream import (Stream_neg, Stream_function) sage: f = Stream_function(lambda n: n, True, 1) - sage: g = Stream_neg(f) + sage: g = Stream_neg(f, True) sage: g.is_nonzero() False sage: from sage.data_structures.stream import Stream_cauchy_invert sage: fi = Stream_cauchy_invert(f) - sage: g = Stream_neg(fi) + sage: g = Stream_neg(fi, True) sage: g.is_nonzero() True """ @@ -2541,9 +2528,10 @@ class Stream_cauchy_invert(Stream_unary): INPUT: - ``series`` -- a :class:`Stream` - - ``approximate_order`` -- ``None``, or a lower bound on the - order of ``Stream_cauchy_invert(series)`` + order of the resulting stream + + Instances of this class are always dense. EXAMPLES:: @@ -2564,7 +2552,7 @@ def __init__(self, series, approximate_order=None): sage: f = Stream_exact([1, -1]) sage: g = Stream_cauchy_invert(f) """ - super().__init__(series, series._is_sparse) + super().__init__(series, False) if approximate_order is not None: self._approximate_order = approximate_order self._zero = ZZ.zero() @@ -2615,39 +2603,6 @@ def _ainv(self): except TypeError: return self._series[v].inverse_of_unit() - def get_coefficient(self, n): - """ - Return the ``n``-th coefficient of ``self``. - - INPUT: - - - ``n`` -- integer; the degree for the coefficient - - EXAMPLES:: - - sage: from sage.data_structures.stream import (Stream_cauchy_invert, Stream_function) - sage: f = Stream_function(lambda n: n, True, 1) - sage: g = Stream_cauchy_invert(f) - sage: g.get_coefficient(5) - 0 - sage: [g.get_coefficient(i) for i in range(10)] - [-2, 1, 0, 0, 0, 0, 0, 0, 0, 0] - """ - if not self._series._true_order: - self._ainv # this computes the true order of ``self`` - v = self._approximate_order - if n < v: - return ZZ.zero() - if n == v: - return self._ainv - - c = self._zero - for k in range(v, n): - l = self[k] - if l: - c += l * self._series[n - v - k] - return -c * self._ainv - def iterate_coefficients(self): """ A generator for the coefficients of ``self``. @@ -2668,7 +2623,6 @@ def iterate_coefficients(self): # Note that the first entry of the cache will correspond to # z^v, when the stream corresponds to a Laurent series. - # TODO: don't we need to distinguish between sparse and dense here? while True: n += 1 c = self._zero @@ -2715,12 +2669,12 @@ class Stream_map_coefficients(Stream_inexact): sage: from sage.data_structures.stream import (Stream_map_coefficients, Stream_function) sage: f = Stream_function(lambda n: 1, True, 1) - sage: g = Stream_map_coefficients(f, lambda n: -n) + sage: g = Stream_map_coefficients(f, lambda n: -n, True) sage: [g[i] for i in range(10)] [0, -1, -1, -1, -1, -1, -1, -1, -1, -1] """ - def __init__(self, series, function, approximate_order=None, true_order=False): + def __init__(self, series, function, is_sparse, approximate_order=None, true_order=False): """ Initialize ``self``. @@ -2728,12 +2682,12 @@ def __init__(self, series, function, approximate_order=None, true_order=False): sage: from sage.data_structures.stream import (Stream_map_coefficients, Stream_function) sage: f = Stream_function(lambda n: -1, True, 0) - sage: g = Stream_map_coefficients(f, lambda n: n + 1) + sage: g = Stream_map_coefficients(f, lambda n: n + 1, True) sage: TestSuite(g).run(skip="_test_pickling") """ self._function = function self._series = series - super().__init__(series._is_sparse, true_order) + super().__init__(is_sparse, true_order) if approximate_order is not None: self._approximate_order = approximate_order @@ -2746,7 +2700,7 @@ def _approximate_order(self): sage: from sage.data_structures.stream import Stream_function, Stream_map_coefficients sage: f = Stream_function(lambda n: Zmod(6)(n), True, 2) - sage: h = Stream_map_coefficients(f, lambda c: 3*c) + sage: h = Stream_map_coefficients(f, lambda c: 3*c, True) sage: h._approximate_order 2 sage: [h[i] for i in range(5)] @@ -2767,7 +2721,7 @@ def get_coefficient(self, n): sage: from sage.data_structures.stream import (Stream_map_coefficients, Stream_function) sage: f = Stream_function(lambda n: n, True, -1) - sage: g = Stream_map_coefficients(f, lambda n: n^2 + 1) + sage: g = Stream_map_coefficients(f, lambda n: n^2 + 1, True) sage: g.get_coefficient(5) 26 sage: [g.get_coefficient(i) for i in range(-1, 10)] @@ -2775,7 +2729,7 @@ def get_coefficient(self, n): sage: R. = ZZ[] sage: f = Stream_function(lambda n: n, True, -1) - sage: g = Stream_map_coefficients(f, lambda n: R(n).degree() + 1) + sage: g = Stream_map_coefficients(f, lambda n: R(n).degree() + 1, True) sage: [g.get_coefficient(i) for i in range(-1, 3)] [1, 0, 1, 1] """ @@ -2792,7 +2746,7 @@ def __hash__(self): sage: from sage.data_structures.stream import (Stream_map_coefficients, Stream_function) sage: f = Stream_function(lambda n: -1, True, 0) - sage: g = Stream_map_coefficients(f, lambda n: n + 1) + sage: g = Stream_map_coefficients(f, lambda n: n + 1, True) sage: hash(g) == hash(g) True """ @@ -2812,19 +2766,21 @@ def __eq__(self, other): sage: from sage.data_structures.stream import (Stream_map_coefficients, Stream_function) sage: f = Stream_function(lambda n: -1, True, 0) sage: def plus_one(n): return n + 1 - sage: g = Stream_map_coefficients(f, plus_one) + sage: g = Stream_map_coefficients(f, plus_one, True) sage: g == f False - sage: g == Stream_map_coefficients(f, lambda n: n + 1) + sage: g == Stream_map_coefficients(f, lambda n: n + 1, True) False """ return (isinstance(other, type(self)) and self._series == other._series and self._function == other._function) -class Stream_shift(Stream_inexact): +class Stream_shift(Stream): """ - Operator for shifting the stream. + Operator for shifting a nonzero, nonexact stream. + + Instances of this class share the cache with its input stream. INPUT: @@ -2838,14 +2794,14 @@ def __init__(self, series, shift): EXAMPLES:: sage: from sage.data_structures.stream import Stream_shift - sage: from sage.data_structures.stream import Stream_exact - sage: h = Stream_exact([1], constant=3) + sage: from sage.data_structures.stream import Stream_function + sage: h = Stream_function(lambda n: n, True, -5) sage: M = Stream_shift(h, 2) sage: TestSuite(M).run(skip="_test_pickling") """ self._series = series self._shift = shift - super().__init__(series._is_sparse, series._true_order) + super().__init__(series._true_order) @lazy_attribute def _approximate_order(self): @@ -2862,9 +2818,24 @@ def _approximate_order(self): sage: [h[i] for i in range(5)] [2, 3, 4, 5, 0] """ - # this is the true order + # this is the true order, if self._series._true_order return self._series._approximate_order + self._shift + def order(self): + r""" + Return the order of ``self``, which is the minimum index + ``n`` such that ``self[n]`` is nonzero. + + EXAMPLES:: + + sage: from sage.data_structures.stream import Stream_function, Stream_shift + sage: s = Stream_shift(Stream_function(lambda n: n, True, 0), 2) + sage: s.order() + 3 + """ + return self._series.order() + self._shift + + def __getitem__(self, n): """ Return the ``n``-th coefficient of ``self``. @@ -2947,7 +2918,7 @@ class Stream_derivative(Stream_inexact): - ``series`` -- a :class:`Stream` - ``shift`` -- a positive integer """ - def __init__(self, series, shift): + def __init__(self, series, shift, is_sparse): """ Initialize ``self``. @@ -2955,12 +2926,12 @@ def __init__(self, series, shift): sage: from sage.data_structures.stream import Stream_exact, Stream_derivative sage: f = Stream_exact([1,2,3]) - sage: f2 = Stream_derivative(f, 2) + sage: f2 = Stream_derivative(f, 2, True) sage: TestSuite(f2).run() """ self._series = series self._shift = shift - super().__init__(series._is_sparse, False) + super().__init__(is_sparse, False) @lazy_attribute def _approximate_order(self): @@ -2971,7 +2942,7 @@ def _approximate_order(self): sage: from sage.data_structures.stream import Stream_function, Stream_derivative sage: f = Stream_function(lambda n: Zmod(6)(n), True, 2) - sage: h = Stream_derivative(f, 3) + sage: h = Stream_derivative(f, 3, True) sage: h._approximate_order 0 sage: [h[i] for i in range(5)] @@ -2993,14 +2964,14 @@ def __getitem__(self, n): sage: f = Stream_function(lambda n: 1/n if n else 0, True, -2) sage: [f[i] for i in range(-5, 3)] [0, 0, 0, -1/2, -1, 0, 1, 1/2] - sage: f2 = Stream_derivative(f, 2) + sage: f2 = Stream_derivative(f, 2, True) sage: [f2[i] for i in range(-5, 3)] [0, -3, -2, 0, 0, 1, 2, 3] sage: f = Stream_function(lambda n: 1/n, True, 2) sage: [f[i] for i in range(-1, 4)] [0, 0, 0, 1/2, 1/3] - sage: f2 = Stream_derivative(f, 3) + sage: f2 = Stream_derivative(f, 3, True) sage: [f2[i] for i in range(-1, 4)] [0, 2, 6, 12, 20] """ @@ -3016,8 +2987,8 @@ def __hash__(self): sage: from sage.data_structures.stream import Stream_function sage: from sage.data_structures.stream import Stream_derivative sage: a = Stream_function(lambda n: 2*n, False, 1) - sage: f = Stream_derivative(a, 1) - sage: g = Stream_derivative(a, 2) + sage: f = Stream_derivative(a, 1, True) + sage: g = Stream_derivative(a, 2, True) sage: hash(f) == hash(f) True sage: hash(f) == hash(g) @@ -3039,11 +3010,11 @@ def __eq__(self, other): sage: from sage.data_structures.stream import Stream_function sage: from sage.data_structures.stream import Stream_derivative sage: a = Stream_function(lambda n: 2*n, False, 1) - sage: f = Stream_derivative(a, 1) - sage: g = Stream_derivative(a, 2) + sage: f = Stream_derivative(a, 1, True) + sage: g = Stream_derivative(a, 2, True) sage: f == g False - sage: f == Stream_derivative(a, 1) + sage: f == Stream_derivative(a, 1, True) True """ return (isinstance(other, type(self)) and self._shift == other._shift @@ -3058,9 +3029,9 @@ def is_nonzero(self): sage: from sage.data_structures.stream import Stream_exact, Stream_derivative sage: f = Stream_exact([1,2]) - sage: Stream_derivative(f, 1).is_nonzero() + sage: Stream_derivative(f, 1, True).is_nonzero() True - sage: Stream_derivative(f, 2).is_nonzero() # it might be nice if this gave False + sage: Stream_derivative(f, 2, True).is_nonzero() # it might be nice if this gave False True """ return self._series.is_nonzero() diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index a4d8005167f..5b573ef5b19 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -578,7 +578,8 @@ def map_coefficients(self, f): degree=coeff_stream._degree, constant=P.base_ring()(c)) return P.element_class(P, coeff_stream) - coeff_stream = Stream_map_coefficients(self._coeff_stream, func) + coeff_stream = Stream_map_coefficients(self._coeff_stream, func, + P.is_sparse()) return P.element_class(P, coeff_stream) def truncate(self, d): @@ -1732,8 +1733,10 @@ def _acted_upon_(self, scalar, self_on_left): constant=c, degree=coeff_stream._degree)) if self_on_left or R.is_commutative(): - return P.element_class(P, Stream_lmul(coeff_stream, scalar)) - return P.element_class(P, Stream_rmul(coeff_stream, scalar)) + return P.element_class(P, Stream_lmul(coeff_stream, scalar, + P.is_sparse())) + return P.element_class(P, Stream_rmul(coeff_stream, scalar, + P.is_sparse())) def _neg_(self): """ @@ -1796,7 +1799,7 @@ def _neg_(self): # -(-f) = f if isinstance(coeff_stream, Stream_neg): return P.element_class(P, coeff_stream._series) - return P.element_class(P, Stream_neg(coeff_stream)) + return P.element_class(P, Stream_neg(coeff_stream, P.is_sparse())) # === special functions === @@ -3903,10 +3906,12 @@ def derivative(self, *args): constant=coeff_stream._constant) return P.element_class(P, coeff_stream) - coeff_stream = Stream_derivative(self._coeff_stream, order) + coeff_stream = Stream_derivative(self._coeff_stream, order, + P.is_sparse()) if vars: coeff_stream = Stream_map_coefficients(coeff_stream, - lambda c: c.derivative(vars)) + lambda c: c.derivative(vars), + P.is_sparse()) return P.element_class(P, coeff_stream) def approximate_series(self, prec, name=None): @@ -4679,7 +4684,8 @@ def derivative(self, *args): v = gen_vars + vars d = -len(gen_vars) coeff_stream = Stream_map_coefficients(coeff_stream, - lambda c: R(c).derivative(v)) + lambda c: R(c).derivative(v), + P.is_sparse()) coeff_stream = Stream_shift(coeff_stream, d) return P.element_class(P, coeff_stream) @@ -4700,10 +4706,12 @@ def derivative(self, *args): constant=coeff_stream._constant) return P.element_class(P, coeff_stream) - coeff_stream = Stream_derivative(self._coeff_stream, order) + coeff_stream = Stream_derivative(self._coeff_stream, order, + P.is_sparse()) if vars: coeff_stream = Stream_map_coefficients(coeff_stream, - lambda c: c.derivative(vars)) + lambda c: c.derivative(vars), + P.is_sparse()) return P.element_class(P, coeff_stream) def _format_series(self, formatter, format_strings=False): @@ -5508,7 +5516,8 @@ def derivative_with_respect_to_p1(self, n=1): raise ValueError("arity must be equal to 1") coeff_stream = Stream_map_coefficients(self._coeff_stream, - lambda c: c.derivative_with_respect_to_p1(n)) + lambda c: c.derivative_with_respect_to_p1(n), + P.is_sparse()) coeff_stream = Stream_shift(coeff_stream, -n) return P.element_class(P, coeff_stream) @@ -5643,8 +5652,8 @@ def functorial_composition(self, *args): p = R.realization_of().p() # TODO: does the following introduce a memory leak? - g = Stream_map_coefficients(g._coeff_stream, p) - f = Stream_map_coefficients(self._coeff_stream, p) + g = Stream_map_coefficients(g._coeff_stream, p, P.is_sparse()) + f = Stream_map_coefficients(self._coeff_stream, p, P.is_sparse()) def g_cycle_type(s, n): # the cycle type of G[sigma] of any permutation sigma @@ -5900,8 +5909,8 @@ def arithmetic_product(self, *args, check=True): p = R.realization_of().p() # TODO: does the following introduce a memory leak? - g = Stream_map_coefficients(g._coeff_stream, p) - f = Stream_map_coefficients(self._coeff_stream, p) + g = Stream_map_coefficients(g._coeff_stream, p, P.is_sparse()) + f = Stream_map_coefficients(self._coeff_stream, p, P.is_sparse()) def coefficient(n): if not n: @@ -6162,7 +6171,8 @@ def __invert__(self): ZeroDivisionError: rational division by zero """ P = self.parent() - return P.element_class(P, Stream_dirichlet_invert(self._coeff_stream)) + return P.element_class(P, Stream_dirichlet_invert(self._coeff_stream, + P.is_sparse())) def __call__(self, p, *, check=True): r""" diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index fb7b3177e17..3bf145e62e7 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -725,7 +725,7 @@ def zero(self): sage: L.zero() 0 """ - return self.element_class(self, Stream_zero(self._sparse)) + return self.element_class(self, Stream_zero()) def characteristic(self): """ @@ -1847,7 +1847,7 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No if x in R: if not x and not constant: - coeff_stream = Stream_zero(self._sparse) + coeff_stream = Stream_zero() else: if not x: coeff_stream = Stream_exact([], @@ -2314,7 +2314,7 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No pass if x in R: if not x: - coeff_stream = Stream_zero(self._sparse) + coeff_stream = Stream_zero() else: p_dict = {} if self._arity == 1: From 7043d1cf5d937c3f947634c088e8bdc2c13e4d9f Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 11 Oct 2022 15:56:59 +0200 Subject: [PATCH 199/632] remove redundant __init__ methods, remove finished TODOs --- src/sage/data_structures/stream.py | 62 ++------------------------ src/sage/rings/lazy_series_ring.py | 71 +++++++++--------------------- 2 files changed, 25 insertions(+), 108 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index a8d9f8f05d9..92aa652403a 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -1390,19 +1390,6 @@ class Stream_add(Stream_binaryCommutative): sage: [u[i] for i in range(10)] [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] """ - def __init__(self, left, right, is_sparse): - """ - Initialize ``self``. - - TESTS:: - - sage: from sage.data_structures.stream import (Stream_function, Stream_add) - sage: f = Stream_function(lambda n: 1, True, 0) - sage: g = Stream_function(lambda n: n^2, True, 0) - sage: h = Stream_add(f, g, True) - """ - super().__init__(left, right, is_sparse) - @lazy_attribute def _approximate_order(self): """ @@ -1461,19 +1448,6 @@ class Stream_sub(Stream_binary): sage: [u[i] for i in range(10)] [1, 0, -1, -2, -3, -4, -5, -6, -7, -8] """ - def __init__(self, left, right, is_sparse): - """ - initialize ``self``. - - TESTS:: - - sage: from sage.data_structures.stream import (Stream_function, Stream_sub) - sage: f = Stream_function(lambda n: 1, True, 0) - sage: g = Stream_function(lambda n: n^2, True, 0) - sage: h = Stream_sub(f, g, True) - """ - super().__init__(left, right, is_sparse) - @lazy_attribute def _approximate_order(self): """ @@ -1540,19 +1514,6 @@ class Stream_cauchy_mul(Stream_binary): sage: [u[i] for i in range(10)] [0, 1, 3, 6, 10, 15, 21, 28, 36, 45] """ - def __init__(self, left, right, is_sparse): - """ - initialize ``self``. - - TESTS:: - - sage: from sage.data_structures.stream import (Stream_function, Stream_cauchy_mul) - sage: f = Stream_function(lambda n: 1, True, 0) - sage: g = Stream_function(lambda n: n^2, True, 0) - sage: h = Stream_cauchy_mul(f, g, True) - """ - super().__init__(left, right, is_sparse) - @lazy_attribute def _approximate_order(self): """ @@ -1647,26 +1608,6 @@ class Stream_dirichlet_convolve(Stream_binary): sage: [u[i] for i in range(1, 10)] [1, 3, 4, 7, 6, 12, 8, 15, 13] """ - def __init__(self, left, right, is_sparse): - """ - Initialize ``self``. - - sage: from sage.data_structures.stream import (Stream_dirichlet_convolve, Stream_function, Stream_exact) - sage: f = Stream_function(lambda n: n, True, 1) - sage: g = Stream_exact([1], constant=0) - sage: h = Stream_dirichlet_convolve(f, g, True) - sage: h[1] - Traceback (most recent call last): - ... - ValueError: Dirichlet convolution is only defined for coefficient streams with minimal index of nonzero coefficient at least 1 - sage: h = Stream_dirichlet_convolve(g, f, True) - sage: h[1] - Traceback (most recent call last): - ... - ValueError: Dirichlet convolution is only defined for coefficient streams with minimal index of nonzero coefficient at least 1 - """ - super().__init__(left, right, is_sparse) - @lazy_attribute def _approximate_order(self): """ @@ -2448,6 +2389,9 @@ class Stream_neg(Stream_unary): sage: [g[i] for i in range(10)] [0, -1, -1, -1, -1, -1, -1, -1, -1, -1] """ + # TODO: maybe we should just inherit from `Stream` instead of + # inheriting from `Stream_unary` and do not create a copy of the + # cache def __init__(self, series, is_sparse): """ Initialize ``self``. diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 3bf145e62e7..27e0f963625 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -426,11 +426,6 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No sage: L(filter(is_odd, NN), -3) z^-3 + 3*z^-2 + 5*z^-1 + 7 + 9*z + 11*z^2 + 13*z^3 + O(z^4) - - .. TODO:: - - Add a method to change the sparse/dense implementation. - """ if valuation is not None and valuation not in ZZ: raise ValueError("the valuation must be an integer") @@ -482,10 +477,6 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No # Handle when it is a lazy series if isinstance(x, self.Element): - # if x._coeff_stream._is_sparse is not self._sparse: - # TODO: Implement a way to make a self._sparse copy - # raise NotImplementedError("cannot convert between sparse and dense") - # If x is known to be 0 if isinstance(x._coeff_stream, Stream_zero): if not constant: @@ -1769,10 +1760,6 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No sage: g = L([1,3,5,7,9], 5, -1); g z^5 + 3*z^6 + 5*z^7 + 7*z^8 + 9*z^9 - z^10 - z^11 - z^12 + O(z^13) - .. TODO:: - - Add a method to change the sparse/dense implementation. - Finally, ``x`` can be a polynomial:: sage: P. = QQ[] @@ -1875,30 +1862,27 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No return self.element_class(self, coeff_stream) if isinstance(x, LazyPowerSeries): - # if x._coeff_stream._is_sparse is self._sparse: - stream = x._coeff_stream - if isinstance(stream, Stream_exact): - if self._arity == 1: - BR = self.base_ring() - else: - BR = self._laurent_poly_ring - coeffs = [BR(val) for val in stream._initial_coefficients] - valuation = stream._approximate_order - for i, c in enumerate(coeffs): - if c: - valuation += i - coeffs = coeffs[i:] - break - else: - valuation += len(coeffs) - coeffs = [] - return self(coeffs, - degree=stream._degree, - constant=self.base_ring()(stream._constant), - valuation=valuation) - return self.element_class(self, stream) - # TODO: Implement a way to make a self._sparse copy - # raise NotImplementedError("cannot convert between sparse and dense") + stream = x._coeff_stream + if isinstance(stream, Stream_exact): + if self._arity == 1: + BR = self.base_ring() + else: + BR = self._laurent_poly_ring + coeffs = [BR(val) for val in stream._initial_coefficients] + valuation = stream._approximate_order + for i, c in enumerate(coeffs): + if c: + valuation += i + coeffs = coeffs[i:] + break + else: + valuation += len(coeffs) + coeffs = [] + return self(coeffs, + degree=stream._degree, + constant=self.base_ring()(stream._constant), + valuation=valuation) + return self.element_class(self, stream) if callable(x) or isinstance(x, (GeneratorType, map, filter)): if valuation is None: @@ -2259,10 +2243,6 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No sage: f = L([m[1],m[2],m[3]], valuation=1); f m[1] + m[2] + m[3] - .. TODO:: - - Add a method to change the sparse/dense implementation. - Finally, ``x`` can be a symmetric function:: sage: m = SymmetricFunctions(ZZ).m() @@ -2340,10 +2320,7 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No return self.element_class(self, coeff_stream) if isinstance(x, self.Element): - # if x._coeff_stream._is_sparse is self._sparse: - return self.element_class(self, x._coeff_stream) - # TODO: Implement a way to make a self._sparse copy - # raise NotImplementedError("cannot convert between sparse and dense") + return self.element_class(self, x._coeff_stream) if self._arity == 1: def check_homogeneous_of_degree(f, d): @@ -2712,10 +2689,6 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No sage: D = LazyDirichletSeriesRing(QQ, 't') sage: D(m) -1/(2^t) - 1/(3^t) - 1/(5^t) + 1/(6^t) - 1/(7^t) + O(1/(9^t)) - - .. TODO:: - - Add a method to make a copy of ``self._sparse``. """ if isinstance(x, (list, tuple)): p = self._internal_poly_ring(x) From d5d32de70c8f8a2566f46cc837007cd7dfd9ff03 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Mon, 1 Aug 2022 15:12:30 -0500 Subject: [PATCH 200/632] Initial commit --- src/sage/combinat/diagram.py | 66 ++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 src/sage/combinat/diagram.py diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py new file mode 100644 index 00000000000..3ebe236b9a9 --- /dev/null +++ b/src/sage/combinat/diagram.py @@ -0,0 +1,66 @@ +r""" +Combinatorial diagrams + +A combinatorial diagram is a collection of cells `(i,j)` indexed by pairs of +natural numbers. The positions are indexed by rows and columns. For example, +a Ferrer's diagram is a diagram obtained from a partition +`\lambda = (\lambda_0, \lambda_1, \ldots, \lambda_\ell)` where the cells are +in rows `i` for `0 \leq i \leq \ell` and the cells in row `i` consist of +`(i,j)` for `0 \leq j < \lambda_i`. In English notation, the indices are read +from top left to bottom right as in a matrix. + +Indexing conventions are the same as +:class:`~sage.combinat.partition.Partition`. + +EXAMPLES: + + +Diagrams can be created by: + - explictly passing an iterable of all the cells + - providing a :class:`~sage.combinat.partition.Partition`, + in which case the diagram is the corresponding (English notation) Ferrers + diagram (see also :meth:`~sage.combination.partition.Partition.ferrers_diagram`) + - providing a :class:`~sage.combinat.permutation.Permutation`, in which + case the diagram is the corresponding Rothe diagram. + - providing a list of *death rays* which are cells `(i_0,j_0)` not present in the + diagram and have the property that there are no cells in the diagram that + have the form `(i,j_0)` with `i > i_0` and no cells in the diagram that + have the form `(i_0, j)` with `j > j_0`. The death rays kill all of the + cells to right and below of them. + + +Passing a list of all cells:: + + sage: from sage.combinat.diagram import Diagram, Diagrams + sage: cells = [(0,0), (0,1), (1,0), (1,1), (4,4), (4,5), (4,6), (5,4), (7, 6)] + sage: D = Diagram(cells); D + [(0,0), (0,1), (1,0), (1,1), (4,4), (4,5), (4,6), (5,4), (7, 6)] + sage: pp(D) + + +AUTHORS: + +- Trevor K. Karn (2022-08-01): initial version +""" + +# **************************************************************************** +# Copyright (C) 2013 Trevor K. Karn +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +class Diagram(CombinatorialElement): + def __init__(self, parent, cells): + self._cells = cells + +class Diagrams(UniqueRepresentation, Parent): + def __init__(self): + pass + +class Diagrams_all(Diagrams): + def __init__(self): + pass From 9830fa34ea9334a6c91b7493cf2256f765a7165a Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Tue, 2 Aug 2022 09:16:17 -0500 Subject: [PATCH 201/632] Implement parent framework --- src/sage/combinat/diagram.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index 3ebe236b9a9..78c7471c452 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -31,11 +31,10 @@ Passing a list of all cells:: - sage: from sage.combinat.diagram import Diagram, Diagrams + sage: from sage.combinat.diagram import Diagram sage: cells = [(0,0), (0,1), (1,0), (1,1), (4,4), (4,5), (4,6), (5,4), (7, 6)] sage: D = Diagram(cells); D [(0,0), (0,1), (1,0), (1,1), (4,4), (4,5), (4,6), (5,4), (7, 6)] - sage: pp(D) AUTHORS: @@ -53,14 +52,23 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -class Diagram(CombinatorialElement): - def __init__(self, parent, cells): +from sage.categories.sets_cat import Sets +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.list_clone import ClonableArray +from sage.structure.parent import Parent + +class Diagram(ClonableArray): + def __init__(self, cells): self._cells = cells class Diagrams(UniqueRepresentation, Parent): - def __init__(self): - pass -class Diagrams_all(Diagrams): def __init__(self): - pass + + Parent.__init__(self) + + def _element_constructor_(self, cells) + + return self.element_class(self, cells) + + Element = Diagram \ No newline at end of file From 4e22f9838f6aacb99f3d06c59b21f6ea6ba2a803 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Tue, 2 Aug 2022 12:36:18 -0500 Subject: [PATCH 202/632] Add parent/element framework of northwest diagrams --- src/sage/combinat/diagram.py | 393 ++++++++++++++++++++++++++++++++--- 1 file changed, 369 insertions(+), 24 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index 78c7471c452..cef520e0e73 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -14,28 +14,26 @@ EXAMPLES: - -Diagrams can be created by: - - explictly passing an iterable of all the cells - - providing a :class:`~sage.combinat.partition.Partition`, - in which case the diagram is the corresponding (English notation) Ferrers - diagram (see also :meth:`~sage.combination.partition.Partition.ferrers_diagram`) - - providing a :class:`~sage.combinat.permutation.Permutation`, in which - case the diagram is the corresponding Rothe diagram. - - providing a list of *death rays* which are cells `(i_0,j_0)` not present in the - diagram and have the property that there are no cells in the diagram that - have the form `(i,j_0)` with `i > i_0` and no cells in the diagram that - have the form `(i_0, j)` with `j > j_0`. The death rays kill all of the - cells to right and below of them. - - -Passing a list of all cells:: +To create an arbirtrary diagram, pass a list of all cells:: sage: from sage.combinat.diagram import Diagram sage: cells = [(0,0), (0,1), (1,0), (1,1), (4,4), (4,5), (4,6), (5,4), (7, 6)] sage: D = Diagram(cells); D - [(0,0), (0,1), (1,0), (1,1), (4,4), (4,5), (4,6), (5,4), (7, 6)] - + [(0, 0), (0, 1), (1, 0), (1, 1), (4, 4), (4, 5), (4, 6), (5, 4), (7, 6)] + +We can visualize the diagram by printing ``O``'s and ``.``'s. ``O``'s are +present in the cells which are present in the diagram and a ``.`` represents +the absence of a cell in the diagram:: + + sage: D.pp() + O O . . . . . + O O . . . . . + . . . . . . . + . . . . . . . + . . . . O O O + . . . . O . . + . . . . . . . + . . . . . . O AUTHORS: @@ -52,23 +50,370 @@ # https://www.gnu.org/licenses/ # **************************************************************************** + from sage.categories.sets_cat import Sets +from sage.sets.non_negative_integers import NonNegativeIntegers as NN +from sage.combinat.partition import Partition +from sage.combinat.permutation import Permutation from sage.structure.unique_representation import UniqueRepresentation from sage.structure.list_clone import ClonableArray from sage.structure.parent import Parent class Diagram(ClonableArray): - def __init__(self, cells): - self._cells = cells + r""" + EXAMPLES:: + + sage: + """ + @staticmethod + def __classcall_private__(self, cells, **kwargs): + r""" + Normalize the input so that it lives in the correct parent. + + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagram + sage: D = Diagram([(0,0), (0,3), (2,2), (2,4)]) + sage: D.parent() + Combinatorial diagrams + """ + return Diagrams()(cells, **kwargs) + + def __init__(self, cells, **kwargs): + r""" + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagram + sage: D1 = Diagram([(0,2),(0,3),(1,1),(3,2)]) + sage: D1.cells() + [(0, 2), (0, 3), (1, 1), (3, 2)] + sage: D1.n_rows() + 4 + sage: D1.n_cols() + 4 + + We can specify the number of rows and columns explicitly, + in case they are supposed to be empty:: + + sage: D2 = Diagram([(0,2),(0,3),(1,1),(3,2)], n_cols=5) + sage: D2.cells() + [(0, 2), (0, 3), (1, 1), (3, 2)] + sage: D2.n_cols() + 5 + sage: D2.pp() + . . O O . + . O . . . + . . . . . + . . O . . + """ + self._cells = {c: True for c in cells} + self._n_rows = kwargs.pop('n_rows', max(c[0] for c in self._cells) + 1) + self._n_cols = kwargs.pop('n_cols', max(c[1] for c in self._cells) + 1) + + ClonableArray.__init__(self, Diagrams(), cells, check=False) + + def _hash_(self): + r""" + TESTS:: + + sage: from sage.combinat.diagram import Diagram + sage: D = Diagram([(0,2),(0,3),(1,1),(3,2)]) + sage: hash(D) + 5125392318921082108 + """ + return hash(tuple(sorted(self._cells))) + + def __contains__(self, other): + r""" + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagram + sage: D = Diagram([(0,2),(0,3),(1,1),(3,2)]) + sage: (1, 2) in D + False + sage: (0, 2) in D + True + sage: (2, 1) in D + False + sage: (3, 2) in D + True + """ + return other in self._cells + + def __repr__(self): + r""" + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagram + sage: D = Diagram([(0,2),(0,3),(1,1),(3,2)]); D + [(0, 2), (0, 3), (1, 1), (3, 2)] + """ + return str(list(self._cells)) + + def pp(self): + r""" + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagram + sage: Diagram([(0,0), (0,3), (2,2), (2,4)]).pp() + O . . O . + . . . . . + . . O . O + """ + output_str = '' + + for i in range(self._n_rows): + for j in range(self._n_cols): + if (i, j) in self: + output_str += 'O ' + else: + output_str += '. ' + output_str += '\n' + + print(output_str) + + def n_rows(self): + r""" + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagram + sage: D1 = Diagram([(0,2),(0,3),(1,1),(3,2)]) + sage: D1.n_rows() + 4 + """ + + return self._n_rows + + def n_cols(self): + r""" + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagram + sage: D1 = Diagram([(0,2),(0,3),(1,1),(3,2)]) + sage: D1.n_cols() + 4 + """ + return self._n_cols + + def cells(self): + r""" + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagram + sage: D1 = Diagram([(0,2),(0,3),(1,1),(3,2)]) + sage: D1.cells() + [(0, 2), (0, 3), (1, 1), (3, 2)] + """ + return list(self._cells.keys()) + + def n_cells(self): + r""" + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagram + sage: D1 = Diagram([(0,2),(0,3),(1,1),(3,2)]) + sage: D1.n_cells() + 4 + """ + return len(self._cells) + + def check(self): + r""" + Check that this is a valid diagram by checking that it is an iterable + of length-two tuples of integers. + + .. WARNING:: + + This method is required for ``Diagram`` to be a subclass of + :class:`~sage.structure.list_clone.ClonableArray`, however the check + is *not* automatically performed upon creation of the element. + + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagram + sage: D = Diagram([(0,0), (0,3), (2,2), (2,4)]) + sage: D.check() + sage: notD = Diagram([(0,1,2), (0,1), (2,2)]) + sage: notD.check() + Traceback (most recent call last): + ... + AssertionError + """ + assert all(isinstance(c, tuple) and len(c) == 2 for c in self._cells) class Diagrams(UniqueRepresentation, Parent): + r""" + The class of combinatorial diagrams. + A *combinatorial diagram* is a set of cells indexed by pairs of natural + numbers. + """ def __init__(self): + r""" + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagrams + sage: Dgms = Diagrams(); Dgms + Combinatorial diagrams + + TESTS:: + + sage: TestSuite(Dgms).run() + """ + + Parent.__init__(self, category=Sets()) + + def __repr__(self): + r""" + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagrams + sage: Dgms = Diagrams(); Dgms + Combinatorial diagrams + """ + return 'Combinatorial diagrams' + + def _element_constructor_(self, cells, **kwargs): + r""" + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagrams + sage: Dgms = Diagrams() + sage: Dgms([(0,1),(2,2)]).pp() + . O . + . . . + . . O + + TESTS:: + + sage: TestSuite(Dgms).run() + """ + return self.element_class(cells, **kwargs) + + def _an_element_(self): + r""" + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagrams + sage: Dgms = Diagrams() + sage: D = Dgms.an_element(); D + [(0, 2), (1, 1), (2, 3)] + sage: D.pp() + . . O . + . O . . + . . . O + """ + return self([(0, 2), (1, 1), (2, 3)]) + + Element = Diagram + +#################### +# Northwest diagrams +#################### + +class NorthwestDiagram(Diagram): + r""" + A diagram is a set of cells indexed by natural numbers. Such a diagram + has the *northwest property* if the presence of cells `(i1, j1)` and + `(i2, j2)` implies the presence of the cell + `(\min(i1, i2), \min(j1, j2))`. Diagrams with the northwest property are + called *northwest diagrams*. + + For general diagrams see :class:`Diagram`. + """ + @staticmethod + def __classcall_private__(self, cells, **kwargs): + r""" + """ + return NorthwestDiagrams()(cells, **kwargs) + + def check(self): + r""" + A diagram has the northwest property if the presence of cells + `(i1, j1)` and `(i2, j2)` implies the presence of the cell + `(min(i1, i2), min(j1, j2))` + + .. WARNING:: + + This method is required for `Diagram` to be a subclass of + :class:`~sage.structure.list_clone.ClonableArray`, however the check + is *not* automatically performed upon creation of the element. + """ + from itertools import combinations + assert all((min(i1,i2), min(j1,j2)) in self + for (i1, j1), (i2, j2) in combinations(self._cells, 2)) + +class NorthwestDiagrams(Diagrams): + r""" + The class of northwest diagrams. + + A diagram has the *northwest property* if the presence of cells + `(i1, j1)` and `(i2, j2)` implies the presence of the cell + `(min(i1, i2), min(j1, j2))`. For the class of general diagrams, see + :class:`Diagrams`. + + One may create an instance of a northwest diagram either by directly + calling :class:`NorthwestDiagram` or by creating an instance of the + parent class `NorthwestDiagrams` (with an `s`) and calling it on a `list` + of tuples consisting of the coordinates of the diagram. + + EXAMPLES:: + + Additionally, there are natural constructions of a northwest diagram + given the data of a permutation (Rothe diagrams are the protypical example + of northwest diagrams), or the data of a partition of an integer, or a + skew partition. + + The Rothe diagram `D(\omega)` of a permutation `\omega` is specified by + the cells + + .. MATH:: + + \{ (i, j) : i < \omega^{-1}(j) \text{ and } j < \omega(i)} + + We can construct one by calling :meth:`rothe_diagram` method on the parent + class :class:`NorthwestDiagrams`. + + EXAMPLES:: + + sage: from sage.combinat.diagram import NorthwestDiagrams + sage: NWDgms = NorthwestDiagrams + + To turn a Ferrers diagram into a northwest diagram, we may call the + :meth:`ferrers_diagram` method. This will return a Ferrer's diagram in the + parent of all northwest diagrams. For many use-cases it is probably better + to get Ferrer's diagrams by the corresponding method on partitons, namely + :meth:`sage.combinat.partitions.Partitions.ferrers_diagram`. + + It is also possible to turn a Ferrers diagram of a skew partition into a + northwest diagram, altough it is more subtle than just using the skew + diagram itself. + """ + + def __repr__(self): + r""" + EXAMPLES:: + + sage: from sage.combinat.diagram import NorthwestDiagrams + sage: NWDgms = NorthwestDiagrams(); NWDgms + Combinatorial northwest diagrams + """ + return 'Combinatorial northwest diagrams' - Parent.__init__(self) + def _an_element_(self): + r""" + EXAMPLES:: - def _element_constructor_(self, cells) + sage: from sage.combinat.diagram import NorthwestDiagrams + sage: NWDgms = NorthwestDiagrams() + sage: NWD = NWDgms.an_element(); NWD + [(0, 1), (0, 2), (1, 1), (2, 3)] + sage: NWD.pp() + . O O . + . O . . + . . . O + """ + return self([(0, 1), (0, 2), (1, 1), (2, 3)]) - return self.element_class(self, cells) - Element = Diagram \ No newline at end of file + Element = NorthwestDiagram \ No newline at end of file From 9946e835c8726d5108e4cafa61d38d1479990650 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Wed, 3 Aug 2022 12:24:38 -0500 Subject: [PATCH 203/632] Add outside_corners to SkewPartition --- src/sage/combinat/skew_partition.py | 36 ++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/skew_partition.py b/src/sage/combinat/skew_partition.py index ea6b3b52057..dcb376b0a90 100644 --- a/src/sage/combinat/skew_partition.py +++ b/src/sage/combinat/skew_partition.py @@ -126,6 +126,7 @@ - Mike Hansen: Initial version - Travis Scrimshaw (2013-02-11): Factored out ``CombinatorialClass`` +- Trevor K. Karn (2022-08-03): Add ``outside_corners`` """ #***************************************************************************** # Copyright (C) 2007 Mike Hansen , @@ -739,7 +740,14 @@ def conjugate(self): def outer_corners(self): """ - Return a list of the outer corners of ``self``. + Return a list of the outer corners of ``self``. These are corners + which are contained inside of the shape. For the corners which are + outside of the shape, use :meth:`outside_corners`. + + .. SEEALSO:: + + - :meth:`sage.combinat.skew_partition.SkewPartition.outside_corners` + - :meth:`sage.combinat.partition.Partition.outside_corners` EXAMPLES:: @@ -1209,6 +1217,32 @@ def jacobi_trudi(self): m.append(row) return H(m) + def outside_corners(self): + r""" + Return the outside corners of ``self``. + + The outside corners are corners which are outside of the shape. This + should not be confused with :meth:`outer_corners` which consists of + corners inside the shape. It returns a result analogous to the + ``.outside_corners()`` method on (non-skew) ``Partitions``. + + .. SEEALSO:: + + - :meth:`sage.combinat.skew_partition.SkewPartition.outer_corners` + - :meth:`sage.combinat.partition.Partition.outside_corners` + + EXAMPLES:: + + sage: mu = SkewPartition([[3,2,1],[2,1]]) + sage: mu.pp() + * + * + * + sage: mu.outside_corners() + [(0, 3), (1, 2), (2, 1), (3, 0)] + """ + return self.outer().outside_corners() + def row_lengths_aux(skp): """ EXAMPLES:: From 40656a13e1293dac4d1c11a218cabefbf86f4251 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Wed, 3 Aug 2022 20:04:52 -0500 Subject: [PATCH 204/632] Clarify some of the language around corners' --- src/sage/combinat/partition.py | 8 ++++++++ src/sage/combinat/skew_partition.py | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index f6cf8e4f364..b1f9810beaf 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -3957,6 +3957,10 @@ def corners(self): where `i` and `j` are the coordinates of the respective corner. The coordinates are counted from `0`. + .. NOTE:: + + This is referred to as an "inner corner" in [Sag2001]_. + EXAMPLES:: sage: Partition([3,2,1]).corners() @@ -4029,6 +4033,10 @@ def outside_corners(self): where `i` and `j` are the coordinates of the respective corner. The coordinates are counted from `0`. + .. NOTE:: + + These are called "outer corners" in [Sag2001]_. + EXAMPLES:: sage: Partition([2,2,1]).outside_corners() diff --git a/src/sage/combinat/skew_partition.py b/src/sage/combinat/skew_partition.py index dcb376b0a90..3f60e008c2e 100644 --- a/src/sage/combinat/skew_partition.py +++ b/src/sage/combinat/skew_partition.py @@ -744,6 +744,13 @@ def outer_corners(self): which are contained inside of the shape. For the corners which are outside of the shape, use :meth:`outside_corners`. + .. WARNING:: + + In the case that `self` is an honest (rather than skew) partition, + these are the :meth:`~sage.combinat.partition.Partition.corners` + of the outer partition. In the language of [Sag2001]_ these would + be the "inner corners" of the outer partition. + .. SEEALSO:: - :meth:`sage.combinat.skew_partition.SkewPartition.outside_corners` From 3e994923efbf2934d5657b44381e5e055e7986ca Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Wed, 3 Aug 2022 20:06:16 -0500 Subject: [PATCH 205/632] Add backward_slide (and alias reverse_slide) --- src/sage/combinat/skew_tableau.py | 122 ++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/src/sage/combinat/skew_tableau.py b/src/sage/combinat/skew_tableau.py index 32b116784dd..9d45cdc69f4 100644 --- a/src/sage/combinat/skew_tableau.py +++ b/src/sage/combinat/skew_tableau.py @@ -907,6 +907,128 @@ def slide(self, corner=None, return_vacated=False): return (SkewTableau(new_st), (spotl, spotc)) return SkewTableau(new_st) + def backward_slide(self, corner=None): + r""" + Apply a backward jeu de taquin slide on the specified outside corner. + + Backward jeu de taquin slides are defined in section 3.7 of + [Sag2001]_. + + .. WARNING:: + + The :meth:`inner_corners` and :meth:`outer_corners` are the + :meth:`sage.combinat.partition.Partition.corners` of the inner and + outer partitions of the skew shape. They are different from the + inner/outer corners defined in [Sag2001]_. + + The "inner corners" of [Sag2001]_ may be found by calling + :meth:`outer_corners`. The "outer corners" of [Sag2001]_ may be + found by calling ``self.outer_shape().outside_corners()``. + + EXAMPLES:: + + sage: T = SkewTableaux()([[2, 2], [4, 4], [5]]) + sage: Tableaux.options.display='array' + sage: Q = T.backward_slide(); Q + . 2 2 + 4 4 + 5 + sage: Q.backward_slide((1, 2)) + . 2 2 + . 4 4 + 5 + sage: Q.reverse_slide((1, 2)) == Q.backward_slide((1, 2)) + True + + TESTS:: + + sage: T = SkewTableaux()([[2, 2], [4, 4], [5]]) + sage: Q = T.backward_slide((0, 2)) + sage: Q.backward_slide((2,1)) + . 2 2 + . 4 + 4 5 + sage: Q.backward_slide((3,0)) + . 2 2 + . 4 + 4 + 5 + sage: Q = T.backward_slide((2,1)); Q + . 2 + 2 4 + 4 5 + sage: Q.backward_slide((3,0)) + . 2 + . 4 + 2 5 + 4 + sage: Q = T.backward_slide((3,0)); Q + . 2 + 2 4 + 4 + 5 + sage: Q.backward_slide((4,0)) + . 2 + . 4 + 2 + 4 + 5 + sage: Tableaux.options.display='list' + """ + new_st = self.to_list() + inner_outside_corners = self.inner_shape().outside_corners() + outer_outisde_corners = self.outer_shape().outside_corners() + if corner is not None: + if tuple(corner) not in outer_outisde_corners: + raise ValueError("corner must be an outside corner \ + of the outer shape") + else: + if not outer_outisde_corners: + return self + else: + corner = outer_outisde_corners[0] + + i, j = corner + + # add the empty cell + # the column only matters if it is zeroth column, in which + # case we need to add a new row. + if not j: + new_st.append(list()) + new_st[i].append(None) + + while (i, j) not in inner_outside_corners: + # get the value of the cell above the temporarily empty cell (if + # it exists) + if i > 0: + P_up = new_st[i-1][j] + else: + P_up = None + + # get the value of the cell to the left of the temp. empty cell + # (if it exists) + if j > 0: + P_left = new_st[i][j-1] + else: + P_left = None + + # get the next cell + if P_left > P_up: + new_st[i][j] = P_left + i, j = (i, j-1) + else: # if they are equal, we slide up + new_st[i][j] = P_up + i, j = (i-1, j) + + # We don't need to reset the intermediate cells inside the loop + # because the conditional above will continue to overwrite it until + # the while loop terminates. We do need to reset it at the end. + new_st[i][j] = None + + return SkewTableau(new_st) + + reverse_slide = backward_slide + def rectify(self, algorithm=None): """ Return a :class:`StandardTableau`, :class:`SemistandardTableau`, From 19380a6497053a863b6843db7b7bf458b67aa12e Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Wed, 3 Aug 2022 22:23:24 -0500 Subject: [PATCH 206/632] First draft of peelables algorithm --- src/sage/combinat/diagram.py | 64 +++++++++++++++++++++++++++++++ src/sage/combinat/skew_tableau.py | 19 +++++---- 2 files changed, 76 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index cef520e0e73..0a0341005f1 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -58,6 +58,8 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.structure.list_clone import ClonableArray from sage.structure.parent import Parent +from sage.combinat.tableau import Tableau +from sage.combinat.skew_tableau import SkewTableaux class Diagram(ClonableArray): r""" @@ -109,6 +111,8 @@ def __init__(self, cells, **kwargs): self._cells = {c: True for c in cells} self._n_rows = kwargs.pop('n_rows', max(c[0] for c in self._cells) + 1) self._n_cols = kwargs.pop('n_cols', max(c[1] for c in self._cells) + 1) + self._n_nonempty_rows = len(set(i for i in self._cells)) + self._n_nonempty_cols = len(set(j for j in self._cells)) ClonableArray.__init__(self, Diagrams(), cells, check=False) @@ -343,6 +347,64 @@ def check(self): assert all((min(i1,i2), min(j1,j2)) in self for (i1, j1), (i2, j2) in combinations(self._cells, 2)) + def peelable_tableaux(self): + r""" + Given a northwest diagram `D`, a tableau `T` is said to be + `D`-peelable if... + + EXAMPLES: + + If the diagram is only one column, there is only one peelable tableau + + sage: from sage.combinat.diagram import NorthwestDiagram + sage: NWD = NorthwestDiagram([(0,0), (2,0)]) + sage: NWD.peelable_tableaux() + {[[0], [2]]} + + .. ALGORITHM:: + + This implementation uses the algorithm suggested in remark 25 + of [...]_. + """ + # if there is a single column in the diagram then there is only + # one posslbe peelable tableau. + if self._n_nonempty_cols == 1: + return {Tableau([[i+1] for i,j in self._cells])} + + first_col = min(j for i,j in self._cells) + + # get the diagram without the first column + Dhat = NorthwestDiagram([c for c in self._cells if c[1] != first_col]) + + k = self.n_cells() - Dhat.n_cells() + + peelables = set() + + for Q in Dhat.peelable_tableaux(): + print(Q) + # get the vertical strips + mu = Q.shape() + vertical_strips = mu.add_vertical_border_strip(k) + for s in vertical_strips: + sQ = SkewTableaux()(Q) # sQ is skew - get it? + new_cells = (s/mu).cells() + + # perform the jeu de taquin slides + for c in new_cells: + sQ = sQ.backward_slide(c) + print(sQ) + + # create the new tableau by filling the columns + sQ_new = sQ.to_list() + for n, (i, j) in enumerate(sQ.cells_containing(None)): + sQ_new[i][j] = n + 1 + + T = Tableau(sQ_new) + if T.is_column_strict(): + peelables.add(T) + + return peelables + class NorthwestDiagrams(Diagrams): r""" The class of northwest diagrams. @@ -412,6 +474,8 @@ def _an_element_(self): . O O . . O . . . . . O + sage: NWD.parent() is NWDgms + True """ return self([(0, 1), (0, 2), (1, 1), (2, 3)]) diff --git a/src/sage/combinat/skew_tableau.py b/src/sage/combinat/skew_tableau.py index 9d45cdc69f4..8c7ee144fcc 100644 --- a/src/sage/combinat/skew_tableau.py +++ b/src/sage/combinat/skew_tableau.py @@ -1013,13 +1013,18 @@ def backward_slide(self, corner=None): P_left = None # get the next cell - if P_left > P_up: - new_st[i][j] = P_left - i, j = (i, j-1) - else: # if they are equal, we slide up - new_st[i][j] = P_up - i, j = (i-1, j) - + try: + if P_left > P_up: + new_st[i][j] = P_left + i, j = (i, j-1) + else: # if they are equal, we slide up + new_st[i][j] = P_up + i, j = (i-1, j) + except TypeError: + # if the addition of the original corner + # is automatically a skew partition, just + # return it. + break # We don't need to reset the intermediate cells inside the loop # because the conditional above will continue to overwrite it until # the while loop terminates. We do need to reset it at the end. From 513948fe63abe4c63c7dd9d80328cb1b4618f24a Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Thu, 4 Aug 2022 17:29:22 -0500 Subject: [PATCH 207/632] Peelable tableaux pass all tests --- src/sage/combinat/diagram.py | 157 +++++++++++++++++++++++++----- src/sage/combinat/skew_tableau.py | 10 ++ 2 files changed, 142 insertions(+), 25 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index 0a0341005f1..3165d13112f 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -3,7 +3,7 @@ A combinatorial diagram is a collection of cells `(i,j)` indexed by pairs of natural numbers. The positions are indexed by rows and columns. For example, -a Ferrer's diagram is a diagram obtained from a partition +a Ferrer's diagram is a diagram obtained from a partition `\lambda = (\lambda_0, \lambda_1, \ldots, \lambda_\ell)` where the cells are in rows `i` for `0 \leq i \leq \ell` and the cells in row `i` consist of `(i,j)` for `0 \leq j < \lambda_i`. In English notation, the indices are read @@ -21,7 +21,7 @@ sage: D = Diagram(cells); D [(0, 0), (0, 1), (1, 0), (1, 1), (4, 4), (4, 5), (4, 6), (5, 4), (7, 6)] -We can visualize the diagram by printing ``O``'s and ``.``'s. ``O``'s are +We can visualize the diagram by printing ``O``'s and ``.``'s. ``O``'s are present in the cells which are present in the diagram and a ``.`` represents the absence of a cell in the diagram:: @@ -34,7 +34,7 @@ . . . . O . . . . . . . . . . . . . . . O - + AUTHORS: - Trevor K. Karn (2022-08-01): initial version @@ -111,8 +111,8 @@ def __init__(self, cells, **kwargs): self._cells = {c: True for c in cells} self._n_rows = kwargs.pop('n_rows', max(c[0] for c in self._cells) + 1) self._n_cols = kwargs.pop('n_cols', max(c[1] for c in self._cells) + 1) - self._n_nonempty_rows = len(set(i for i in self._cells)) - self._n_nonempty_cols = len(set(j for j in self._cells)) + self._n_nonempty_rows = len(set(i for i,j in self._cells)) + self._n_nonempty_cols = len(set(j for i,j in self._cells)) ClonableArray.__init__(self, Diagrams(), cells, check=False) @@ -228,7 +228,7 @@ def check(self): .. WARNING:: - This method is required for ``Diagram`` to be a subclass of + This method is required for ``Diagram`` to be a subclass of :class:`~sage.structure.list_clone.ClonableArray`, however the check is *not* automatically performed upon creation of the element. @@ -237,11 +237,6 @@ def check(self): sage: from sage.combinat.diagram import Diagram sage: D = Diagram([(0,0), (0,3), (2,2), (2,4)]) sage: D.check() - sage: notD = Diagram([(0,1,2), (0,1), (2,2)]) - sage: notD.check() - Traceback (most recent call last): - ... - AssertionError """ assert all(isinstance(c, tuple) and len(c) == 2 for c in self._cells) @@ -250,7 +245,7 @@ class Diagrams(UniqueRepresentation, Parent): The class of combinatorial diagrams. A *combinatorial diagram* is a set of cells indexed by pairs of natural - numbers. + numbers. """ def __init__(self): r""" @@ -319,7 +314,7 @@ class NorthwestDiagram(Diagram): r""" A diagram is a set of cells indexed by natural numbers. Such a diagram has the *northwest property* if the presence of cells `(i1, j1)` and - `(i2, j2)` implies the presence of the cell + `(i2, j2)` implies the presence of the cell `(\min(i1, i2), \min(j1, j2))`. Diagrams with the northwest property are called *northwest diagrams*. @@ -329,8 +324,14 @@ class NorthwestDiagram(Diagram): def __classcall_private__(self, cells, **kwargs): r""" """ + # TODO: Assert that cells is sorted in lex order to speed up lookup. return NorthwestDiagrams()(cells, **kwargs) + def __init__(self, cells, **kwargs): + + Diagram.__init__(self, cells, **kwargs) + ClonableArray.__init__(self, NorthwestDiagrams(), cells, check=False) + def check(self): r""" A diagram has the northwest property if the presence of cells @@ -339,9 +340,9 @@ def check(self): .. WARNING:: - This method is required for `Diagram` to be a subclass of + This method is required for `Diagram` to be a subclass of :class:`~sage.structure.list_clone.ClonableArray`, however the check - is *not* automatically performed upon creation of the element. + is *not* automatically performed upon creation of the element. """ from itertools import combinations assert all((min(i1,i2), min(j1,j2)) in self @@ -354,18 +355,123 @@ def peelable_tableaux(self): EXAMPLES: - If the diagram is only one column, there is only one peelable tableau + If the diagram is only one column, there is only one peelable tableau:: sage: from sage.combinat.diagram import NorthwestDiagram sage: NWD = NorthwestDiagram([(0,0), (2,0)]) sage: NWD.peelable_tableaux() - {[[0], [2]]} + {[[1], [3]]} + + From [...]_ we know that there is only one peelable tableau for the + Rothe diagram of the permutation (in one line notation) `251643`:: + + sage: D = NorthwestDiagram([(1, 2), (1, 3), (3, 2), (3, 3), (4, 2)]) + sage: D.pp() + . . . . + . . O O + . . . . + . . O O + . . O . + + sage: D.peelable_tableaux() + {[[2, 2], [4, 4], [5]]} + + Here are all the intermediate steps to compute the peelables for the + Rothe diagram of (in one-line notation) `64817235`. They are listed from + deepest in the recursion to the final step. The recursion has depth five + in this case so we will label the intermediate tableaux by `D_i` where + `i` is the step in the recursion at which they appear. + + Start with the one that has a single column:: + + sage: D5 = NorthwestDiagram([(2,0)]); D5.pp() + . + . + O + sage: D5.peelable_tableaux() + {[[3]]} + + Now we know all of the `D_5` peelables, so we can compute the `D_4` + peelables. + + sage: D4 = NorthwestDiagram([(0, 0), (2,0), (4, 0), (2, 2)]) + sage: D4.pp() + O . . + . . . + O . O + . . . + O . . + + sage: D4.peelable_tableaux() + {[[1, 3], [3], [5]]} + + There is only one `D_4` peelable, so we can compute the `D_3` + peelables:: + + sage: D3 = NorthwestDiagram([(0,0), (0,1), (2, 1), (2, 3), (4,1)]) + sage: D3.pp() + O O . . + . . . . + . O . O + . . . . + . O . . + + sage: D3.peelable_tableaux() + {[[1, 1], [3, 3], [5]], [[1, 1, 3], [3], [5]]} + + Now compute the `D_2` peelables:: + + sage: cells = [(0,0), (0,1), (0,2), (1,0), (2,0), (2,2), (2,4), + ....: (4,0), (4,2)] + sage: D2 = NorthwestDiagram(cells); D2.pp() + O O O . . + O . . . . + O . O . O + . . . . . + O . O . . + + sage: D2.peelable_tableaux() + {[[1, 1, 1], [2, 3, 3], [3, 5], [5]], + [[1, 1, 1, 3], [2, 3], [3, 5], [5]]} + + And the `D_1` peelables:: + + sage: cells = [(0,0), (0,1), (0,2), (0,3), (1,0), (1,1), (2,0), + ....: (2,1), (2,3), (2,5), (4,0), (4,1), (4,3)] + sage: D1 = NorthwestDiagram(cells); D1.pp() + O O O O . . + O O . . . . + O O . O . O + . . . . . . + O O . O . . + + sage: D1.peelable_tableaux() + {[[1, 1, 1, 1], [2, 2, 3, 3], [3, 3, 5], [5, 5]], + [[1, 1, 1, 1, 3], [2, 2, 3], [3, 3, 5], [5, 5]]} + + Which we can use to get the `D` peelables:: + + sage: cells = [(0,0), (0,1), (0,2), (0,3), (0,4), + ....: (1,0), (1,1), (1,2), + ....: (2,0), (2,1), (2,2), (2,4), (2,6), + ....: (4,1), (4,2), (4,4)] + sage: D = NorthwestDiagram(cells); D.pp() + O O O O O . . + O O O . . . . + O O O . O . O + . . . . . . . + . O O . O . . + sage: D.peelable_tableaux() .. ALGORITHM:: This implementation uses the algorithm suggested in remark 25 of [...]_. """ + # TODO: There is a condition on the first column (if the rows in Dhat + # are a subset of the rows in the first column) which simplifies the + # description without performing JDT, so we should implement that + # if there is a single column in the diagram then there is only # one posslbe peelable tableau. if self._n_nonempty_cols == 1: @@ -373,31 +479,32 @@ def peelable_tableaux(self): first_col = min(j for i,j in self._cells) + # TODO: The next two lines of code could be optimized by only + # looping over self._cells once (rather than two separate times) # get the diagram without the first column Dhat = NorthwestDiagram([c for c in self._cells if c[1] != first_col]) + # get the values from the first column + new_vals = sorted(i + 1 for i, j in self._cells if j == first_col) + k = self.n_cells() - Dhat.n_cells() peelables = set() for Q in Dhat.peelable_tableaux(): - print(Q) # get the vertical strips mu = Q.shape() vertical_strips = mu.add_vertical_border_strip(k) for s in vertical_strips: sQ = SkewTableaux()(Q) # sQ is skew - get it? new_cells = (s/mu).cells() - # perform the jeu de taquin slides for c in new_cells: sQ = sQ.backward_slide(c) - print(sQ) - # create the new tableau by filling the columns sQ_new = sQ.to_list() - for n, (i, j) in enumerate(sQ.cells_containing(None)): - sQ_new[i][j] = n + 1 + for n in range(k): + sQ_new[n][0] = new_vals[n] T = Tableau(sQ_new) if T.is_column_strict(): @@ -449,7 +556,7 @@ class :class:`NorthwestDiagrams`. It is also possible to turn a Ferrers diagram of a skew partition into a northwest diagram, altough it is more subtle than just using the skew - diagram itself. + diagram itself. """ def __repr__(self): @@ -480,4 +587,4 @@ def _an_element_(self): return self([(0, 1), (0, 2), (1, 1), (2, 3)]) - Element = NorthwestDiagram \ No newline at end of file + Element = NorthwestDiagram diff --git a/src/sage/combinat/skew_tableau.py b/src/sage/combinat/skew_tableau.py index 8c7ee144fcc..20add441c6b 100644 --- a/src/sage/combinat/skew_tableau.py +++ b/src/sage/combinat/skew_tableau.py @@ -7,6 +7,7 @@ - Mike Hansen: Initial version - Travis Scrimshaw, Arthur Lubovsky (2013-02-11): Factored out ``CombinatorialClass`` +- Trevor K. Karn (2022-08-03): added `backward_slide` """ # **************************************************************************** # Copyright (C) 2007 Mike Hansen , @@ -940,6 +941,15 @@ def backward_slide(self, corner=None): sage: Q.reverse_slide((1, 2)) == Q.backward_slide((1, 2)) True + sage: T = SkewTableaux()([[1, 3],[3],[5]]); T + 1 3 + 3 + 5 + sage: T.reverse_slide((1,1)) + . 1 + 3 3 + 5 + TESTS:: sage: T = SkewTableaux()([[2, 2], [4, 4], [5]]) From 676747900bd264f05300a42bb605ab423936b549 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Fri, 5 Aug 2022 08:06:36 -0500 Subject: [PATCH 208/632] Fix bug in JDT --- src/sage/combinat/skew_tableau.py | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/sage/combinat/skew_tableau.py b/src/sage/combinat/skew_tableau.py index 20add441c6b..0578404a643 100644 --- a/src/sage/combinat/skew_tableau.py +++ b/src/sage/combinat/skew_tableau.py @@ -1007,34 +1007,33 @@ def backward_slide(self, corner=None): new_st.append(list()) new_st[i].append(None) + + while (i, j) not in inner_outside_corners: # get the value of the cell above the temporarily empty cell (if # it exists) if i > 0: P_up = new_st[i-1][j] else: - P_up = None + P_up = -1 # a dummy value less than all positive numbers # get the value of the cell to the left of the temp. empty cell # (if it exists) if j > 0: P_left = new_st[i][j-1] else: - P_left = None + P_left = -1 # a dummy value less than all positive numbers # get the next cell - try: - if P_left > P_up: - new_st[i][j] = P_left - i, j = (i, j-1) - else: # if they are equal, we slide up - new_st[i][j] = P_up - i, j = (i-1, j) - except TypeError: - # if the addition of the original corner - # is automatically a skew partition, just - # return it. - break + # p_left will always be positive, but if P_left + # and P_up are both None, then it will return + # -1, which doesn't trigger the conditional + if P_left > P_up: + new_st[i][j] = P_left + i, j = (i, j - 1) + else: # if they are equal, we slide up + new_st[i][j] = P_up + i, j = (i - 1, j) # We don't need to reset the intermediate cells inside the loop # because the conditional above will continue to overwrite it until # the while loop terminates. We do need to reset it at the end. From 39b5f6a8f1f445b7f2f69fce4495ce7ae6c9b84f Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Fri, 5 Aug 2022 17:24:29 -0500 Subject: [PATCH 209/632] Add RotheDiagram --- src/sage/combinat/diagram.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index 3165d13112f..77b7da27035 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -54,7 +54,7 @@ from sage.categories.sets_cat import Sets from sage.sets.non_negative_integers import NonNegativeIntegers as NN from sage.combinat.partition import Partition -from sage.combinat.permutation import Permutation +from sage.combinat.permutation import Permutations from sage.structure.unique_representation import UniqueRepresentation from sage.structure.list_clone import ClonableArray from sage.structure.parent import Parent @@ -588,3 +588,25 @@ def _an_element_(self): Element = NorthwestDiagram + +def RotheDiagram(w): + r""" + A constructor to build the Rothe diagram of a permutation w as an element + in the parent of :class:`NorthwestDiagrams`. + + EXAMPLES:: + + sage: w = Permutations(9)([1, 7, 4, 5, 9, 3, 2, 8, 6]) + sage: from sage.combinat.diagram import RotheDiagram + sage: D = RotheDiagram(w) + """ + + from sage.misc.mrange import cartesian_product_iterator + + if not w in Permutations(): + raise ValueError('w must be a Permutation') + + cells = [c for c in cartesian_product_iterator((range(9),range(9))) + if c[0]+1 < w.inverse()(c[1]+1) and c[1]+1 < w(c[0]+1)] + + return NorthwestDiagram(cells) From cc85d22d4b868623e81543cce2bfbca2223ffa16 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Fri, 5 Aug 2022 19:09:59 -0500 Subject: [PATCH 210/632] Add rothe_diagram, n_reduced_words, and Stanley symmetric function --- src/sage/combinat/permutation.py | 35 +++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index f6d802e2beb..94748effe15 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -220,7 +220,8 @@ - Travis Scrimshaw (2014-02-05): Made :class:`StandardPermutations_n` a finite Weyl group to make it more uniform with :class:`SymmetricGroup`. Added ability to compute the conjugacy classes. - +- Trevor K. Karn (2022-08-05): Add :meth:`Permutation.n_reduced_words` and + :meth:`Permutation.stanley_symmetric_function`. - Amrutha P, Shriya M, Divya Aggarwal (2022-08-16): Added Multimajor Index. Classes and methods @@ -229,6 +230,7 @@ # **************************************************************************** # Copyright (C) 2007 Mike Hansen +# 2022 Trevor K. Karn # 2022 Amrutha P # 2022 Shriya M <25shriya@gmail.com> # 2022 Divya Aggarwal @@ -2993,6 +2995,32 @@ def reduced_word_lexmin(self): return rw + def rothe_diagram(self): + r""" + Return the Rothe diagram of ``self``. + """ + from sage.combinat.diagram import RotheDiagram + return RotheDiagram(self) + + def n_reduced_words(self): + r""" + Return the number of reduced words of ``self`` without explicitly + computing them all. + """ + Tx = self.rothe_diagram().peelable_tableaux() + + return sum(map(_tableau_contribution, Tx)) + + def stanley_symmetric_function(self): + r""" + Return the Stanley symmetric function associated to ``self``. + """ + + from sage.combinat.sf.sf import SymmetricFunctions + from sage.rings.rational_field import RationalField as QQ + + s = SymmetricFunctions(QQ).s() + return sum(s[T.shape()] for T in self.rothe_diagram().peelable_tableaux()) ################ # Fixed Points # @@ -5242,6 +5270,11 @@ def shifted_shuffle(self, other): return self.shifted_concatenation(other, "right").\ right_permutohedron_interval(self.shifted_concatenation(other, "left")) +def _tableau_contribution(T): + r""" + Get the number of SYT of shape(``T``). + """ + return(StandardTableaux(T.shape()).cardinality()) ################################################################ # Parent classes ################################################################ From 1bf86402ab293535b22730b1c05c3610bbe77d97 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Fri, 5 Aug 2022 20:28:00 -0500 Subject: [PATCH 211/632] Add documentation/examples --- src/sage/combinat/diagram.py | 204 +++++++++++++++++++++++++++++++++-- 1 file changed, 195 insertions(+), 9 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index 77b7da27035..148bc89a685 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -35,6 +35,122 @@ . . . . . . . . . . . . . O +We can also check if certain cells are contained in a given diagram:: + + sage: (1, 0) in T + True + sage: (2, 2) in T + False + +A ``Diagram`` is an element of the parent class ``Diagrams``, so we can also +construct a diagram by calling an instance of ``Diagrams``:: + + sage: from sage.combinat.diagram import Diagrams + sage: Dgms = Diagrams() + sage: D in Dgms + True + sage: Dgms([(0,1),(3,3)].pp() + . . . . + . O . . + . . . . + . . . O + +There are two other specific types of diagrams which are implemented in Sage, +namely northwest diagrams (:class:`NorthwestDiagram`) and Rothe diagrams +(:func:`RotheDiagram`, a special kind of northwest diagram). + +A diagram is a +*northwest diagram* if it satsifies the property that: the presence of two +cells `(i_1, j_1)` and `(i_2, j_2)` in a diagram `D` implies the presence of +the cell `(\min(i_1, i_2), \min(j_1, j_2))`:: + + sage: from sage.combinat.diagram import NorthwestDiagram + sage: N = NorthwestDiagram([(0,0), (0, 10), (5,0)]); N.pp() + O . . . . . . . . . O + . . . . . . . . . . . + . . . . . . . . . . . + . . . . . . . . . . . + . . . . . . . . . . . + O . . . . . . . . . . + +Note that checking whether or not the northwest property is satisfied is +automatically checked. The diagram found by adding the cell `(1,1)` to the +diagram above is *not* a northwest diagram. The cell `(1,0)` should be +present due to the presence of `(5,0)` and `(1,1)`:: + + sage: Diagram([(0, 0), (0, 10), (5, 0), (1, 1)]).pp() + O . . . . . . . . . O + . O . . . . . . . . . + . . . . . . . . . . . + . . . . . . . . . . . + . . . . . . . . . . . + O . . . . . . . . . . + sage: NorthwestDiagram([(0, 0), (0, 10), (5, 0), (1, 1)]) + Traceback (most recent call last): + ... + ValueError + +However, this behavior can be turned off if you are confident that you are +providing a northwest diagram:: + + sage: N = NorthwestDiagram([(0, 0), (0, 10), (5, 0), + ....: (1, 1), (0, 1), (1, 0)], + ....: check=False) + sage: N.pp() + O O . . . . . . . . O + O O . . . . . . . . . + . . . . . . . . . . . + . . . . . . . . . . . + . . . . . . . . . . . + O . . . . . . . . . . + +Note that arbitrary diagrams which happen to be northwest diagrams only live +in the parent of :class:`Diagrams`:: + + sage: D = Diagram([(0, 0), (0, 10), (5, 0), (1, 1), (0, 1), (1, 0)]) + sage: D.pp() + O O . . . . . . . . O + O O . . . . . . . . . + . . . . . . . . . . . + . . . . . . . . . . . + . . . . . . . . . . . + O . . . . . . . . . . + sage: D in NorthwestDiagrams() + False + +For a fixed northwest diagram `D`, we say that a Young tableau `T` is +`D`-peelable if the row indices of the cells in the first column of `D` are +the entries in an initial segment in the first column of `T`, and the + +One particular way of constructing a northwest diagram from a permutation is +by constructing its Rothe diagram. Formally, if `\omega` is a permutation, +then the Rothe diagram `D(\omega)` is the diagram whose cells are + +.. MATH:: + + D(\omega) = \{(\omega_i, j) : i Date: Mon, 8 Aug 2022 17:15:17 -0700 Subject: [PATCH 212/632] Add examples and documentation --- src/sage/combinat/diagram.py | 209 ++++++++++++++++++++++++++++++----- 1 file changed, 181 insertions(+), 28 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index 148bc89a685..cffd4ce4af8 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -37,11 +37,28 @@ We can also check if certain cells are contained in a given diagram:: - sage: (1, 0) in T + sage: (1, 0) in D True - sage: (2, 2) in T + sage: (2, 2) in D False +If you know that there are entire empty rows or columns at the end of the +diagram, you can manually pass them with keyword arguments ``n_rows=`` or +``n_cols=``:: + + sage: Diagram([(0,0), (0,3), (2,2), (2,4)]).pp() + O . . O . + . . . . . + . . O . O + sage: Diagram([(0,0), (0,3), (2,2), (2,4)], n_rows=6, n_cols=6).pp() + O . . O . . + . . . . . . + . . O . O . + . . . . . . + . . . . . . + . . . . . . + + A ``Diagram`` is an element of the parent class ``Diagrams``, so we can also construct a diagram by calling an instance of ``Diagrams``:: @@ -49,10 +66,10 @@ sage: Dgms = Diagrams() sage: D in Dgms True - sage: Dgms([(0,1),(3,3)].pp() - . . . . + sage: Dgms([(0,1),(3,3)]).pp() . O . . . . . . + . . . . . . . O There are two other specific types of diagrams which are implemented in Sage, @@ -88,7 +105,7 @@ sage: NorthwestDiagram([(0, 0), (0, 10), (5, 0), (1, 1)]) Traceback (most recent call last): ... - ValueError + AssertionError However, this behavior can be turned off if you are confident that you are providing a northwest diagram:: @@ -115,20 +132,17 @@ . . . . . . . . . . . . . . . . . . . . . . O . . . . . . . . . . + sage: from sage.combinat.diagram import NorthwestDiagrams sage: D in NorthwestDiagrams() False -For a fixed northwest diagram `D`, we say that a Young tableau `T` is -`D`-peelable if the row indices of the cells in the first column of `D` are -the entries in an initial segment in the first column of `T`, and the - One particular way of constructing a northwest diagram from a permutation is by constructing its Rothe diagram. Formally, if `\omega` is a permutation, then the Rothe diagram `D(\omega)` is the diagram whose cells are .. MATH:: - D(\omega) = \{(\omega_i, j) : i \omega_j \}. Informally, one can construct the Rothe diagram by starting with all `n^2` possible cells, and then deleting the cells `(i, \omega(i))` as well as all @@ -145,11 +159,47 @@ O . . . . . . . O . O O . . . . O . O . . . . . - . . O . . . . . . . . . . . . . - . . O . . . . . - . . O . . . . . - . . O . . . . . + . . . . . . . . + . . . . . . . . + . . . . . . . . + . . . . . . . . + +For a fixed northwest diagram `D`, we say that a Young tableau `T` is +`D`-peelable if: + +- the row indices of the cells in the first column of `D` are +the entries in an initial segment in the first column of `T` and + +- the tableau `Q` obtained by removing those cells from `T` and playing jeu de +taquin is `D-C`-peelable, where `D-C` is the diagram formed by forgetting the +first column of `D`. + +Reiner and Shimozono [RS1995]_ showed that the number `\operatorname{red}(w)` +of reduced words of a permutation `w` may be computed using the peelable +tableaux of the Rothe diagram `D(w)`. Explicitly, + +.. MATH:: + + \operatorname{red}(w) = \sum_{T} f_{\operatorname{shape} T} + +where the sum runs over the `D(w)`-peelable tableaux `T` and `f_\lambda` is the +number of standard Young tableaux of shape `\lambda` (which may be computed +using the hook-length formula). + +We can compute the `D`-peelable diagrams for a northwest diagram `D`:: + + sage: cells = [(0,0), (0,1), (0,2), (1,0), (2,0), (2,2), (2,4), + ....: (4,0), (4,2)] + sage: D = NorthwestDiagram(cells); D.pp() + O O O . . + O . . . . + O . O . O + . . . . . + O . O . . + sage: D.peelable_tableaux() + {[[1, 1, 1], [2, 3, 3], [3, 5], [5]], + [[1, 1, 1, 3], [2, 3], [3, 5], [5]]} AUTHORS: @@ -157,7 +207,7 @@ """ # **************************************************************************** -# Copyright (C) 2013 Trevor K. Karn +# Copyright (C) 2022 Trevor K. Karn # # 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 @@ -166,7 +216,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** - from sage.categories.sets_cat import Sets from sage.sets.non_negative_integers import NonNegativeIntegers as NN from sage.combinat.partition import Partition @@ -184,7 +233,8 @@ class Diagram(ClonableArray): EXAMPLES:: sage: from sage.combinat.diagram import Diagram - sage: D = Diagram([(0,0), (0,3), (2,2), (2,4)]) + sage: D = Diagram([(0,0), (0,3), (2,2), (2,4)]); D + [(0, 0), (0, 3), (2, 2), (2, 4)] TESTS:: @@ -292,6 +342,13 @@ def pp(self): O . . O . . . . . . . . O . O + sage: Diagram([(0,0), (0,3), (2,2), (2,4)], n_rows=6, n_cols=6).pp() + O . . O . . + . . . . . . + . . O . O . + . . . . . . + . . . . . . + . . . . . . """ output_str = '' @@ -395,7 +452,8 @@ def n_cells(self): def check(self): r""" Check that this is a valid diagram by checking that it is an iterable - of length-two tuples of integers. + of length-two tuples. (The fact that each tuple is length-two is + implicitly checked during creation of the diagram). .. WARNING:: @@ -408,8 +466,30 @@ def check(self): sage: from sage.combinat.diagram import Diagram sage: D = Diagram([(0,0), (0,3), (2,2), (2,4)]) sage: D.check() + + In the next two examples, the diagram ``D`` is initialized but with + bad information that is not detected untill we call ``D.check()``. + The first example fails because one cells is indexed by negative + integers:: + + sage: D = Diagram([(0,0), (0,-3), (2,2), (2,4)]) + sage: D.check() + Traceback (most recent call last): + ... + AssertionError + + The next example fails because one cell is indexed by rational + numbers:: + + sage: D = Diagram([(0,0), (0,3), (2/3,2), (2,4)]) + sage: D.check() + Traceback (most recent call last): + ... + AssertionError """ - assert all(isinstance(c, tuple) and len(c) == 2 for c in self._cells) + from sage.sets.non_negative_integers import NonNegativeIntegers + NN = NonNegativeIntegers() + assert all(all(list(i in NN for i in c)) for c in self._cells.keys()) class Diagrams(UniqueRepresentation, Parent): @@ -417,7 +497,17 @@ class Diagrams(UniqueRepresentation, Parent): The class of combinatorial diagrams. A *combinatorial diagram* is a set of cells indexed by pairs of natural - numbers. + numbers. Calling an instance of :class:`Diagrams` is one way to construct + diagrams. + + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagrams + sage: Dgms = Diagrams() + sage: D = Dgms([(0,0), (0,3), (2,2), (2,4)]) + sage: D.parent() + Combinatorial diagrams + """ def __init__(self): r""" @@ -492,15 +582,64 @@ class NorthwestDiagram(Diagram): called *northwest diagrams*. For general diagrams see :class:`Diagram`. + + EXAMPLES:: + + sage: from sage.combinat.diagram import NorthwestDiagram + sage: N = NorthwestDiagram([(0,0), (0, 2), (2,0)]) + + To visualize them, use the ``.pp()`` method:: + + sage: N.pp() + O . O + . . . + O . . + + One can also create northwest diagrams from a partition:: + + sage: mu = Partition([5,4,3,2,1]) + sage: mu.pp() + ***** + **** + *** + ** + * + sage: D = NorthwestDiagram(mu.cells()).pp() + O O O O O + O O O O . + O O O . . + O O . . . + O . . . . """ @staticmethod def __classcall_private__(self, cells, **kwargs): - r""" + """ + Normalize input to ensure a correct parent. + + EXAMPLES:: + + sage: from sage.combinat.diagram import NorthwestDiagram + sage: N1 = NorthwestDiagram([(0,1), (0,2)]) + sage: N2 = NorthwestDiagram([(0,1), (0,3)]) + sage: N1.parent() is N2.parent() + True """ # TODO: Assert that cells is sorted in lex order to speed up lookup. return NorthwestDiagrams()(cells, **kwargs) def __init__(self, cells, **kwargs): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: from sage.combinat.diagram import NorthwestDiagram + sage: N = NorthwestDiagram([(0,1), (0,2)]) + + TESTS:: + + sage: TestSuite(N).run() + """ check = kwargs.get('check', True) @@ -511,13 +650,14 @@ def check(self): r""" A diagram has the northwest property if the presence of cells `(i1, j1)` and `(i2, j2)` implies the presence of the cell - `(min(i1, i2), min(j1, j2))` + `(min(i1, i2), min(j1, j2))`. This method checks if the northwest + property is satisfied for ``self`` - .. WARNING:: + EXAMPLES:: - This method is required for `Diagram` to be a subclass of - :class:`~sage.structure.list_clone.ClonableArray`, however the check - is *not* automatically performed upon creation of the element. + sage: from sage.combinat.diagram import Diagram + sage: D = Diagram([(0,0), (0,3), (2,2), (2,4)]) + sage: D.check() """ from itertools import combinations assert all((min(i1,i2), min(j1,j2)) in self @@ -785,6 +925,17 @@ def RotheDiagram(w): . . . . . . . . . . . . . . O . . . . . . . . . . . . + + + Currently, only elements of the parent + :class:`sage.combinat.permutations.Permutations` are supported. In + particular, elements of permutation groups are not supported:: + + sage: w = SymmetricGroup(9).an_element() + sage: RotheDiagram(w) + Traceback (most recent call last): + ... + ValueError: w must be a Permutation """ from sage.misc.mrange import cartesian_product_iterator @@ -792,7 +943,9 @@ def RotheDiagram(w): if not w in Permutations(): raise ValueError('w must be a Permutation') - cells = [c for c in cartesian_product_iterator((range(9),range(9))) + N = w.size() + + cells = [c for c in cartesian_product_iterator((range(N),range(N))) if c[0]+1 < w.inverse()(c[1]+1) and c[1]+1 < w(c[0]+1)] - return NorthwestDiagram(cells, check=False) + return NorthwestDiagram(cells, n_rows=N, n_cols=N, check=False) From 93c5bb719cdc3381cbca5322e9c7619c8c2fa150 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Mon, 8 Aug 2022 18:29:15 -0700 Subject: [PATCH 213/632] PEP8 and example --- src/sage/combinat/diagram.py | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index cffd4ce4af8..9d8f8fa62b6 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -56,7 +56,7 @@ . . O . O . . . . . . . . . . . . . - . . . . . . + . . . . . . A ``Diagram`` is an element of the parent class ``Diagrams``, so we can also @@ -226,6 +226,7 @@ from sage.combinat.tableau import Tableau from sage.combinat.skew_tableau import SkewTableaux + class Diagram(ClonableArray): r""" A class to model arbitrary combinatorial diagrams. @@ -286,8 +287,8 @@ def __init__(self, cells, **kwargs): self._cells = {c: True for c in cells} self._n_rows = kwargs.pop('n_rows', max(c[0] for c in self._cells) + 1) self._n_cols = kwargs.pop('n_cols', max(c[1] for c in self._cells) + 1) - self._n_nonempty_rows = len(set(i for i,j in self._cells)) - self._n_nonempty_cols = len(set(j for i,j in self._cells)) + self._n_nonempty_rows = len(set(i for i, j in self._cells)) + self._n_nonempty_cols = len(set(j for i, j in self._cells)) ClonableArray.__init__(self, Diagrams(), cells, check=False) @@ -452,7 +453,7 @@ def n_cells(self): def check(self): r""" Check that this is a valid diagram by checking that it is an iterable - of length-two tuples. (The fact that each tuple is length-two is + of length-two tuples. (The fact that each tuple is length-two is implicitly checked during creation of the diagram). .. WARNING:: @@ -468,7 +469,7 @@ def check(self): sage: D.check() In the next two examples, the diagram ``D`` is initialized but with - bad information that is not detected untill we call ``D.check()``. + bad information that is not detected untill we call ``D.check()``. The first example fails because one cells is indexed by negative integers:: @@ -487,7 +488,7 @@ def check(self): ... AssertionError """ - from sage.sets.non_negative_integers import NonNegativeIntegers + from sage.sets.non_negative_integers import NonNegativeIntegers NN = NonNegativeIntegers() assert all(all(list(i in NN for i in c)) for c in self._cells.keys()) @@ -660,8 +661,8 @@ def check(self): sage: D.check() """ from itertools import combinations - assert all((min(i1,i2), min(j1,j2)) in self - for (i1, j1), (i2, j2) in combinations(self._cells, 2)) + assert all((min(i1, i2), min(j1, j2)) in self + for (i1, j1), (i2, j2) in combinations(self._cells, 2)) def peelable_tableaux(self): r""" @@ -777,6 +778,10 @@ def peelable_tableaux(self): . . . . . . . . O O . O . . sage: D.peelable_tableaux() + {[[1, 1, 1, 1, 1], [2, 2, 2, 3, 3], [3, 3, 3], [5, 5, 5]], + [[1, 1, 1, 1, 1], [2, 2, 2, 3, 3], [3, 3, 3, 5], [5, 5]], + [[1, 1, 1, 1, 1, 3], [2, 2, 2, 3], [3, 3, 3], [5, 5, 5]], + [[1, 1, 1, 1, 1, 3], [2, 2, 2, 3], [3, 3, 3, 5], [5, 5]]} .. ALGORITHM:: @@ -790,9 +795,9 @@ def peelable_tableaux(self): # if there is a single column in the diagram then there is only # one posslbe peelable tableau. if self._n_nonempty_cols == 1: - return {Tableau([[i+1] for i,j in self._cells])} + return {Tableau([[i+1] for i, j in self._cells])} - first_col = min(j for i,j in self._cells) + first_col = min(j for i, j in self._cells) # TODO: The next two lines of code could be optimized by only # looping over self._cells once (rather than two separate times) @@ -902,7 +907,6 @@ def _an_element_(self): """ return self([(0, 1), (0, 2), (1, 1), (2, 3)]) - Element = NorthwestDiagram @@ -927,7 +931,7 @@ def RotheDiagram(w): . . . . . . . . . - Currently, only elements of the parent + Currently, only elements of the parent :class:`sage.combinat.permutations.Permutations` are supported. In particular, elements of permutation groups are not supported:: @@ -940,12 +944,12 @@ def RotheDiagram(w): from sage.misc.mrange import cartesian_product_iterator - if not w in Permutations(): + if w not in Permutations(): raise ValueError('w must be a Permutation') N = w.size() - cells = [c for c in cartesian_product_iterator((range(N),range(N))) + cells = [c for c in cartesian_product_iterator((range(N), range(N))) if c[0]+1 < w.inverse()(c[1]+1) and c[1]+1 < w(c[0]+1)] return NorthwestDiagram(cells, n_rows=N, n_cols=N, check=False) From f1864a3faa782dd8e44e56cb5fb86de79ba8e2b8 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Mon, 8 Aug 2022 18:46:43 -0700 Subject: [PATCH 214/632] Fix __init__ --- src/sage/combinat/diagram.py | 45 +++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index 9d8f8fa62b6..af6862c0db0 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -285,11 +285,23 @@ def __init__(self, cells, **kwargs): . . O . . """ self._cells = {c: True for c in cells} - self._n_rows = kwargs.pop('n_rows', max(c[0] for c in self._cells) + 1) - self._n_cols = kwargs.pop('n_cols', max(c[1] for c in self._cells) + 1) + if n_rows is not None: + if n_rows < max(c[0] for c in self._cells) + 1: + raise ValueError('n_rows is too small') + self._n_rows = n_rows + else: + self._n_rows = max(c[0] for c in self._cells) + 1 + if n_cols is not None: + if n_cols < max(c[1] for c in self._cells) + 1: + raise ValueError('n_cols is too small') + self._n_cols = n_cols + else: + self._n_cols = max(c[1] for c in self._cells) + 1 + self._n_nonempty_rows = len(set(i for i, j in self._cells)) self._n_nonempty_cols = len(set(j for i, j in self._cells)) + ClonableArray.__init__(self, Diagrams(), cells, check=False) def _hash_(self): @@ -628,7 +640,7 @@ def __classcall_private__(self, cells, **kwargs): # TODO: Assert that cells is sorted in lex order to speed up lookup. return NorthwestDiagrams()(cells, **kwargs) - def __init__(self, cells, **kwargs): + def __init__(self, cells, n_rows=None, n_cols=None, check=True): r""" Initialize ``self``. @@ -640,11 +652,32 @@ def __init__(self, cells, **kwargs): TESTS:: sage: TestSuite(N).run() + sage: N = NorthwestDiagram([(0,1), (0,2)], n_cols = 1) + Traceback (most recent call last): + ... + ValueError: n_cols is too small + sage: N = NorthwestDiagram([(0,0), (1,0)], n_rows = 1) + Traceback (most recent call last): + ... + ValueError: n_rows is too small """ + self._cells = {c: True for c in cells} + if n_rows is not None: + if n_rows < max(c[0] for c in self._cells) + 1: + raise ValueError('n_rows is too small') + self._n_rows = n_rows + else: + self._n_rows = max(c[0] for c in self._cells) + 1 + if n_cols is not None: + if n_cols < max(c[1] for c in self._cells) + 1: + raise ValueError('n_cols is too small') + self._n_cols = n_cols + else: + self._n_cols = max(c[1] for c in self._cells) + 1 - check = kwargs.get('check', True) + self._n_nonempty_rows = len(set(i for i, j in self._cells)) + self._n_nonempty_cols = len(set(j for i, j in self._cells)) - Diagram.__init__(self, cells, **kwargs) ClonableArray.__init__(self, NorthwestDiagrams(), cells, check=check) def check(self): @@ -880,7 +913,7 @@ class :class:`NorthwestDiagrams`. diagram itself. """ - def __repr__(self): + def _repr_(self): r""" EXAMPLES:: From f893d43d49be88cf7d64bda44a0c5f293d6008f0 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Tue, 9 Aug 2022 12:32:01 -0700 Subject: [PATCH 215/632] Fix kwargs, repr, and remove double .__init__ call --- src/sage/combinat/diagram.py | 86 ++++++++++------------------ src/sage/combinat/diagram_minimal.py | 26 +++++++++ 2 files changed, 55 insertions(+), 57 deletions(-) create mode 100644 src/sage/combinat/diagram_minimal.py diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index af6862c0db0..c2738971a78 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -225,9 +225,10 @@ from sage.structure.parent import Parent from sage.combinat.tableau import Tableau from sage.combinat.skew_tableau import SkewTableaux +from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass -class Diagram(ClonableArray): +class Diagram(ClonableArray, metaclass=InheritComparisonClasscallMetaclass): r""" A class to model arbitrary combinatorial diagrams. @@ -244,7 +245,7 @@ class Diagram(ClonableArray): sage: TestSuite(D).run() """ @staticmethod - def __classcall_private__(self, cells, **kwargs): + def __classcall_private__(self, cells, n_rows=None, n_cols=None, check=False): r""" Normalize the input so that it lives in the correct parent. @@ -255,9 +256,9 @@ def __classcall_private__(self, cells, **kwargs): sage: D.parent() Combinatorial diagrams """ - return Diagrams()(cells, **kwargs) + return Diagrams()(cells, n_rows, n_cols, check) - def __init__(self, cells, **kwargs): + def __init__(self, parent, cells, n_rows=None, n_cols=None, check=False): r""" EXAMPLES:: @@ -301,8 +302,7 @@ def __init__(self, cells, **kwargs): self._n_nonempty_rows = len(set(i for i, j in self._cells)) self._n_nonempty_cols = len(set(j for i, j in self._cells)) - - ClonableArray.__init__(self, Diagrams(), cells, check=False) + ClonableArray.__init__(self, parent, cells, check=False) def _hash_(self): r""" @@ -332,7 +332,7 @@ def __contains__(self, other): """ return other in self._cells - def __repr__(self): + def _repr_(self): r""" EXAMPLES:: @@ -537,7 +537,7 @@ def __init__(self): Parent.__init__(self, category=Sets()) - def __repr__(self): + def _repr_(self): r""" EXAMPLES:: @@ -547,7 +547,7 @@ def __repr__(self): """ return 'Combinatorial diagrams' - def _element_constructor_(self, cells, **kwargs): + def _element_constructor_(self, cells, n_rows=None, n_cols=None, check=False): r""" EXAMPLES:: @@ -562,7 +562,7 @@ def _element_constructor_(self, cells, **kwargs): sage: TestSuite(Dgms).run() """ - return self.element_class(cells, **kwargs) + return self.element_class(self, cells, n_rows, n_cols, check) def _an_element_(self): r""" @@ -586,7 +586,7 @@ def _an_element_(self): # Northwest diagrams #################### -class NorthwestDiagram(Diagram): +class NorthwestDiagram(Diagram, metaclass=InheritComparisonClasscallMetaclass): r""" A diagram is a set of cells indexed by natural numbers. Such a diagram has the *northwest property* if the presence of cells `(i1, j1)` and @@ -625,60 +625,25 @@ class NorthwestDiagram(Diagram): O . . . . """ @staticmethod - def __classcall_private__(self, cells, **kwargs): + def __classcall_private__(self, cells, n_rows=None, n_cols=None, check=True): """ Normalize input to ensure a correct parent. EXAMPLES:: - sage: from sage.combinat.diagram import NorthwestDiagram + sage: from sage.combinat.diagram import NorthwestDiagram, NorthwestDiagrams sage: N1 = NorthwestDiagram([(0,1), (0,2)]) sage: N2 = NorthwestDiagram([(0,1), (0,3)]) sage: N1.parent() is N2.parent() True + sage: N3 = NorthwestDiagrams()([(0,1), (0,2)]) + sage: N3.parent() is NorthwestDiagrams() + True + sage: N1.parent() is NorthwestDiagrams() + True """ # TODO: Assert that cells is sorted in lex order to speed up lookup. - return NorthwestDiagrams()(cells, **kwargs) - - def __init__(self, cells, n_rows=None, n_cols=None, check=True): - r""" - Initialize ``self``. - - EXAMPLES:: - - sage: from sage.combinat.diagram import NorthwestDiagram - sage: N = NorthwestDiagram([(0,1), (0,2)]) - - TESTS:: - - sage: TestSuite(N).run() - sage: N = NorthwestDiagram([(0,1), (0,2)], n_cols = 1) - Traceback (most recent call last): - ... - ValueError: n_cols is too small - sage: N = NorthwestDiagram([(0,0), (1,0)], n_rows = 1) - Traceback (most recent call last): - ... - ValueError: n_rows is too small - """ - self._cells = {c: True for c in cells} - if n_rows is not None: - if n_rows < max(c[0] for c in self._cells) + 1: - raise ValueError('n_rows is too small') - self._n_rows = n_rows - else: - self._n_rows = max(c[0] for c in self._cells) + 1 - if n_cols is not None: - if n_cols < max(c[1] for c in self._cells) + 1: - raise ValueError('n_cols is too small') - self._n_cols = n_cols - else: - self._n_cols = max(c[1] for c in self._cells) + 1 - - self._n_nonempty_rows = len(set(i for i, j in self._cells)) - self._n_nonempty_cols = len(set(j for i, j in self._cells)) - - ClonableArray.__init__(self, NorthwestDiagrams(), cells, check=check) + return NorthwestDiagrams()(cells, n_rows, n_cols, check) def check(self): r""" @@ -689,9 +654,16 @@ def check(self): EXAMPLES:: - sage: from sage.combinat.diagram import Diagram - sage: D = Diagram([(0,0), (0,3), (2,2), (2,4)]) - sage: D.check() + sage: from sage.combinat.diagram import NorthwestDiagram + sage: N = NorthwestDiagram([(0,0), (0,3), (3,0)]) + sage: N.check() + + Here is a non-example:: + + sage: notN = NorthwestDiagram([(0,1), (1,0)]) #.check() is implicit + Traceback (most recent call last): + ... + AssertionError """ from itertools import combinations assert all((min(i1, i2), min(j1, j2)) in self diff --git a/src/sage/combinat/diagram_minimal.py b/src/sage/combinat/diagram_minimal.py new file mode 100644 index 00000000000..d7fea533651 --- /dev/null +++ b/src/sage/combinat/diagram_minimal.py @@ -0,0 +1,26 @@ +from sage.categories.sets_cat import Sets +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.list_clone import ClonableArray +from sage.structure.parent import Parent +from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass + +class Diagram(ClonableArray, metaclass=InheritComparisonClasscallMetaclass): + @staticmethod + def __classcall_private__(cls, cells): + return Diagrams()(cells) + + def __init__(self, parent, cells): + self._cells = {c: True for c in cells} + ClonableArray.__init__(self, parent, cells) + + def check(self): + pass + +class Diagrams(UniqueRepresentation, Parent): + def __init__(self): + Parent.__init__(self, category=Sets()) + + def _element_constructor_(self, cells): + return self.element_class(self, cells) + + Element = Diagram \ No newline at end of file From cd5df3fcae0534b1c6d19da2d6e37cda5948817f Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Tue, 9 Aug 2022 12:49:23 -0700 Subject: [PATCH 216/632] Fix accidental check=False --- src/sage/combinat/diagram.py | 5 ++--- src/sage/combinat/diagram_minimal.py | 26 -------------------------- 2 files changed, 2 insertions(+), 29 deletions(-) delete mode 100644 src/sage/combinat/diagram_minimal.py diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index c2738971a78..6eeb1d82399 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -302,7 +302,7 @@ def __init__(self, parent, cells, n_rows=None, n_cols=None, check=False): self._n_nonempty_rows = len(set(i for i, j in self._cells)) self._n_nonempty_cols = len(set(j for i, j in self._cells)) - ClonableArray.__init__(self, parent, cells, check=False) + ClonableArray.__init__(self, parent, cells, check) def _hash_(self): r""" @@ -373,7 +373,7 @@ def pp(self): output_str += '. ' output_str += '\n' - print(output_str) + print(output_str, end='') # don't double up on `\n`'ss def n_rows(self): r""" @@ -935,7 +935,6 @@ def RotheDiagram(w): . . . . . O . . . . . . . . . . . . - Currently, only elements of the parent :class:`sage.combinat.permutations.Permutations` are supported. In particular, elements of permutation groups are not supported:: diff --git a/src/sage/combinat/diagram_minimal.py b/src/sage/combinat/diagram_minimal.py deleted file mode 100644 index d7fea533651..00000000000 --- a/src/sage/combinat/diagram_minimal.py +++ /dev/null @@ -1,26 +0,0 @@ -from sage.categories.sets_cat import Sets -from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.list_clone import ClonableArray -from sage.structure.parent import Parent -from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass - -class Diagram(ClonableArray, metaclass=InheritComparisonClasscallMetaclass): - @staticmethod - def __classcall_private__(cls, cells): - return Diagrams()(cells) - - def __init__(self, parent, cells): - self._cells = {c: True for c in cells} - ClonableArray.__init__(self, parent, cells) - - def check(self): - pass - -class Diagrams(UniqueRepresentation, Parent): - def __init__(self): - Parent.__init__(self, category=Sets()) - - def _element_constructor_(self, cells): - return self.element_class(self, cells) - - Element = Diagram \ No newline at end of file From 261983ad0473a18a967afb34d039bd1e9e7605d4 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Tue, 9 Aug 2022 14:48:19 -0700 Subject: [PATCH 217/632] Add references and more examples. All doctests pass --- src/doc/en/reference/combinat/module_list.rst | 1 + src/doc/en/reference/references/index.rst | 5 +- src/sage/combinat/diagram.py | 191 +++++++++++++++--- 3 files changed, 164 insertions(+), 33 deletions(-) diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index 7a97a40e30d..93201401ddb 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -109,6 +109,7 @@ Comprehensive Module List sage/combinat/designs/steiner_quadruple_systems sage/combinat/designs/subhypergraph_search sage/combinat/designs/twographs + sage/combinat/diagram sage/combinat/diagram_algebras sage/combinat/dlx sage/combinat/dyck_word diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index e6a0e5c2000..ee5166235ab 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -5100,7 +5100,10 @@ REFERENCES: **6** (1997), 59-87. .. [RSS] :wikipedia:`Residual_sum_of_squares`, accessed 13th - October 2009. + October 2009. + +.. [RS1995] Victor Reiner, Mark Shimozono, "Plactification", + J. Algebraic Combin. **4** (1995), 331-351. .. [RS2012] G. Rudolph and M. Schmidt, "Differential Geometry and Mathematical Physics. Part I. Manifolds, Lie Groups and Hamiltonian Systems", Springer, 2012. diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index 6eeb1d82399..10103b92a6f 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -146,8 +146,8 @@ Informally, one can construct the Rothe diagram by starting with all `n^2` possible cells, and then deleting the cells `(i, \omega(i))` as well as all -cells to the right and below. (These are the "death rays" of []_.) To compute -a Rothe diagram in Sage, start with a +cells to the right and below. (These are sometimes called "death rays".) To +compute a Rothe diagram in Sage, start with a :class:`~sage.combinat.permutation.Permutation`:: sage: w = Permutations(8)([2,5,4,1,3,6,7,8]) @@ -219,6 +219,7 @@ from sage.categories.sets_cat import Sets from sage.sets.non_negative_integers import NonNegativeIntegers as NN from sage.combinat.partition import Partition +from sage.combinat.skew_partition import SkewPartition from sage.combinat.permutation import Permutations from sage.structure.unique_representation import UniqueRepresentation from sage.structure.list_clone import ClonableArray @@ -607,27 +608,16 @@ class NorthwestDiagram(Diagram, metaclass=InheritComparisonClasscallMetaclass): O . O . . . O . . - - One can also create northwest diagrams from a partition:: - - sage: mu = Partition([5,4,3,2,1]) - sage: mu.pp() - ***** - **** - *** - ** - * - sage: D = NorthwestDiagram(mu.cells()).pp() - O O O O O - O O O O . - O O O . . - O O . . . - O . . . . """ @staticmethod - def __classcall_private__(self, cells, n_rows=None, n_cols=None, check=True): + def __classcall_private__(self, cells, n_rows=None, n_cols=None, + check=True): """ - Normalize input to ensure a correct parent. + Normalize input to ensure a correct parent. This method also allows + one to specify whether or not to check the northwest property for the + provided cells. Note that the default behavior is to check for the + northwest property (``check=True``), while in arbitrary diagrams, the + default behavior is that ``check=False``. EXAMPLES:: @@ -683,7 +673,7 @@ def peelable_tableaux(self): sage: NWD.peelable_tableaux() {[[1], [3]]} - From [...]_ we know that there is only one peelable tableau for the + From [RS1995]_ we know that there is only one peelable tableau for the Rothe diagram of the permutation (in one line notation) `251643`:: sage: D = NorthwestDiagram([(1, 2), (1, 3), (3, 2), (3, 3), (4, 2)]) @@ -791,7 +781,7 @@ def peelable_tableaux(self): .. ALGORITHM:: This implementation uses the algorithm suggested in remark 25 - of [...]_. + of [RS1995]_. """ # TODO: There is a condition on the first column (if the rows in Dhat # are a subset of the rows in the first column) which simplifies the @@ -849,11 +839,23 @@ class NorthwestDiagrams(Diagrams): One may create an instance of a northwest diagram either by directly calling :class:`NorthwestDiagram` or by creating an instance of the - parent class `NorthwestDiagrams` (with an `s`) and calling it on a `list` - of tuples consisting of the coordinates of the diagram. + parent class ``NorthwestDiagrams`` (with an `s`) and calling it on a + ``list`` of ``tuple``s consisting of the coordinates of the diagram. EXAMPLES:: + sage: from sage.combinat.diagram import NorthwestDiagram, NorthwestDiagrams + sage: D = NorthwestDiagram([(0,1), (0,2), (1,1)]); D.pp() + . O O + . O . + sage: NWDgms = NorthwestDiagrams() + sage: D = NWDgms([(1,1), (1,2), (2,1)]); D.pp() + . . . + . O O + . O . + sage: D.parent() + Combinatorial northwest diagrams + Additionally, there are natural constructions of a northwest diagram given the data of a permutation (Rothe diagrams are the protypical example of northwest diagrams), or the data of a partition of an integer, or a @@ -867,22 +869,51 @@ class NorthwestDiagrams(Diagrams): \{ (i, j) : i < \omega^{-1}(j) \text{ and } j < \omega(i)} We can construct one by calling :meth:`rothe_diagram` method on the parent - class :class:`NorthwestDiagrams`. - - EXAMPLES:: + class :class:`NorthwestDiagrams`:: - sage: from sage.combinat.diagram import NorthwestDiagrams - sage: NWDgms = NorthwestDiagrams + sage: w = Permutations(4)([4,3,2,1]) + sage: NorthwestDiagrams().rothe_diagram(w).pp() + O O O . + O O . . + O . . . + . . . . To turn a Ferrers diagram into a northwest diagram, we may call the - :meth:`ferrers_diagram` method. This will return a Ferrer's diagram in the + :meth:`from_partition` method. This will return a Ferrer's diagram in the parent of all northwest diagrams. For many use-cases it is probably better to get Ferrer's diagrams by the corresponding method on partitons, namely - :meth:`sage.combinat.partitions.Partitions.ferrers_diagram`. + :meth:`sage.combinat.partitions.Partitions.ferrers_diagram`:: + + sage: mu = Partition([7,3,1,1]) + sage: mu.pp() + ******* + *** + * + * + sage: NorthwestDiagrams().from_partition(mu).pp() + O O O O O O O + O O O . . . . + O . . . . . . + O . . . . . . It is also possible to turn a Ferrers diagram of a skew partition into a northwest diagram, altough it is more subtle than just using the skew - diagram itself. + diagram itself. One must first reflect the partition about a vertical axis + so that the skew partition looks "backwards":: + + sage: mu, nu = Partition([5,4,3,2,1]), Partition([3,2,1]) + sage: s = mu/nu; s.pp() + ** + ** + ** + ** + * + sage: NorthwestDiagrams().from_skew_partition(s).pp() + O O . . . + . O O . . + . . O O . + . . . O O + . . . . O """ def _repr_(self): @@ -912,6 +943,102 @@ def _an_element_(self): """ return self([(0, 1), (0, 2), (1, 1), (2, 3)]) + def rothe_diagram(self, w): + r""" + Return the Rothe diagram of ``w``. + + EXAMPLES:: + + sage: w = Permutations(3)([2,1,3]) + sage: from sage.combinat.diagram import NorthwestDiagrams + sage: NorthwestDiagrams().rothe_diagram(w).pp() + O . . + . . . + . . . + """ + return RotheDiagram(w) + + def from_partition(self, mu): + r""" + Return the Ferrer's diagram of ``mu`` as an element of the parent + ``NorthwestDiagrams`` + + EXAMPLES:: + + sage: mu = Partition([5,2,1]); mu.pp() + ***** + ** + * + sage: mu.parent() + Partitions + sage: from sage.combinat.diagram import NorthwestDiagrams + sage: D = NorthwestDiagrams().from_partition(mu) + sage: D.pp() + O O O O O + O O . . . + O . . . . + sage: D.parent() + Combinatorial northwest diagrams + + TESTS:: + + sage: from sage.combinat.diagram import NorthwestDiagrams + sage: mu = [5, 2, 1] + sage: D = NorthwestDiagrams().from_partition(mu) + Traceback (most recent call last): + ... + ValueError: mu must be a Partition + """ + if not isinstance(mu, Partition): + raise ValueError("mu must be a Partition") + return self.element_class(self, mu.cells(), check=False) + + def from_skew_partition(self, s): + r""" + Get the northwest diagram found by reflecting a skew shape across + a vertical plane. + + EXAMPLES:: + + sage: mu, nu = Partition([3,2,1]), Partition([2,1]) + sage: s = mu/nu; s.pp() + * + * + * + sage: from sage.combinat.diagram import NorthwestDiagrams + sage: D = NorthwestDiagrams().from_skew_partition(s) + sage: D.pp() + O . . + . O . + . . O + + sage: mu, nu = Partition([3,3,2]), Partition([2,2,2]) + sage: s = mu/nu; s.pp() + * + * + sage: NorthwestDiagrams().from_skew_partition(s).pp() + O . . + O . . + . . . + + TESTS:: + + sage: mu = Partition([3,2,1]) + sage: NorthwestDiagrams().from_skew_partition(mu) + Traceback (most recent call last): + ... + ValueError: mu must be a SkewPartition + """ + if not isinstance(s, SkewPartition): + raise ValueError("mu must be a SkewPartition") + + n_cols = s.outer()[0] + n_rows = len(s.outer()) + + cells = [(i, n_cols - 1 - j) for i, j in s.cells()] + + return self.element_class(self, cells, n_rows, n_cols, check=False) + Element = NorthwestDiagram From 8c5bc2bea123f2e6cdfa7832cf5c4cb4696b8f1b Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Tue, 9 Aug 2022 14:51:30 -0700 Subject: [PATCH 218/632] Fix a typo in Rothe diagrams --- src/sage/combinat/diagram.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index 10103b92a6f..e0fcbd0afe0 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -866,7 +866,7 @@ class NorthwestDiagrams(Diagrams): .. MATH:: - \{ (i, j) : i < \omega^{-1}(j) \text{ and } j < \omega(i)} + D(\omega) = \{(\omega_j, i) : i \omega_j \}. We can construct one by calling :meth:`rothe_diagram` method on the parent class :class:`NorthwestDiagrams`:: From 507aad05110451daef19ae3a6e80cc37b8295b7f Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Wed, 10 Aug 2022 08:43:38 -0700 Subject: [PATCH 219/632] Fix bug in identity partition/empty diagram --- src/sage/combinat/diagram.py | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index e0fcbd0afe0..c7328f69541 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -287,18 +287,27 @@ def __init__(self, parent, cells, n_rows=None, n_cols=None, check=False): . . O . . """ self._cells = {c: True for c in cells} + + if self._cells: + # minimum possible number of rows/cols + N_rows = max(c[0] for c in self._cells) + N_cols = max(c[1] for c in self._cells) + else: # if there are no cells + N_rows = 0 + N_cols = 0 + if n_rows is not None: - if n_rows < max(c[0] for c in self._cells) + 1: + if n_rows < N_rows + 1: raise ValueError('n_rows is too small') self._n_rows = n_rows else: - self._n_rows = max(c[0] for c in self._cells) + 1 + self._n_rows = N_rows + 1 if n_cols is not None: - if n_cols < max(c[1] for c in self._cells) + 1: + if n_cols < N_cols + 1: raise ValueError('n_cols is too small') self._n_cols = n_cols else: - self._n_cols = max(c[1] for c in self._cells) + 1 + self._n_cols = N_cols + 1 self._n_nonempty_rows = len(set(i for i, j in self._cells)) self._n_nonempty_cols = len(set(j for i, j in self._cells)) @@ -1071,6 +1080,18 @@ def RotheDiagram(w): Traceback (most recent call last): ... ValueError: w must be a Permutation + + + TESTS:: + + sage: w = Permutations(5)([1,2,3,4,5]) + sage: from sage.combinat.diagram import RotheDiagram + sage: RotheDiagram(w).pp() + . . . . . + . . . . . + . . . . . + . . . . . + . . . . . """ from sage.misc.mrange import cartesian_product_iterator From 0f5ee00ed03905893d73d8593b0cc001d12bf984 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Thu, 11 Aug 2022 13:43:45 -0700 Subject: [PATCH 220/632] Speed up sliding by skipping classcall --- src/sage/combinat/skew_tableau.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/skew_tableau.py b/src/sage/combinat/skew_tableau.py index 0578404a643..840a6864d0f 100644 --- a/src/sage/combinat/skew_tableau.py +++ b/src/sage/combinat/skew_tableau.py @@ -1039,7 +1039,7 @@ def backward_slide(self, corner=None): # the while loop terminates. We do need to reset it at the end. new_st[i][j] = None - return SkewTableau(new_st) + return SkewTableaux()(new_st) reverse_slide = backward_slide From cc0c83c0312cdba1bc252d17e218de0c974784b8 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Thu, 11 Aug 2022 17:43:04 -0700 Subject: [PATCH 221/632] Remove unneeded creation of skew tableaux --- src/sage/combinat/diagram.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index c7328f69541..7b4aa40563a 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -818,12 +818,11 @@ def peelable_tableaux(self): for Q in Dhat.peelable_tableaux(): # get the vertical strips mu = Q.shape() - vertical_strips = mu.add_vertical_border_strip(k) - for s in vertical_strips: + vertical_strip_cells = mu.vertical_border_strip_cells(k) + for s in vertical_strip_cells: sQ = SkewTableaux()(Q) # sQ is skew - get it? - new_cells = (s/mu).cells() # perform the jeu de taquin slides - for c in new_cells: + for c in s: sQ = sQ.backward_slide(c) # create the new tableau by filling the columns sQ_new = sQ.to_list() From a145dabb1350d923a7bb353df7c231812b170676 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Tue, 16 Aug 2022 10:56:59 -0700 Subject: [PATCH 222/632] First round of reviewer suggestions --- src/sage/combinat/diagram.py | 74 ++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 38 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index 7b4aa40563a..c0ef71ec74b 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -58,20 +58,6 @@ . . . . . . . . . . . . - -A ``Diagram`` is an element of the parent class ``Diagrams``, so we can also -construct a diagram by calling an instance of ``Diagrams``:: - - sage: from sage.combinat.diagram import Diagrams - sage: Dgms = Diagrams() - sage: D in Dgms - True - sage: Dgms([(0,1),(3,3)]).pp() - . O . . - . . . . - . . . . - . . . O - There are two other specific types of diagrams which are implemented in Sage, namely northwest diagrams (:class:`NorthwestDiagram`) and Rothe diagrams (:func:`RotheDiagram`, a special kind of northwest diagram). @@ -267,9 +253,9 @@ def __init__(self, parent, cells, n_rows=None, n_cols=None, check=False): sage: D1 = Diagram([(0,2),(0,3),(1,1),(3,2)]) sage: D1.cells() [(0, 2), (0, 3), (1, 1), (3, 2)] - sage: D1.n_rows() + sage: D1.nrows() 4 - sage: D1.n_cols() + sage: D1.ncols() 4 We can specify the number of rows and columns explicitly, @@ -278,7 +264,7 @@ def __init__(self, parent, cells, n_rows=None, n_cols=None, check=False): sage: D2 = Diagram([(0,2),(0,3),(1,1),(3,2)], n_cols=5) sage: D2.cells() [(0, 2), (0, 3), (1, 1), (3, 2)] - sage: D2.n_cols() + sage: D2.ncols() 5 sage: D2.pp() . . O O . @@ -286,7 +272,7 @@ def __init__(self, parent, cells, n_rows=None, n_cols=None, check=False): . . . . . . . O . . """ - self._cells = {c: True for c in cells} + self._cells = frozenset(cells) if self._cells: # minimum possible number of rows/cols @@ -350,7 +336,7 @@ def _repr_(self): sage: D = Diagram([(0,2),(0,3),(1,1),(3,2)]); D [(0, 2), (0, 3), (1, 1), (3, 2)] """ - return str(list(self._cells)) + return str(sorted(self._cells)) def pp(self): r""" @@ -385,7 +371,7 @@ def pp(self): print(output_str, end='') # don't double up on `\n`'ss - def n_rows(self): + def number_of_rows(self): r""" Return the total number of rows of the cell, including those which do not have any cells. @@ -397,14 +383,16 @@ def n_rows(self): sage: from sage.combinat.diagram import Diagram sage: D1 = Diagram([(0,2),(0,3),(1,1),(3,2)]) - sage: D1.n_rows() + sage: D1.number_of_rows() + 4 + sage: D1.nrows() 4 We can also include empty rows at the end:: sage: from sage.combinat.diagram import Diagram sage: D = Diagram([(0,2),(0,3),(1,1),(3,2)], n_rows=6) - sage: D.n_rows() + sage: D.number_of_rows() 6 sage: D.pp() . . O O @@ -416,7 +404,9 @@ def n_rows(self): """ return self._n_rows - def n_cols(self): + nrows = number_of_rows + + def number_of_cols(self): r""" Return the total number of rows of the cell, including those which do not have any cells. @@ -428,14 +418,16 @@ def n_cols(self): sage: from sage.combinat.diagram import Diagram sage: D = Diagram([(0,2),(0,3),(1,1),(3,2)]) - sage: D.n_cols() + sage: D.number_of_cols() + 4 + sage: D.ncols() 4 We can also include empty columns at the end:: sage: from sage.combinat.diagram import Diagram sage: D = Diagram([(0,2),(0,3),(1,1),(3,2)], n_cols=6) - sage: D.n_cols() + sage: D.number_of_cols() 6 sage: D.pp() . . O O . . @@ -446,6 +438,8 @@ def n_cols(self): return self._n_cols + ncols = number_of_cols + def cells(self): r""" Return a ``list`` of the cells contained in the diagram ``self``. @@ -457,9 +451,9 @@ def cells(self): sage: D1.cells() [(0, 2), (0, 3), (1, 1), (3, 2)] """ - return list(self._cells.keys()) + return sorted(self._cells) - def n_cells(self): + def number_of_cells(self): r""" Return the total number of cells contained in the diagram ``self``. @@ -467,11 +461,17 @@ def n_cells(self): sage: from sage.combinat.diagram import Diagram sage: D1 = Diagram([(0,2),(0,3),(1,1),(3,2)]) + sage: D1.number_of_cells() + 4 sage: D1.n_cells() 4 """ return len(self._cells) + n_cells = number_of_cells + + size = number_of_cells + def check(self): r""" Check that this is a valid diagram by checking that it is an iterable @@ -499,7 +499,7 @@ def check(self): sage: D.check() Traceback (most recent call last): ... - AssertionError + ValueError: Diagrams must be indexed by non-negative integers The next example fails because one cell is indexed by rational numbers:: @@ -508,11 +508,12 @@ def check(self): sage: D.check() Traceback (most recent call last): ... - AssertionError + ValueError: Diagrams must be indexed by non-negative integers """ from sage.sets.non_negative_integers import NonNegativeIntegers NN = NonNegativeIntegers() - assert all(all(list(i in NN for i in c)) for c in self._cells.keys()) + if not all(all(list(i in NN for i in c)) for c in self._cells): + raise ValueError("Diagrams must be indexed by non-negative integers") class Diagrams(UniqueRepresentation, Parent): @@ -557,7 +558,7 @@ def _repr_(self): """ return 'Combinatorial diagrams' - def _element_constructor_(self, cells, n_rows=None, n_cols=None, check=False): + def _element_constructor_(self, cells, n_rows=None, n_cols=None, check=True): r""" EXAMPLES:: @@ -619,14 +620,12 @@ class NorthwestDiagram(Diagram, metaclass=InheritComparisonClasscallMetaclass): O . . """ @staticmethod - def __classcall_private__(self, cells, n_rows=None, n_cols=None, - check=True): + def __classcall_private__(self, cells, n_rows=None, + n_cols=None, check=True): """ Normalize input to ensure a correct parent. This method also allows one to specify whether or not to check the northwest property for the - provided cells. Note that the default behavior is to check for the - northwest property (``check=True``), while in arbitrary diagrams, the - default behavior is that ``check=False``. + provided cells. EXAMPLES:: @@ -641,7 +640,6 @@ def __classcall_private__(self, cells, n_rows=None, n_cols=None, sage: N1.parent() is NorthwestDiagrams() True """ - # TODO: Assert that cells is sorted in lex order to speed up lookup. return NorthwestDiagrams()(cells, n_rows, n_cols, check) def check(self): @@ -799,7 +797,7 @@ def peelable_tableaux(self): # if there is a single column in the diagram then there is only # one posslbe peelable tableau. if self._n_nonempty_cols == 1: - return {Tableau([[i+1] for i, j in self._cells])} + return {Tableau([[i+1] for i, j in self.cells()])} first_col = min(j for i, j in self._cells) From 9870a889e4243df61629aff4c45f848f943b9f4d Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Tue, 16 Aug 2022 11:56:50 -0700 Subject: [PATCH 223/632] Assertion -> ValueError --- src/sage/combinat/diagram.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index c0ef71ec74b..5437d35b728 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -91,7 +91,7 @@ sage: NorthwestDiagram([(0, 0), (0, 10), (5, 0), (1, 1)]) Traceback (most recent call last): ... - AssertionError + ValueError: diagram is not northwest However, this behavior can be turned off if you are confident that you are providing a northwest diagram:: @@ -660,11 +660,20 @@ def check(self): sage: notN = NorthwestDiagram([(0,1), (1,0)]) #.check() is implicit Traceback (most recent call last): ... - AssertionError + ValueError: diagram is not northwest + + TESTS:: + + sage: NorthwestDiagram([(0,1/2)]) + Traceback (most recent call last): + ... + ValueError: Diagrams must be indexed by non-negative integers """ from itertools import combinations - assert all((min(i1, i2), min(j1, j2)) in self - for (i1, j1), (i2, j2) in combinations(self._cells, 2)) + Diagram.check(self) + if not all((min(i1, i2), min(j1, j2)) in self + for (i1, j1), (i2, j2) in combinations(self._cells, 2)): + raise ValueError("diagram is not northwest") def peelable_tableaux(self): r""" From 40ae181d23859f9d86b42cabf4e76dd68c9cc090 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Tue, 16 Aug 2022 12:32:13 -0700 Subject: [PATCH 224/632] Add tests and remove Stanley s.f. --- src/sage/combinat/permutation.py | 45 ++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index 94748effe15..48b188aacc7 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -2998,14 +2998,31 @@ def reduced_word_lexmin(self): def rothe_diagram(self): r""" Return the Rothe diagram of ``self``. + + EXAMPLES:: + + sage: p = Permutation([4,2,1,3]) + sage: D = p.rothe_diagram(); D + [(0, 0), (0, 1), (0, 2), (1, 0)] + sage: D.pp() + O O O . + O . . . + . . . . + . . . . """ from sage.combinat.diagram import RotheDiagram return RotheDiagram(self) - def n_reduced_words(self): + def number_of_reduced_words(self): r""" Return the number of reduced words of ``self`` without explicitly computing them all. + + EXAMPLES:: + + sage: p = Permutation([6,4,2,5,1,8,3,7]) + sage: len(p.reduced_words()) == p.number_of_reduced_words() + True """ Tx = self.rothe_diagram().peelable_tableaux() @@ -3014,13 +3031,22 @@ def n_reduced_words(self): def stanley_symmetric_function(self): r""" Return the Stanley symmetric function associated to ``self``. - """ - from sage.combinat.sf.sf import SymmetricFunctions - from sage.rings.rational_field import RationalField as QQ + EXAMPLES:: + sage: p = Permutation([4,5,2,3,1]) + sage: p.stanley_symmetric_function() + 56*m[1, 1, 1, 1, 1, 1, 1, 1] + 30*m[2, 1, 1, 1, 1, 1, 1] + + 16*m[2, 2, 1, 1, 1, 1] + 9*m[2, 2, 2, 1, 1] + 6*m[2, 2, 2, 2] + + 10*m[3, 1, 1, 1, 1, 1] + 5*m[3, 2, 1, 1, 1] + 3*m[3, 2, 2, 1] + + m[3, 3, 1, 1] + m[3, 3, 2] + 2*m[4, 1, 1, 1, 1] + m[4, 2, 1, 1] + + m[4, 2, 2] + """ + from sage.combinat.sf.sf import SymmetricFunctions + from sage.rings.rational_field import QQ s = SymmetricFunctions(QQ).s() - return sum(s[T.shape()] for T in self.rothe_diagram().peelable_tableaux()) + m = SymmetricFunctions(QQ).m() + return m(sum(s[T.shape()] for T in self.rothe_diagram().peelable_tableaux())) ################ # Fixed Points # @@ -5273,8 +5299,17 @@ def shifted_shuffle(self, other): def _tableau_contribution(T): r""" Get the number of SYT of shape(``T``). + + EXAMPLES:: + + sage: T = Tableau([[1,1,1],[2]]) + sage: from sage.combinat.permutation import _tableau_contribution + sage: _tableau_contribution(T) + 3 """ + from sage.combinat.tableau import StandardTableaux return(StandardTableaux(T.shape()).cardinality()) + ################################################################ # Parent classes ################################################################ From 9e4963781e99617bf38a2e691b618ea7e0e449a1 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Wed, 17 Aug 2022 13:51:57 -0500 Subject: [PATCH 225/632] Add methods to interact with polyominos/compositions --- src/sage/combinat/diagram.py | 109 ++++++++++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index 5437d35b728..8aa037a43e3 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -245,7 +245,7 @@ def __classcall_private__(self, cells, n_rows=None, n_cols=None, check=False): """ return Diagrams()(cells, n_rows, n_cols, check) - def __init__(self, parent, cells, n_rows=None, n_cols=None, check=False): + def __init__(self, parent, cells, n_rows=None, n_cols=None, check=True): r""" EXAMPLES:: @@ -590,6 +590,86 @@ def _an_element_(self): """ return self([(0, 2), (1, 1), (2, 3)]) + def from_polyomino(self, p): + r""" + Create the diagram corresponding to a 2d + :class:`~sage.combinat.tiling.Polyomino.` + + EXAMPLES:: + + sage: from sage.combinat.tiling import Polyomino + sage: p = Polyomino([(0,0),(1,0),(1,1),(1,2)]) + sage: from sage.combinat.diagram import Diagrams + sage: Diagrams().from_polyomino(p).pp() + O . . + O O O + sage: p = Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') + sage: Diagrams().from_polyomino(p) + Traceback (most recent call last): + ... + ValueError: Dimension of the polyomino must be 2 + """ + if not p._dimension == 2: + raise ValueError("Dimension of the polyomino must be 2") + cells = map(tuple, list(p)) + return self.element_class(self, cells) + + def from_composition(self, alpha): + r""" + Create the diagram corresponding to a weak composition `alpha \vDash n`. + + EXAMPLES:: + + sage: alpha = Composition([3,0,2,1,4,4]) + sage: from sage.combinat.diagram import Diagrams + sage: Diagrams().from_composition(alpha).pp() + O O O . + . . . . + O O . . + O . . . + O O O O + O O O O + """ + cells = [] + for i, n in enumerate(alpha): + cells.extend((i, j) for j in range(n)) + return self.element_class(self, cells, check=False) + + def from_zero_one_matrix(self, M, check=True): + r""" + Get a diagram from a matrix with entries in `\{0, 1\}`, where + positions of cells are indicated by the `1`'s. + + EXAMPLES:: + + sage: M = matrix([[1,0,1,1],[0,1,1,0]]) + sage: from sage.combinat.diagram import Diagrams + sage: Diagrams().from_zero_one_matrix(M).pp() + O . O O + . O O . + + sage: M = matrix([[1, 0, 0], [1, 0, 0], [0, 0, 0]]) + sage: Diagrams().from_zero_one_matrix(M).pp() + O . . + O . . + . . . + + """ + # check matrix is zero-one + n_rows, n_cols = M.dimensions() + + zero = M.base_ring().zero() + one = M.base_ring().one() + + if check: + for i in range(n_rows): + for j in range(n_cols): + if not (M[i,j] == zero or M[i,j] == one): + raise ValueError("Matrix entries must be 0 or 1") + cells = [(i, j) for i in range(n_rows) for j in range(n_cols) if M[i,j]] + + return self.element_class(self, cells, n_rows, n_cols, check=False) + Element = Diagram @@ -970,9 +1050,15 @@ def rothe_diagram(self, w): O . . . . . . . . + sage: NorthwestDiagrams().from_permutation(w).pp() + O . . + . . . + . . . """ return RotheDiagram(w) + from_permutation = rothe_diagram + def from_partition(self, mu): r""" Return the Ferrer's diagram of ``mu`` as an element of the parent @@ -1054,6 +1140,27 @@ def from_skew_partition(self, s): return self.element_class(self, cells, n_rows, n_cols, check=False) + def from_parallelogram_polyomino(self, p): + r""" + Create the diagram corresponding to a + :class:`~sage.combinat.parallelogram_polyomino.ParallelogramPolyomino`. + + EXAMPLES:: + + sage: p = ParallelogramPolyomino([[0, 0, 1, 0, 0, 0, 1, 1], + ....: [1, 1, 0, 1, 0, 0, 0, 0]]) + sage: from sage.combinat.diagram import NorthwestDiagrams + sage: NorthwestDiagrams().from_parallelogram_polyomino(p).pp() + O O . + O O O + . O O + . O O + . O O + """ + from sage.matrix.constructor import Matrix + M = Matrix(p.get_array()) + return self.from_zero_one_matrix(M) + Element = NorthwestDiagram From 5f1dc60af6dc579a035a38c2fcd2946cef81f385 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Mon, 12 Sep 2022 17:24:59 -0500 Subject: [PATCH 226/632] Move docmentation to class level --- src/sage/combinat/diagram.py | 409 ++++++++++++++++++----------------- 1 file changed, 207 insertions(+), 202 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index 8aa037a43e3..cbd88846a26 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -2,190 +2,12 @@ Combinatorial diagrams A combinatorial diagram is a collection of cells `(i,j)` indexed by pairs of -natural numbers. The positions are indexed by rows and columns. For example, -a Ferrer's diagram is a diagram obtained from a partition -`\lambda = (\lambda_0, \lambda_1, \ldots, \lambda_\ell)` where the cells are -in rows `i` for `0 \leq i \leq \ell` and the cells in row `i` consist of -`(i,j)` for `0 \leq j < \lambda_i`. In English notation, the indices are read -from top left to bottom right as in a matrix. - -Indexing conventions are the same as -:class:`~sage.combinat.partition.Partition`. - -EXAMPLES: - -To create an arbirtrary diagram, pass a list of all cells:: - - sage: from sage.combinat.diagram import Diagram - sage: cells = [(0,0), (0,1), (1,0), (1,1), (4,4), (4,5), (4,6), (5,4), (7, 6)] - sage: D = Diagram(cells); D - [(0, 0), (0, 1), (1, 0), (1, 1), (4, 4), (4, 5), (4, 6), (5, 4), (7, 6)] - -We can visualize the diagram by printing ``O``'s and ``.``'s. ``O``'s are -present in the cells which are present in the diagram and a ``.`` represents -the absence of a cell in the diagram:: - - sage: D.pp() - O O . . . . . - O O . . . . . - . . . . . . . - . . . . . . . - . . . . O O O - . . . . O . . - . . . . . . . - . . . . . . O - -We can also check if certain cells are contained in a given diagram:: - - sage: (1, 0) in D - True - sage: (2, 2) in D - False - -If you know that there are entire empty rows or columns at the end of the -diagram, you can manually pass them with keyword arguments ``n_rows=`` or -``n_cols=``:: - - sage: Diagram([(0,0), (0,3), (2,2), (2,4)]).pp() - O . . O . - . . . . . - . . O . O - sage: Diagram([(0,0), (0,3), (2,2), (2,4)], n_rows=6, n_cols=6).pp() - O . . O . . - . . . . . . - . . O . O . - . . . . . . - . . . . . . - . . . . . . - -There are two other specific types of diagrams which are implemented in Sage, -namely northwest diagrams (:class:`NorthwestDiagram`) and Rothe diagrams -(:func:`RotheDiagram`, a special kind of northwest diagram). - -A diagram is a -*northwest diagram* if it satsifies the property that: the presence of two -cells `(i_1, j_1)` and `(i_2, j_2)` in a diagram `D` implies the presence of -the cell `(\min(i_1, i_2), \min(j_1, j_2))`:: - - sage: from sage.combinat.diagram import NorthwestDiagram - sage: N = NorthwestDiagram([(0,0), (0, 10), (5,0)]); N.pp() - O . . . . . . . . . O - . . . . . . . . . . . - . . . . . . . . . . . - . . . . . . . . . . . - . . . . . . . . . . . - O . . . . . . . . . . - -Note that checking whether or not the northwest property is satisfied is -automatically checked. The diagram found by adding the cell `(1,1)` to the -diagram above is *not* a northwest diagram. The cell `(1,0)` should be -present due to the presence of `(5,0)` and `(1,1)`:: - - sage: Diagram([(0, 0), (0, 10), (5, 0), (1, 1)]).pp() - O . . . . . . . . . O - . O . . . . . . . . . - . . . . . . . . . . . - . . . . . . . . . . . - . . . . . . . . . . . - O . . . . . . . . . . - sage: NorthwestDiagram([(0, 0), (0, 10), (5, 0), (1, 1)]) - Traceback (most recent call last): - ... - ValueError: diagram is not northwest - -However, this behavior can be turned off if you are confident that you are -providing a northwest diagram:: - - sage: N = NorthwestDiagram([(0, 0), (0, 10), (5, 0), - ....: (1, 1), (0, 1), (1, 0)], - ....: check=False) - sage: N.pp() - O O . . . . . . . . O - O O . . . . . . . . . - . . . . . . . . . . . - . . . . . . . . . . . - . . . . . . . . . . . - O . . . . . . . . . . - -Note that arbitrary diagrams which happen to be northwest diagrams only live -in the parent of :class:`Diagrams`:: - - sage: D = Diagram([(0, 0), (0, 10), (5, 0), (1, 1), (0, 1), (1, 0)]) - sage: D.pp() - O O . . . . . . . . O - O O . . . . . . . . . - . . . . . . . . . . . - . . . . . . . . . . . - . . . . . . . . . . . - O . . . . . . . . . . - sage: from sage.combinat.diagram import NorthwestDiagrams - sage: D in NorthwestDiagrams() - False - -One particular way of constructing a northwest diagram from a permutation is -by constructing its Rothe diagram. Formally, if `\omega` is a permutation, -then the Rothe diagram `D(\omega)` is the diagram whose cells are - -.. MATH:: - - D(\omega) = \{(\omega_j, i) : i \omega_j \}. - -Informally, one can construct the Rothe diagram by starting with all `n^2` -possible cells, and then deleting the cells `(i, \omega(i))` as well as all -cells to the right and below. (These are sometimes called "death rays".) To -compute a Rothe diagram in Sage, start with a -:class:`~sage.combinat.permutation.Permutation`:: - - sage: w = Permutations(8)([2,5,4,1,3,6,7,8]) - -Then call :func:`RotheDiagram` on ``w``:: - - sage: from sage.combinat.diagram import RotheDiagram - sage: RotheDiagram(w).pp() - O . . . . . . . - O . O O . . . . - O . O . . . . . - . . . . . . . . - . . . . . . . . - . . . . . . . . - . . . . . . . . - . . . . . . . . - -For a fixed northwest diagram `D`, we say that a Young tableau `T` is -`D`-peelable if: - -- the row indices of the cells in the first column of `D` are -the entries in an initial segment in the first column of `T` and - -- the tableau `Q` obtained by removing those cells from `T` and playing jeu de -taquin is `D-C`-peelable, where `D-C` is the diagram formed by forgetting the -first column of `D`. - -Reiner and Shimozono [RS1995]_ showed that the number `\operatorname{red}(w)` -of reduced words of a permutation `w` may be computed using the peelable -tableaux of the Rothe diagram `D(w)`. Explicitly, - -.. MATH:: - - \operatorname{red}(w) = \sum_{T} f_{\operatorname{shape} T} - -where the sum runs over the `D(w)`-peelable tableaux `T` and `f_\lambda` is the -number of standard Young tableaux of shape `\lambda` (which may be computed -using the hook-length formula). - -We can compute the `D`-peelable diagrams for a northwest diagram `D`:: - - sage: cells = [(0,0), (0,1), (0,2), (1,0), (2,0), (2,2), (2,4), - ....: (4,0), (4,2)] - sage: D = NorthwestDiagram(cells); D.pp() - O O O . . - O . . . . - O . O . O - . . . . . - O . O . . - sage: D.peelable_tableaux() - {[[1, 1, 1], [2, 3, 3], [3, 5], [5]], - [[1, 1, 1, 3], [2, 3], [3, 5], [5]]} +natural numbers. + +For arbitrary diagrams, see :class:`Diagram`. There are also two other specific +types of diagrams implemented here. They are northwest diagrams +(:class:`NorthwestDiagram`) and Rothe diagrams (:func:`RotheDiagram`, a special +kind of northwest diagram). AUTHORS: @@ -217,13 +39,64 @@ class Diagram(ClonableArray, metaclass=InheritComparisonClasscallMetaclass): r""" - A class to model arbitrary combinatorial diagrams. + Combinatorial diagrams with positions indexed by rows in columns. - EXAMPLES:: + The positions are indexed by rows and columns as in a matrix. For example, + a Ferrer's diagram is a diagram obtained from a partition + `\lambda = (\lambda_0, \lambda_1, \ldots, \lambda_\ell)` where the cells are + in rows `i` for `0 \leq i \leq \ell` and the cells in row `i` consist of + `(i,j)` for `0 \leq j < \lambda_i`. In English notation, the indices are + read from top left to bottom right as in a matrix. + + Indexing conventions are the same as + :class:`~sage.combinat.partition.Partition`. Printing the diagram of a + partition, however, will always be in English notation. + + EXAMPLES: + + To create an arbirtrary diagram, pass a list of all cells:: sage: from sage.combinat.diagram import Diagram - sage: D = Diagram([(0,0), (0,3), (2,2), (2,4)]); D - [(0, 0), (0, 3), (2, 2), (2, 4)] + sage: cells = [(0,0), (0,1), (1,0), (1,1), (4,4), (4,5), (4,6), (5,4), (7, 6)] + sage: D = Diagram(cells); D + [(0, 0), (0, 1), (1, 0), (1, 1), (4, 4), (4, 5), (4, 6), (5, 4), (7, 6)] + + We can visualize the diagram by printing ``O``'s and ``.``'s. ``O``'s are + present in the cells which are present in the diagram and a ``.`` represents + the absence of a cell in the diagram:: + + sage: D.pp() + O O . . . . . + O O . . . . . + . . . . . . . + . . . . . . . + . . . . O O O + . . . . O . . + . . . . . . . + . . . . . . O + + We can also check if certain cells are contained in a given diagram:: + + sage: (1, 0) in D + True + sage: (2, 2) in D + False + + If you know that there are entire empty rows or columns at the end of the + diagram, you can manually pass them with keyword arguments ``n_rows=`` or + ``n_cols=``:: + + sage: Diagram([(0,0), (0,3), (2,2), (2,4)]).pp() + O . . O . + . . . . . + . . O . O + sage: Diagram([(0,0), (0,3), (2,2), (2,4)], n_rows=6, n_cols=6).pp() + O . . O . . + . . . . . . + . . O . O . + . . . . . . + . . . . . . + . . . . . . TESTS:: @@ -469,7 +342,7 @@ def number_of_cells(self): return len(self._cells) n_cells = number_of_cells - + size = number_of_cells def check(self): @@ -757,8 +630,45 @@ def check(self): def peelable_tableaux(self): r""" - Given a northwest diagram `D`, a tableau `T` is said to be - `D`-peelable if... + For a fixed northwest diagram `D`, we say that a Young tableau `T` is + `D`-peelable if: + + - the row indices of the cells in the first column of `D` are + the entries in an initial segment in the first column of `T` and + + - the tableau `Q` obtained by removing those cells from `T` and playing + jeu de taquin is `D-C`-peelable, where `D-C` is the diagram formed by + forgetting the first column of `D`. + + Reiner and Shimozono [RS1995]_ showed that the number + `\operatorname{red}(w)` of reduced words of a permutation `w` may be + computed using the peelable tableaux of the Rothe diagram `D(w)`. + Explicitly, + + .. MATH:: + + \operatorname{red}(w) = \sum_{T} f_{\operatorname{shape} T} + + where the sum runs over the `D(w)`-peelable tableaux `T` and `f_\lambda` + is the number of standard Young tableaux of shape `\lambda` (which may + be computed using the hook-length formula). + + EXAMPLES: + + We can compute the `D`-peelable diagrams for a northwest diagram `D`:: + + sage: from sage.combinat.diagram import NorthwestDiagram + sage: cells = [(0,0), (0,1), (0,2), (1,0), (2,0), (2,2), (2,4), + ....: (4,0), (4,2)] + sage: D = NorthwestDiagram(cells); D.pp() + O O O . . + O . . . . + O . O . O + . . . . . + O . O . . + sage: D.peelable_tableaux() + {[[1, 1, 1], [2, 3, 3], [3, 5], [5]], + [[1, 1, 1, 3], [2, 3], [3, 5], [5]]} EXAMPLES: @@ -925,20 +835,73 @@ def peelable_tableaux(self): class NorthwestDiagrams(Diagrams): r""" - The class of northwest diagrams. - - A diagram has the *northwest property* if the presence of cells - `(i1, j1)` and `(i2, j2)` implies the presence of the cell - `(min(i1, i2), min(j1, j2))`. For the class of general diagrams, see - :class:`Diagrams`. + Diagrams satisfying the northwest property. - One may create an instance of a northwest diagram either by directly - calling :class:`NorthwestDiagram` or by creating an instance of the - parent class ``NorthwestDiagrams`` (with an `s`) and calling it on a - ``list`` of ``tuple``s consisting of the coordinates of the diagram. + A diagram is a + *northwest diagram* if it satsifies the property that: the presence of two + cells `(i_1, j_1)` and `(i_2, j_2)` in a diagram `D` implies the presence of + the cell `(\min(i_1, i_2), \min(j_1, j_2))`. EXAMPLES:: + sage: from sage.combinat.diagram import NorthwestDiagram + sage: N = NorthwestDiagram([(0,0), (0, 10), (5,0)]); N.pp() + O . . . . . . . . . O + . . . . . . . . . . . + . . . . . . . . . . . + . . . . . . . . . . . + . . . . . . . . . . . + O . . . . . . . . . . + + Note that checking whether or not the northwest property is satisfied is + automatically checked. The diagram found by adding the cell `(1,1)` to the + diagram above is *not* a northwest diagram. The cell `(1,0)` should be + present due to the presence of `(5,0)` and `(1,1)`:: + + sage: from sage.combinat.diagram import Diagram + sage: Diagram([(0, 0), (0, 10), (5, 0), (1, 1)]).pp() + O . . . . . . . . . O + . O . . . . . . . . . + . . . . . . . . . . . + . . . . . . . . . . . + . . . . . . . . . . . + O . . . . . . . . . . + sage: NorthwestDiagram([(0, 0), (0, 10), (5, 0), (1, 1)]) + Traceback (most recent call last): + ... + ValueError: diagram is not northwest + + However, this behavior can be turned off if you are confident that you are + providing a northwest diagram:: + + sage: N = NorthwestDiagram([(0, 0), (0, 10), (5, 0), + ....: (1, 1), (0, 1), (1, 0)], + ....: check=False) + sage: N.pp() + O O . . . . . . . . O + O O . . . . . . . . . + . . . . . . . . . . . + . . . . . . . . . . . + . . . . . . . . . . . + O . . . . . . . . . . + + Note that arbitrary diagrams which happen to be northwest diagrams only live + in the parent of :class:`Diagrams`:: + + sage: D = Diagram([(0, 0), (0, 10), (5, 0), (1, 1), (0, 1), (1, 0)]) + sage: D.pp() + O O . . . . . . . . O + O O . . . . . . . . . + . . . . . . . . . . . + . . . . . . . . . . . + . . . . . . . . . . . + O . . . . . . . . . . + sage: from sage.combinat.diagram import NorthwestDiagrams + sage: D in NorthwestDiagrams() + False + + Here are some more examples:: + sage: from sage.combinat.diagram import NorthwestDiagram, NorthwestDiagrams sage: D = NorthwestDiagram([(0,1), (0,2), (1,1)]); D.pp() . O O @@ -1042,6 +1005,35 @@ def rothe_diagram(self, w): r""" Return the Rothe diagram of ``w``. + One particular way of constructing a northwest diagram from a permutation is + by constructing its Rothe diagram. Formally, if `\omega` is a permutation, + then the Rothe diagram `D(\omega)` is the diagram whose cells are + + .. MATH:: + + D(\omega) = \{(\omega_j, i) : i \omega_j \}. + + Informally, one can construct the Rothe diagram by starting with all + `n^2` possible cells, and then deleting the cells `(i, \omega(i))` as + well as all cells to the right and below. (These are sometimes called + "death rays".) To compute a Rothe diagram in Sage, start with a + :class:`~sage.combinat.permutation.Permutation`:: + + sage: w = Permutations(8)([2,5,4,1,3,6,7,8]) + + Then call :func:`RotheDiagram` on ``w``:: + + sage: from sage.combinat.diagram import RotheDiagram + sage: RotheDiagram(w).pp() + O . . . . . . . + O . O O . . . . + O . O . . . . . + . . . . . . . . + . . . . . . . . + . . . . . . . . + . . . . . . . . + . . . . . . . . + EXAMPLES:: sage: w = Permutations(3)([2,1,3]) @@ -1081,6 +1073,19 @@ def from_partition(self, mu): sage: D.parent() Combinatorial northwest diagrams + This will print in English notation even if the notation is set to + French for the partition. + + sage: Partitions.options.convention="french" + sage: mu.pp() + * + ** + ***** + sage: D.pp() + O O O O O + O O . . . + O . . . . + TESTS:: sage: from sage.combinat.diagram import NorthwestDiagrams @@ -1133,7 +1138,7 @@ def from_skew_partition(self, s): if not isinstance(s, SkewPartition): raise ValueError("mu must be a SkewPartition") - n_cols = s.outer()[0] + n_cols = s.outer()[0] n_rows = len(s.outer()) cells = [(i, n_cols - 1 - j) for i, j in s.cells()] From 1042fb79feb17f67d96a96a892a45872ab8e8f18 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Mon, 12 Sep 2022 17:43:19 -0500 Subject: [PATCH 227/632] Default check -> true + documentation change --- src/sage/combinat/diagram.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index cbd88846a26..f801f9d2787 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -105,7 +105,7 @@ class Diagram(ClonableArray, metaclass=InheritComparisonClasscallMetaclass): sage: TestSuite(D).run() """ @staticmethod - def __classcall_private__(self, cells, n_rows=None, n_cols=None, check=False): + def __classcall_private__(self, cells, n_rows=None, n_cols=None, check=True): r""" Normalize the input so that it lives in the correct parent. @@ -363,13 +363,11 @@ def check(self): sage: D = Diagram([(0,0), (0,3), (2,2), (2,4)]) sage: D.check() - In the next two examples, the diagram ``D`` is initialized but with - bad information that is not detected untill we call ``D.check()``. + In the next two examples, a bad diagram is passed. The first example fails because one cells is indexed by negative integers:: sage: D = Diagram([(0,0), (0,-3), (2,2), (2,4)]) - sage: D.check() Traceback (most recent call last): ... ValueError: Diagrams must be indexed by non-negative integers @@ -378,7 +376,6 @@ def check(self): numbers:: sage: D = Diagram([(0,0), (0,3), (2/3,2), (2,4)]) - sage: D.check() Traceback (most recent call last): ... ValueError: Diagrams must be indexed by non-negative integers From 60470dbe79396d611c87e452dfbe96df29285eb0 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Mon, 12 Sep 2022 17:43:41 -0500 Subject: [PATCH 228/632] Clean up documentation --- src/sage/combinat/diagram.py | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index f801f9d2787..22171731c0b 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -213,7 +213,9 @@ def _repr_(self): def pp(self): r""" - Return a visualization of the diagram. Cells which are present in the + Return a visualization of the diagram. + + Cells which are present in the diagram are filled with a ``O``. Cells which are not present in the diagram are filled with a ``.``. @@ -246,8 +248,7 @@ def pp(self): def number_of_rows(self): r""" - Return the total number of rows of the cell, including those which do - not have any cells. + Return the total number of rows of the cell. EXAMPLES: @@ -261,6 +262,7 @@ def number_of_rows(self): sage: D1.nrows() 4 + The total number of rows includes including those which are empty. We can also include empty rows at the end:: sage: from sage.combinat.diagram import Diagram @@ -281,8 +283,7 @@ def number_of_rows(self): def number_of_cols(self): r""" - Return the total number of rows of the cell, including those which do - not have any cells. + Return the total number of rows of the cell. EXAMPLES: @@ -347,15 +348,7 @@ def number_of_cells(self): def check(self): r""" - Check that this is a valid diagram by checking that it is an iterable - of length-two tuples. (The fact that each tuple is length-two is - implicitly checked during creation of the diagram). - - .. WARNING:: - - This method is required for ``Diagram`` to be a subclass of - :class:`~sage.structure.list_clone.ClonableArray`, however the check - is *not* automatically performed upon creation of the element. + Check that this is a valid diagram. EXAMPLES:: @@ -549,6 +542,8 @@ def from_zero_one_matrix(self, M, check=True): class NorthwestDiagram(Diagram, metaclass=InheritComparisonClasscallMetaclass): r""" + Diagrams with the northwest property. + A diagram is a set of cells indexed by natural numbers. Such a diagram has the *northwest property* if the presence of cells `(i1, j1)` and `(i2, j2)` implies the presence of the cell @@ -1050,8 +1045,7 @@ def rothe_diagram(self, w): def from_partition(self, mu): r""" - Return the Ferrer's diagram of ``mu`` as an element of the parent - ``NorthwestDiagrams`` + Return the Ferrer's diagram of ``mu`` as a northwest diagram. EXAMPLES:: @@ -1168,8 +1162,7 @@ def from_parallelogram_polyomino(self, p): def RotheDiagram(w): r""" - A constructor to build the Rothe diagram of a permutation w as an element - in the parent of :class:`NorthwestDiagrams`. + The Rothe diagram of a permutation ``w`` EXAMPLES:: @@ -1186,6 +1179,11 @@ def RotheDiagram(w): . . . . . O . . . . . . . . . . . . + The Rothe diagram is a northwest diagram:: + + sage: D.parent() + Combinatorial northwest diagrams + Currently, only elements of the parent :class:`sage.combinat.permutations.Permutations` are supported. In particular, elements of permutation groups are not supported:: From 578a87f6dd2151f1e03b8c11a1f5b8be33c2a3c6 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Mon, 12 Sep 2022 17:59:34 -0500 Subject: [PATCH 229/632] < N_rows + 1 -> <= N_rows --- src/sage/combinat/diagram.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index 22171731c0b..fe45f5b5bd9 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -156,13 +156,13 @@ def __init__(self, parent, cells, n_rows=None, n_cols=None, check=True): N_cols = 0 if n_rows is not None: - if n_rows < N_rows + 1: + if n_rows <= N_rows: raise ValueError('n_rows is too small') self._n_rows = n_rows else: self._n_rows = N_rows + 1 if n_cols is not None: - if n_cols < N_cols + 1: + if n_cols <= N_cols: raise ValueError('n_cols is too small') self._n_cols = n_cols else: From 944fc764400aa553aab054cbd2b6ba8bbe685b75 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Mon, 12 Sep 2022 18:09:32 -0500 Subject: [PATCH 230/632] Remove _hash_, __contains__, _repr_ overrides --- src/sage/combinat/diagram.py | 42 ++---------------------------------- 1 file changed, 2 insertions(+), 40 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index fe45f5b5bd9..d1ff6e1ce7c 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -171,45 +171,7 @@ def __init__(self, parent, cells, n_rows=None, n_cols=None, check=True): self._n_nonempty_rows = len(set(i for i, j in self._cells)) self._n_nonempty_cols = len(set(j for i, j in self._cells)) - ClonableArray.__init__(self, parent, cells, check) - - def _hash_(self): - r""" - TESTS:: - - sage: from sage.combinat.diagram import Diagram - sage: D = Diagram([(0,2),(0,3),(1,1),(3,2)]) - sage: hash(D) - 5125392318921082108 - """ - return hash(tuple(sorted(self._cells))) - - def __contains__(self, other): - r""" - EXAMPLES:: - - sage: from sage.combinat.diagram import Diagram - sage: D = Diagram([(0,2),(0,3),(1,1),(3,2)]) - sage: (1, 2) in D - False - sage: (0, 2) in D - True - sage: (2, 1) in D - False - sage: (3, 2) in D - True - """ - return other in self._cells - - def _repr_(self): - r""" - EXAMPLES:: - - sage: from sage.combinat.diagram import Diagram - sage: D = Diagram([(0,2),(0,3),(1,1),(3,2)]); D - [(0, 2), (0, 3), (1, 1), (3, 2)] - """ - return str(sorted(self._cells)) + ClonableArray.__init__(self, parent, sorted(cells), check) def pp(self): r""" @@ -474,7 +436,7 @@ def from_polyomino(self, p): """ if not p._dimension == 2: raise ValueError("Dimension of the polyomino must be 2") - cells = map(tuple, list(p)) + cells = list(map(tuple, p)) return self.element_class(self, cells) def from_composition(self, alpha): From 6ff9bc5c65db0f6a86a9fdb4e5ccdf18553dca38 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Mon, 12 Sep 2022 19:28:55 -0500 Subject: [PATCH 231/632] Fix peelable tableaux documentation --- src/sage/combinat/diagram.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index d1ff6e1ce7c..3722795a820 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -663,7 +663,7 @@ def peelable_tableaux(self): {[[3]]} Now we know all of the `D_5` peelables, so we can compute the `D_4` - peelables. + peelables:: sage: D4 = NorthwestDiagram([(0, 0), (2,0), (4, 0), (2, 2)]) sage: D4.pp() @@ -738,10 +738,10 @@ def peelable_tableaux(self): [[1, 1, 1, 1, 1, 3], [2, 2, 2, 3], [3, 3, 3], [5, 5, 5]], [[1, 1, 1, 1, 1, 3], [2, 2, 2, 3], [3, 3, 3, 5], [5, 5]]} - .. ALGORITHM:: + ALGORITHM: - This implementation uses the algorithm suggested in remark 25 - of [RS1995]_. + This implementation uses the algorithm suggested in Remark 25 + of [RS1995]_. """ # TODO: There is a condition on the first column (if the rows in Dhat # are a subset of the rows in the first column) which simplifies the From 7fc3ad0264efcecfb75d4cd5ad3354c00d549f60 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Mon, 12 Sep 2022 19:29:24 -0500 Subject: [PATCH 232/632] Redo loop to avoid looping over self._cells twice --- src/sage/combinat/diagram.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index 3722795a820..9c7c1eaa5e1 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -754,14 +754,17 @@ def peelable_tableaux(self): first_col = min(j for i, j in self._cells) - # TODO: The next two lines of code could be optimized by only - # looping over self._cells once (rather than two separate times) - # get the diagram without the first column - Dhat = NorthwestDiagram([c for c in self._cells if c[1] != first_col]) + dhat_cells = [] + new_vals_cells = [] + for i, j in self._cells: + if j != first_col: + dhat_cells.append((i, j)) + else: + new_vals_cells.append(i + 1) - # get the values from the first column - new_vals = sorted(i + 1 for i, j in self._cells if j == first_col) + new_vals = sorted(new_vals_cells) + Dhat = NorthwestDiagram(dhat_cells) k = self.n_cells() - Dhat.n_cells() peelables = set() From 5e9c78281da095ccafbf4926f81ea8c5ad39e929 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Mon, 12 Sep 2022 19:31:53 -0500 Subject: [PATCH 233/632] Move zero/one inside check --- src/sage/combinat/diagram.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index 9c7c1eaa5e1..393da822569 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -483,10 +483,9 @@ def from_zero_one_matrix(self, M, check=True): # check matrix is zero-one n_rows, n_cols = M.dimensions() - zero = M.base_ring().zero() - one = M.base_ring().one() - if check: + zero = M.base_ring().zero() + one = M.base_ring().one() for i in range(n_rows): for j in range(n_cols): if not (M[i,j] == zero or M[i,j] == one): From 90dd64c736bb63e3d715f1cd003893ac85b7e44b Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Mon, 12 Sep 2022 19:33:26 -0500 Subject: [PATCH 234/632] Fix pyflakes issue --- src/sage/combinat/diagram.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index 393da822569..80b292a0041 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -335,8 +335,6 @@ def check(self): ... ValueError: Diagrams must be indexed by non-negative integers """ - from sage.sets.non_negative_integers import NonNegativeIntegers - NN = NonNegativeIntegers() if not all(all(list(i in NN for i in c)) for c in self._cells): raise ValueError("Diagrams must be indexed by non-negative integers") From 5d1f75813e3ebd7d827a8ec0a418ac34ec7fe18d Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Mon, 12 Sep 2022 19:40:20 -0500 Subject: [PATCH 235/632] Fix pyflakes issue --- src/sage/combinat/diagram.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index 80b292a0041..6838ebefa06 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -25,7 +25,6 @@ # **************************************************************************** from sage.categories.sets_cat import Sets -from sage.sets.non_negative_integers import NonNegativeIntegers as NN from sage.combinat.partition import Partition from sage.combinat.skew_partition import SkewPartition from sage.combinat.permutation import Permutations @@ -335,6 +334,8 @@ def check(self): ... ValueError: Diagrams must be indexed by non-negative integers """ + from sage.sets.non_negative_integers import NonNegativeIntegers + NN = NonNegativeIntegers() if not all(all(list(i in NN for i in c)) for c in self._cells): raise ValueError("Diagrams must be indexed by non-negative integers") @@ -1027,7 +1028,7 @@ def from_partition(self, mu): Combinatorial northwest diagrams This will print in English notation even if the notation is set to - French for the partition. + French for the partition:: sage: Partitions.options.convention="french" sage: mu.pp() From 4278363d322b82cb112b4db43de75087251250a7 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Tue, 13 Sep 2022 10:00:01 -0500 Subject: [PATCH 236/632] Add .from_* methods to __element_constructor__ --- src/sage/combinat/diagram.py | 65 ++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index 6838ebefa06..d6f07a149da 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -25,16 +25,19 @@ # **************************************************************************** from sage.categories.sets_cat import Sets +from sage.combinat.composition import Composition from sage.combinat.partition import Partition -from sage.combinat.skew_partition import SkewPartition from sage.combinat.permutation import Permutations -from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.list_clone import ClonableArray -from sage.structure.parent import Parent from sage.combinat.tableau import Tableau +from sage.combinat.tiling import Polyomino +from sage.combinat.skew_partition import SkewPartition from sage.combinat.skew_tableau import SkewTableaux +from sage.matrix.matrix_dense import Matrix_dense +from sage.matrix.matrix_sparse import Matrix_sparse from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass - +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.list_clone import ClonableArray +from sage.structure.parent import Parent class Diagram(ClonableArray, metaclass=InheritComparisonClasscallMetaclass): r""" @@ -393,10 +396,41 @@ def _element_constructor_(self, cells, n_rows=None, n_cols=None, check=True): . . . . . O + + sage: from sage.combinat.tiling import Polyomino + sage: p = Polyomino([(0,0),(1,0),(1,1),(1,2)]) + sage: Dgms(p).pp() + O . . + O O O + + sage: from sage.combinat.composition import Composition + sage: a = Composition([4,2,0,2,4]) + sage: Dgms(a).pp() + O O O O + O O . . + . . . . + O O . . + O O O O + + sage: M = Matrix([[1,1,1,1],[1,1,0,0],[0,0,0,0],[1,1,0,0],[1,1,1,1]]) + sage: Dgms(M).pp() + O O O O + O O . . + . . . . + O O . . + O O O O + TESTS:: sage: TestSuite(Dgms).run() """ + if isinstance(cells, Polyomino): + return self.from_polyomino(cells) + if isinstance(cells, Composition): + return self.from_composition(cells) + if isinstance(cells, (Matrix_dense, Matrix_sparse)): + return self.from_zero_one_matrix(cells) + return self.element_class(self, cells, n_rows, n_cols, check) def _an_element_(self): @@ -424,9 +458,18 @@ def from_polyomino(self, p): sage: from sage.combinat.tiling import Polyomino sage: p = Polyomino([(0,0),(1,0),(1,1),(1,2)]) sage: from sage.combinat.diagram import Diagrams + sage: Diagrams()(p).pp() + O . . + O O O + + We can also call this method directly:: + sage: Diagrams().from_polyomino(p).pp() O . . O O O + + The method only works for 2d `Polyomino`s:: + sage: p = Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') sage: Diagrams().from_polyomino(p) Traceback (most recent call last): @@ -446,6 +489,13 @@ def from_composition(self, alpha): sage: alpha = Composition([3,0,2,1,4,4]) sage: from sage.combinat.diagram import Diagrams + sage: Diagrams()(alpha).pp() + O O O . + . . . . + O O . . + O . . . + O O O O + O O O O sage: Diagrams().from_composition(alpha).pp() O O O . . . . . @@ -468,12 +518,15 @@ def from_zero_one_matrix(self, M, check=True): sage: M = matrix([[1,0,1,1],[0,1,1,0]]) sage: from sage.combinat.diagram import Diagrams + sage: Diagrams()(M).pp() + O . O O + . O O . sage: Diagrams().from_zero_one_matrix(M).pp() O . O O . O O . sage: M = matrix([[1, 0, 0], [1, 0, 0], [0, 0, 0]]) - sage: Diagrams().from_zero_one_matrix(M).pp() + sage: Diagrams()(M).pp() O . . O . . . . . From d7c74f465a15d83f505dc8eda1bb972c873b011d Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Sat, 17 Sep 2022 12:18:59 -0500 Subject: [PATCH 237/632] Add diagram iterator --- src/sage/combinat/diagram.py | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index d6f07a149da..71239537797 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -25,6 +25,7 @@ # **************************************************************************** from sage.categories.sets_cat import Sets +from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.combinat.composition import Composition from sage.combinat.partition import Partition from sage.combinat.permutation import Permutations @@ -360,7 +361,7 @@ class Diagrams(UniqueRepresentation, Parent): Combinatorial diagrams """ - def __init__(self): + def __init__(self, category=None): r""" EXAMPLES:: @@ -373,7 +374,38 @@ def __init__(self): sage: TestSuite(Dgms).run() """ - Parent.__init__(self, category=Sets()) + Parent.__init__(self, category=InfiniteEnumeratedSets().or_subcategory(category)) + + def __iter__(self): + r""" + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagrams + sage: I = iter(Diagrams()) + sage: for i in range(3): + ....: print(next(I)) + [] + [(0, 0)] + [(0, 1)] + sage: next(I).parent() + Combinatorial diagrams + + sage: del(I) + sage: D = Diagrams() + sage: for d in D: + ....: if len(d) >= 3: + ....: break + sage: d + [(0, 0), (0, 1), (0, 2)] + """ + from sage.sets.non_negative_integers import NonNegativeIntegers + from sage.misc.mrange import cartesian_product_iterator + from sage.misc.misc import subsets + NN = NonNegativeIntegers() + P = cartesian_product_iterator([NN, NN]) + X = subsets(P) + while True: + yield self.element_class(self, next(X)) def _repr_(self): r""" From ae1d71a5eb1e14b71bfb0e6a8a6c87342e7e4fdf Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Sat, 17 Sep 2022 13:48:26 -0500 Subject: [PATCH 238/632] Add tests and add a catch for subclasses --- src/sage/combinat/diagram.py | 60 ++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index 71239537797..a904618dffe 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -382,30 +382,64 @@ def __iter__(self): sage: from sage.combinat.diagram import Diagrams sage: I = iter(Diagrams()) - sage: for i in range(3): + sage: for i in range(10): ....: print(next(I)) [] [(0, 0)] + [(1, 0)] + [(0, 0), (1, 0)] [(0, 1)] + [(0, 0), (0, 1)] + [(0, 1), (1, 0)] + [(0, 0), (0, 1), (1, 0)] + [(2, 0)] + [(0, 0), (2, 0)] sage: next(I).parent() Combinatorial diagrams - sage: del(I) - sage: D = Diagrams() - sage: for d in D: - ....: if len(d) >= 3: - ....: break - sage: d - [(0, 0), (0, 1), (0, 2)] + sage: from sage.combinat.diagram import NorthwestDiagrams + sage: I = iter(NorthwestDiagrams()) + sage: for i in range(20): + ....: print(next(I)) + [] + [(0, 0)] + [(1, 0)] + [(0, 0), (1, 0)] + [(0, 1)] + [(0, 0), (0, 1)] + [(0, 0), (0, 1), (1, 0)] + [(2, 0)] + [(0, 0), (2, 0)] + [(1, 0), (2, 0)] + [(0, 0), (1, 0), (2, 0)] + [(0, 0), (0, 1), (2, 0)] + [(0, 0), (0, 1), (1, 0), (2, 0)] + [(1, 1)] + [(0, 0), (1, 1)] + [(1, 0), (1, 1)] + [(0, 0), (1, 0), (1, 1)] + [(0, 1), (1, 1)] + [(0, 0), (0, 1), (1, 1)] + [(0, 0), (0, 1), (1, 0), (1, 1)] """ - from sage.sets.non_negative_integers import NonNegativeIntegers - from sage.misc.mrange import cartesian_product_iterator + from sage.sets.positive_integers import PositiveIntegers + from sage.all import cartesian_product from sage.misc.misc import subsets - NN = NonNegativeIntegers() - P = cartesian_product_iterator([NN, NN]) + # the product of positive integers automatically implements an + # an enumeration which allows us to get out of the first column + PP = PositiveIntegers() + P = cartesian_product([PP, PP]) X = subsets(P) while True: - yield self.element_class(self, next(X)) + # we want to allow cells in the index-0 row but we + # dont want all of them to be in the index-0 row + cells = next(X) + try: + yield self.element_class(self, tuple((i-1, j-1) for i,j in cells)) + except ValueError: + # if cells causes the .check method of a + # subclass to fail, just go to the next one + pass def _repr_(self): r""" From 217c2039c96fcf230d361572bfa1f24361d23679 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Tue, 20 Sep 2022 07:42:56 -0500 Subject: [PATCH 239/632] PositiveIntegers -> NonNegativeIntegers --- src/sage/combinat/diagram.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index a904618dffe..324eb96312e 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -422,20 +422,18 @@ def __iter__(self): [(0, 0), (0, 1), (1, 1)] [(0, 0), (0, 1), (1, 0), (1, 1)] """ - from sage.sets.positive_integers import PositiveIntegers - from sage.all import cartesian_product + from sage.sets.non_negative_integers import NonNegativeIntegers + from sage.categories.cartesian_product import cartesian_product from sage.misc.misc import subsets # the product of positive integers automatically implements an # an enumeration which allows us to get out of the first column - PP = PositiveIntegers() - P = cartesian_product([PP, PP]) - X = subsets(P) + N = NonNegativeIntegers() + NxN = cartesian_product([N, N]) + X = subsets(NxN) while True: - # we want to allow cells in the index-0 row but we - # dont want all of them to be in the index-0 row cells = next(X) try: - yield self.element_class(self, tuple((i-1, j-1) for i,j in cells)) + yield self.element_class(self, tuple((i, j) for i,j in cells)) except ValueError: # if cells causes the .check method of a # subclass to fail, just go to the next one From 192d2e03ed61927fb540b3d704c62afe2de64907 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Thu, 22 Sep 2022 21:51:06 -0500 Subject: [PATCH 240/632] Add unicode/ascii art --- src/sage/combinat/diagram.py | 79 +++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index 324eb96312e..bb49857afb5 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -199,14 +199,89 @@ def pp(self): . . . . . . . . . . . . """ + return self._pretty_print() + + def _ascii_art_(self): + r""" + Return a visualization of the diagram. + + Cells which are present in the + diagram are filled with a ``O``. Cells which are not present in the + diagram are filled with a ``.``. + + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagram + sage: ascii_art(Diagram([(0,0), (0,3), (2,2), (2,4)])) + O . . O . + . . . . . + . . O . O + sage: ascii_art(Diagram([(0,0), (0,3), (2,2), (2,4)], n_rows=6, n_cols=6)) + O . . O . . + . . . . . . + . . O . O . + . . . . . . + . . . . . . + . . . . . . + """ + return self._pretty_print() + + def _unicode_art_(self): + r""" + Return a unicode visualization of the diagram. + + Cells which are present in the + diagram are filled with a crossed box. Cells which are not present in the + diagram are filled with an empty box. + + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagram + sage: unicode_art(Diagram([(0,0), (0,3), (2,2), (2,4)])) + ☒☐☐☒☐ + ☐☐☐☐☐ + ☐☐☒☐☒ + sage: unicode_art(Diagram([(0,0), (0,3), (2,2), (2,4)], n_rows=6, n_cols=6)) + ☒☐☐☒☐☐ + ☐☐☐☐☐☐ + ☐☐☒☐☒☐ + ☐☐☐☐☐☐ + ☐☐☐☐☐☐ + ☐☐☐☐☐☐ + """ + return self._pretty_print('☒', '☐') + + def _pretty_print(self, cell='O ', empty='. '): + r""" + Return a visualization of the diagram. + + Cells which are present in the + diagram are filled with ``cell``. Cells which are not present in the + diagram are filled with ``empty``. + + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagram + sage: Diagram([(0,0), (0,3), (2,2), (2,4)])._pretty_print('x ','. ') + x . . x . + . . . . . + . . x . x + sage: Diagram([(0,0), (0,3), (2,2), (2,4)], n_rows=6, n_cols=6)._pretty_print('x ','. ') + x . . x . . + . . . . . . + . . x . x . + . . . . . . + . . . . . . + . . . . . . + """ output_str = '' for i in range(self._n_rows): for j in range(self._n_cols): if (i, j) in self: - output_str += 'O ' + output_str += cell else: - output_str += '. ' + output_str += empty output_str += '\n' print(output_str, end='') # don't double up on `\n`'ss From a4a94749cb08ea350fb9d8ee1b630b0f28bf6b2b Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Fri, 23 Sep 2022 07:47:42 -0500 Subject: [PATCH 241/632] Add draft of _latex_ and fix pretty_print/ascii_art string things --- src/sage/combinat/diagram.py | 47 +++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index bb49857afb5..5348a9c100c 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -199,7 +199,7 @@ def pp(self): . . . . . . . . . . . . """ - return self._pretty_print() + print(self._pretty_print(), end='') def _ascii_art_(self): r""" @@ -224,7 +224,7 @@ def _ascii_art_(self): . . . . . . . . . . . . """ - return self._pretty_print() + return ascii_art(self._pretty_print()) def _unicode_art_(self): r""" @@ -249,7 +249,7 @@ def _unicode_art_(self): ☐☐☐☐☐☐ ☐☐☐☐☐☐ """ - return self._pretty_print('☒', '☐') + return unicode_art(self._pretty_print('☒', '☐')) def _pretty_print(self, cell='O ', empty='. '): r""" @@ -284,7 +284,46 @@ def _pretty_print(self, cell='O ', empty='. '): output_str += empty output_str += '\n' - print(output_str, end='') # don't double up on `\n`'ss + return output_str + + def _latex_(self): + r""" + EXAMPLES:: + + sage: from sage.combinat.diagram import Diagram + sage: latex(Diagram([])) + sage: latex(Diagram([(0,0), (0,3), (2,2), (2,4)])) + """ + if not self.cells(): + return "{\\emptyset}" + from string import Template + lr = Template(r'\def\lr#1{\multicolumn{1}{$|@{\hspace{.6ex}}c@{\hspace{.6ex}}$|}{\raisebox{-.3ex}{$$#1$$}}}') + + array = [] + for i in range(self._n_rows): + row = [] + for j in range(self._n_cols): + row.append("\\phantom{x}" if (i, j) in self else None) + array.append(row) + + def end_line(r): + # give the line ending to row ``r`` + if r == 0: + return "".join(r'\cline{%s-%s}'%(i+1, i+1) for i,j in enumerate(array[0]) if j != None) + elif r == len(array): + return "".join(r'\cline{%s-%s}'%(i+1, i+1) for i,j in enumerate(array[r-1]) if j != None) + else: + out = "".join(r'\cline{%s-%s}'%(i+1, i+1) for i,j in enumerate(array[r-1]) if j != None) + out += "".join(r'\cline{%s-%s}'%(i+1, i+1) for i,j in enumerate(array[r]) if j != None) + return out + + # now we draw the arrayarray + tex=r'\raisebox{-.6ex}{$\begin{array}[b]{*{%s}{p{0.6ex}}}'%(max(map(len,array))) + tex+=end_line(0)+'\n' + for r in range(len(array)): + tex+='&'.join('' if c is None else r'\lr{%s}'%(c,) for c in array[r]) + tex += end_line(r+1)+'\n' + return tex+r'\end{array}$}' def number_of_rows(self): r""" From 740651ff031dcb1bb81c6eb67e61bfa32ea78846 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Fri, 23 Sep 2022 14:09:28 -0500 Subject: [PATCH 242/632] Fix latex and unicode/ascii art --- src/sage/combinat/diagram.py | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index 5348a9c100c..8662fb0a8e1 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -224,6 +224,7 @@ def _ascii_art_(self): . . . . . . . . . . . . """ + from sage.typeset.ascii_art import ascii_art return ascii_art(self._pretty_print()) def _unicode_art_(self): @@ -249,6 +250,7 @@ def _unicode_art_(self): ☐☐☐☐☐☐ ☐☐☐☐☐☐ """ + from sage.typeset.unicode_art import unicode_art return unicode_art(self._pretty_print('☒', '☐')) def _pretty_print(self, cell='O ', empty='. '): @@ -263,16 +265,9 @@ def _pretty_print(self, cell='O ', empty='. '): sage: from sage.combinat.diagram import Diagram sage: Diagram([(0,0), (0,3), (2,2), (2,4)])._pretty_print('x ','. ') - x . . x . - . . . . . - . . x . x + 'x . . x . \n. . . . . \n. . x . x \n' sage: Diagram([(0,0), (0,3), (2,2), (2,4)], n_rows=6, n_cols=6)._pretty_print('x ','. ') - x . . x . . - . . . . . . - . . x . x . - . . . . . . - . . . . . . - . . . . . . + 'x . . x . . \n. . . . . . \n. . x . x . \n. . . . . . \n. . . . . . \n. . . . . . \n' """ output_str = '' @@ -292,12 +287,21 @@ def _latex_(self): sage: from sage.combinat.diagram import Diagram sage: latex(Diagram([])) + {\emptyset} sage: latex(Diagram([(0,0), (0,3), (2,2), (2,4)])) + {\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}} + \raisebox{-.6ex}{$\begin{array}[b]{*{5}{p{0.6ex}}}\cline{1-1}\cline{4-4} + \lr{\phantom{x}}&&&\lr{\phantom{x}}&\\\cline{1-1}\cline{4-4} + &&&&\\\cline{3-3}\cline{5-5} + &&\lr{\phantom{x}}&&\lr{\phantom{x}}\\\cline{3-3}\cline{5-5} + \end{array}$} + } + """ if not self.cells(): return "{\\emptyset}" - from string import Template - lr = Template(r'\def\lr#1{\multicolumn{1}{$|@{\hspace{.6ex}}c@{\hspace{.6ex}}$|}{\raisebox{-.3ex}{$$#1$$}}}') + + lr = r'\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}}' array = [] for i in range(self._n_rows): @@ -305,25 +309,24 @@ def _latex_(self): for j in range(self._n_cols): row.append("\\phantom{x}" if (i, j) in self else None) array.append(row) - + def end_line(r): # give the line ending to row ``r`` if r == 0: return "".join(r'\cline{%s-%s}'%(i+1, i+1) for i,j in enumerate(array[0]) if j != None) elif r == len(array): - return "".join(r'\cline{%s-%s}'%(i+1, i+1) for i,j in enumerate(array[r-1]) if j != None) + return r"\\" + "".join(r'\cline{%s-%s}'%(i+1, i+1) for i,j in enumerate(array[r-1]) if j != None) else: - out = "".join(r'\cline{%s-%s}'%(i+1, i+1) for i,j in enumerate(array[r-1]) if j != None) + out = r"\\" + "".join(r'\cline{%s-%s}'%(i+1, i+1) for i,j in enumerate(array[r-1]) if j != None) out += "".join(r'\cline{%s-%s}'%(i+1, i+1) for i,j in enumerate(array[r]) if j != None) return out - # now we draw the arrayarray tex=r'\raisebox{-.6ex}{$\begin{array}[b]{*{%s}{p{0.6ex}}}'%(max(map(len,array))) tex+=end_line(0)+'\n' for r in range(len(array)): tex+='&'.join('' if c is None else r'\lr{%s}'%(c,) for c in array[r]) tex += end_line(r+1)+'\n' - return tex+r'\end{array}$}' + return '{%s\n%s\n}' % (lr, tex+r'\end{array}$}') def number_of_rows(self): r""" From b3a378394cca4b7c8e248695966cf51a18d65c2d Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Sat, 24 Sep 2022 08:28:59 -0500 Subject: [PATCH 243/632] !=None -> is not None --- src/sage/combinat/diagram.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index 8662fb0a8e1..ba84ef2dc08 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -313,12 +313,12 @@ def _latex_(self): def end_line(r): # give the line ending to row ``r`` if r == 0: - return "".join(r'\cline{%s-%s}'%(i+1, i+1) for i,j in enumerate(array[0]) if j != None) + return "".join(r'\cline{%s-%s}'%(i+1, i+1) for i,j in enumerate(array[0]) if j is not None) elif r == len(array): - return r"\\" + "".join(r'\cline{%s-%s}'%(i+1, i+1) for i,j in enumerate(array[r-1]) if j != None) + return r"\\" + "".join(r'\cline{%s-%s}'%(i+1, i+1) for i,j in enumerate(array[r-1]) if j is not None) else: - out = r"\\" + "".join(r'\cline{%s-%s}'%(i+1, i+1) for i,j in enumerate(array[r-1]) if j != None) - out += "".join(r'\cline{%s-%s}'%(i+1, i+1) for i,j in enumerate(array[r]) if j != None) + out = r"\\" + "".join(r'\cline{%s-%s}'%(i+1, i+1) for i,j in enumerate(array[r-1]) if j is not None) + out += "".join(r'\cline{%s-%s}'%(i+1, i+1) for i,j in enumerate(array[r]) if j is not None) return out tex=r'\raisebox{-.6ex}{$\begin{array}[b]{*{%s}{p{0.6ex}}}'%(max(map(len,array))) From e6c4cae0902abbdef92b68badad66c8833021eb3 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 12 Oct 2022 16:42:58 +0200 Subject: [PATCH 244/632] make Stream_uninitialized always dense to avoid maximal recursion error --- src/sage/data_structures/stream.py | 40 ++++++++---------------------- src/sage/rings/lazy_series.py | 2 ++ src/sage/rings/lazy_series_ring.py | 8 +++--- 3 files changed, 16 insertions(+), 34 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index 92aa652403a..e9587b3d009 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -955,7 +955,7 @@ class Stream_function(Stream_inexact): [1, 1, 1, 1, 1, 1, 1, 1, 1, 1] sage: f = Stream_function(lambda n: n, True, 0) - sage: f.get_coefficient(4) + sage: f[4] 4 """ def __init__(self, function, is_sparse, approximate_order, true_order=False): @@ -979,58 +979,38 @@ class Stream_uninitialized(Stream_inexact): INPUT: - - ``is_sparse`` -- boolean; which specifies whether the stream is sparse - ``approximate_order`` -- integer; a lower bound for the order of the stream + Instances of this class are always dense. + EXAMPLES:: sage: from sage.data_structures.stream import Stream_uninitialized sage: from sage.data_structures.stream import Stream_exact sage: one = Stream_exact([1]) - sage: C = Stream_uninitialized(True, 0) + sage: C = Stream_uninitialized(0) sage: C._target sage: C._target = one - sage: C.get_coefficient(4) + sage: C[4] 0 """ - def __init__(self, is_sparse, approximate_order, true_order=False): + def __init__(self, approximate_order, true_order=False): """ Initialize ``self``. TESTS:: sage: from sage.data_structures.stream import Stream_uninitialized - sage: C = Stream_uninitialized(False, 0) + sage: C = Stream_uninitialized(0) sage: TestSuite(C).run(skip="_test_pickling") """ self._target = None if approximate_order is None: raise ValueError("the valuation must be specified for undefined series") - super().__init__(is_sparse, true_order) + super().__init__(False, true_order) self._approximate_order = approximate_order - def get_coefficient(self, n): - """ - Return the ``n``-th coefficient of ``self``. - - INPUT: - - - ``n`` -- integer; the degree for the coefficient - - EXAMPLES:: - - sage: from sage.data_structures.stream import Stream_uninitialized - sage: from sage.data_structures.stream import Stream_exact - sage: one = Stream_exact([1]) - sage: C = Stream_uninitialized(True, 0) - sage: C._target - sage: C._target = one - sage: C.get_coefficient(0) - 1 - """ - return self._target[n] - def iterate_coefficients(self): """ A generator for the coefficients of ``self``. @@ -1040,7 +1020,7 @@ def iterate_coefficients(self): sage: from sage.data_structures.stream import Stream_uninitialized sage: from sage.data_structures.stream import Stream_exact sage: z = Stream_exact([1], order=1) - sage: C = Stream_uninitialized(True, 0) + sage: C = Stream_uninitialized(0) sage: C._target sage: C._target = z sage: n = C.iterate_coefficients() @@ -2520,7 +2500,7 @@ def _approximate_order(self): """ try: return -self._series.order() - except RecursionError: + except (ValueError, RecursionError): raise ValueError("inverse does not exist") @lazy_attribute diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 5b573ef5b19..c302de2a016 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -978,6 +978,8 @@ def define(self, s): sage: C.define(1 + z*C^2) sage: C 1 + z + 2*z^2 + 5*z^3 + 14*z^4 + 42*z^5 + 132*z^6 + O(z^7) + sage: binomial(2000, 1000) / C[1000] + 1001 The Catalan numbers but with a valuation 1:: diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 27e0f963625..a513d903674 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -433,7 +433,7 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No if x is None and coefficients is None: if valuation is None: raise ValueError("the valuation must be specified") - return self.element_class(self, Stream_uninitialized(self._sparse, valuation)) + return self.element_class(self, Stream_uninitialized(valuation)) # WARNING: if x is not explicitly specified as None, it is # set to 0 by Parent.__call__ @@ -606,7 +606,7 @@ def undefined(self, valuation=None): """ if valuation is None: valuation = self._minimal_valuation - coeff_stream = Stream_uninitialized(self._sparse, valuation) + coeff_stream = Stream_uninitialized(valuation) return self.element_class(self, coeff_stream) unknown = undefined @@ -1817,7 +1817,7 @@ def _element_constructor_(self, x=None, valuation=None, constant=None, degree=No BR = self.base_ring() if x is None: assert degree is None - coeff_stream = Stream_uninitialized(self._sparse, valuation) + coeff_stream = Stream_uninitialized(valuation) return self.element_class(self, coeff_stream) try: @@ -2285,7 +2285,7 @@ def _element_constructor_(self, x=None, valuation=None, degree=None, constant=No R = self._laurent_poly_ring if x is None: assert degree is None - coeff_stream = Stream_uninitialized(self._sparse, valuation) + coeff_stream = Stream_uninitialized(valuation) return self.element_class(self, coeff_stream) try: # Try to build stuff using the polynomial ring constructor From e09725c4973792a294d84923746b3097befe5770 Mon Sep 17 00:00:00 2001 From: Johann Birnick Date: Wed, 12 Oct 2022 16:27:09 +0100 Subject: [PATCH 245/632] Added multivariate_interpolation for multivariate polynomial rings. --- .../polynomial/multi_polynomial_ring_base.pyx | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx index 02eeb51bacf..f0e2ac0d6ca 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx @@ -1,6 +1,12 @@ r""" Base class for multivariate polynomial rings """ +import itertools +import warnings +from collections.abc import Iterable +from sage.matrix.constructor import matrix +from sage.modules.free_module_element import vector + import sage.misc.latex from sage.misc.cachefunc import cached_method from sage.misc.misc_c import prod @@ -341,6 +347,109 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): """ return self.remove_var(x)[str(x)] + def multivariate_interpolation(self, bound, *args): + """ + Create a polynomial with specified evaluations. + + CALL FORMATS: + + This function can be called in two ways: + + 1. multivariate_interpolation(bound, points, values) + + 2. multivariate_interpolation(bound, function) + + INPUT: + + * "bound" -- either an integer bounding the total degree or a list/tuple of + integers bounding the degree of the variables + + * "points" -- a list/tuple containing the evaluation points + + * "values" -- a list/tuple containing the desired values at "points" + + * "function" -- a evaluable function in n variables, where n is the number + of variables of the polynomial ring + + OUTPUT: + + 1. A polynomial respecting the bounds and having "values" as values when + evaluated at "points". + + 2. A polynomial respecting the bounds and having the same values as + "function" at exactly so many points so that the polynomial is unique. + + EXAMPLES:: + + sage: def F(a,b,c): + ....: return a^3*b + b + c^2 + 25 + ....: + sage: R. = PolynomialRing(QQ) + sage: R.multivariate_interpolation([3,1,2], F) + x^3*y + z^2 + y + 25 + """ + # get ring and number of variables + R = self.base_ring() + n = self.ngens() + + # we only run the algorithm over fields + if not R.is_field(): + raise TypeError(f'The base ring {R} is not a field.') + + # helper function to sample "num_samples" elements from R + def sample_points(num_samples): + try: + samples = list(itertools.islice(R, num_samples)) + if len(samples) < num_samples: + raise ValueError(f'Could not sample {num_samples} different elements of {R}.') + except NotImplementedError: + if R.characteristic() == 0 or R.characteristic() >= num_samples: + samples = [R(k) for k in range(num_samples)] + else: + raise NotImplementedError(f'Could not sample {num_samples} different elements of {R}.') + + return samples + + # set points and values + if len(args) == 2: + points, values = args + else: + F, = args + + if isinstance(bound, Iterable): + R_points = sample_points(max(bound) + 1) + points = list(itertools.product(*[R_points[:bound[i] + 1] for i in range(n)])) + else: + points = list(itertools.combinations_with_replacement(sample_points(bound + 1), n)) + + values = [F(*x) for x in points] + + # find all possibly appearing exponents + if isinstance(bound, Iterable): + exponents_space = list(itertools.product(*(range(bound[i] + 1) for i in range(n)))) + else: + exponents_space = [] + for entry in itertools.combinations_with_replacement(range(bound + 1), n): + exponents_space.append([entry[0]] + [entry[i] - entry[i - 1] for i in range(1, n)]) + + # build matrix + M = matrix.zero(R, 0, len(points)) + for exponents in exponents_space: + M = M.stack(vector(R, [self.monomial(*exponents)(*x) for x in points])) + + # solve for coefficients and construct polynomial + try: + coeff = M.solve_left(vector(R, values)) + except ValueError: + raise ValueError('Could not find a solution.') + solution = sum(coeff[i] * self.monomial(*exponents_space[i]) for i in range(len(exponents_space))) + + # warn the user if the solution is not unique + if M.left_kernel().dimension() > 0: + warnings.warn('The solution is not unique.') + + return solution + def _coerce_map_from_base_ring(self): """ Return a coercion map from the base ring of ``self``. From 1ff619c2c10ea50ae2b58bac0a2ea50c9db7e991 Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Wed, 12 Oct 2022 22:41:36 -0500 Subject: [PATCH 246/632] Remove SSF --- src/sage/combinat/permutation.py | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index 48b188aacc7..d8fb066a72e 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -220,8 +220,7 @@ - Travis Scrimshaw (2014-02-05): Made :class:`StandardPermutations_n` a finite Weyl group to make it more uniform with :class:`SymmetricGroup`. Added ability to compute the conjugacy classes. -- Trevor K. Karn (2022-08-05): Add :meth:`Permutation.n_reduced_words` and - :meth:`Permutation.stanley_symmetric_function`. +- Trevor K. Karn (2022-08-05): Add :meth:`Permutation.n_reduced_words` - Amrutha P, Shriya M, Divya Aggarwal (2022-08-16): Added Multimajor Index. Classes and methods @@ -3028,26 +3027,6 @@ def number_of_reduced_words(self): return sum(map(_tableau_contribution, Tx)) - def stanley_symmetric_function(self): - r""" - Return the Stanley symmetric function associated to ``self``. - - EXAMPLES:: - - sage: p = Permutation([4,5,2,3,1]) - sage: p.stanley_symmetric_function() - 56*m[1, 1, 1, 1, 1, 1, 1, 1] + 30*m[2, 1, 1, 1, 1, 1, 1] - + 16*m[2, 2, 1, 1, 1, 1] + 9*m[2, 2, 2, 1, 1] + 6*m[2, 2, 2, 2] - + 10*m[3, 1, 1, 1, 1, 1] + 5*m[3, 2, 1, 1, 1] + 3*m[3, 2, 2, 1] - + m[3, 3, 1, 1] + m[3, 3, 2] + 2*m[4, 1, 1, 1, 1] + m[4, 2, 1, 1] - + m[4, 2, 2] - """ - from sage.combinat.sf.sf import SymmetricFunctions - from sage.rings.rational_field import QQ - s = SymmetricFunctions(QQ).s() - m = SymmetricFunctions(QQ).m() - return m(sum(s[T.shape()] for T in self.rothe_diagram().peelable_tableaux())) - ################ # Fixed Points # ################ From 965b28499a900390601bce60fc8d1e3f9ceff9ed Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Wed, 12 Oct 2022 23:12:06 -0500 Subject: [PATCH 247/632] Fix w-inverse --- src/sage/combinat/diagram.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index ba84ef2dc08..b6982155197 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -1410,8 +1410,8 @@ def RotheDiagram(w): raise ValueError('w must be a Permutation') N = w.size() - + winv = w.inverse() cells = [c for c in cartesian_product_iterator((range(N), range(N))) - if c[0]+1 < w.inverse()(c[1]+1) and c[1]+1 < w(c[0]+1)] + if c[0]+1 < winv(c[1]+1) and c[1]+1 < w(c[0]+1)] return NorthwestDiagram(cells, n_rows=N, n_cols=N, check=False) From de424bd78aab5b7e151316644ad460b39261734f Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 13 Oct 2022 08:50:39 +0200 Subject: [PATCH 248/632] make internal rings sparse or dense if the lazy series ring is sparse or dense --- src/sage/rings/lazy_series_ring.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index a513d903674..ca4cb8aa8ff 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -1197,8 +1197,7 @@ def __init__(self, base_ring, names, sparse=True, category=None): raise ValueError("only univariate lazy Laurent series are implemented") self._arity = 1 self._minimal_valuation = None - # We always use the dense because our CS_exact is implemented densely - self._laurent_poly_ring = LaurentPolynomialRing(base_ring, names) + self._laurent_poly_ring = LaurentPolynomialRing(base_ring, names, sparse=sparse) self._internal_poly_ring = self._laurent_poly_ring category = Algebras(base_ring.category()) @@ -1563,12 +1562,13 @@ def __init__(self, base_ring, names, sparse=True, category=None): """ self._sparse = sparse self._minimal_valuation = 0 - self._laurent_poly_ring = PolynomialRing(base_ring, names) self._arity = len(names) if self._arity == 1: + self._laurent_poly_ring = PolynomialRing(base_ring, names, sparse=sparse) self._internal_poly_ring = self._laurent_poly_ring else: - self._internal_poly_ring = PolynomialRing(self._laurent_poly_ring, "DUMMY_VARIABLE") + self._laurent_poly_ring = PolynomialRing(base_ring, names) + self._internal_poly_ring = PolynomialRing(self._laurent_poly_ring, "DUMMY_VARIABLE", sparse=sparse) category = Algebras(base_ring.category()) mixin_gcd = False if self._arity == 1: @@ -2160,7 +2160,7 @@ def __init__(self, basis, sparse=True, category=None): from sage.algebras.free_algebra import FreeAlgebra self._internal_poly_ring = FreeAlgebra(self._laurent_poly_ring, 1, "DUMMY_VARIABLE") else: - self._internal_poly_ring = PolynomialRing(self._laurent_poly_ring, "DUMMY_VARIABLE") + self._internal_poly_ring = PolynomialRing(self._laurent_poly_ring, "DUMMY_VARIABLE", sparse=sparse) def _repr_(self): """ @@ -2560,7 +2560,7 @@ def __init__(self, base_ring, names, sparse=True, category=None): self._minimal_valuation = 1 self._arity = 1 self._laurent_poly_ring = SR # TODO: it would be good to have something better than the symbolic ring - self._internal_poly_ring = PolynomialRing(base_ring, names, sparse=True) + self._internal_poly_ring = PolynomialRing(base_ring, names, sparse=sparse) category = Algebras(base_ring.category()) if base_ring in IntegralDomains(): From 58de6133486d60c05d8c474faf6f376ed5bc890b Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 13 Oct 2022 11:55:10 +0200 Subject: [PATCH 249/632] fix pickling for FrobeniusEndomorphism_generic --- src/sage/rings/morphism.pyx | 38 +++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/sage/rings/morphism.pyx b/src/sage/rings/morphism.pyx index 96a55bffd69..cc6b6e9efc8 100644 --- a/src/sage/rings/morphism.pyx +++ b/src/sage/rings/morphism.pyx @@ -2939,6 +2939,44 @@ cdef class FrobeniusEndomorphism_generic(RingHomomorphism): self._q = self._p ** self._power RingHomomorphism.__init__(self, Hom(domain, domain)) + cdef _update_slots(self, dict _slots): + """ + Helper for copying and pickling. + + EXAMPLES:: + + sage: K = Frac(GF(5)['T']) + sage: phi = K.frobenius_endomorphism() + sage: psi = copy(phi) + sage: phi == psi + True + """ + self._p = _slots['prime'] + self._power = _slots['power'] + self._q = self._p ** self._power + RingHomomorphism._update_slots(self, _slots) + + cdef dict _extra_slots(self): + """ + Helper for copying and pickling. + + EXAMPLES:: + + sage: K = Frac(GF(25)['T']) + sage: phi = K.frobenius_endomorphism(2) + sage: phi + Frobenius endomorphism x |--> x^(5^2) of Fraction Field of Univariate Polynomial Ring in T over Finite Field in z2 of size 5^2 + + sage: psi = loads(dumps(phi)); psi + Frobenius endomorphism x |--> x^(5^2) of Fraction Field of Univariate Polynomial Ring in T over Finite Field in z2 of size 5^2 + sage: phi == psi + True + """ + slots = RingHomomorphism._extra_slots(self) + slots['prime'] = self._p + slots['power'] = self._power + return slots + def _repr_(self): """ Return a string representation of this endomorphism. From 5e29ad644717a5c3ef8af53a8fbda8e1bdfdf4a8 Mon Sep 17 00:00:00 2001 From: Johann Birnick Date: Thu, 13 Oct 2022 11:45:19 +0100 Subject: [PATCH 250/632] Improved documentation. --- .../polynomial/multi_polynomial_ring_base.pyx | 63 ++++++++++++++++--- 1 file changed, 54 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx index f0e2ac0d6ca..d5fbf8059ac 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx @@ -348,7 +348,7 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): return self.remove_var(x)[str(x)] def multivariate_interpolation(self, bound, *args): - """ + r""" Create a polynomial with specified evaluations. CALL FORMATS: @@ -361,32 +361,77 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): INPUT: - * "bound" -- either an integer bounding the total degree or a list/tuple of - integers bounding the degree of the variables + * "bound" -- either an integer bounding the total degree or a list/tuple of integers bounding the degree of the variables * "points" -- a list/tuple containing the evaluation points * "values" -- a list/tuple containing the desired values at "points" - * "function" -- a evaluable function in n variables, where n is the number - of variables of the polynomial ring + * "function" -- a evaluable function in n variables, where n is the number of variables of the polynomial ring OUTPUT: - 1. A polynomial respecting the bounds and having "values" as values when - evaluated at "points". + 1. A polynomial respecting the bounds and having "values" as values when evaluated at "points". - 2. A polynomial respecting the bounds and having the same values as - "function" at exactly so many points so that the polynomial is unique. + 2. A polynomial respecting the bounds and having the same values as "function" at exactly so many points so that the polynomial is unique. EXAMPLES:: + sage: def F(a,b,c): + ....: return a^3*b + b + c^2 + 25 + ....: + sage: R. = PolynomialRing(QQ) + sage: R.multivariate_interpolation(4, F) + x^3*y + z^2 + y + 25 + + sage: def F(a,b,c): ....: return a^3*b + b + c^2 + 25 ....: sage: R. = PolynomialRing(QQ) sage: R.multivariate_interpolation([3,1,2], F) x^3*y + z^2 + y + 25 + + + sage: def F(a,b,c): + ....: return a^3*b + b + c^2 + 25 + ....: + sage: R. = PolynomialRing(QQ) + sage: points = [(5,1,1),(7,2,2),(8,5,-1),(2,5,3),(1,4,0),(5,9,0), + ....: (2,7,0),(1,10,13),(0,0,1),(-1,1,0),(2,5,3),(1,1,1),(7,4,11), + ....: (12,1,9),(1,1,3),(4,-1,2),(0,1,5),(5,1,3),(3,1,-2),(2,11,3), + ....: (4,12,19),(3,1,1),(5,2,-3),(12,1,1),(2,3,4)] + sage: R.multivariate_interpolation([3,1,2], points, [F(*x) for x in points]) + x^3*y + z^2 + y + 25 + + ALGORITHM: + + Solves a linear system of equations with the linear algebra module. + If the points are not specified, it samples exactly as many points as needed for a unique solution. + + NOTE: + + It will only run if the base ring is a field, even though it might work otherwise as well. + If your base ring is an integral domain, let it run over the fraction field. + + WARNING:: + + If you don't provide point/value pairs but just a function, it + will only use as many points as needed for a unique solution with + the given bounds. In particular it will *not* notice or check + whether the result yields the correct evaluation for other points + as well. So if you give wrong bounds, you will get a wrong answer + without a warning. + + sage: def F(a,b,c): + ....: return a^3*b + b + c^2 + 25 + ....: + sage: R. = PolynomialRing(QQ) + sage: R.multivariate_interpolation(3,F) + 1/2*x^3 + x*y + z^2 - 1/2*x + y + 25 + + SEEALSO: + :meth:`lagrange_polynomial` """ # get ring and number of variables R = self.base_ring() From 5020b9decfe8a8f375102c254c28cb51ce3c7a12 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 13 Oct 2022 13:10:00 +0200 Subject: [PATCH 251/632] adapt exp and log to new sparsity --- src/sage/data_structures/stream.py | 6 ++++++ src/sage/rings/lazy_series.py | 17 ++++++++++------- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/sage/data_structures/stream.py b/src/sage/data_structures/stream.py index e9587b3d009..f8f6dc6a186 100644 --- a/src/sage/data_structures/stream.py +++ b/src/sage/data_structures/stream.py @@ -984,6 +984,11 @@ class Stream_uninitialized(Stream_inexact): Instances of this class are always dense. + .. TODO:: + + shouldn't instances of this class share the cache with its + ``_target``? + EXAMPLES:: sage: from sage.data_structures.stream import Stream_uninitialized @@ -994,6 +999,7 @@ class Stream_uninitialized(Stream_inexact): sage: C._target = one sage: C[4] 0 + """ def __init__(self, approximate_order, true_order=False): """ diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 2b0c9adc937..902d57d5bab 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -1213,12 +1213,13 @@ def define(self, s): if not isinstance(s, LazyModuleElement): s = self.parent()(s) + coeff_stream = s._coeff_stream # Special case when it has a trivial definition - if isinstance(s._coeff_stream, (Stream_zero, Stream_exact)): - self._coeff_stream = s._coeff_stream + if isinstance(coeff_stream, (Stream_zero, Stream_exact)): + self._coeff_stream = coeff_stream return - self._coeff_stream._target = s._coeff_stream + self._coeff_stream._target = coeff_stream # an alias for compatibility with padics set = define @@ -3137,12 +3138,13 @@ def exp(self): raise ValueError("can only compose with a positive valuation series") # WARNING: d_self need not be a proper element of P, e.g. for # multivariate power series + # We make the streams dense, because all coefficients have to be computed anyway d_self = Stream_function(lambda n: (n + 1) * coeff_stream[n + 1], - P.is_sparse(), 0) + False, 0) f = P.undefined(valuation=0) - d_self_f = Stream_cauchy_mul(d_self, f._coeff_stream) + d_self_f = Stream_cauchy_mul(d_self, f._coeff_stream, False) int_d_self_f = Stream_function(lambda n: d_self_f[n-1] / R(n) if n else R.one(), - P.is_sparse(), 0) + False, 0) f._coeff_stream._target = int_d_self_f return f @@ -3190,7 +3192,8 @@ def log(self): d_self = Stream_function(lambda n: (n + 1) * coeff_stream[n + 1], P.is_sparse(), 0) d_self_quo_self = Stream_cauchy_mul(d_self, - Stream_cauchy_invert(coeff_stream)) + Stream_cauchy_invert(coeff_stream), + P.is_sparse()) int_d_self_quo_self = Stream_function(lambda n: d_self_quo_self[n-1] / R(n), P.is_sparse(), 1) return P.element_class(P, int_d_self_quo_self) From 8730e64d5453ed78fcd93ea403109c81e36e954f Mon Sep 17 00:00:00 2001 From: Johann Birnick Date: Thu, 13 Oct 2022 14:50:54 +0100 Subject: [PATCH 252/632] Improved style. --- .../polynomial/multi_polynomial_ring_base.pyx | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx index d5fbf8059ac..f59c521a0bb 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx @@ -348,7 +348,7 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): return self.remove_var(x)[str(x)] def multivariate_interpolation(self, bound, *args): - r""" + """ Create a polynomial with specified evaluations. CALL FORMATS: @@ -361,19 +361,24 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): INPUT: - * "bound" -- either an integer bounding the total degree or a list/tuple of integers bounding the degree of the variables + * "bound" -- either an integer bounding the total degree or a + list/tuple of integers bounding the degree of the variables * "points" -- a list/tuple containing the evaluation points * "values" -- a list/tuple containing the desired values at "points" - * "function" -- a evaluable function in n variables, where n is the number of variables of the polynomial ring + * "function" -- a evaluable function in n variables, where n is the + number of variables of the polynomial ring OUTPUT: - 1. A polynomial respecting the bounds and having "values" as values when evaluated at "points". + 1. A polynomial respecting the bounds and having "values" as values + when evaluated at "points". - 2. A polynomial respecting the bounds and having the same values as "function" at exactly so many points so that the polynomial is unique. + 2. A polynomial respecting the bounds and having the same values as + "function" at exactly so many points so that the polynomial is + unique. EXAMPLES:: @@ -407,12 +412,14 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): ALGORITHM: Solves a linear system of equations with the linear algebra module. - If the points are not specified, it samples exactly as many points as needed for a unique solution. + If the points are not specified, it samples exactly as many points + as needed for a unique solution. NOTE: - It will only run if the base ring is a field, even though it might work otherwise as well. - If your base ring is an integral domain, let it run over the fraction field. + It will only run if the base ring is a field, even though it might + work otherwise as well. If your base ring is an integral domain, + let it run over the fraction field. WARNING:: From 291d799f05303a25b4afbbc2b74e87651aad8cc2 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 14 Oct 2022 16:28:57 +0900 Subject: [PATCH 253/632] Some last reviewer changes to improve some doc and unicode art. --- src/doc/en/reference/references/index.rst | 6 +- src/sage/combinat/diagram.py | 248 +++++++++++++--------- src/sage/combinat/permutation.py | 4 - src/sage/combinat/skew_partition.py | 11 +- src/sage/combinat/skew_tableau.py | 11 +- 5 files changed, 166 insertions(+), 114 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index ee5166235ab..bb2360b3ef5 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -5100,10 +5100,10 @@ REFERENCES: **6** (1997), 59-87. .. [RSS] :wikipedia:`Residual_sum_of_squares`, accessed 13th - October 2009. + October 2009. -.. [RS1995] Victor Reiner, Mark Shimozono, "Plactification", - J. Algebraic Combin. **4** (1995), 331-351. +.. [RS1995] Victor Reiner, Mark Shimozono, *Plactification*, + J. Algebraic Combin. **4** (1995), pp. 331-351. .. [RS2012] G. Rudolph and M. Schmidt, "Differential Geometry and Mathematical Physics. Part I. Manifolds, Lie Groups and Hamiltonian Systems", Springer, 2012. diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index b6982155197..a4a4169848a 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -24,7 +24,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.categories.sets_cat import Sets from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.combinat.composition import Composition from sage.combinat.partition import Partition @@ -46,10 +45,10 @@ class Diagram(ClonableArray, metaclass=InheritComparisonClasscallMetaclass): The positions are indexed by rows and columns as in a matrix. For example, a Ferrer's diagram is a diagram obtained from a partition - `\lambda = (\lambda_0, \lambda_1, \ldots, \lambda_\ell)` where the cells are - in rows `i` for `0 \leq i \leq \ell` and the cells in row `i` consist of - `(i,j)` for `0 \leq j < \lambda_i`. In English notation, the indices are - read from top left to bottom right as in a matrix. + `\lambda = (\lambda_0, \lambda_1, \ldots, \lambda_{\ell})`, where the + cells are in rows `i` for `0 \leq i \leq \ell` and the cells in row `i` + consist of `(i,j)` for `0 \leq j < \lambda_i`. In English notation, the + indices are read from top left to bottom right as in a matrix. Indexing conventions are the same as :class:`~sage.combinat.partition.Partition`. Printing the diagram of a @@ -100,12 +99,6 @@ class Diagram(ClonableArray, metaclass=InheritComparisonClasscallMetaclass): . . . . . . . . . . . . . . . . . . - - TESTS:: - - sage: from sage.combinat.diagram import Diagrams - sage: D = Diagrams().an_element() - sage: TestSuite(D).run() """ @staticmethod def __classcall_private__(self, cells, n_rows=None, n_cols=None, check=True): @@ -123,6 +116,8 @@ def __classcall_private__(self, cells, n_rows=None, n_cols=None, check=True): def __init__(self, parent, cells, n_rows=None, n_cols=None, check=True): r""" + Initialize ``self``. + EXAMPLES:: sage: from sage.combinat.diagram import Diagram @@ -133,6 +128,7 @@ def __init__(self, parent, cells, n_rows=None, n_cols=None, check=True): 4 sage: D1.ncols() 4 + sage: TestSuite(D1).run() We can specify the number of rows and columns explicitly, in case they are supposed to be empty:: @@ -147,6 +143,7 @@ def __init__(self, parent, cells, n_rows=None, n_cols=None, check=True): . O . . . . . . . . . . O . . + sage: TestSuite(D2).run() """ self._cells = frozenset(cells) @@ -155,8 +152,8 @@ def __init__(self, parent, cells, n_rows=None, n_cols=None, check=True): N_rows = max(c[0] for c in self._cells) N_cols = max(c[1] for c in self._cells) else: # if there are no cells - N_rows = 0 - N_cols = 0 + N_rows = -1 + N_cols = -1 if n_rows is not None: if n_rows <= N_rows: @@ -198,8 +195,13 @@ def pp(self): . . . . . . . . . . . . . . . . . . + sage: Diagram([]).pp() + - """ - print(self._pretty_print(), end='') + if self._n_rows == 0 or self._n_cols == 0: + print('-') + return + print("\n".join(self._pretty_print())) def _ascii_art_(self): r""" @@ -223,9 +225,13 @@ def _ascii_art_(self): . . . . . . . . . . . . . . . . . . + sage: ascii_art(Diagram([])) + - """ from sage.typeset.ascii_art import ascii_art - return ascii_art(self._pretty_print()) + if self._n_rows == 0 or self._n_cols == 0: + return ascii_art("-") + return ascii_art("\n".join(self._pretty_print())) def _unicode_art_(self): r""" @@ -239,19 +245,45 @@ def _unicode_art_(self): sage: from sage.combinat.diagram import Diagram sage: unicode_art(Diagram([(0,0), (0,3), (2,2), (2,4)])) - ☒☐☐☒☐ - ☐☐☐☐☐ - ☐☐☒☐☒ + ┌─┬─┬─┬─┬─┐ + │X│ │ │X│ │ + ├─┼─┼─┼─┼─┤ + │ │ │ │ │ │ + ├─┼─┼─┼─┼─┤ + │ │ │X│ │X│ + └─┴─┴─┴─┴─┘ sage: unicode_art(Diagram([(0,0), (0,3), (2,2), (2,4)], n_rows=6, n_cols=6)) - ☒☐☐☒☐☐ - ☐☐☐☐☐☐ - ☐☐☒☐☒☐ - ☐☐☐☐☐☐ - ☐☐☐☐☐☐ - ☐☐☐☐☐☐ + ┌─┬─┬─┬─┬─┬─┐ + │X│ │ │X│ │ │ + ├─┼─┼─┼─┼─┼─┤ + │ │ │ │ │ │ │ + ├─┼─┼─┼─┼─┼─┤ + │ │ │X│ │X│ │ + ├─┼─┼─┼─┼─┼─┤ + │ │ │ │ │ │ │ + ├─┼─┼─┼─┼─┼─┤ + │ │ │ │ │ │ │ + ├─┼─┼─┼─┼─┼─┤ + │ │ │ │ │ │ │ + └─┴─┴─┴─┴─┴─┘ + sage: unicode_art(Diagram([])) + ∅ """ from sage.typeset.unicode_art import unicode_art - return unicode_art(self._pretty_print('☒', '☐')) + if self._n_rows == 0 or self._n_cols == 0: + return unicode_art("∅") + + ndivs = self._n_cols - 1 + cell = "│X" + empty = "│ " + it = self._pretty_print(cell, empty) + ret = "┌─" + "┬─"*ndivs + "┐" + ret += "\n" + next(it) + "│" + for row in it: + ret += "\n├─" + "┼─"*ndivs + "┤" + ret += "\n" + row + "│" + ret += "\n└─" + "┴─"*ndivs + "┘" + return unicode_art(ret) def _pretty_print(self, cell='O ', empty='. '): r""" @@ -264,25 +296,24 @@ def _pretty_print(self, cell='O ', empty='. '): EXAMPLES:: sage: from sage.combinat.diagram import Diagram - sage: Diagram([(0,0), (0,3), (2,2), (2,4)])._pretty_print('x ','. ') - 'x . . x . \n. . . . . \n. . x . x \n' - sage: Diagram([(0,0), (0,3), (2,2), (2,4)], n_rows=6, n_cols=6)._pretty_print('x ','. ') - 'x . . x . . \n. . . . . . \n. . x . x . \n. . . . . . \n. . . . . . \n. . . . . . \n' + sage: "\n".join(Diagram([(0,0), (0,3), (2,2), (2,4)])._pretty_print('x ','. ')) + 'x . . x . \n. . . . . \n. . x . x ' + sage: "\n".join(Diagram([(0,0), (0,3), (2,2), (2,4)], n_rows=6, n_cols=6)._pretty_print('x ','. ')) + 'x . . x . . \n. . . . . . \n. . x . x . \n. . . . . . \n. . . . . . \n. . . . . . ' """ - output_str = '' - for i in range(self._n_rows): + output_str = '' for j in range(self._n_cols): if (i, j) in self: output_str += cell else: output_str += empty - output_str += '\n' - - return output_str + yield output_str def _latex_(self): r""" + Return a latex representation of ``self``. + EXAMPLES:: sage: from sage.combinat.diagram import Diagram @@ -296,9 +327,8 @@ def _latex_(self): &&\lr{\phantom{x}}&&\lr{\phantom{x}}\\\cline{3-3}\cline{5-5} \end{array}$} } - """ - if not self.cells(): + if self._n_rows == 0 or self._n_cols == 0: return "{\\emptyset}" lr = r'\def\lr#1{\multicolumn{1}{|@{\hspace{.6ex}}c@{\hspace{.6ex}}|}{\raisebox{-.3ex}{$#1$}}}' @@ -330,7 +360,7 @@ def end_line(r): def number_of_rows(self): r""" - Return the total number of rows of the cell. + Return the total number of rows of ``self``. EXAMPLES: @@ -365,7 +395,7 @@ def number_of_rows(self): def number_of_cols(self): r""" - Return the total number of rows of the cell. + Return the total number of rows of ``self``. EXAMPLES: @@ -391,7 +421,6 @@ def number_of_cols(self): . . . . . . . . O . . . """ - return self._n_cols ncols = number_of_cols @@ -480,6 +509,8 @@ class Diagrams(UniqueRepresentation, Parent): """ def __init__(self, category=None): r""" + Initialize ``self``. + EXAMPLES:: sage: from sage.combinat.diagram import Diagrams @@ -490,11 +521,12 @@ def __init__(self, category=None): sage: TestSuite(Dgms).run() """ - Parent.__init__(self, category=InfiniteEnumeratedSets().or_subcategory(category)) def __iter__(self): r""" + Iterate over ``self``. + EXAMPLES:: sage: from sage.combinat.diagram import Diagrams @@ -558,6 +590,8 @@ def __iter__(self): def _repr_(self): r""" + Return a string representation of ``self``. + EXAMPLES:: sage: from sage.combinat.diagram import Diagrams @@ -568,6 +602,8 @@ def _repr_(self): def _element_constructor_(self, cells, n_rows=None, n_cols=None, check=True): r""" + Cosntruct an element of ``self``. + EXAMPLES:: sage: from sage.combinat.diagram import Diagrams @@ -616,6 +652,8 @@ def _element_constructor_(self, cells, n_rows=None, n_cols=None, check=True): def _an_element_(self): r""" + Return an element of ``self``. + EXAMPLES:: sage: from sage.combinat.diagram import Diagrams @@ -632,7 +670,7 @@ def _an_element_(self): def from_polyomino(self, p): r""" Create the diagram corresponding to a 2d - :class:`~sage.combinat.tiling.Polyomino.` + :class:`~sage.combinat.tiling.Polyomino` EXAMPLES:: @@ -649,22 +687,22 @@ def from_polyomino(self, p): O . . O O O - The method only works for 2d `Polyomino`s:: + This only works for a 2d :class:`~sage.combinat.tiling.Polyomino`:: sage: p = Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') sage: Diagrams().from_polyomino(p) Traceback (most recent call last): ... - ValueError: Dimension of the polyomino must be 2 + ValueError: the polyomino must be 2 dimensional """ if not p._dimension == 2: - raise ValueError("Dimension of the polyomino must be 2") + raise ValueError("the polyomino must be 2 dimensional") cells = list(map(tuple, p)) return self.element_class(self, cells) def from_composition(self, alpha): r""" - Create the diagram corresponding to a weak composition `alpha \vDash n`. + Create the diagram corresponding to a weak composition `\alpha \vDash n`. EXAMPLES:: @@ -711,7 +749,6 @@ def from_zero_one_matrix(self, M, check=True): O . . O . . . . . - """ # check matrix is zero-one n_rows, n_cols = M.dimensions() @@ -759,8 +796,7 @@ class NorthwestDiagram(Diagram, metaclass=InheritComparisonClasscallMetaclass): O . . """ @staticmethod - def __classcall_private__(self, cells, n_rows=None, - n_cols=None, check=True): + def __classcall_private__(self, cells, n_rows=None, n_cols=None, check=True): """ Normalize input to ensure a correct parent. This method also allows one to specify whether or not to check the northwest property for the @@ -816,15 +852,16 @@ def check(self): def peelable_tableaux(self): r""" + Return the set of peelable tableaux whose diagram is ``self``. + For a fixed northwest diagram `D`, we say that a Young tableau `T` is `D`-peelable if: - - - the row indices of the cells in the first column of `D` are - the entries in an initial segment in the first column of `T` and - - - the tableau `Q` obtained by removing those cells from `T` and playing - jeu de taquin is `D-C`-peelable, where `D-C` is the diagram formed by - forgetting the first column of `D`. + + 1. the row indices of the cells in the first column of `D` are + the entries in an initial segment in the first column of `T` and + 2. the tableau `Q` obtained by removing those cells from `T` and playing + jeu de taquin is `(D-C)`-peelable, where `D-C` is the diagram formed + by forgetting the first column of `D`. Reiner and Shimozono [RS1995]_ showed that the number `\operatorname{red}(w)` of reduced words of a permutation `w` may be @@ -833,7 +870,7 @@ def peelable_tableaux(self): .. MATH:: - \operatorname{red}(w) = \sum_{T} f_{\operatorname{shape} T} + \operatorname{red}(w) = \sum_{T} f_{\operatorname{shape} T}, where the sum runs over the `D(w)`-peelable tableaux `T` and `f_\lambda` is the number of standard Young tableaux of shape `\lambda` (which may @@ -865,7 +902,7 @@ def peelable_tableaux(self): sage: NWD.peelable_tableaux() {[[1], [3]]} - From [RS1995]_ we know that there is only one peelable tableau for the + From [RS1995]_, we know that there is only one peelable tableau for the Rothe diagram of the permutation (in one line notation) `251643`:: sage: D = NorthwestDiagram([(1, 2), (1, 3), (3, 2), (3, 3), (4, 2)]) @@ -1026,10 +1063,9 @@ class NorthwestDiagrams(Diagrams): r""" Diagrams satisfying the northwest property. - A diagram is a - *northwest diagram* if it satsifies the property that: the presence of two - cells `(i_1, j_1)` and `(i_2, j_2)` in a diagram `D` implies the presence of - the cell `(\min(i_1, i_2), \min(j_1, j_2))`. + A diagram `D` is a *northwest diagram* if for every two cells `(i_1, j_1)` + and `(i_2, j_2)` in `D` then there exists the cell + `(\min(i_1, i_2), \min(j_1, j_2)) \in D`. EXAMPLES:: @@ -1060,8 +1096,8 @@ class NorthwestDiagrams(Diagrams): ... ValueError: diagram is not northwest - However, this behavior can be turned off if you are confident that you are - providing a northwest diagram:: + However, this behavior can be turned off if you are confident that + you are providing a northwest diagram:: sage: N = NorthwestDiagram([(0, 0), (0, 10), (5, 0), ....: (1, 1), (0, 1), (1, 0)], @@ -1074,8 +1110,8 @@ class NorthwestDiagrams(Diagrams): . . . . . . . . . . . O . . . . . . . . . . - Note that arbitrary diagrams which happen to be northwest diagrams only live - in the parent of :class:`Diagrams`:: + Note that arbitrary diagrams which happen to be northwest diagrams + only live in the parent of :class:`Diagrams`:: sage: D = Diagram([(0, 0), (0, 10), (5, 0), (1, 1), (0, 1), (1, 0)]) sage: D.pp() @@ -1115,8 +1151,8 @@ class NorthwestDiagrams(Diagrams): D(\omega) = \{(\omega_j, i) : i \omega_j \}. - We can construct one by calling :meth:`rothe_diagram` method on the parent - class :class:`NorthwestDiagrams`:: + We can construct one by calling :meth:`rothe_diagram` method on the set + of all :class:`~sage.combinat.diagram.NorthwestDiagrams`:: sage: w = Permutations(4)([4,3,2,1]) sage: NorthwestDiagrams().rothe_diagram(w).pp() @@ -1125,9 +1161,9 @@ class :class:`NorthwestDiagrams`:: O . . . . . . . - To turn a Ferrers diagram into a northwest diagram, we may call the - :meth:`from_partition` method. This will return a Ferrer's diagram in the - parent of all northwest diagrams. For many use-cases it is probably better + To turn a Ferrers diagram into a northwest diagram, we may call + :meth:`from_partition`. This will return a Ferrer's diagram in the + set of all northwest diagrams. For many use-cases it is probably better to get Ferrer's diagrams by the corresponding method on partitons, namely :meth:`sage.combinat.partitions.Partitions.ferrers_diagram`:: @@ -1165,6 +1201,8 @@ class :class:`NorthwestDiagrams`:: def _repr_(self): r""" + Return a string representation of ``self``. + EXAMPLES:: sage: from sage.combinat.diagram import NorthwestDiagrams @@ -1175,6 +1213,8 @@ def _repr_(self): def _an_element_(self): r""" + Return an element of ``self``. + EXAMPLES:: sage: from sage.combinat.diagram import NorthwestDiagrams @@ -1194,8 +1234,9 @@ def rothe_diagram(self, w): r""" Return the Rothe diagram of ``w``. - One particular way of constructing a northwest diagram from a permutation is - by constructing its Rothe diagram. Formally, if `\omega` is a permutation, + We construct a northwest diagram from a permutation by + constructing its Rothe diagram. Formally, if `\omega` is + a :class:`~sage.combinat.permutation.Permutation` then the Rothe diagram `D(\omega)` is the diagram whose cells are .. MATH:: @@ -1205,28 +1246,16 @@ def rothe_diagram(self, w): Informally, one can construct the Rothe diagram by starting with all `n^2` possible cells, and then deleting the cells `(i, \omega(i))` as well as all cells to the right and below. (These are sometimes called - "death rays".) To compute a Rothe diagram in Sage, start with a - :class:`~sage.combinat.permutation.Permutation`:: + "death rays".) - sage: w = Permutations(8)([2,5,4,1,3,6,7,8]) + .. SEEALSO:: - Then call :func:`RotheDiagram` on ``w``:: - - sage: from sage.combinat.diagram import RotheDiagram - sage: RotheDiagram(w).pp() - O . . . . . . . - O . O O . . . . - O . O . . . . . - . . . . . . . . - . . . . . . . . - . . . . . . . . - . . . . . . . . - . . . . . . . . + :func:`~sage.combinat.diagram.RotheDiagram` EXAMPLES:: - sage: w = Permutations(3)([2,1,3]) sage: from sage.combinat.diagram import NorthwestDiagrams + sage: w = Permutations(3)([2,1,3]) sage: NorthwestDiagrams().rothe_diagram(w).pp() O . . . . . @@ -1235,6 +1264,17 @@ def rothe_diagram(self, w): O . . . . . . . . + + sage: w = Permutations(8)([2,5,4,1,3,6,7,8]) + sage: NorthwestDiagrams().rothe_diagram(w).pp() + O . . . . . . . + O . O O . . . . + O . O . . . . . + . . . . . . . . + . . . . . . . . + . . . . . . . . + . . . . . . . . + . . . . . . . . """ return RotheDiagram(w) @@ -1359,7 +1399,7 @@ def from_parallelogram_polyomino(self, p): def RotheDiagram(w): r""" - The Rothe diagram of a permutation ``w`` + The Rothe diagram of a permutation ``w``. EXAMPLES:: @@ -1381,7 +1421,21 @@ def RotheDiagram(w): sage: D.parent() Combinatorial northwest diagrams - Currently, only elements of the parent + Some other examples:: + + sage: RotheDiagram([2, 1, 4, 3]).pp() + O . . . + . . . . + . . O . + . . . . + + sage: RotheDiagram([4, 1, 3, 2]).pp() + O O O . + . . . . + . O . . + . . . . + + Currently, only elements of the set of :class:`sage.combinat.permutations.Permutations` are supported. In particular, elements of permutation groups are not supported:: @@ -1389,8 +1443,7 @@ def RotheDiagram(w): sage: RotheDiagram(w) Traceback (most recent call last): ... - ValueError: w must be a Permutation - + ValueError: w must be a permutation TESTS:: @@ -1403,15 +1456,16 @@ def RotheDiagram(w): . . . . . . . . . . """ - - from sage.misc.mrange import cartesian_product_iterator - - if w not in Permutations(): - raise ValueError('w must be a Permutation') + P = Permutations() + if w not in P: + raise ValueError('w must be a permutation') + w = P(w) N = w.size() winv = w.inverse() + from sage.misc.mrange import cartesian_product_iterator cells = [c for c in cartesian_product_iterator((range(N), range(N))) if c[0]+1 < winv(c[1]+1) and c[1]+1 < w(c[0]+1)] return NorthwestDiagram(cells, n_rows=N, n_cols=N, check=False) + diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index d8fb066a72e..3228a79623a 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -200,11 +200,8 @@ AUTHORS: - Mike Hansen - - Dan Drake (2008-04-07): allow Permutation() to take lists of tuples - - Sébastien Labbé (2009-03-17): added robinson_schensted_inverse - - Travis Scrimshaw: * (2012-08-16): ``to_standard()`` no longer modifies input @@ -216,7 +213,6 @@ - Darij Grinberg (2013-09-07): added methods; ameliorated :trac:`14885` by exposing and documenting methods for global-independent multiplication. - - Travis Scrimshaw (2014-02-05): Made :class:`StandardPermutations_n` a finite Weyl group to make it more uniform with :class:`SymmetricGroup`. Added ability to compute the conjugacy classes. diff --git a/src/sage/combinat/skew_partition.py b/src/sage/combinat/skew_partition.py index 3f60e008c2e..91644f47462 100644 --- a/src/sage/combinat/skew_partition.py +++ b/src/sage/combinat/skew_partition.py @@ -740,13 +740,15 @@ def conjugate(self): def outer_corners(self): """ - Return a list of the outer corners of ``self``. These are corners - which are contained inside of the shape. For the corners which are - outside of the shape, use :meth:`outside_corners`. + Return a list of the outer corners of ``self``. + + These are corners that are contained inside of the shape. + For the corners which are outside of the shape, + use :meth:`outside_corners`. .. WARNING:: - In the case that `self` is an honest (rather than skew) partition, + In the case that ``self`` is an honest (rather than skew) partition, these are the :meth:`~sage.combinat.partition.Partition.corners` of the outer partition. In the language of [Sag2001]_ these would be the "inner corners" of the outer partition. @@ -1965,3 +1967,4 @@ def __iter__(self): from sage.misc.persist import register_unpickle_override register_unpickle_override('sage.combinat.skew_partition', 'SkewPartition_class', SkewPartition) + diff --git a/src/sage/combinat/skew_tableau.py b/src/sage/combinat/skew_tableau.py index 840a6864d0f..3eeae3d8d56 100644 --- a/src/sage/combinat/skew_tableau.py +++ b/src/sage/combinat/skew_tableau.py @@ -910,9 +910,10 @@ def slide(self, corner=None, return_vacated=False): def backward_slide(self, corner=None): r""" - Apply a backward jeu de taquin slide on the specified outside corner. + Apply a backward jeu de taquin slide on the specified outside + ``corner`` of ``self``. - Backward jeu de taquin slides are defined in section 3.7 of + Backward jeu de taquin slides are defined in Section 3.7 of [Sag2001]_. .. WARNING:: @@ -990,8 +991,8 @@ def backward_slide(self, corner=None): outer_outisde_corners = self.outer_shape().outside_corners() if corner is not None: if tuple(corner) not in outer_outisde_corners: - raise ValueError("corner must be an outside corner \ - of the outer shape") + raise ValueError("corner must be an outside corner" + " of the outer shape") else: if not outer_outisde_corners: return self @@ -1007,8 +1008,6 @@ def backward_slide(self, corner=None): new_st.append(list()) new_st[i].append(None) - - while (i, j) not in inner_outside_corners: # get the value of the cell above the temporarily empty cell (if # it exists) From 382926c5cabb572bf912de7ce1137f07d5cc42eb Mon Sep 17 00:00:00 2001 From: "Trevor K. Karn" Date: Sat, 15 Oct 2022 09:04:59 -0500 Subject: [PATCH 254/632] Fix unused variable --- src/sage/combinat/partition.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index b1f9810beaf..491108c180d 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -4799,7 +4799,6 @@ def vertical_border_strip_cells(self, k): return [] shelf = [] - res = [] i = 0 ell = len(self._list) while i < ell: @@ -4856,7 +4855,6 @@ def horizontal_border_strip_cells(self, k): return list() L = self._list - res = [] shelf = [k] # the number of boxes which will fit in a row mapping = [0] # a record of the rows for i in range(len(L)-1): From b39ffd2e38fe788cc674c8be22d2cbe946d2f4ab Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 15 Oct 2022 17:24:08 -0700 Subject: [PATCH 255/632] build/pkgs/openssl/spkg-configure.m4: Also require openssl if curl needs to be built --- build/pkgs/openssl/spkg-configure.m4 | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/build/pkgs/openssl/spkg-configure.m4 b/build/pkgs/openssl/spkg-configure.m4 index fd2d257d721..51dcb7de779 100644 --- a/build/pkgs/openssl/spkg-configure.m4 +++ b/build/pkgs/openssl/spkg-configure.m4 @@ -56,9 +56,10 @@ On Cygwin, openssl must be installed as a system package. This is an error."]) ]) ], [dnl REQUIRED-CHECK AC_REQUIRE([SAGE_SPKG_CONFIGURE_PYTHON3]) - dnl openssl is a dependency only of python3; so if we use system python3, + AC_REQUIRE([SAGE_SPKG_CONFIGURE_CURL]) + dnl openssl is a dependency only of python3 and curl; so if we use system python3 and curl, dnl we do not require it. (In particular, we do not need a specific version.) - AS_IF([test x$sage_spkg_install_python3 = xno], [ + AS_IF([test x$sage_spkg_install_python3 = xno -a x$sage_spkg_install_curl = xno], [ sage_require_openssl=no ]) ]) From 8990d53f335631f4926cfb1c1e18bfa9ecda5689 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 10:55:58 -0700 Subject: [PATCH 256/632] .github/workflows/docker.yml: Add option free_disk_space --- .github/workflows/ci-linux.yml | 7 +++++++ .github/workflows/docker.yml | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index d5935789219..17f0217ead7 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -51,6 +51,7 @@ jobs: with: # Build incrementally from previous stage (pre) incremental: true + free_disk_space: true from_docker_repository: ghcr.io/${{ github.repository }}/ from_docker_target: "with-targets-pre" docker_targets: "with-targets with-targets-optional" @@ -82,6 +83,7 @@ jobs: with: # Build incrementally from previous stage (pre) incremental: true + free_disk_space: true from_docker_repository: ghcr.io/${{ github.repository }}/ from_docker_target: "with-targets-pre" docker_targets: "with-targets with-targets-optional" @@ -97,6 +99,7 @@ jobs: needs: [minimal] uses: ./.github/workflows/docker.yml with: + free_disk_space: true # Build from scratch docker_targets: "with-system-packages configured with-targets-pre" # FIXME: duplicated from env.TARGETS @@ -111,6 +114,7 @@ jobs: uses: ./.github/workflows/docker.yml with: incremental: true + free_disk_space: true from_docker_repository: ghcr.io/${{ github.repository }}/ from_docker_target: "with-targets-pre" tox_packages_factors: >- @@ -125,6 +129,7 @@ jobs: uses: ./.github/workflows/docker.yml with: incremental: true + free_disk_space: true from_docker_repository: ghcr.io/${{ github.repository }}/ from_docker_target: "with-targets-pre" tox_packages_factors: >- @@ -138,6 +143,7 @@ jobs: uses: ./.github/workflows/docker.yml with: incremental: true + free_disk_space: true from_docker_repository: ghcr.io/${{ github.repository }}/ from_docker_target: "with-targets-pre" tox_packages_factors: >- @@ -151,6 +157,7 @@ jobs: uses: ./.github/workflows/docker.yml with: incremental: true + free_disk_space: true from_docker_repository: ghcr.io/${{ github.repository }}/ from_docker_target: "with-targets-pre" tox_packages_factors: >- diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index b9af3ea60a1..6b387dad6c8 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -65,6 +65,9 @@ on: max_parallel: type: number default: 24 + free_disk_space: + default: false + type: boolean # # Publishing to GitHub Packages # @@ -150,6 +153,7 @@ jobs: dpkg-query -Wf '${Installed-Size}\t${Package}\n' | sort -n | tail -n 50 sudo apt-get --fix-broken --yes remove $(dpkg-query -f '${Package}\n' -W | grep -E '^(ghc-|google-cloud-sdk|google-chrome|firefox|mysql-server|dotnet-sdk|hhvm|mono)') || echo "(error ignored)" df -h + if: inputs.free_disk_space - name: Check out git-trac-command uses: actions/checkout@v2 with: From 09244b05468c5b7b407074a60cbd3452eebd60a7 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 18:01:55 -0700 Subject: [PATCH 257/632] tox.ini (python3.x): Do not clobber user-provided EXTRA_SAGE_PACKAGES --- tox.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tox.ini b/tox.ini index 3e815e2d060..7a50b34095b 100644 --- a/tox.ini +++ b/tox.ini @@ -511,7 +511,7 @@ setenv = CONFIG_CONFIGURE_ARGS_1=--with-system-python3=yes python3_spkg: CONFIG_CONFIGURE_ARGS_1=--without-system-python3 python3.8,python3.9,python3.10,python3.11: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=python{env:PYTHON_MAJOR}.{env:PYTHON_MINOR} - python3.8,python3.9,python3.10,python3.11: EXTRA_SAGE_PACKAGES=_python{env:PYTHON_MAJOR}.{env:PYTHON_MINOR} _bootstrap liblzma bzip2 libffi libpng + python3.8,python3.9,python3.10,python3.11: EXTRA_SAGE_PACKAGES_5=_python{env:PYTHON_MAJOR}.{env:PYTHON_MINOR} _bootstrap liblzma bzip2 libffi libpng macos-python3_xcode: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=/usr/bin/python3 macos-{python3_xcode,nohomebrew}-{python3.8}: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/{env:PYTHON_MAJOR}.{env:PYTHON_MINOR}/bin/python3 # Homebrew keg installs @@ -520,7 +520,7 @@ setenv = macos-python3_pythonorg: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=/Library/Frameworks/Python.framework/Versions/{env:PYTHON_MAJOR}.{env:PYTHON_MINOR}/bin/python3 # https://github.com/pypa/manylinux manylinux-standard: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=/opt/python/cp{env:PYTHON_MAJOR}{env:PYTHON_MINOR}-cp{env:PYTHON_MAJOR}{env:PYTHON_MINOR}/bin/python3 - manylinux-{python3.8,python3.9,python3.10,python3.11}: EXTRA_SAGE_PACKAGES=_bootstrap xz bzip2 libffi libpng + manylinux-{python3.8,python3.9,python3.10,python3.11}: EXTRA_SAGE_PACKAGES_5=_bootstrap xz bzip2 libffi libpng conda: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=python3 # # - toolchain @@ -573,7 +573,7 @@ setenv = # # Resulting EXTRA_SAGE_PACKAGES # - ALL_EXTRA_SAGE_PACKAGES={env:EXTRA_SAGE_PACKAGES_0:} {env:EXTRA_SAGE_PACKAGES_1:} {env:EXTRA_SAGE_PACKAGES_2:} {env:EXTRA_SAGE_PACKAGES_3:} {env:EXTRA_SAGE_PACKAGES_4:} {env:EXTRA_SAGE_PACKAGES:} + ALL_EXTRA_SAGE_PACKAGES={env:EXTRA_SAGE_PACKAGES_0:} {env:EXTRA_SAGE_PACKAGES_1:} {env:EXTRA_SAGE_PACKAGES_2:} {env:EXTRA_SAGE_PACKAGES_3:} {env:EXTRA_SAGE_PACKAGES_4:} {env:EXTRA_SAGE_PACKAGES_5:} {env:EXTRA_SAGE_PACKAGES:} # environment will be skipped if regular expression does not match against the sys.platform string platform = From 9fb4eac6f4a87fa73fa79a1b173cd09e590ab2fa Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 18:07:12 -0700 Subject: [PATCH 258/632] .github/workflows/docker.yml: Add option extra_sage_packages --- .github/workflows/docker.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 6b387dad6c8..e58732fa810 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -62,6 +62,10 @@ on: ["minimal", "standard", ] + extra_sage_packages: + description: 'Extra Sage packages to install as system packages' + type: string + default: "" max_parallel: type: number default: 24 @@ -132,7 +136,7 @@ jobs: FROM_DOCKER_TARGET: ${{ inputs.from_docker_target }} FROM_DOCKER_TAG: ${{ inputs.from_docker_tag }} EXTRA_CONFIGURE_ARGS: --enable-fat-binary - + EXTRA_SAGE_PACKAGES: ${{ inputs.extra_sage_packages }} steps: - name: Check out SageMath uses: actions/checkout@v2 From 3897adb6aa1c59f4410315ea813e4ccc08cc3df0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 21:00:05 -0700 Subject: [PATCH 259/632] tox.ini (manylinux-minimal): Provide system liblzma so that system python can be accepted; not system xz --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 7a50b34095b..25d9e9f70ad 100644 --- a/tox.ini +++ b/tox.ini @@ -520,7 +520,7 @@ setenv = macos-python3_pythonorg: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=/Library/Frameworks/Python.framework/Versions/{env:PYTHON_MAJOR}.{env:PYTHON_MINOR}/bin/python3 # https://github.com/pypa/manylinux manylinux-standard: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=/opt/python/cp{env:PYTHON_MAJOR}{env:PYTHON_MINOR}-cp{env:PYTHON_MAJOR}{env:PYTHON_MINOR}/bin/python3 - manylinux-{python3.8,python3.9,python3.10,python3.11}: EXTRA_SAGE_PACKAGES_5=_bootstrap xz bzip2 libffi libpng + manylinux-{python3.8,python3.9,python3.10,python3.11}: EXTRA_SAGE_PACKAGES_5=_bootstrap liblzma bzip2 libffi libpng conda: CONFIG_CONFIGURE_ARGS_1=--with-system-python3=force --with-python=python3 # # - toolchain From f19e4b69794a2b137e1371c61ac220acc85a7510 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Tue, 18 Oct 2022 16:49:52 -0700 Subject: [PATCH 260/632] space after comma --- src/sage/algebras/fusion_rings/f_matrix.py | 274 +++++++++--------- .../fast_parallel_fmats_methods.pyx | 104 +++---- .../fast_parallel_fusion_ring_braid_repn.pyx | 88 +++--- src/sage/algebras/fusion_rings/fusion_ring.py | 230 +++++++-------- .../algebras/fusion_rings/poly_tup_engine.pyx | 53 ++-- .../algebras/fusion_rings/shm_managers.pyx | 184 ++++++------ 6 files changed, 467 insertions(+), 466 deletions(-) diff --git a/src/sage/algebras/fusion_rings/f_matrix.py b/src/sage/algebras/fusion_rings/f_matrix.py index 61053cc62af..9ec23340e66 100644 --- a/src/sage/algebras/fusion_rings/f_matrix.py +++ b/src/sage/algebras/fusion_rings/f_matrix.py @@ -1,5 +1,5 @@ r""" -F-Matries of fusion rings +F-Matrices of Fusion Rings """ # **************************************************************************** # Copyright (C) 2019 Daniel Bump @@ -96,7 +96,7 @@ class FMatrix(SageObject): +------------------------+----------+ | `D_r, r\geq 4` | `\leq 2` | +------------------------+----------+ - | `G_2,F_4,E_6,E_7` | `\leq 2` | + | `G_2, F_4, E_6, E_7` | `\leq 2` | +------------------------+----------+ | `E_8` | `\leq 3` | +------------------------+----------+ @@ -119,15 +119,15 @@ class FMatrix(SageObject): .. MATH:: - \text{Hom}(D,(A\otimes B)\otimes C) - \to \text{Hom}(D,A\otimes(B\otimes C)) + \text{Hom}(D, (A\otimes B)\otimes C) + \to \text{Hom}(D, A\otimes(B\otimes C)) by a matrix `F^{ABC}_D`. This depends on a pair of additional simple objects `X` and `Y`. Indeed, we can - get a basis for `\text{Hom}(D,(A\otimes B)\otimes C)` + get a basis for `\text{Hom}(D, (A\otimes B)\otimes C)` indexed by simple objects `X` in which the corresponding homomorphism factors through `X\otimes C`, and similarly - `\text{Hom}(D,A\otimes(B\otimes C))` has a basis indexed + `\text{Hom}(D, A\otimes(B\otimes C))` has a basis indexed by `Y`, in which the basis vector factors through `A\otimes Y`. See [TTWL2009]_ for an introduction to this topic, @@ -139,7 +139,7 @@ class FMatrix(SageObject): The F-matrix is only determined up to a *gauge*. This is a family of embeddings `C \to A\otimes B` for - simple objects `A,B,C` such that `\text{Hom}(C, A\otimes B)` + simple objects `A, B, C` such that `\text{Hom}(C, A\otimes B)` is nonzero. Changing the gauge changes the F-matrix though not in a very essential way. By varying the gauge it is possible to make the F-matrices unitary, or it is possible @@ -151,8 +151,8 @@ class FMatrix(SageObject): EXAMPLES:: sage: I = FusionRing("E8", 2, conjugate=True) - sage: I.fusion_labels(["i0","p","s"],inject_variables=True) - sage: f = FMatrix(I,inject_variables=True); f + sage: I.fusion_labels(["i0", "p", "s"], inject_variables=True) + sage: f = FMatrix(I, inject_variables=True); f creating variables fx1..fx14 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 F-Matrix factory for The Fusion Ring of Type E8 and level 2 with Integer Ring coefficients @@ -170,7 +170,7 @@ class FMatrix(SageObject): EXAMPLES:: - sage: f.fmatrix(s,s,s,s) + sage: f.fmatrix(s, s, s, s) [fx10 fx11] [fx12 fx13] @@ -179,15 +179,15 @@ class FMatrix(SageObject): ``fx10``, ``fx11``, ``fx12``, ``fx13``. The task is to solve for these. - As explained above The F-matrix `(F^{ABC}_D)_{X,Y}` + As explained above The F-matrix `(F^{ABC}_D)_{X, Y}` two other variables `X` and `Y`. We have methods to - tell us (depending on `A,B,C,D`) what the possibilities + tell us (depending on `A, B, C, D`) what the possibilities for these are. In this example with `A=B=C=D=s` both `X` and `Y` are allowed to be `i_0` or `s`. :: - sage: f.f_from(s,s,s,s), f.f_to(s,s,s,s) + sage: f.f_from(s, s, s, s), f.f_to(s, s, s, s) ([i0, p], [i0, p]) The last two statments show that the possible values of @@ -238,10 +238,10 @@ class FMatrix(SageObject): We now have access to the values of the F-matrix using the methods :meth:`fmatrix` and :meth:`fmat`:: - sage: f.fmatrix(s,s,s,s) + sage: f.fmatrix(s, s, s, s) [(-1/2*zeta128^48 + 1/2*zeta128^16) 1] [ 1/2 (1/2*zeta128^48 - 1/2*zeta128^16)] - sage: f.fmat(s,s,s,s,p,p) + sage: f.fmat(s, s, s, s, p, p) (1/2*zeta128^48 - 1/2*zeta128^16) :meth:`find_orthogonal_solution` is much more powerful @@ -258,12 +258,12 @@ class FMatrix(SageObject): :: - sage: f = FMatrix(FusionRing("B3",2)) - sage: f.find_orthogonal_solution(verbose=False,checkpoint=True) # not tested (~100 s) + sage: f = FMatrix(FusionRing("B3", 2)) + sage: f.find_orthogonal_solution(verbose=False, checkpoint=True) # not tested (~100 s) sage: all(v in CyclotomicField(56) for v in f.get_fvars().values()) # not tested True - sage: f = FMatrix(FusionRing("G2",2)) + sage: f = FMatrix(FusionRing("G2", 2)) sage: f.find_orthogonal_solution(verbose=False) # long time (~11 s) sage: f.field() # long time Algebraic Field @@ -274,7 +274,7 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab EXAMPLES:: - sage: f = FMatrix(FusionRing("B3",2)) + sage: f = FMatrix(FusionRing("B3", 2)) sage: TestSuite(f).run(skip="_test_pickling") """ self._FR = fusion_ring @@ -284,9 +284,9 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab raise NotImplementedError("FMatrix is only available for multiplicity free FusionRings") #Set up F-symbols entry by entry n_vars = self.findcases() - self._poly_ring = PolynomialRing(self._FR.field(),n_vars,var_prefix) + self._poly_ring = PolynomialRing(self._FR.field(), n_vars, var_prefix) if inject_variables: - print("creating variables %s%s..%s%s"%(var_prefix,1,var_prefix,n_vars)) + print("creating variables %s%s..%s%s"%(var_prefix, 1, var_prefix, n_vars)) self._poly_ring.inject_variables(get_main_globals()) self._idx_to_sextuple, self._fvars = self.findcases(output=True) @@ -403,8 +403,8 @@ def _reset_solver_state(self): n = self._poly_ring.ngens() self._var_degs = [0] * n self._kp = {} - self._ks = KSHandler(n,self._field) - self._singles = self.get_fvars_by_size(1,indices=True) + self._ks = KSHandler(n, self._field) + self._singles = self.get_fvars_by_size(1, indices=True) self._nnz = self._get_known_nonz() #Clear relevant caches @@ -414,12 +414,12 @@ def _reset_solver_state(self): def fmat(self, a, b, c, d, x, y, data=True): r""" - Return the F-Matrix coefficient `(F^{a,b,c}_d)_{x,y}`. + Return the F-Matrix coefficient `(F^{a, b, c}_d)_{x, y}`. EXAMPLES:: - sage: f=FMatrix(FusionRing("G2", 1, fusion_labels=("i0","t"), inject_variables=True)) - sage: [f.fmat(t,t,t,t,x,y) for x in f._FR.basis() for y in f._FR.basis()] + sage: f=FMatrix(FusionRing("G2", 1, fusion_labels=("i0", "t"), inject_variables=True)) + sage: [f.fmat(t, t, t, t, x, y) for x in f._FR.basis() for y in f._FR.basis()] [fx1, fx2, fx3, fx4] sage: f.find_cyclotomic_solution(output=True) Setting up hexagons and pentagons... @@ -433,14 +433,14 @@ def fmat(self, a, b, c, d, x, y, data=True): (t, t, t, t, i0, t): 1, (t, t, t, t, t, i0): (-zeta60^14 + zeta60^6 + zeta60^4 - 1), (t, t, t, t, t, t): (zeta60^14 - zeta60^6 - zeta60^4 + 1)} - sage: [f.fmat(t,t,t,t,x,y) for x in f._FR.basis() for y in f._FR.basis()] + sage: [f.fmat(t, t, t, t, x, y) for x in f._FR.basis() for y in f._FR.basis()] [(-zeta60^14 + zeta60^6 + zeta60^4 - 1), 1, (-zeta60^14 + zeta60^6 + zeta60^4 - 1), (zeta60^14 - zeta60^6 - zeta60^4 + 1)] """ - if (self._FR.Nk_ij(a,b,x) == 0 or self._FR.Nk_ij(x,c,d) == 0 - or self._FR.Nk_ij(b,c,y) == 0 or self._FR.Nk_ij(a,y,d) == 0): + if (self._FR.Nk_ij(a, b, x) == 0 or self._FR.Nk_ij(x, c, d) == 0 + or self._FR.Nk_ij(b, c, y) == 0 or self._FR.Nk_ij(a, y, d) == 0): return 0 #Some known zero F-symbols @@ -463,15 +463,15 @@ def fmat(self, a, b, c, d, x, y, data=True): #Better to use try/except for speed. Somewhat trivial, but worth #hours when method is called ~10^11 times try: - return self._fvars[a,b,c,d,x,y] + return self._fvars[a, b, c, d, x, y] except KeyError: return 0 else: - return (a,b,c,d,x,y) + return (a, b, c, d, x, y) - def fmatrix(self,a,b,c,d): + def fmatrix(self, a, b, c, d): r""" - Return the F-Matrix `F^{a,b,c}_d`. + Return the F-Matrix `F^{a, b, c}_d`. INPUT: @@ -480,23 +480,23 @@ def fmatrix(self,a,b,c,d): EXAMPLES:: sage: f = FMatrix(FusionRing("A1", 2, fusion_labels="c", inject_variables=True)) - sage: f.fmatrix(c1,c1,c1,c1) + sage: f.fmatrix(c1, c1, c1, c1) [fx0 fx1] [fx2 fx3] sage: f.find_cyclotomic_solution(verbose=False); adding equation... fx4 - 1 adding equation... fx10 - 1 - sage: f.f_from(c1,c1,c1,c1) + sage: f.f_from(c1, c1, c1, c1) [c0, c2] - sage: f.f_to(c1,c1,c1,c1) + sage: f.f_to(c1, c1, c1, c1) [c0, c2] - sage: f.fmatrix(c1,c1,c1,c1) + sage: f.fmatrix(c1, c1, c1, c1) [ (1/2*zeta32^12 - 1/2*zeta32^4) (-1/2*zeta32^12 + 1/2*zeta32^4)] [ (1/2*zeta32^12 - 1/2*zeta32^4) (1/2*zeta32^12 - 1/2*zeta32^4)] """ - X = self.f_from(a,b,c,d) - Y = self.f_to(a,b,c,d) - return matrix([[self.fmat(a,b,c,d,x,y) for y in Y] for x in X]) + X = self.f_from(a, b, c, d) + Y = self.f_to(a, b, c, d) + return matrix([[self.fmat(a, b, c, d, x, y) for y in Y] for x in X]) def field(self): r""" @@ -526,7 +526,7 @@ def field(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("G2",1)) + sage: f = FMatrix(FusionRing("G2", 1)) sage: f.field() Cyclotomic Field of order 60 and degree 16 sage: f.find_orthogonal_solution(verbose=False) @@ -551,13 +551,13 @@ def FR(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("D3",1)) + sage: f = FMatrix(FusionRing("D3", 1)) sage: f.FR() The Fusion Ring of Type D3 and level 1 with Integer Ring coefficients """ return self._FR - def findcases(self,output=False): + def findcases(self, output=False): r""" Return unknown F-matrix entries. @@ -567,7 +567,7 @@ def findcases(self,output=False): EXAMPLES:: - sage: f = FMatrix(FusionRing("G2", 1, fusion_labels=("i0","t"))) + sage: f = FMatrix(FusionRing("G2", 1, fusion_labels=("i0", "t"))) sage: f.findcases() 5 sage: f.findcases(output=True) @@ -587,14 +587,14 @@ def findcases(self,output=False): idx_map = dict() ret = dict() id_anyon = self._FR.one() - for (a,b,c,d) in product(self._FR.basis(), repeat=4): + for (a, b, c, d) in product(self._FR.basis(), repeat=4): if a == id_anyon or b == id_anyon or c == id_anyon: continue for x in self.f_from(a, b, c, d): for y in self.f_to(a, b, c, d): if output: v = self._poly_ring.gen(i) - ret[(a,b,c,d,x,y)] = v + ret[(a, b, c, d, x, y)] = v idx_map[i] = (a, b, c, d, x, y) i += 1 if output: @@ -602,7 +602,7 @@ def findcases(self,output=False): else: return i - def f_from(self,a,b,c,d): + def f_from(self, a, b, c, d): r""" Return the possible `x` such that there are morphisms `d \to x \otimes c \to (a \otimes b) \otimes c`. @@ -615,18 +615,18 @@ def f_from(self,a,b,c,d): sage: fr = FusionRing("A1", 3, fusion_labels="a", inject_variables=True) sage: f = FMatrix(fr) - sage: f.fmatrix(a1,a1,a2,a2) + sage: f.fmatrix(a1, a1, a2, a2) [fx6 fx7] [fx8 fx9] - sage: f.f_from(a1,a1,a2,a2) + sage: f.f_from(a1, a1, a2, a2) [a0, a2] - sage: f.f_to(a1,a1,a2,a2) + sage: f.f_to(a1, a1, a2, a2) [a1, a3] """ return [x for x in self._FR.basis() - if self._FR.Nk_ij(a,b,x) != 0 and self._FR.Nk_ij(x,c,d) != 0] + if self._FR.Nk_ij(a, b, x) != 0 and self._FR.Nk_ij(x, c, d) != 0] - def f_to(self,a,b,c,d): + def f_to(self, a, b, c, d): r""" Return the possible `y` such that there are morphisms `d \to a \otimes y \to a \otimes (b \otimes c)`. @@ -650,7 +650,7 @@ def f_to(self,a,b,c,d): [b1, b3, b5] """ return [y for y in self._FR.basis() - if self._FR.Nk_ij(b,c,y) != 0 and self._FR.Nk_ij(a,y,d) != 0] + if self._FR.Nk_ij(b, c, y) != 0 and self._FR.Nk_ij(a, y, d) != 0] #################### ### Data getters ### @@ -660,9 +660,9 @@ def get_fvars(self): r""" Return a dictionary of F-symbols. - The keys are sextuples `(a,b,c,d,x,y)` of basis elements of + The keys are sextuples `(a, b, c, d, x, y)` of basis elements of ``self.FR()`` and the values are the corresponding F-symbols - `(F^{a,b,c}_d)_{xy}`. + `(F^{a, b, c}_d)_{xy}`. These values reflect the current state of a solver's computation. @@ -762,7 +762,7 @@ def get_qqbar_embedding(self): [1] Computing appropriate NumberField... sage: phi = f.get_qqbar_embedding() - sage: phi(f.fmat(g1,g1,g1,g1,g1,g1)).n() + sage: phi(f.fmat(g1, g1, g1, g1, g1, g1)).n() -0.618033988749895 + 1.46674215951686e-29*I """ return self._qqbar_embedding @@ -915,12 +915,12 @@ def largest_fmat_size(self): sage: f.largest_fmat_size() 4 """ - return max(self.fmatrix(*tup).nrows() for tup in product(self._FR.basis(),repeat=4)) + return max(self.fmatrix(*tup).nrows() for tup in product(self._FR.basis(), repeat=4)) - def get_fvars_by_size(self,n,indices=False): + def get_fvars_by_size(self, n, indices=False): r""" Return the set of F-symbols that are entries of an `n \times n` matrix - `F^{a,b,c}_d`. + `F^{a, b, c}_d`. INPUT: @@ -928,7 +928,7 @@ def get_fvars_by_size(self,n,indices=False): - ``indices`` -- boolean (default: ``False``) If ``indices`` is ``False`` (default), - this method returns a set of sextuples `(a,b,c,d,x,y)` identifying + this method returns a set of sextuples `(a, b, c, d, x, y)` identifying the corresponding F-symbol. Each sextuple is a key in the dictionary returned by :meth:`get_fvars`. @@ -938,7 +938,7 @@ def get_fvars_by_size(self,n,indices=False): EXAMPLES:: - sage: f = FMatrix(FusionRing("A2",2), inject_variables=True) + sage: f = FMatrix(FusionRing("A2", 2), inject_variables=True) creating variables fx1..fx287 Defining fx0, ..., fx286 sage: f.largest_fmat_size() @@ -952,9 +952,9 @@ def get_fvars_by_size(self,n,indices=False): """ var_set = set() one = self._FR.one() - for a,b,c,d in product(self._FR.basis(),repeat=4): - X = self.f_from(a,b,c,d) - Y = self.f_to(a,b,c,d) + for a, b, c, d in product(self._FR.basis(), repeat=4): + X = self.f_from(a, b, c, d) + Y = self.f_to(a, b, c, d) if len(X) == n and len(Y) == n: for x in X: for y in Y: @@ -963,7 +963,7 @@ def get_fvars_by_size(self,n,indices=False): trivial |= b == one and x == a and y == c trivial |= c == one and x == d and y == b if not trivial: - var_set.add((a,b,c,d,x,y)) + var_set.add((a, b, c, d, x, y)) if indices: sext_to_idx = {v: k for k, v in self._idx_to_sextuple.items()} return {sext_to_idx[fx] for fx in var_set} @@ -1004,7 +1004,7 @@ def save_fvars(self, filename): sage: filename = f.get_fr_str() + "_solver_results.pickle" sage: f.save_fvars(filename) sage: del f - sage: f2 = FMatrix(FusionRing("A2",1)) + sage: f2 = FMatrix(FusionRing("A2", 1)) sage: f2.load_fvars(filename) sage: fvars == f2.get_fvars() True @@ -1029,14 +1029,14 @@ def load_fvars(self, filename): EXAMPLES:: - sage: f = FMatrix(FusionRing("A2",1)) + sage: f = FMatrix(FusionRing("A2", 1)) sage: f.find_orthogonal_solution(verbose=False) sage: fvars = f.get_fvars() sage: K = f.field() sage: filename = f.get_fr_str() + "_solver_results.pickle" sage: f.save_fvars(filename) sage: del f - sage: f2 = FMatrix(FusionRing("A2",1)) + sage: f2 = FMatrix(FusionRing("A2", 1)) sage: f2.load_fvars(filename) sage: fvars == f2.get_fvars() True @@ -1081,19 +1081,19 @@ def _checkpoint(self, do_chkpt, status, verbose=True): sage: f = FMatrix(FusionRing("A1", 3)) sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) - sage: f.get_defining_equations('hexagons',output=False) + sage: f.get_defining_equations('hexagons', output=False) sage: f.ideal_basis = f._par_graph_gb(verbose=False) sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey, poly_to_tup sage: f.ideal_basis.sort(key=poly_tup_sortkey) sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() - sage: f._fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars) + sage: f._fvars = FvarsHandler(n, f._field, f._idx_to_sextuple, init_data=f._fvars) sage: f._triangular_elim(verbose=False) sage: f._update_reduction_params() - sage: f._checkpoint(do_chkpt=True,status=2) + sage: f._checkpoint(do_chkpt=True, status=2) Checkpoint 2 reached! sage: del f - sage: f = FMatrix(FusionRing("A1",3)) + sage: f = FMatrix(FusionRing("A1", 3)) sage: f.find_orthogonal_solution(warm_start="fmatrix_solver_checkpoint_A13.pickle") Computing F-symbols for The Fusion Ring of Type A1 and level 3 with Integer Ring coefficients with 71 variables... Set up 121 reduced pentagons... @@ -1111,10 +1111,10 @@ def _checkpoint(self, do_chkpt, status, verbose=True): sage: sum(f._solved) == f._poly_ring.ngens() True sage: os.remove("fmatrix_solver_checkpoint_A13.pickle") - sage: f = FMatrix(FusionRing("A1",2)) + sage: f = FMatrix(FusionRing("A1", 2)) sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) - sage: f.get_defining_equations('hexagons',output=False) + sage: f.get_defining_equations('hexagons', output=False) sage: f.ideal_basis = f._par_graph_gb(verbose=False) sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey sage: f.ideal_basis.sort(key=poly_tup_sortkey) @@ -1123,10 +1123,10 @@ def _checkpoint(self, do_chkpt, status, verbose=True): sage: f._fvars = FvarsHandler(n, f._field, f._idx_to_sextuple, init_data=f._fvars) sage: f._triangular_elim(verbose=False) sage: f._update_reduction_params() - sage: f.get_defining_equations('pentagons',output=False) + sage: f.get_defining_equations('pentagons', output=False) sage: f.ideal_basis.sort(key=poly_tup_sortkey) sage: f._triangular_elim(verbose=False) - sage: f._checkpoint(do_chkpt=True,status=4) + sage: f._checkpoint(do_chkpt=True, status=4) Checkpoint 4 reached! sage: del f sage: f = FMatrix(FusionRing("A1", 2)) @@ -1196,7 +1196,7 @@ def _restore_state(self, filename): TESTS:: sage: f = FMatrix(FusionRing("A1", 3)) - sage: f.find_orthogonal_solution(save_results="test.pickle",verbose=False) # long time + sage: f.find_orthogonal_solution(save_results="test.pickle", verbose=False) # long time sage: del f sage: f = FMatrix(FusionRing("A1", 3)) sage: f.find_orthogonal_solution(warm_start="test.pickle") # long time @@ -1235,7 +1235,7 @@ def start_worker_pool(self, processes=None): .. NOTE:: Python 3.8+ is required, since the ``multiprocessing.shared_memory`` - module must be imported. + module must be imported. INPUT: @@ -1290,9 +1290,9 @@ class methods. self._var_degs = shared_memory.ShareableList(self._var_degs) vd_name = self._var_degs.shm.name n = self._poly_ring.ngens() - self._ks = KSHandler(n,self._field,use_mp=True,init_data=self._ks) + self._ks = KSHandler(n, self._field, use_mp=True, init_data=self._ks) ks_names = self._ks.shm.name - self._shared_fvars = FvarsHandler(n,self._field,self._idx_to_sextuple,use_mp=n_proc,pids_name=pids_name,init_data=self._fvars) + self._shared_fvars = FvarsHandler(n, self._field, self._idx_to_sextuple, use_mp=n_proc, pids_name=pids_name, init_data=self._fvars) fvar_names = self._shared_fvars.shm.name #Initialize worker pool processes args = (id(self), s_name, vd_name, ks_names, fvar_names, n_proc, pids_name) @@ -1305,10 +1305,10 @@ def init(fmats_id, solved_name, vd_name, ks_names, fvar_names, n_proc, pids_name fmats_obj._var_degs = shared_memory.ShareableList(name=vd_name) n = fmats_obj._poly_ring.ngens() K = fmats_obj._field - fmats_obj._fvars = FvarsHandler(n,K,fmats_obj._idx_to_sextuple,name=fvar_names,use_mp=n_proc,pids_name=pids_name) - fmats_obj._ks = KSHandler(n,K,name=ks_names,use_mp=True) + fmats_obj._fvars = FvarsHandler(n, K, fmats_obj._idx_to_sextuple, name=fvar_names, use_mp=n_proc, pids_name=pids_name) + fmats_obj._ks = KSHandler(n, K, name=ks_names, use_mp=True) - self.pool = Pool(processes=n_proc,initializer=init,initargs=args) + self.pool = Pool(processes=n_proc, initializer=init, initargs=args) self._pid_list[0] = getpid() for i, p in enumerate(self.pool._pool): self._pid_list[i+1] = p.pid @@ -1367,11 +1367,11 @@ def _map_triv_reduce(self, mapper, input_iter, worker_pool=None, chunksize=None, sage: f = FMatrix(FusionRing("A1", 2)) sage: f._reset_solver_state() - sage: len(f._map_triv_reduce('get_reduced_hexagons',[(0,1,False)])) + sage: len(f._map_triv_reduce('get_reduced_hexagons', [(0, 1, False)])) 11 sage: f.start_worker_pool() - sage: mp_params = [(i,f.pool._processes,True) for i in range(f.pool._processes)] - sage: len(f._map_triv_reduce('get_reduced_pentagons',mp_params,worker_pool=f.pool,chunksize=1,mp_thresh=0)) + sage: mp_params = [(i, f.pool._processes, True) for i in range(f.pool._processes)] + sage: len(f._map_triv_reduce('get_reduced_pentagons', mp_params, worker_pool=f.pool, chunksize=1, mp_thresh=0)) 33 sage: f.shutdown_worker_pool() """ @@ -1387,11 +1387,11 @@ def _map_triv_reduce(self, mapper, input_iter, worker_pool=None, chunksize=None, chunksize = n // (worker_pool._processes**2) + 1 no_mp = worker_pool is None or n < mp_thresh #Map phase - input_iter = zip_longest([],input_iter,fillvalue=(mapper,id(self))) + input_iter = zip_longest([], input_iter, fillvalue=(mapper, id(self))) if no_mp: - mapped = map(executor,input_iter) + mapped = map(executor, input_iter) else: - mapped = worker_pool.imap_unordered(executor,input_iter,chunksize=chunksize) + mapped = worker_pool.imap_unordered(executor, input_iter, chunksize=chunksize) #Reduce phase results = set() for child_eqns in mapped: @@ -1501,7 +1501,7 @@ def get_defining_equations(self, option, output=True): self._reset_solver_state() n_proc = self.pool._processes if self.pool is not None else 1 params = [(child_id, n_proc, output) for child_id in range(n_proc)] - eqns = self._map_triv_reduce('get_reduced_'+option,params,worker_pool=self.pool,chunksize=1,mp_thresh=0) + eqns = self._map_triv_reduce('get_reduced_'+option, params, worker_pool=self.pool, chunksize=1, mp_thresh=0) if output: F = self._field for i, eq_tup in enumerate(eqns): @@ -1536,7 +1536,7 @@ def _tup_to_fpoly(self, eq_tup): True sage: f.shutdown_worker_pool() """ - return _tup_to_poly(eq_tup,parent=self._poly_ring) + return _tup_to_poly(eq_tup, parent=self._poly_ring) def _update_reduction_params(self, eqns=None): r""" @@ -1562,10 +1562,10 @@ def _update_reduction_params(self, eqns=None): if eqns is None: eqns = self.ideal_basis self._ks.update(eqns) - for i, d in enumerate(get_variables_degrees(eqns,self._poly_ring.ngens())): + for i, d in enumerate(get_variables_degrees(eqns, self._poly_ring.ngens())): self._var_degs[i] = d self._nnz = self._get_known_nonz() - self._kp = compute_known_powers(self._var_degs,self._get_known_vals(),self._field.one()) + self._kp = compute_known_powers(self._var_degs, self._get_known_vals(), self._field.one()) def _triangular_elim(self, eqns=None, verbose=True): r""" @@ -1597,7 +1597,7 @@ def _triangular_elim(self, eqns=None, verbose=True): eqns = self.ideal_basis while True: - linear_terms_exist = _solve_for_linear_terms(self,eqns) + linear_terms_exist = _solve_for_linear_terms(self, eqns) if not linear_terms_exist: break _backward_subs(self) @@ -1611,7 +1611,7 @@ def _triangular_elim(self, eqns=None, verbose=True): eqns = chunks else: eqns = [eqns] - eqns = self._map_triv_reduce('update_reduce',eqns,worker_pool=self.pool,mp_thresh=0) + eqns = self._map_triv_reduce('update_reduce', eqns, worker_pool=self.pool, mp_thresh=0) eqns.sort(key=poly_tup_sortkey) if verbose: print("Elimination epoch completed... {} eqns remain in ideal basis".format(len(eqns))) @@ -1687,7 +1687,7 @@ def equations_graph(self, eqns=None): for x in s: for y in s: if y!=x: - G.add_edge(x,y) + G.add_edge(x, y) return G def _partition_eqns(self, eqns=None, verbose=True): @@ -1703,8 +1703,8 @@ def _partition_eqns(self, eqns=None, verbose=True): EXAMPLES:: - sage: f = FMatrix(FusionRing("C2",1)) - sage: f.get_defining_equations('hexagons',output=False) + sage: f = FMatrix(FusionRing("C2", 1)) + sage: f.get_defining_equations('hexagons', output=False) sage: partition = f._partition_eqns() Partitioned 11 equations into 5 components of size: [4, 3, 3, 3, 1] @@ -1721,7 +1721,7 @@ def _partition_eqns(self, eqns=None, verbose=True): sage: eqns_in_partition == set(f.ideal_basis) True sage: from itertools import product - sage: for e1, e2 in product(partition.values(),repeat=2): + sage: for e1, e2 in product(partition.values(), repeat=2): ....: assert e1 == e2 or set(e1).isdisjoint(set(e2)) """ if eqns is None: @@ -1731,7 +1731,7 @@ def _partition_eqns(self, eqns=None, verbose=True): for eq_tup in eqns: partition[tuple(graph.connected_component_containing_vertex(variables(eq_tup)[0]))].append(eq_tup) if verbose: - print("Partitioned {} equations into {} components of size:".format(len(eqns),len(graph.connected_components()))) + print("Partitioned {} equations into {} components of size:".format(len(eqns), len(graph.connected_components()))) print(graph.connected_components_sizes()) return partition @@ -1751,11 +1751,11 @@ def _par_graph_gb(self, eqns=None, term_order="degrevlex", largest_comp=45, verb EXAMPLES:: - sage: f = FMatrix(FusionRing("F4",1)) + sage: f = FMatrix(FusionRing("F4", 1)) sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) sage: f.start_worker_pool() - sage: f.get_defining_equations('hexagons',output=False) + sage: f.get_defining_equations('hexagons', output=False) sage: gb = f._par_graph_gb() Partitioned 10 equations into 2 components of size: [4, 1] @@ -1772,7 +1772,7 @@ def _par_graph_gb(self, eqns=None, term_order="degrevlex", largest_comp=45, verb if eqns is None: eqns = self.ideal_basis small_comps = list() temp_eqns = list() - for comp, comp_eqns in self._partition_eqns(eqns=eqns,verbose=verbose).items(): + for comp, comp_eqns in self._partition_eqns(eqns=eqns, verbose=verbose).items(): #Check if component is too large to process if len(comp) > largest_comp: temp_eqns.extend(comp_eqns) @@ -1783,7 +1783,7 @@ def _par_graph_gb(self, eqns=None, term_order="degrevlex", largest_comp=45, verb ret = small_comp_gb + temp_eqns return ret - def _get_component_variety(self,var,eqns): + def _get_component_variety(self, var, eqns): r""" Translate equations in each connected component to smaller polynomial rings so we can call built-in variety method. @@ -1816,8 +1816,8 @@ def _get_component_variety(self,var,eqns): #Zip tuples into R and compute Groebner basis idx_map = {old: new for new, old in enumerate(sorted(var))} nvars = len(var) - eqns = [_unflatten_coeffs(self._field,eq_tup) for eq_tup in eqns] - polys = [_tup_to_poly(resize(eq_tup,idx_map,nvars),parent=R) for eq_tup in eqns] + eqns = [_unflatten_coeffs(self._field, eq_tup) for eq_tup in eqns] + polys = [_tup_to_poly(resize(eq_tup, idx_map, nvars), parent=R) for eq_tup in eqns] var_in_R = Ideal(sorted(polys)).variety(ring=AA) #Change back to fmats poly ring and append to temp_eqns @@ -1884,7 +1884,7 @@ def attempt_number_field_computation(self): return False return True - def _get_explicit_solution(self,eqns=None,verbose=True): + def _get_explicit_solution(self, eqns=None, verbose=True): r""" Construct an explicit solution of ``self``. @@ -1923,8 +1923,8 @@ def _get_explicit_solution(self,eqns=None,verbose=True): one = self._field.one() for fx, rhs in self._ks.items(): if not self._solved[fx]: - lt = (ETuple({fx : 2},n), one) - eqns.append(((lt, (ETuple({},n), -rhs)))) + lt = (ETuple({fx : 2}, n), one) + eqns.append(((lt, (ETuple({}, n), -rhs)))) eqns_partition = self._partition_eqns(verbose=verbose) F = self._field @@ -1932,27 +1932,27 @@ def _get_explicit_solution(self,eqns=None,verbose=True): numeric_fvars = dict() non_cyclotomic_roots = list() must_change_base_field = False - phi = F.hom([F.gen()],F) + phi = F.hom([F.gen()], F) for comp, part in eqns_partition.items(): #If component has only one equation in a single variable, get a root if len(comp) == 1 and len(part) == 1: #Attempt to find cyclotomic root - univ_poly = tup_to_univ_poly(part[0],R) + univ_poly = tup_to_univ_poly(part[0], R) roots = univ_poly.roots(multiplicities=False) if roots: numeric_fvars[comp[0]] = roots[0] else: #A real solution is preferred - roots = univ_poly.roots(ring=AA,multiplicities=False) + roots = univ_poly.roots(ring=AA, multiplicities=False) if not roots: - roots = univ_poly.roots(ring=QQbar,multiplicities=False) - non_cyclotomic_roots.append((comp[0],roots[0])) + roots = univ_poly.roots(ring=QQbar, multiplicities=False) + non_cyclotomic_roots.append((comp[0], roots[0])) must_change_base_field = True #Otherwise, compute the component variety and select a point to obtain a numerical solution else: - sols = self._get_component_variety(comp,part) + sols = self._get_component_variety(comp, part) for fx, rhs in sols[0].items(): - non_cyclotomic_roots.append((fx,rhs)) + non_cyclotomic_roots.append((fx, rhs)) must_change_base_field = True if must_change_base_field: @@ -1962,11 +1962,11 @@ def _get_explicit_solution(self,eqns=None,verbose=True): if verbose: print("Computing appropriate NumberField...") roots = [self._FR.field().gen()]+[r[1] for r in non_cyclotomic_roots] - self._field, bf_elts, self._qqbar_embedding = number_field_elements_from_algebraics(roots,minimal=True) + self._field, bf_elts, self._qqbar_embedding = number_field_elements_from_algebraics(roots, minimal=True) else: self._field = QQbar bf_elts = [self._qqbar_embedding(F.gen())] - bf_elts += [rhs for fx,rhs in non_cyclotomic_roots] + bf_elts += [rhs for fx, rhs in non_cyclotomic_roots] self._qqbar_embedding = lambda x : x self._non_cyc_roots = bf_elts[1:] @@ -1987,11 +1987,11 @@ def _get_explicit_solution(self,eqns=None,verbose=True): assert sum(self._solved) == nvars, "Some F-symbols are still missing...{}".format([self._poly_ring.gen(fx) for fx in range(nvars) if not self._solved[fx]]) #Backward substitution step. Traverse variables in reverse lexicographical order. (System is in triangular form) - self._fvars = {sextuple : apply_coeff_map(rhs,phi) for sextuple, rhs in self._fvars.items()} + self._fvars = {sextuple : apply_coeff_map(rhs, phi) for sextuple, rhs in self._fvars.items()} for fx, rhs in numeric_fvars.items(): - self._fvars[self._idx_to_sextuple[fx]] = ((ETuple({},nvars),rhs),) - _backward_subs(self,flatten=False) - self._fvars = {sextuple : constant_coeff(rhs,self._field) for sextuple, rhs in self._fvars.items()} + self._fvars[self._idx_to_sextuple[fx]] = ((ETuple({}, nvars), rhs), ) + _backward_subs(self, flatten=False) + self._fvars = {sextuple : constant_coeff(rhs, self._field) for sextuple, rhs in self._fvars.items()} #Update base field attributes self._FR._field = self.field() @@ -2075,7 +2075,7 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start sage: f.fmat(b2, b2, b2, b2, b0, b1) -1/2*zeta80^30 + 1/2*zeta80^10 - Every F-matrix `F^{a,b,c}_d` is orthogonal and in many cases real. + Every F-matrix `F^{a, b, c}_d` is orthogonal and in many cases real. We may use :meth:`fmats_are_orthogonal` and :meth:`fvars_are_real` to obtain correctness certificates. @@ -2116,7 +2116,7 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start if self._chkpt_status < 1: #Set up hexagon equations and orthogonality constraints self.get_orthogonality_constraints(output=False) - self.get_defining_equations('hexagons',output=False) + self.get_defining_equations('hexagons', output=False) #Report progress if verbose: print("Set up {} hex and orthogonality constraints...".format(len(self.ideal_basis))) @@ -2126,8 +2126,8 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start self._fvars = self._shared_fvars else: n = self._poly_ring.ngens() - self._fvars = FvarsHandler(n,self._field,self._idx_to_sextuple,init_data=self._fvars) - self._checkpoint(checkpoint,1,verbose=verbose) + self._fvars = FvarsHandler(n, self._field, self._idx_to_sextuple, init_data=self._fvars) + self._checkpoint(checkpoint, 1, verbose=verbose) if self._chkpt_status < 2: #Set up equations graph. Find GB for each component in parallel. Eliminate variables @@ -2137,15 +2137,15 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start #Report progress if verbose: print("Hex elim step solved for {} / {} variables".format(sum(self._solved), len(self._poly_ring.gens()))) - self._checkpoint(checkpoint,2,verbose=verbose) + self._checkpoint(checkpoint, 2, verbose=verbose) if self._chkpt_status < 3: #Set up pentagon equations in parallel - self.get_defining_equations('pentagons',output=False) + self.get_defining_equations('pentagons', output=False) #Report progress if verbose: print("Set up {} reduced pentagons...".format(len(self.ideal_basis))) - self._checkpoint(checkpoint,3,verbose=verbose) + self._checkpoint(checkpoint, 3, verbose=verbose) if self._chkpt_status < 4: #Simplify and eliminate variables @@ -2154,16 +2154,16 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start #Report progress if verbose: print("Pent elim step solved for {} / {} variables".format(sum(self._solved), len(self._poly_ring.gens()))) - self._checkpoint(checkpoint,4,verbose=verbose) + self._checkpoint(checkpoint, 4, verbose=verbose) #Try adding degrevlex gb -> elim loop until len(ideal_basis) does not change #Set up new equations graph and compute variety for each component if self._chkpt_status < 5: - self.ideal_basis = self._par_graph_gb(term_order="lex",verbose=verbose) + self.ideal_basis = self._par_graph_gb(term_order="lex", verbose=verbose) self.ideal_basis.sort(key=poly_tup_sortkey) self._triangular_elim(verbose=verbose) - self._checkpoint(checkpoint,5,verbose=verbose) + self._checkpoint(checkpoint, 5, verbose=verbose) self.shutdown_worker_pool() #Find numeric values for each F-symbol @@ -2375,8 +2375,8 @@ def fmats_are_orthogonal(self): True """ is_orthog = [] - for a,b,c,d in product(self._FR.basis(),repeat=4): - mat = self.fmatrix(a,b,c,d) + for a, b, c, d in product(self._FR.basis(), repeat=4): + mat = self.fmatrix(a, b, c, d) is_orthog.append(mat.T * mat == matrix.identity(mat.nrows())) return all(is_orthog) @@ -2395,11 +2395,11 @@ def fvars_are_real(self): for k, v in self._fvars.items(): AA(self._qqbar_embedding(v)) except ValueError: - print("the F-symbol {} (key {}) has a nonzero imaginary part".format(v,k)) + print("the F-symbol {} (key {}) has a nonzero imaginary part".format(v, k)) return False return True - def certify_pentagons(self,use_mp=True, verbose=False): + def certify_pentagons(self, use_mp=True, verbose=False): r""" Obtain a certificate of satisfaction for the pentagon equations, up to floating-point error. @@ -2446,7 +2446,7 @@ def certify_pentagons(self,use_mp=True, verbose=False): n_proc = pool._processes if pool is not None else 1 params = [(child_id, n_proc, verbose) for child_id in range(n_proc)] pe = self._map_triv_reduce('pent_verify', params, worker_pool=pool, chunksize=1, mp_thresh=0) - if np.all(np.isclose(np.array(pe),0,atol=1e-7)): + if np.all(np.isclose(np.array(pe), 0, atol=1e-7)): if verbose: print("Found valid F-symbols for {}".format(self._FR)) pe = None diff --git a/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx b/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx index c3158585904..98983f0e171 100644 --- a/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx +++ b/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx @@ -10,12 +10,12 @@ Fast F-Matrix methods cimport cython from sage.algebras.fusion_rings.poly_tup_engine cimport ( - compute_known_powers, - get_variables_degrees, variables, - poly_to_tup, _tup_to_poly, - subs, subs_squares, reduce_poly_dict, resize, - _flatten_coeffs, _unflatten_coeffs, - has_appropriate_linear_term, + compute_known_powers, + get_variables_degrees, variables, + poly_to_tup, _tup_to_poly, + subs, subs_squares, reduce_poly_dict, resize, + _flatten_coeffs, _unflatten_coeffs, + has_appropriate_linear_term, resize ) from sage.algebras.fusion_rings.shm_managers cimport KSHandler, FvarsHandler @@ -39,7 +39,7 @@ cpdef _solve_for_linear_terms(factory, list eqns=None): EXAMPLES:: - sage: f = FMatrix(FusionRing("D3",1), inject_variables=True) + sage: f = FMatrix(FusionRing("D3", 1), inject_variables=True) creating variables fx1..fx27 Defining fx0, ..., fx26 sage: f._reset_solver_state() @@ -50,7 +50,7 @@ cpdef _solve_for_linear_terms(factory, list eqns=None): sage: f.ideal_basis.sort(key=poly_tup_sortkey) sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() - sage: f._fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars) + sage: f._fvars = FvarsHandler(n, f._field, f._idx_to_sextuple, init_data=f._fvars) sage: from sage.algebras.fusion_rings.fast_parallel_fmats_methods import _solve_for_linear_terms sage: _solve_for_linear_terms(f) True @@ -84,7 +84,7 @@ cpdef _solve_for_linear_terms(factory, list eqns=None): #TESTS: # s = factory._idx_to_sextuple[vars[0]] # factory.test_fvars[s] = tuple() - # assert factory.test_fvars[s] == fvars[s], "OG value {}, Shared: {}".format(fvars[s],factory.test_fvars[s]) + # assert factory.test_fvars[s] == fvars[s], "OG value {}, Shared: {}".format(fvars[s], factory.test_fvars[s]) if len(eq_tup) == 2: idx = has_appropriate_linear_term(eq_tup) if idx < 0: continue @@ -96,14 +96,14 @@ cpdef _solve_for_linear_terms(factory, list eqns=None): coeff = factory._field(list(eq_tup[(idx+1) % 2][1])) other = factory._field(list(eq_tup[idx][1])) rhs_coeff = tuple((-coeff / other)._coefficients()) - fvars[factory._idx_to_sextuple[max_var]] = ((rhs_exp,rhs_coeff),) + fvars[factory._idx_to_sextuple[max_var]] = ((rhs_exp, rhs_coeff), ) factory._solved[max_var] = True linear_terms_exist = True #TESTS: # s = factory._idx_to_sextuple[max_var] - # factory.test_fvars[s] = ((rhs_exp,rhs_coeff),) - # assert _unflatten_coeffs(factory._field,factory.test_fvars[s]) == fvars[s], "OG value {}, Shared: {}".format(factory.test_fvars[s],fvars[s]) + # factory.test_fvars[s] = ((rhs_exp, rhs_coeff), ) + # assert _unflatten_coeffs(factory._field, factory.test_fvars[s]) == fvars[s], "OG value {}, Shared: {}".format(factory.test_fvars[s], fvars[s]) return linear_terms_exist cpdef _backward_subs(factory, bint flatten=True): @@ -113,7 +113,7 @@ cpdef _backward_subs(factory, bint flatten=True): EXAMPLES:: - sage: f = FMatrix(FusionRing("D3",1), inject_variables=True) + sage: f = FMatrix(FusionRing("D3", 1), inject_variables=True) creating variables fx1..fx27 Defining fx0, ..., fx26 sage: f._reset_solver_state() @@ -124,7 +124,7 @@ cpdef _backward_subs(factory, bint flatten=True): sage: f.ideal_basis.sort(key=poly_tup_sortkey) sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() - sage: f._fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars) + sage: f._fvars = FvarsHandler(n, f._field, f._idx_to_sextuple, init_data=f._fvars) sage: from sage.algebras.fusion_rings.fast_parallel_fmats_methods import _solve_for_linear_terms sage: _solve_for_linear_terms(f) True @@ -157,14 +157,14 @@ cpdef _backward_subs(factory, bint flatten=True): cdef KSHandler _ks = factory._ks cdef dict idx_to_sextuple = factory._idx_to_sextuple cdef int nvars = len(idx_to_sextuple) - for i in range(nvars-1,-1,-1): + for i in range(nvars-1, -1, -1): sextuple = idx_to_sextuple[i] rhs = fvars[sextuple] d = {var_idx: fvars[idx_to_sextuple[var_idx]] for var_idx in variables(rhs) if solved[var_idx]} if d: - kp = compute_known_powers(get_variables_degrees([rhs],nvars), d, one) - res = tuple(subs_squares(subs(rhs,kp,one),_ks).items()) + kp = compute_known_powers(get_variables_degrees([rhs], nvars), d, one) + res = tuple(subs_squares(subs(rhs, kp, one), _ks).items()) if flatten: res = _flatten_coeffs(res) fvars[sextuple] = res @@ -173,7 +173,7 @@ cdef _fmat(fvars, _Nk_ij, id_anyon, a, b, c, d, x, y): """ Cython version of fmat class method. Using cdef for fastest dispatch """ - if _Nk_ij(a,b,x) == 0 or _Nk_ij(x,c,d) == 0 or _Nk_ij(b,c,y) == 0 or _Nk_ij(a,y,d) == 0: + if _Nk_ij(a, b, x) == 0 or _Nk_ij(x, c, d) == 0 or _Nk_ij(b, c, y) == 0 or _Nk_ij(a, y, d) == 0: return 0 #Some known F-symbols if a == id_anyon: @@ -191,7 +191,7 @@ cdef _fmat(fvars, _Nk_ij, id_anyon, a, b, c, d, x, y): return 1 else: return 0 - return fvars[a,b,c,d,x,y] + return fvars[a, b, c, d, x, y] ###################################### ### Fast fusion coefficients cache ### @@ -200,16 +200,16 @@ cdef _fmat(fvars, _Nk_ij, id_anyon, a, b, c, d, x, y): # from sage.misc.cachefunc import cached_function # cdef dict _Nk_ij = dict() -# cpdef _Nk_ij(factory,proc): +# cpdef _Nk_ij(factory, proc): # cdef int coeff -# for a,b,c in product(factory._FR.basis(),repeat=3): +# for a, b, c in product(factory._FR.basis(), repeat=3): # try: # coeff = (a*b).monomial_coefficients(copy=False)[c.weight()] # except: # coeff = 0 -# _Nk_ij[a,b,c] = coeff +# _Nk_ij[a, b, c] = coeff -# cpdef int _Nk_ij(a,b,c): +# cpdef int _Nk_ij(a, b, c): # try: # return (a*b).monomial_coefficients(copy=False)[c.weight()] # except KeyError: @@ -229,10 +229,10 @@ cdef req_cy(tuple basis, r_matrix, dict fvars, Nk_ij, id_anyon, tuple sextuple): a, b, c, d, e, g = sextuple #To add typing we need to ensure all fmats.fmat are of the same type? #Return fmats._poly_ring.zero() and fmats._poly_ring.one() instead of 0 and 1? - lhs = r_matrix(a,c,e,base_coercion=False) * _fmat(fvars,Nk_ij,id_anyon,a,c,b,d,e,g) * r_matrix(b,c,g,base_coercion=False) + lhs = r_matrix(a, c, e, base_coercion=False) * _fmat(fvars, Nk_ij, id_anyon, a, c, b, d, e, g) * r_matrix(b, c, g, base_coercion=False) rhs = 0 for f in basis: - rhs += _fmat(fvars,Nk_ij,id_anyon,c,a,b,d,e,f) * r_matrix(f,c,d,base_coercion=False) * _fmat(fvars,Nk_ij,id_anyon,a,b,c,d,f,g) + rhs += _fmat(fvars, Nk_ij, id_anyon, c, a, b, d, e, f) * r_matrix(f, c, d, base_coercion=False) * _fmat(fvars, Nk_ij, id_anyon, a, b, c, d, f, g) return lhs-rhs @cython.wraparound(False) @@ -261,7 +261,7 @@ cdef get_reduced_hexagons(factory, tuple mp_params): must_zip_up = isinstance(v, tuple) break if must_zip_up: - fvars = {k: _tup_to_poly(v,parent=factory._poly_ring) for k, v in factory._fvars.items()} + fvars = {k: _tup_to_poly(v, parent=factory._poly_ring) for k, v in factory._fvars.items()} else: fvars = factory._fvars r_matrix = factory._FR.r_matrix @@ -277,9 +277,9 @@ cdef get_reduced_hexagons(factory, tuple mp_params): for i in range(len(basis)**6): sextuple = next(it) if i % n_proc == child_id: - he = req_cy(basis,r_matrix,fvars,_Nk_ij,id_anyon,sextuple) + he = req_cy(basis, r_matrix, fvars, _Nk_ij, id_anyon, sextuple) if he: - red = reduce_poly_dict(he.dict(),_nnz,_ks,one) + red = reduce_poly_dict(he.dict(), _nnz, _ks, one) #Avoid pickling cyclotomic coefficients red = _flatten_coeffs(red) @@ -294,12 +294,12 @@ cdef MPolynomial_libsingular feq_cy(tuple basis, fvars, Nk_ij, id_anyon, zero, t as a polynomial object. """ a, b, c, d, e, f, g, k, l = nonuple - lhs = _fmat(fvars,Nk_ij,id_anyon,f,c,d,e,g,l) * _fmat(fvars,Nk_ij,id_anyon,a,b,l,e,f,k) + lhs = _fmat(fvars, Nk_ij, id_anyon, f, c, d, e, g, l) * _fmat(fvars, Nk_ij, id_anyon, a, b, l, e, f, k) if lhs == 0 and prune: # it is believed that if lhs=0, the equation carries no new information return zero rhs = zero for h in basis: - rhs += _fmat(fvars,Nk_ij,id_anyon,a,b,c,g,f,h)*_fmat(fvars,Nk_ij,id_anyon,a,h,d,e,g,k)*_fmat(fvars,Nk_ij,id_anyon,b,c,d,k,h,l) + rhs += _fmat(fvars, Nk_ij, id_anyon, a, b, c, g, f, h)*_fmat(fvars, Nk_ij, id_anyon, a, h, d, e, g, k)*_fmat(fvars, Nk_ij, id_anyon, b, c, d, k, h, l) return lhs - rhs @cython.wraparound(False) @@ -326,7 +326,7 @@ cdef get_reduced_pentagons(factory, tuple mp_params): break cdef dict fvars if must_zip_up: - fvars = {k: _tup_to_poly(v,parent=factory._poly_ring) for k, v in factory._fvars.items()} + fvars = {k: _tup_to_poly(v, parent=factory._poly_ring) for k, v in factory._fvars.items()} else: fvars = factory._fvars _Nk_ij = factory._FR.Nk_ij @@ -339,7 +339,7 @@ cdef get_reduced_pentagons(factory, tuple mp_params): cdef ETuple _nnz = factory._nnz #Computation loop - it = product(basis,repeat=9) + it = product(basis, repeat=9) for i in range(len(basis)**9): nonuple = next(it) if i % n_proc == child_id: @@ -367,7 +367,7 @@ cdef list update_reduce(factory, list eqns): cdef KSHandler _ks = factory._ks #Update reduction params factory._nnz = factory._get_known_nonz() - factory._kp = compute_known_powers(factory._var_degs,factory._get_known_vals(),factory._field.one()) + factory._kp = compute_known_powers(factory._var_degs, factory._get_known_vals(), factory._field.one()) cdef dict _kp = factory._kp cdef ETuple _nnz = factory._nnz @@ -376,8 +376,8 @@ cdef list update_reduce(factory, list eqns): #Construct cyclotomic field elts from list repn unflat = _unflatten_coeffs(_field, eq_tup) - eq_dict = subs(unflat,_kp,one) - red = reduce_poly_dict(eq_dict,_nnz,_ks,one) + eq_dict = subs(unflat, _kp, one) + red = reduce_poly_dict(eq_dict, _nnz, _ks, one) #Avoid pickling cyclotomic coefficients red = _flatten_coeffs(red) @@ -400,7 +400,7 @@ cdef list compute_gb(factory, tuple args): for fx in variables(eq_tup): sorted_vars.append(fx) sorted_vars = sorted(set(sorted_vars)) - cdef MPolynomialRing_libsingular R = PolynomialRing(factory._FR.field(),len(sorted_vars),'a',order=term_order) + cdef MPolynomialRing_libsingular R = PolynomialRing(factory._FR.field(), len(sorted_vars), 'a', order=term_order) #Zip tuples into R and compute Groebner basis cdef idx_map = { old : new for new, old in enumerate(sorted_vars) } @@ -409,7 +409,7 @@ cdef list compute_gb(factory, tuple args): cdef list polys = list() for eq_tup in eqns: eq_tup = _unflatten_coeffs(F, eq_tup) - polys.append(_tup_to_poly(resize(eq_tup,idx_map,nvars),parent=R)) + polys.append(_tup_to_poly(resize(eq_tup, idx_map, nvars), parent=R)) gb = Ideal(sorted(polys)).groebner_basis(algorithm="libsingular:slimgb") #Change back to fmats poly ring and append to temp_eqns @@ -417,7 +417,7 @@ cdef list compute_gb(factory, tuple args): cdef tuple t nvars = factory._poly_ring.ngens() for p in gb: - t = resize(poly_to_tup(p),inv_idx_map,nvars) + t = resize(poly_to_tup(p), inv_idx_map, nvars) #Avoid pickling cyclotomic coefficients t = _flatten_coeffs(t) @@ -447,10 +447,10 @@ cdef inline list collect_eqns(list eqns): #Hard-coded module __dict__-style attribute with visible cdef methods cdef dict mappers = { - "get_reduced_hexagons": get_reduced_hexagons, - "get_reduced_pentagons": get_reduced_pentagons, - "update_reduce": update_reduce, - "compute_gb": compute_gb, + "get_reduced_hexagons": get_reduced_hexagons, + "get_reduced_pentagons": get_reduced_pentagons, + "update_reduce": update_reduce, + "compute_gb": compute_gb, "pent_verify": pent_verify } @@ -480,14 +480,14 @@ cpdef executor(tuple params): TESTS:: sage: from sage.algebras.fusion_rings.fast_parallel_fmats_methods import executor - sage: fmats = FMatrix(FusionRing("A1",3)) + sage: fmats = FMatrix(FusionRing("A1", 3)) sage: fmats._reset_solver_state() - sage: params = (('get_reduced_hexagons', id(fmats)), (0,1,True)) + sage: params = (('get_reduced_hexagons', id(fmats)), (0, 1, True)) sage: len(executor(params)) == 63 True - sage: fmats = FMatrix(FusionRing("E6",1)) + sage: fmats = FMatrix(FusionRing("E6", 1)) sage: fmats._reset_solver_state() - sage: params = (('get_reduced_hexagons', id(fmats)), (0,1,False)) + sage: params = (('get_reduced_hexagons', id(fmats)), (0, 1, False)) sage: len(executor(params)) == 6 True """ @@ -495,7 +495,7 @@ cpdef executor(tuple params): #Construct a reference to global FMatrix object in this worker's memory fmats_obj = cast(fmats_id, py_object).value #Bind module method to FMatrix object in worker process, and call the method - return mappers[fn_name](fmats_obj,args) + return mappers[fn_name](fmats_obj, args) #################### ### Verification ### @@ -508,10 +508,10 @@ cdef feq_verif(factory, worker_results, fvars, Nk_ij, id_anyon, tuple nonuple, f a, b, c, d, e, f, g, k, l = nonuple cdef float diff, lhs, rhs - lhs = _fmat(fvars,Nk_ij,id_anyon,f,c,d,e,g,l)*_fmat(fvars,Nk_ij,id_anyon,a,b,l,e,f,k) + lhs = _fmat(fvars, Nk_ij, id_anyon, f, c, d, e, g, l)*_fmat(fvars, Nk_ij, id_anyon, a, b, l, e, f, k) rhs = 0.0 for h in factory._FR.basis(): - rhs += _fmat(fvars,Nk_ij,id_anyon,a,b,c,g,f,h)*_fmat(fvars,Nk_ij,id_anyon,a,h,d,e,g,k)*_fmat(fvars,Nk_ij,id_anyon,b,c,d,k,h,l) + rhs += _fmat(fvars, Nk_ij, id_anyon, a, b, c, g, f, h)*_fmat(fvars, Nk_ij, id_anyon, a, h, d, e, g, k)*_fmat(fvars, Nk_ij, id_anyon, b, c, d, k, h, l) diff = lhs - rhs if diff > tol or diff < -tol: worker_results.append(diff) @@ -521,7 +521,7 @@ cdef feq_verif(factory, worker_results, fvars, Nk_ij, id_anyon, tuple nonuple, f @cython.cdivision(True) cdef pent_verify(factory, tuple mp_params): r""" - Generate all the pentagon equations assigned to this process, + Generate all the pentagon equations assigned to this process, and reduce them. """ child_id, n_proc, verbose = mp_params @@ -536,7 +536,7 @@ cdef pent_verify(factory, tuple mp_params): id_anyon = factory._FR.one() for i, nonuple in enumerate(product(factory._FR.basis(), repeat=9)): if i % n_proc == child_id: - feq_verif(factory,worker_results,fvars,Nk_ij,id_anyon,nonuple) + feq_verif(factory, worker_results, fvars, Nk_ij, id_anyon, nonuple) if i % 50000000 == 0 and i and verbose: - print("{:5d}m equations checked... {} potential misses so far...".format(i // 1000000,len(worker_results))) + print("{:5d}m equations checked... {} potential misses so far...".format(i // 1000000, len(worker_results))) diff --git a/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx b/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx index 49af0008602..7d60bdb0d6e 100644 --- a/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx +++ b/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx @@ -18,9 +18,9 @@ from sage.rings.qqbar import QQbar ### Mappers ### ############### -cdef mid_sig_ij(fusion_ring,row,col,a,b): +cdef mid_sig_ij(fusion_ring, row, col, a, b): r""" - Compute the (xi,yi), (xj,yj) entry of generator braiding the middle two + Compute the (xi, yi), (xj, yj) entry of generator braiding the middle two strands in the tree b -> xi # yi -> (a # a) # (a # a), which results in a sum over j of trees b -> xj # yj -> (a # a) # (a # a) @@ -40,15 +40,15 @@ cdef mid_sig_ij(fusion_ring,row,col,a,b): for c in basis: for d in basis: ##Warning: We assume F-matrices are orthogonal!!! (using transpose for inverse) - f1 = _fmat(_fvars,_Nk_ij,one,a,a,yi,b,xi,c) - f2 = _fmat(_fvars,_Nk_ij,one,a,a,a,c,d,yi) - f3 = _fmat(_fvars,_Nk_ij,one,a,a,a,c,d,yj) - f4 = _fmat(_fvars,_Nk_ij,one,a,a,yj,b,xj,c) - r = fusion_ring.r_matrix(a,a,d) + f1 = _fmat(_fvars, _Nk_ij, one, a, a, yi, b, xi, c) + f2 = _fmat(_fvars, _Nk_ij, one, a, a, a, c, d, yi) + f3 = _fmat(_fvars, _Nk_ij, one, a, a, a, c, d, yj) + f4 = _fmat(_fvars, _Nk_ij, one, a, a, yj, b, xj, c) + r = fusion_ring.r_matrix(a, a, d) entry += f1 * f2 * r * f3 * f4 return entry -cdef odd_one_out_ij(fusion_ring,xi,xj,a,b): +cdef odd_one_out_ij(fusion_ring, xi, xj, a, b): r""" Compute the `xi`, `xj` entry of the braid generator on the two right-most strands, corresponding to the tree b -> (xi # a) -> (a # a) # a, which @@ -66,9 +66,9 @@ cdef odd_one_out_ij(fusion_ring,xi,xj,a,b): entry = 0 for c in fusion_ring.basis(): ##Warning: We assume F-matrices are orthogonal!!! (using transpose for inverse) - f1 = _fmat(_fvars,_Nk_ij,one,a,a,a,b,xi,c) - f2 = _fmat(_fvars,_Nk_ij,one,a,a,a,b,xj,c) - r = fusion_ring.r_matrix(a,a,c) + f1 = _fmat(_fvars, _Nk_ij, one, a, a, a, b, xi, c) + f2 = _fmat(_fvars, _Nk_ij, one, a, a, a, b, xj, c) + r = fusion_ring.r_matrix(a, a, c) entry += f1 * r * f2 return entry @@ -76,24 +76,24 @@ cdef odd_one_out_ij(fusion_ring,xi,xj,a,b): cdef odd_one_out_ij_cache = dict() cdef mid_sig_ij_cache = dict() -cdef cached_mid_sig_ij(fusion_ring,row,col,a,b): +cdef cached_mid_sig_ij(fusion_ring, row, col, a, b): r""" Cached version of :meth:`mid_sig_ij`. """ - if (row,col,a,b) in mid_sig_ij_cache: - return mid_sig_ij_cache[row,col,a,b] - entry = mid_sig_ij(fusion_ring,row,col,a,b) - mid_sig_ij_cache[row,col,a,b] = entry + if (row, col, a, b) in mid_sig_ij_cache: + return mid_sig_ij_cache[row, col, a, b] + entry = mid_sig_ij(fusion_ring, row, col, a, b) + mid_sig_ij_cache[row, col, a, b] = entry return entry -cdef cached_odd_one_out_ij(fusion_ring,xi,xj,a,b): +cdef cached_odd_one_out_ij(fusion_ring, xi, xj, a, b): r""" Cached version of :meth:`odd_one_out_ij`. """ - if (xi,xj,a,b) in odd_one_out_ij_cache: - return odd_one_out_ij_cache[xi,xj,a,b] - entry = odd_one_out_ij(fusion_ring,xi,xj,a,b) - odd_one_out_ij_cache[xi,xj,a,b] = entry + if (xi, xj, a, b) in odd_one_out_ij_cache: + return odd_one_out_ij_cache[xi, xj, a, b] + entry = odd_one_out_ij(fusion_ring, xi, xj, a, b) + odd_one_out_ij_cache[xi, xj, a, b] = entry return entry @cython.nonecheck(False) @@ -113,7 +113,7 @@ cdef sig_2k(fusion_ring, tuple args): cdef int ctr = -1 cdef list worker_results = list() #Get computational basis - cdef list comp_basis = fusion_ring.get_computational_basis(a,b,n_strands) + cdef list comp_basis = fusion_ring.get_computational_basis(a, b, n_strands) cdef dict basis_dict = {elt: i for i, elt in enumerate(comp_basis)} cdef int dim = len(comp_basis) cdef set coords = set() @@ -140,7 +140,7 @@ cdef sig_2k(fusion_ring, tuple args): nnz_pos = tuple(nnz_pos) #Skip repeated entries when k = 1 - if nnz_pos in comp_basis and (basis_dict[nnz_pos],i) not in coords: + if nnz_pos in comp_basis and (basis_dict[nnz_pos], i) not in coords: m, l = comp_basis[i][:n_strands//2], comp_basis[i][n_strands//2:] #A few special cases top_left = m[0] @@ -152,21 +152,21 @@ cdef sig_2k(fusion_ring, tuple args): #Handle the special case k = 1 if k == 1: - entry = cached_mid_sig_ij(fusion_ring,m[:2],(f,e),a,root) + entry = cached_mid_sig_ij(fusion_ring, m[:2], (f, e), a, root) #Avoid pickling cyclotomic field element objects if must_flatten_coeff: entry = entry.list() - worker_results.append(((basis_dict[nnz_pos],i), entry)) - coords.add((basis_dict[nnz_pos],i)) + worker_results.append(((basis_dict[nnz_pos], i), entry)) + coords.add((basis_dict[nnz_pos], i)) continue entry = 0 for p in fusion_ring.basis(): - f1 = _fmat(_fvars,_Nk_ij,one,top_left,m[k-1],m[k],root,l[k-2],p) - f2 = _fmat(_fvars,_Nk_ij,one,top_left,f,e,root,q,p) - entry += f1 * cached_mid_sig_ij(fusion_ring,(m[k-1],m[k]),(f,e),a,p) * f2 + f1 = _fmat(_fvars, _Nk_ij, one, top_left, m[k-1], m[k], root, l[k-2], p) + f2 = _fmat(_fvars, _Nk_ij, one, top_left, f, e, root, q, p) + entry += f1 * cached_mid_sig_ij(fusion_ring, (m[k-1], m[k]), (f, e), a, p) * f2 #Avoid pickling cyclotomic field element objects if must_flatten_coeff: @@ -224,7 +224,7 @@ cdef odd_one_out(fusion_ring, tuple args): #Handle a couple of small special cases if n_strands == 3: - entry = cached_odd_one_out_ij(fusion_ring,m[-1],f,a,b) + entry = cached_odd_one_out_ij(fusion_ring, m[-1], f, a, b) #Avoid pickling cyclotomic field element objects if must_flatten_coeff: @@ -240,15 +240,15 @@ cdef odd_one_out(fusion_ring, tuple args): #Compute relevant entry entry = 0 for p in basis: - f1 = _fmat(_fvars,_Nk_ij,one,top_left,m[-1],a,root,l[-1],p) - f2 = _fmat(_fvars,_Nk_ij,one,top_left,f,a,root,q,p) - entry += f1 * cached_odd_one_out_ij(fusion_ring,m[-1],f,a,p) * f2 + f1 = _fmat(_fvars, _Nk_ij, one, top_left, m[-1], a, root, l[-1], p) + f2 = _fmat(_fvars, _Nk_ij, one, top_left, f, a, root, q, p) + entry += f1 * cached_odd_one_out_ij(fusion_ring, m[-1], f, a, p) * f2 #Avoid pickling cyclotomic field element objects if must_flatten_coeff: entry = entry.list() - worker_results.append(((basis_dict[nnz_pos],i), entry)) + worker_results.append(((basis_dict[nnz_pos], i), entry)) return worker_results ############################## @@ -257,7 +257,7 @@ cdef odd_one_out(fusion_ring, tuple args): #Hard-coded module __dict__-style attribute with visible cdef methods cdef dict mappers = { - "sig_2k": sig_2k, + "sig_2k": sig_2k, "odd_one_out": odd_one_out } @@ -279,17 +279,17 @@ cpdef executor(tuple params): TESTS:: sage: from sage.algebras.fusion_rings.fast_parallel_fusion_ring_braid_repn import executor - sage: FR = FusionRing("A1",4) - sage: FR.fusion_labels(['idd','one','two','three','four'],inject_variables=True) + sage: FR = FusionRing("A1", 4) + sage: FR.fusion_labels(['idd', 'one', 'two', 'three', 'four'], inject_variables=True) sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time - sage: params = (('sig_2k',id(FR)),(0,1,(1,one,one,5))) # long time + sage: params = (('sig_2k', id(FR)), (0, 1, (1, one, one, 5))) # long time sage: len(executor(params)) == 13 # long time True sage: from sage.algebras.fusion_rings.fast_parallel_fusion_ring_braid_repn import executor - sage: FR = FusionRing("A1",2) - sage: FR.fusion_labels("a",inject_variables=True) + sage: FR = FusionRing("A1", 2) + sage: FR.fusion_labels("a", inject_variables=True) sage: FR.fmats.find_orthogonal_solution(verbose=False) - sage: params = (('odd_one_out',id(FR)),(0,1,(a2,a2,5))) + sage: params = (('odd_one_out', id(FR)), (0, 1, (a2, a2, 5))) sage: len(executor(params)) == 1 True """ @@ -297,7 +297,7 @@ cpdef executor(tuple params): #Construct a reference to global FMatrix object in this worker's memory fusion_ring_obj = cast(fr_id, py_object).value #Bind module method to FMatrix object in worker process, and call the method - return mappers[fn_name](fusion_ring_obj,args) + return mappers[fn_name](fusion_ring_obj, args) ###################################### ### Pickling circumvention helpers ### @@ -314,10 +314,10 @@ cpdef _unflatten_entries(fusion_ring, list entries): EXAMPLES:: sage: from sage.algebras.fusion_rings.fast_parallel_fusion_ring_braid_repn import _unflatten_entries - sage: fr = FusionRing("B2",2) + sage: fr = FusionRing("B2", 2) sage: F = fr.field() sage: coeff = [F.random_element() for i in range(2)] - sage: entries = [((0,0), coeff[0].list()), ((0,1), coeff[1].list())] + sage: entries = [((0, 0), coeff[0].list()), ((0, 1), coeff[1].list())] sage: _unflatten_entries(fr, entries) sage: all(cyc_elt_obj == c for (coord, cyc_elt_obj), c in zip(entries, coeff)) True diff --git a/src/sage/algebras/fusion_rings/fusion_ring.py b/src/sage/algebras/fusion_rings/fusion_ring.py index bd010614493..be067e64ece 100644 --- a/src/sage/algebras/fusion_rings/fusion_ring.py +++ b/src/sage/algebras/fusion_rings/fusion_ring.py @@ -71,7 +71,7 @@ class FusionRing(WeylCharacterRing): EXAMPLES:: - sage: A22 = FusionRing("A2",2) + sage: A22 = FusionRing("A2", 2) sage: [f1, f2] = A22.fundamental_weights() sage: M = [A22(x) for x in [0*f1, 2*f1, 2*f2, f1+f2, f2, f1]] sage: [M[3] * x for x in M] @@ -92,7 +92,7 @@ class FusionRing(WeylCharacterRing): [B22(0,0), B22(1,0), B22(0,1), B22(2,0), B22(1,1), B22(0,2)] sage: [x.weight() for x in b] [(0, 0), (1, 0), (1/2, 1/2), (2, 0), (3/2, 1/2), (1, 1)] - sage: B22.fusion_labels(['I0','Y1','X','Z','Xp','Y2'], inject_variables=True) + sage: B22.fusion_labels(['I0', 'Y1', 'X', 'Z', 'Xp', 'Y2'], inject_variables=True) sage: b = [B22(x) for x in B22.get_order()]; b [I0, Y1, X, Z, Xp, Y2] sage: [(x, x.weight()) for x in b] @@ -111,7 +111,7 @@ class FusionRing(WeylCharacterRing): This is the order used by methods such as :meth:`s_matrix`. You may use :meth:`CombinatorialFreeModule.set_order` to reorder the basis:: - sage: B22.set_order([x.weight() for x in [I0,Y1,Y2,X,Xp,Z]]) + sage: B22.set_order([x.weight() for x in [I0, Y1, Y2, X, Xp, Z]]) sage: [B22(x) for x in B22.get_order()] [I0, Y1, Y2, X, Xp, Z] @@ -158,7 +158,7 @@ class FusionRing(WeylCharacterRing): .. MATH:: - N^k_{ij} = \sum_l \frac{s(i,\ell)\,s(j,\ell)\,\overline{s(k,\ell)}}{s(I,\ell)}, + N^k_{ij} = \sum_l \frac{s(i, \ell)\, s(j, \ell)\, \overline{s(k, \ell)}}{s(I, \ell)}, where `N^k_{ij}` are the fusion coefficients, i.e. the structure constants of the fusion ring, and ``I`` is the unit object. @@ -167,53 +167,53 @@ class FusionRing(WeylCharacterRing): .. MATH:: - s(i*,j) = s(i,j*) = \overline{s(i,j)}. + s(i*, j) = s(i, j*) = \overline{s(i, j)}. This is equation (16.5) in [DFMS1996]_. Thus with `N_{ijk}=N^{k*}_{ij}` the Verlinde formula is equivalent to .. MATH:: - N_{ijk} = \sum_l \frac{s(i,\ell)\,s(j,\ell)\,s(k,\ell)}{s(I,\ell)}, + N_{ijk} = \sum_l \frac{s(i, \ell)\, s(j, \ell)\, s(k, \ell)}{s(I, \ell)}, In this formula `s` is the normalized unitary S-matrix denoted `s` in [BaKi2001]_. We may define a function that corresponds to the right-hand side, except using `\tilde{s}` instead of `s`:: - sage: def V(i,j,k): + sage: def V(i, j, k): ....: R = i.parent() - ....: return sum(R.s_ij(i,l) * R.s_ij(j,l) * R.s_ij(k,l) / R.s_ij(R.one(),l) + ....: return sum(R.s_ij(i, l) * R.s_ij(j, l) * R.s_ij(k, l) / R.s_ij(R.one(), l) ....: for l in R.basis()) - This does not produce ``self.N_ijk(i,j,k)`` exactly, because of the + This does not produce ``self.N_ijk(i, j, k)`` exactly, because of the missing normalization factor. The following code to check the Verlinde formula takes this into account:: sage: def test_verlinde(R): ....: b0 = R.one() ....: c = R.global_q_dimension() - ....: return all(V(i,j,k) == c * R.N_ijk(i,j,k) for i in R.basis() + ....: return all(V(i, j, k) == c * R.N_ijk(i, j, k) for i in R.basis() ....: for j in R.basis() for k in R.basis()) Every fusion ring should pass this test:: - sage: test_verlinde(FusionRing("A2",1)) + sage: test_verlinde(FusionRing("A2", 1)) True - sage: test_verlinde(FusionRing("B4",2)) # long time (.56s) + sage: test_verlinde(FusionRing("B4", 2)) # long time (.56s) True As an exercise, the reader may verify the examples in Section 5.3 of [RoStWa2009]_. Here we check the example of the Ising modular tensor category, which is related - to the BPZ minimal model `M(4,3)` or to an `E_8` coset + to the BPZ minimal model `M(4, 3)` or to an `E_8` coset model. See [DFMS1996]_ Sections 7.4.2 and 18.4.1. [RoStWa2009]_ Example 5.3.4 tells us how to construct it as the conjugate of the `E_8` level 2 :class:`FusionRing`:: - sage: I = FusionRing("E8",2,conjugate=True) - sage: I.fusion_labels(["i0","p","s"],inject_variables=True) + sage: I = FusionRing("E8", 2, conjugate=True) + sage: I.fusion_labels(["i0", "p", "s"], inject_variables=True) sage: b = I.basis().list(); b [i0, p, s] sage: Matrix([[x*y for x in b] for y in b]) # long time (.93s) @@ -224,7 +224,7 @@ class FusionRing(WeylCharacterRing): [0, 1, 1/8] sage: [x.ribbon() for x in b] [1, -1, zeta128^8] - sage: [I.r_matrix(i, j, k) for (i,j,k) in [(s,s,i0), (p,p,i0), (p,s,s), (s,p,s), (s,s,p)]] + sage: [I.r_matrix(i, j, k) for (i, j, k) in [(s, s, i0), (p, p, i0), (p, s, s), (s, p, s), (s, s, p)]] [-zeta128^56, -1, -zeta128^32, -zeta128^32, zeta128^24] sage: I.r_matrix(s, s, i0) == I.root_of_unity(-1/8) True @@ -245,11 +245,11 @@ class FusionRing(WeylCharacterRing): The term *modular tensor category* refers to the fact that associated with the category there is a projective representation of the modular - group `SL(2,\ZZ)`. We recall that this group is generated by + group `SL(2, \ZZ)`. We recall that this group is generated by .. MATH:: - S = \begin{pmatrix} & -1\\1\end{pmatrix},\qquad + S = \begin{pmatrix} & -1\\1\end{pmatrix}, \qquad T = \begin{pmatrix} 1 & 1\\ &1 \end{pmatrix} subject to the relations `(ST)^3 = S^2`, `S^2T = TS^2`, and `S^4 = I`. @@ -281,7 +281,7 @@ class FusionRing(WeylCharacterRing): of `SL(2, \ZZ)`. Let us confirm these identities for the Fibonacci MTC ``FusionRing("G2", 1)``:: - sage: R = FusionRing("G2",1) + sage: R = FusionRing("G2", 1) sage: S = R.s_matrix(unitary=True) sage: T = R.twists_matrix() sage: C = R.conj_matrix() @@ -347,7 +347,7 @@ def _test_verlinde(self, **options): EXAMPLES:: - sage: G22 = FusionRing("G2",2) + sage: G22 = FusionRing("G2", 2) sage: G22._test_verlinde() """ tester = self._tester(**options) @@ -355,9 +355,9 @@ def _test_verlinde(self, **options): i0 = self.one() from sage.misc.misc import some_tuples B = self.basis() - for x,y,z in some_tuples(B, 3, tester._max_runs): - v = sum(self.s_ij(x,w) * self.s_ij(y,w) * self.s_ij(z,w) / self.s_ij(i0,w) for w in B) - tester.assertEqual(v, c * self.N_ijk(x,y,z)) + for x, y, z in some_tuples(B, 3, tester._max_runs): + v = sum(self.s_ij(x, w) * self.s_ij(y, w) * self.s_ij(z, w) / self.s_ij(i0, w) for w in B) + tester.assertEqual(v, c * self.N_ijk(x, y, z)) def _test_total_q_order(self, **options): r""" @@ -369,7 +369,7 @@ def _test_total_q_order(self, **options): EXAMPLES:: - sage: G22 = FusionRing("G2",2) + sage: G22 = FusionRing("G2", 2) sage: G22._test_total_q_order() """ tester = self._tester(**options) @@ -397,10 +397,10 @@ def test_braid_representation(self, max_strands=6, anyon=None): EXAMPLES:: - sage: A21 = FusionRing("A2",1) + sage: A21 = FusionRing("A2", 1) sage: A21.test_braid_representation(max_strands=4) True - sage: F41 = FusionRing("F4",1) # long time + sage: F41 = FusionRing("F4", 1) # long time sage: F41.test_braid_representation() # long time True """ @@ -409,7 +409,7 @@ def test_braid_representation(self, max_strands=6, anyon=None): b = self.basis() results = [] #Test with different numbers of strands - for n_strands in range(3,max_strands+1): + for n_strands in range(3, max_strands+1): #Randomly select a fusing anyon. Skip the identity element, since #its braiding matrices are trivial if anyon is not None: @@ -426,7 +426,7 @@ def test_braid_representation(self, max_strands=6, anyon=None): if v > 1: d = self(k) break - comp_basis, sig = self.get_braid_generators(a,d,n_strands,verbose=False) + comp_basis, sig = self.get_braid_generators(a, d, n_strands, verbose=False) results.append(len(comp_basis) > 0) results.append(self.gens_satisfy_braid_gp_rels(sig)) return all(results) @@ -509,9 +509,9 @@ def field(self): EXAMPLES:: - sage: FusionRing("A2",2).field() + sage: FusionRing("A2", 2).field() Cyclotomic Field of order 60 and degree 16 - sage: FusionRing("B2",2).field() + sage: FusionRing("B2", 2).field() Cyclotomic Field of order 40 and degree 16 """ # if self._field is None: @@ -550,14 +550,14 @@ def fvars_field(self): EXAMPLES:: - sage: A13 = FusionRing("A1",3,fusion_labels="a",inject_variables=True) + sage: A13 = FusionRing("A1", 3, fusion_labels="a", inject_variables=True) sage: A13.fvars_field() Cyclotomic Field of order 40 and degree 16 sage: A13.field() Cyclotomic Field of order 40 and degree 16 sage: a2**4 2*a0 + 3*a2 - sage: comp_basis, sig = A13.get_braid_generators(a2,a2,3,verbose=False) # long time (<3s) + sage: comp_basis, sig = A13.get_braid_generators(a2, a2, 3, verbose=False) # long time (<3s) sage: A13.fvars_field() # long time Number Field in a with defining polynomial y^32 - ... - 500*y^2 + 25 sage: a2.q_dimension().parent() # long time @@ -572,6 +572,8 @@ def fvars_field(self): """ if self.is_multiplicity_free(): return self.fmats.field() + else: + raise ValueError("Method is only available for multiplicity free fusion rings.") def root_of_unity(self, r, base_coercion=True): r""" @@ -583,7 +585,7 @@ def root_of_unity(self, r, base_coercion=True): EXAMPLES:: - sage: A11 = FusionRing("A1",1) + sage: A11 = FusionRing("A1", 1) sage: A11.field() Cyclotomic Field of order 24 and degree 8 sage: [A11.root_of_unity(2/x) for x in [1..7]] @@ -606,10 +608,10 @@ def get_order(self): EXAMPLES:: - sage: A15 = FusionRing("A1",5) + sage: A15 = FusionRing("A1", 5) sage: w = A15.get_order(); w [(0, 0), (1/2, -1/2), (1, -1), (3/2, -3/2), (2, -2), (5/2, -5/2)] - sage: A15.set_order([w[k] for k in [0,4,1,3,5,2]]) + sage: A15.set_order([w[k] for k in [0, 4, 1, 3, 5, 2]]) sage: [A15(x) for x in A15.get_order()] [A15(0), A15(4), A15(1), A15(3), A15(5), A15(2)] @@ -645,7 +647,7 @@ def fusion_level(self): EXAMPLES:: - sage: B22 = FusionRing('B2',2) + sage: B22 = FusionRing('B2', 2) sage: B22.fusion_level() 2 """ @@ -664,10 +666,10 @@ def fusion_l(self): EXAMPLES:: - sage: B22 = FusionRing('B2',2) + sage: B22 = FusionRing('B2', 2) sage: B22.fusion_l() 10 - sage: D52 = FusionRing('D5',2) + sage: D52 = FusionRing('D5', 2) sage: D52.fusion_l() 10 """ @@ -721,7 +723,7 @@ def conj_matrix(self): EXAMPLES:: - sage: FusionRing("A2",1).conj_matrix() + sage: FusionRing("A2", 1).conj_matrix() [1 0 0] [0 0 1] [0 1 0] @@ -736,7 +738,7 @@ def twists_matrix(self): EXAMPLES:: - sage: B21=FusionRing("B2",1) + sage: B21=FusionRing("B2", 1) sage: [x.twist() for x in B21.basis().list()] [0, 1, 5/8] sage: [B21.root_of_unity(x.twist()) for x in B21.basis().list()] @@ -769,11 +771,11 @@ def N_ijk(self, elt_i, elt_j, elt_k): sage: G23.fusion_labels("g") sage: b = G23.basis().list(); b [g0, g1, g2, g3, g4, g5] - sage: [(x,y,z) for x in b for y in b for z in b if G23.N_ijk(x,y,z) > 1] + sage: [(x, y, z) for x in b for y in b for z in b if G23.N_ijk(x, y, z) > 1] [(g3, g3, g3), (g3, g3, g4), (g3, g4, g3), (g4, g3, g3)] - sage: all(G23.N_ijk(x,y,z)==G23.N_ijk(y,z,x) for x in b for y in b for z in b) + sage: all(G23.N_ijk(x, y, z)==G23.N_ijk(y, z, x) for x in b for y in b for z in b) True - sage: all(G23.N_ijk(x,y,z)==G23.N_ijk(y,x,z) for x in b for y in b for z in b) + sage: all(G23.N_ijk(x, y, z)==G23.N_ijk(y, x, z) for x in b for y in b for z in b) True """ return (elt_i * elt_j).monomial_coefficients().get(elt_k.dual().weight(), 0) @@ -793,7 +795,7 @@ def Nk_ij(self, elt_i, elt_j, elt_k): sage: A22 = FusionRing("A2", 2) sage: b = A22.basis().list() - sage: all(x*y == sum(A22.Nk_ij(x,y,k)*k for k in b) for x in b for y in b) + sage: all(x*y == sum(A22.Nk_ij(x, y, k)*k for k in b) for x in b for y in b) True """ return (elt_i * elt_j).monomial_coefficients(copy=False).get(elt_k.weight(), 0) @@ -808,7 +810,7 @@ def s_ij(self, elt_i, elt_j, base_coercion=True): .. MATH:: - s_{i,j} = \frac{1}{\theta_i\theta_j} \sum_k N_{ik}^j d_k \theta_k, + s_{i, j} = \frac{1}{\theta_i\theta_j} \sum_k N_{ik}^j d_k \theta_k, where `\theta_k` is the twist and `d_k` is the quantum dimension. See [Row2006]_ Equation (2.2) or [EGNO2015]_ @@ -836,7 +838,7 @@ def s_ij(self, elt_i, elt_j, base_coercion=True): def s_ijconj(self, elt_i, elt_j, base_coercion=True): """ Return the conjugate of the element of the S-matrix given by - ``self.s_ij(elt_i,elt_j,base_coercion=base_coercion)``. + ``self.s_ij(elt_i, elt_j, base_coercion=base_coercion)``. See :meth:`s_ij`. @@ -852,19 +854,19 @@ def s_ijconj(self, elt_i, elt_j, base_coercion=True): TESTS:: - sage: E62 = FusionRing("E6",2) + sage: E62 = FusionRing("E6", 2) sage: E62.fusion_labels("e", inject_variables=True) - sage: E62.s_ij(e8,e1).conjugate() == E62.s_ijconj(e8,e1) + sage: E62.s_ij(e8, e1).conjugate() == E62.s_ijconj(e8, e1) True - sage: F41 = FusionRing("F4",1) + sage: F41 = FusionRing("F4", 1) sage: F41.fmats.find_orthogonal_solution(verbose=False) sage: b = F41.basis() - sage: all(F41.s_ijconj(x,y) == F41._basecoer(F41.s_ij(x,y,base_coercion=False).conjugate()) for x in b for y in b) + sage: all(F41.s_ijconj(x, y) == F41._basecoer(F41.s_ij(x, y, base_coercion=False).conjugate()) for x in b for y in b) True - sage: G22 = FusionRing("G2",2) + sage: G22 = FusionRing("G2", 2) sage: G22.fmats.find_orthogonal_solution(verbose=False) # long time (~11 s) sage: b = G22.basis() # long time - sage: all(G22.s_ijconj(x,y) == G22.fmats.field()(G22.s_ij(x,y,base_coercion=False).conjugate()) for x in b for y in b) # long time + sage: all(G22.s_ijconj(x, y) == G22.fmats.field()(G22.s_ij(x, y, base_coercion=False).conjugate()) for x in b for y in b) # long time True """ ret = self.s_ij(elt_i, elt_j, base_coercion=False).conjugate() @@ -944,16 +946,16 @@ def r_matrix(self, i, j, k, base_coercion=True): EXAMPLES:: sage: I = FusionRing("E8", 2, conjugate=True) # Ising MTC - sage: I.fusion_labels(["i0","p","s"], inject_variables=True) - sage: I.r_matrix(s,s,i0) == I.root_of_unity(-1/8) + sage: I.fusion_labels(["i0", "p", "s"], inject_variables=True) + sage: I.r_matrix(s, s, i0) == I.root_of_unity(-1/8) True - sage: I.r_matrix(p,p,i0) + sage: I.r_matrix(p, p, i0) -1 - sage: I.r_matrix(p,s,s) == I.root_of_unity(-1/2) + sage: I.r_matrix(p, s, s) == I.root_of_unity(-1/2) True - sage: I.r_matrix(s,p,s) == I.root_of_unity(-1/2) + sage: I.r_matrix(s, p, s) == I.root_of_unity(-1/2) True - sage: I.r_matrix(s,s,p) == I.root_of_unity(3/8) + sage: I.r_matrix(s, s, p) == I.root_of_unity(3/8) True """ if self.Nk_ij(i, j, k) == 0: @@ -964,8 +966,8 @@ def r_matrix(self, i, j, k, base_coercion=True): i0 = self.one() B = self.basis() ret = sum(y.ribbon(base_coercion=False)**2 / (i.ribbon(base_coercion=False) * x.ribbon(base_coercion=False)**2) - * self.s_ij(i0,y,base_coercion=False) * self.s_ij(i,z,base_coercion=False) * self.s_ijconj(x,z,base_coercion=False) - * self.s_ijconj(k,x,base_coercion=False) * self.s_ijconj(y,z,base_coercion=False) / self.s_ij(i0,z,base_coercion=False) + * self.s_ij(i0, y, base_coercion=False) * self.s_ij(i, z, base_coercion=False) * self.s_ijconj(x, z, base_coercion=False) + * self.s_ijconj(k, x, base_coercion=False) * self.s_ijconj(y, z, base_coercion=False) / self.s_ij(i0, z, base_coercion=False) for x in B for y in B for z in B) / (self.total_q_order(base_coercion=False)**4) if (not base_coercion) or (self._basecoer is None): return ret @@ -980,7 +982,7 @@ def global_q_dimension(self, base_coercion=True): EXAMPLES:: - sage: FusionRing("E6",1).global_q_dimension() + sage: FusionRing("E6", 1).global_q_dimension() 3 """ ret = sum(x.q_dimension(base_coercion=False)**2 for x in self.basis()) @@ -998,7 +1000,7 @@ def total_q_order(self, base_coercion=True): EXAMPLES:: - sage: F = FusionRing("G2",1) + sage: F = FusionRing("G2", 1) sage: tqo=F.total_q_order(); tqo zeta60^15 - zeta60^11 - zeta60^9 + 2*zeta60^3 + zeta60 sage: tqo.is_real_positive() @@ -1007,7 +1009,7 @@ def total_q_order(self, base_coercion=True): True """ c = self.virasoro_central_charge() - ret = self.D_plus(base_coercion=False) * self.root_of_unity(-c/4,base_coercion=False) + ret = self.D_plus(base_coercion=False) * self.root_of_unity(-c/4, base_coercion=False) if (not base_coercion) or (self._basecoer is None): return ret return self._basecoer(ret) @@ -1021,7 +1023,7 @@ def D_plus(self, base_coercion=True): EXAMPLES:: - sage: B31 = FusionRing("B3",1) + sage: B31 = FusionRing("B3", 1) sage: Dp = B31.D_plus(); Dp 2*zeta48^13 - 2*zeta48^5 sage: Dm = B31.D_minus(); Dm @@ -1047,8 +1049,8 @@ def D_minus(self, base_coercion=True): EXAMPLES:: - sage: E83 = FusionRing("E8",3,conjugate=True) - sage: [Dp,Dm] = [E83.D_plus(), E83.D_minus()] + sage: E83 = FusionRing("E8", 3, conjugate=True) + sage: [Dp, Dm] = [E83.D_plus(), E83.D_minus()] sage: Dp*Dm == E83.global_q_dimension() True sage: c = E83.virasoro_central_charge(); c @@ -1071,7 +1073,7 @@ def is_multiplicity_free(self): EXAMPLES:: - sage: [FusionRing(ct,k).is_multiplicity_free() for ct in ("A1","A2","B2","C3") for k in (1,2,3)] + sage: [FusionRing(ct, k).is_multiplicity_free() for ct in ("A1", "A2", "B2", "C3") for k in (1, 2, 3)] [True, True, True, True, True, False, True, True, False, True, False, False] """ ct = self.cartan_type() @@ -1080,8 +1082,8 @@ def is_multiplicity_free(self): if ct.n == 1: return True return k <= 2 - # if ct.letter in ['B','D','G','F','E']: - if ct.letter in ['B','D','F','G']: + # if ct.letter in ['B', 'D', 'G', 'F', 'E']: + if ct.letter in ['B', 'D', 'F', 'G']: return k <= 2 if ct.letter == 'C': if ct.n == 2: @@ -1096,7 +1098,7 @@ def is_multiplicity_free(self): ### Braid group representations ### ################################### - def get_computational_basis(self,a,b,n_strands): + def get_computational_basis(self, a, b, n_strands): r""" Return the so-called computational basis for `\text{Hom}(b, a^n)`. @@ -1107,14 +1109,14 @@ def get_computational_basis(self,a,b,n_strands): - ``n_strands`` -- the number of strands for a braid group Let `n=` ``n_strands`` and let `k` be the greatest integer `\leq n/2`. - The braid group acts on `\text{Hom}(b,a^n)`. This action + The braid group acts on `\text{Hom}(b, a^n)`. This action is computed in :meth:`get_braid_generators`. This method returns the computational basis in the form of a list of fusion trees. Each tree is represented by an `(n-2)`-tuple .. MATH:: - (m_1,\ldots,m_k,l_1,\ldots,l_{k-2}) + (m_1, \ldots, m_k, l_1, \ldots, l_{k-2}) such that each `m_j` is an irreducible constituent in `a \otimes a` and @@ -1122,10 +1124,10 @@ def get_computational_basis(self,a,b,n_strands): .. MATH:: \begin{array}{l} - b \in l_{k-2} \otimes m_{k},\\ - l_{k-2} \in l_{k-3} \otimes m_{k-1},\\ - \cdots,\\ - l_2 \in l_1 \otimes m_3,\\ + b \in l_{k-2} \otimes m_{k}, \\ + l_{k-2} \in l_{k-3} \otimes m_{k-1}, \\ + \cdots, \\ + l_2 \in l_1 \otimes m_3, \\ l_1 \in m_1 \otimes m_2, \end{array} @@ -1139,28 +1141,28 @@ def get_computational_basis(self,a,b,n_strands): EXAMPLES:: - sage: A14 = FusionRing("A1",4) + sage: A14 = FusionRing("A1", 4) sage: A14.get_order() [(0, 0), (1/2, -1/2), (1, -1), (3/2, -3/2), (2, -2)] - sage: A14.fusion_labels(["zero","one","two","three","four"],inject_variables=True) + sage: A14.fusion_labels(["zero", "one", "two", "three", "four"], inject_variables=True) sage: [A14(x) for x in A14.get_order()] [zero, one, two, three, four] - sage: A14.get_computational_basis(one,two,4) + sage: A14.get_computational_basis(one, two, 4) [(two, two), (two, zero), (zero, two)] """ - def _get_trees(fr,top_row,root): + def _get_trees(fr, top_row, root): if len(top_row) == 2: m1, m2 = top_row - return [[]] if fr.Nk_ij(m1,m2,root) else [] + return [[]] if fr.Nk_ij(m1, m2, root) else [] else: m1, m2 = top_row[:2] - return [tuple([l,*b]) for l in fr.basis() for b in _get_trees(fr,[l]+top_row[2:],root) if fr.Nk_ij(m1,m2,l)] + return [tuple([l, *b]) for l in fr.basis() for b in _get_trees(fr, [l]+top_row[2:], root) if fr.Nk_ij(m1, m2, l)] comp_basis = list() - for top in product((a*a).monomials(),repeat=n_strands//2): + for top in product((a*a).monomials(), repeat=n_strands//2): #If the n_strands is odd, we must extend the top row by a fusing anyon top_row = list(top)+[a]*(n_strands%2) - comp_basis.extend(tuple([*top,*levels]) for levels in _get_trees(self,top_row,b)) + comp_basis.extend(tuple([*top, *levels]) for levels in _get_trees(self, top_row, b)) return comp_basis @lazy_attribute @@ -1173,7 +1175,7 @@ def fmats(self): EXAMPLES:: - sage: A15 = FusionRing("A1",5) + sage: A15 = FusionRing("A1", 5) sage: A15.fmats F-Matrix factory for The Fusion Ring of Type A1 and level 5 with Integer Ring coefficients """ @@ -1206,27 +1208,27 @@ def _emap(self, mapper, input_args, worker_pool=None): EXAMPLES:: - sage: FR = FusionRing("A1",4) - sage: FR.fusion_labels(['idd','one','two','three','four'], inject_variables=True) + sage: FR = FusionRing("A1", 4) + sage: FR.fusion_labels(['idd', 'one', 'two', 'three', 'four'], inject_variables=True) sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time - sage: len(FR._emap('sig_2k',(1,one,one,5))) # long time + sage: len(FR._emap('sig_2k', (1, one, one, 5))) # long time 13 - sage: FR = FusionRing("A1",2) - sage: FR.fusion_labels("a",inject_variables=True) + sage: FR = FusionRing("A1", 2) + sage: FR.fusion_labels("a", inject_variables=True) sage: FR.fmats.find_orthogonal_solution(verbose=False) - sage: len(FR._emap('odd_one_out',(a1,a1,7))) + sage: len(FR._emap('odd_one_out', (a1, a1, 7))) 16 """ n_proc = worker_pool._processes if worker_pool is not None else 1 input_iter = [(child_id, n_proc, input_args) for child_id in range(n_proc)] no_mp = worker_pool is None #Map phase - input_iter = zip_longest([],input_iter,fillvalue=(mapper,id(self))) + input_iter = zip_longest([], input_iter, fillvalue=(mapper, id(self))) results = list() if no_mp: - mapped = map(executor,input_iter) + mapped = map(executor, input_iter) else: - mapped = worker_pool.imap_unordered(executor,input_iter,chunksize=1) + mapped = worker_pool.imap_unordered(executor, input_iter, chunksize=1) #Reduce phase for worker_results in mapped: results.extend(worker_results) @@ -1263,7 +1265,7 @@ def get_braid_generators(self, we don't run the solver again. - ``use_mp`` -- (default: ``True``) a boolean indicating whether to use multiprocessing to speed up the computation; this is - highly recommended. Python 3.8+ is required. + highly recommended. Python 3.8+ is required. - ``verbose`` -- (default: ``True``) boolean indicating whether to be verbose with the computation @@ -1279,7 +1281,7 @@ def get_braid_generators(self, OUTPUT: - The method outputs a pair of data ``(comp_basis,sig)`` where + The method outputs a pair of data ``(comp_basis, sig)`` where ``comp_basis`` is a list of basis elements of the braid group module, parametrized by a list of fusion ring elements describing a fusion tree. For example with 5 strands the fusion tree @@ -1299,15 +1301,15 @@ def get_braid_generators(self, EXAMPLES:: - sage: A14 = FusionRing("A1",4) + sage: A14 = FusionRing("A1", 4) sage: A14.get_order() [(0, 0), (1/2, -1/2), (1, -1), (3/2, -3/2), (2, -2)] - sage: A14.fusion_labels(["one","two","three","four","five"],inject_variables=True) + sage: A14.fusion_labels(["one", "two", "three", "four", "five"], inject_variables=True) sage: [A14(x) for x in A14.get_order()] [one, two, three, four, five] sage: two ** 5 5*two + 4*four - sage: comp_basis, sig = A14.get_braid_generators(two,two,5,verbose=False) # long time + sage: comp_basis, sig = A14.get_braid_generators(two, two, 5, verbose=False) # long time sage: A14.gens_satisfy_braid_gp_rels(sig) # long time True sage: len(comp_basis) == 5 # long time @@ -1333,17 +1335,17 @@ def get_braid_generators(self, #Set up computational basis and compute generators one at a time a, b = fusing_anyon, total_charge_anyon - comp_basis = self.get_computational_basis(a,b,n_strands) + comp_basis = self.get_computational_basis(a, b, n_strands) d = len(comp_basis) if verbose: print("Computing an {}-dimensional representation of the Artin braid group on {} strands...".format(d, n_strands)) #Compute diagonal odd-indexed generators using the 3j-symbols - gens = {2*i+1: diagonal_matrix(self.r_matrix(a,a,c[i]) for c in comp_basis) for i in range(n_strands//2)} + gens = {2*i+1: diagonal_matrix(self.r_matrix(a, a, c[i]) for c in comp_basis) for i in range(n_strands//2)} #Compute even-indexed generators using F-matrices for k in range(1, n_strands//2): - entries = self._emap('sig_2k',(k,a,b,n_strands),pool) + entries = self._emap('sig_2k', (k, a, b, n_strands), pool) #Build cyclotomic field element objects from tuple of rationals repn _unflatten_entries(self, entries) @@ -1351,7 +1353,7 @@ def get_braid_generators(self, #If n_strands is odd, we compute the final generator if n_strands % 2: - entries = self._emap('odd_one_out',(a,b,n_strands),pool) + entries = self._emap('odd_one_out', (a, b, n_strands), pool) #Build cyclotomic field element objects from tuple of rationals repn _unflatten_entries(self, entries) @@ -1371,16 +1373,16 @@ def gens_satisfy_braid_gp_rels(self, sig): EXAMPLES:: - sage: F41 = FusionRing("F4",1,fusion_labels="f",inject_variables=True) + sage: F41 = FusionRing("F4", 1, fusion_labels="f", inject_variables=True) sage: f1*f1 f0 + f1 - sage: comp, sig = F41.get_braid_generators(f1,f0,4,verbose=False) + sage: comp, sig = F41.get_braid_generators(f1, f0, 4, verbose=False) sage: F41.gens_satisfy_braid_gp_rels(sig) True """ n = len(sig) braid_rels = all(sig[i] * sig[i+1] * sig[i] == sig[i+1] * sig[i] * sig[i+1] for i in range(n-1)) - far_comm = all(sig[i] * sig[j] == sig[j] * sig[i] for i, j in product(range(n),repeat=2) if abs(i-j) > 1 and i > j) + far_comm = all(sig[i] * sig[j] == sig[j] * sig[i] for i, j in product(range(n), repeat=2) if abs(i-j) > 1 and i > j) singular = any(s.is_singular() for s in sig) return braid_rels and far_comm and not singular @@ -1395,7 +1397,7 @@ def is_simple_object(self): EXAMPLES:: sage: A22 = FusionRing("A2", 2) - sage: x = A22(1,0); x + sage: x = A22(1, 0); x A22(1,0) sage: x.is_simple_object() True @@ -1414,7 +1416,7 @@ def weight(self): EXAMPLES:: - sage: A21 = FusionRing("A2",1) + sage: A21 = FusionRing("A2", 1) sage: [x.weight() for x in A21.basis().list()] [(0, 0, 0), (2/3, -1/3, -1/3), (1/3, 1/3, -2/3)] """ @@ -1477,7 +1479,7 @@ def twist(self, reduced=True): else: return twist - def ribbon(self,base_coercion=True): + def ribbon(self, base_coercion=True): r""" Return the twist or ribbon element of ``self``. @@ -1490,15 +1492,15 @@ def ribbon(self,base_coercion=True): EXAMPLES:: - sage: F = FusionRing("A1",3) + sage: F = FusionRing("A1", 3) sage: [x.twist() for x in F.basis()] [0, 3/10, 4/5, 3/2] sage: [x.ribbon(base_coercion=False) for x in F.basis()] [1, zeta40^6, zeta40^12 - zeta40^8 + zeta40^4 - 1, -zeta40^10] - sage: [F.root_of_unity(x,base_coercion=False) for x in [0, 3/10, 4/5, 3/2]] + sage: [F.root_of_unity(x, base_coercion=False) for x in [0, 3/10, 4/5, 3/2]] [1, zeta40^6, zeta40^12 - zeta40^8 + zeta40^4 - 1, -zeta40^10] """ - ret = self.parent().root_of_unity(self.twist(),base_coercion=False) + ret = self.parent().root_of_unity(self.twist(), base_coercion=False) if (not base_coercion) or (self.parent()._basecoer is None): return ret return self.parent()._basecoer(ret) @@ -1508,13 +1510,13 @@ def q_dimension(self, base_coercion=True): r""" Return the quantum dimension as an element of the cyclotomic field of the `2\ell`-th roots of unity, where `l = m (k+h^\vee)` - with `m=1,2,3` depending on whether type is simply, doubly or + with `m=1, 2, 3` depending on whether type is simply, doubly or triply laced, `k` is the level and `h^\vee` is the dual Coxeter number. EXAMPLES:: - sage: B22 = FusionRing("B2",2) + sage: B22 = FusionRing("B2", 2) sage: [(b.q_dimension())^2 for b in B22.basis()] [1, 4, 5, 1, 5, 4] """ diff --git a/src/sage/algebras/fusion_rings/poly_tup_engine.pyx b/src/sage/algebras/fusion_rings/poly_tup_engine.pyx index a34fa303d15..ac077f376ae 100644 --- a/src/sage/algebras/fusion_rings/poly_tup_engine.pyx +++ b/src/sage/algebras/fusion_rings/poly_tup_engine.pyx @@ -1,5 +1,5 @@ """ -Arithmetic Engine for polynomials as tuples +Arithmetic Engine for Polynomials as Tuples """ # **************************************************************************** # Copyright (C) 2021 Guillermo Aboumrad @@ -20,7 +20,7 @@ cpdef inline tuple poly_to_tup(MPolynomial_libsingular poly): EXAMPLES:: sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup - sage: R. = PolynomialRing(QQ) + sage: R. = PolynomialRing(QQ) sage: poly_to_tup(x**2 + 1) (((2, 0), 1), ((0, 0), 1)) sage: poly_to_tup(x**2*y**4 - 4/5*x*y**2 + 1/3 * y) @@ -51,8 +51,8 @@ cpdef inline MPolynomial_libsingular _tup_to_poly(tuple eq_tup, MPolynomialRing_ sage: from sage.algebras.fusion_rings.poly_tup_engine import _tup_to_poly sage: K = CyclotomicField(20) - sage: R. = PolynomialRing(K) - sage: poly_tup = (((2,0),K.one()), ((0,0),K.one())) + sage: R. = PolynomialRing(K) + sage: poly_tup = (((2, 0), K.one()), ((0, 0), K.one())) sage: _tup_to_poly(poly_tup, parent=R) x^2 + 1 sage: poly = x**2*y**4 - 4/5*x*y**2 + 1/3 * y @@ -63,7 +63,7 @@ cpdef inline MPolynomial_libsingular _tup_to_poly(tuple eq_tup, MPolynomialRing_ TESTS:: sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup, _tup_to_poly - sage: R. = PolynomialRing(CyclotomicField(20)) + sage: R. = PolynomialRing(CyclotomicField(20)) sage: r = R.random_element() sage: _tup_to_poly(poly_to_tup(r), parent=R) == r True @@ -99,7 +99,7 @@ cpdef tuple _unflatten_coeffs(field, tuple eq_tup): EXAMPLES:: sage: from sage.algebras.fusion_rings.poly_tup_engine import _unflatten_coeffs - sage: fm = FMatrix(FusionRing("A2",2)) + sage: fm = FMatrix(FusionRing("A2", 2)) sage: p = fm._poly_ring.random_element() sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup sage: flat_poly_tup = list() @@ -162,14 +162,14 @@ cpdef inline tup_to_univ_poly(tuple eq_tup, univ_poly_ring): sage: from sage.algebras.fusion_rings.poly_tup_engine import tup_to_univ_poly sage: from sage.rings.polynomial.polydict import ETuple sage: K = CyclotomicField(56) - sage: poly_tup = ((ETuple([0,3,0]),K(2)), (ETuple([0,1,0]),K(-1)), (ETuple([0,0,0]),K(-2/3))) + sage: poly_tup = ((ETuple([0, 3, 0]), K(2)), (ETuple([0, 1, 0]), K(-1)), (ETuple([0, 0, 0]), K(-2/3))) sage: R = K['b'] sage: tup_to_univ_poly(poly_tup, R) 2*b^3 - b - 2/3 TESTS:: - sage: poly_tup = ((ETuple([0, 0, 0]), K(-1/5)),) + sage: poly_tup = ((ETuple([0, 0, 0]), K(-1/5)), ) sage: tup_to_univ_poly(poly_tup, R) -1/5 """ @@ -192,9 +192,9 @@ cpdef inline tuple resize(tuple eq_tup, dict idx_map, int nvars): sage: from sage.algebras.fusion_rings.poly_tup_engine import resize sage: from sage.rings.polynomial.polydict import ETuple sage: K = CyclotomicField(56) - sage: poly_tup = ((ETuple([0,3,0,2]),K(2)), (ETuple([0,1,0,1]),K(-1)), (ETuple([0,0,0,0]),K(-2/3))) + sage: poly_tup = ((ETuple([0, 3, 0, 2]), K(2)), (ETuple([0, 1, 0, 1]), K(-1)), (ETuple([0, 0, 0, 0]), K(-2/3))) sage: idx_map = {1: 0, 3: 1} - sage: resize(poly_tup,idx_map,2) + sage: resize(poly_tup, idx_map, 2) (((3, 2), 2), ((1, 1), -1), ((0, 0), -2/3)) sage: R = PolynomialRing(K, 'fx', 20) @@ -202,8 +202,8 @@ cpdef inline tuple resize(tuple eq_tup, dict idx_map, int nvars): Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13, fx14, fx15, fx16, fx17, fx18, fx19 sage: sparse_poly = R(fx0**2 * fx17 + fx3) sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup, _tup_to_poly - sage: S. = PolynomialRing(K) - sage: _tup_to_poly(resize(poly_to_tup(sparse_poly),{0:0,3:1,17:2},3), parent=S) + sage: S. = PolynomialRing(K) + sage: _tup_to_poly(resize(poly_to_tup(sparse_poly), {0:0, 3:1, 17:2}, 3), parent=S) x^2*z + y """ cdef ETuple exp, new_e @@ -239,7 +239,7 @@ cpdef list get_variables_degrees(list eqns, int nvars): EXAMPLES:: sage: from sage.algebras.fusion_rings.poly_tup_engine import get_variables_degrees - sage: R. = PolynomialRing(QQ) + sage: R. = PolynomialRing(QQ) sage: polys = [x**2 + 1, x*y*z**2 - 4*x*y, x*z**3 - 4/3*y + 1] sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup sage: get_variables_degrees([poly_to_tup(p) for p in polys], 3) @@ -265,11 +265,11 @@ cpdef list variables(tuple eq_tup): sage: from sage.algebras.fusion_rings.poly_tup_engine import variables sage: from sage.rings.polynomial.polydict import ETuple - sage: poly_tup = ((ETuple([0,3,0]),2), (ETuple([0,1,0]),-1), (ETuple([0,0,0]),-2/3)) + sage: poly_tup = ((ETuple([0, 3, 0]), 2), (ETuple([0, 1, 0]), -1), (ETuple([0, 0, 0]), -2/3)) sage: variables(poly_tup) [1] sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup - sage: R. = PolynomialRing(QQ) + sage: R. = PolynomialRing(QQ) sage: variables(poly_to_tup(x*2*y + y**3 - 4/3*x)) [0, 1] sage: variables(poly_to_tup(R(1/4))) @@ -286,12 +286,12 @@ cpdef constant_coeff(tuple eq_tup, field): sage: from sage.algebras.fusion_rings.poly_tup_engine import constant_coeff sage: from sage.rings.polynomial.polydict import ETuple - sage: poly_tup = ((ETuple([0,3,0]),2), (ETuple([0,1,0]),-1), (ETuple([0,0,0]),-2/3)) - sage: constant_coeff(poly_tup,QQ) + sage: poly_tup = ((ETuple([0, 3, 0]), 2), (ETuple([0, 1, 0]), -1), (ETuple([0, 0, 0]), -2/3)) + sage: constant_coeff(poly_tup, QQ) -2/3 - sage: R. = PolynomialRing(QQ) + sage: R. = PolynomialRing(QQ) sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup - sage: constant_coeff(poly_to_tup(x**5 + x*y*z - 9),QQ) + sage: constant_coeff(poly_to_tup(x**5 + x*y*z - 9), QQ) -9 """ cdef ETuple exp @@ -308,7 +308,7 @@ cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map): sage: from sage.algebras.fusion_rings.poly_tup_engine import apply_coeff_map sage: sq = lambda x : x**2 - sage: R. = PolynomialRing(ZZ) + sage: R. = PolynomialRing(ZZ) sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup, _tup_to_poly sage: _tup_to_poly(apply_coeff_map(poly_to_tup(x + 2*y + 3*z), sq), parent=R) x + 4*y + 9*z @@ -454,7 +454,7 @@ cpdef dict compute_known_powers(max_degs, dict val_dict, one): EXAMPLES:: sage: from sage.algebras.fusion_rings.poly_tup_engine import compute_known_powers - sage: R. = PolynomialRing(QQ) + sage: R. = PolynomialRing(QQ) sage: polys = [x**3 + 1, x**2*y + z**3, y**2 - 3*y] sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup sage: known_val = { 0 : poly_to_tup(R(-1)), 2 : poly_to_tup(y**2) } @@ -475,7 +475,7 @@ cpdef dict compute_known_powers(max_degs, dict val_dict, one): max_deg = max_deg.emin(ETuple({idx: 100 for idx in val_dict}, len(max_deg))) cdef dict known_powers #Get polynomial unit as tuple to initialize list elements - cdef tuple one_tup = ((max_deg._new(), one),) + cdef tuple one_tup = ((max_deg._new(), one), ) cdef int d, power, var_idx known_powers = {var_idx: [one_tup]*(d+1) for var_idx, d in max_deg.sparse_iter()} for var_idx, d in max_deg.sparse_iter(): @@ -493,11 +493,11 @@ cdef dict subs(tuple poly_tup, dict known_powers, one): cdef tuple temp for exp, coeff in poly_tup: #Get polynomial unit as tuple - temp = ((exp._new(), one),) + temp = ((exp._new(), one), ) for var_idx, power in exp.sparse_iter(): if var_idx in known_powers: - exp = exp.eadd_p(-power,var_idx) - temp = tup_mul(temp,known_powers[var_idx][power]) + exp = exp.eadd_p(-power, var_idx) + temp = tup_mul(temp, known_powers[var_idx][power]) for m, c in temp: shifted_exp = exp.eadd(m) if shifted_exp in subbed: @@ -552,7 +552,7 @@ cpdef tuple poly_tup_sortkey(tuple eq_tup): sage: F = CyclotomicField(20) sage: zeta20 = F.gen() - sage: R. = PolynomialRing(F) + sage: R. = PolynomialRing(F) sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_tup_sortkey, poly_to_tup sage: p = (zeta20 + 1)*x^2 + (zeta20^3 + 6)*x*z + (zeta20^2 + 7*zeta20)*z^2 + (2/3*zeta20 + 1/4)*x + y sage: p1 = poly_to_tup(p); p1 @@ -577,4 +577,3 @@ cpdef tuple poly_tup_sortkey(tuple eq_tup): key.append(-exp._data[2*i]) key.append(exp._data[2*i+1]) return tuple(key) - diff --git a/src/sage/algebras/fusion_rings/shm_managers.pyx b/src/sage/algebras/fusion_rings/shm_managers.pyx index fd5fe8d0637..76095a3242d 100644 --- a/src/sage/algebras/fusion_rings/shm_managers.pyx +++ b/src/sage/algebras/fusion_rings/shm_managers.pyx @@ -47,7 +47,7 @@ cdef class KSHandler: whether the structure contains an entry corresponding to the given index. The parent process should construct this object without a - ``name`` attribute. Children processes use the ``name`` attribute, + ``name`` attribute. Children processes use the ``name`` attribute, accessed via ``self.shm.name`` to attach to the shared memory block. INPUT: @@ -64,7 +64,7 @@ cdef class KSHandler: .. NOTE:: - To properly dispose of shared memory resources, + To properly dispose of shared memory resources, ``self.shm.unlink()`` must be called before exiting. .. WARNING:: @@ -76,15 +76,15 @@ cdef class KSHandler: sage: from sage.algebras.fusion_rings.shm_managers import KSHandler sage: #Create shared data structure - sage: f = FMatrix(FusionRing("A1",2), inject_variables=True) + sage: f = FMatrix(FusionRing("A1", 2), inject_variables=True) creating variables fx1..fx14 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 sage: n = f._poly_ring.ngens() sage: f.start_worker_pool() - sage: ks = KSHandler(n,f._field,use_mp=True) + sage: ks = KSHandler(n, f._field, use_mp=True) sage: #In the same shell or in a different shell, attach to fvars sage: name = ks.shm.name - sage: ks2 = KSHandler(n,f._field,name=name,use_mp=True) + sage: ks2 = KSHandler(n, f._field, name=name, use_mp=True) sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup sage: eqns = [fx1**2 - 4, fx3**2 + f._field.gen()**4 - 1/19*f._field.gen()**2] sage: ks.update([poly_to_tup(p) for p in eqns]) @@ -104,12 +104,12 @@ cdef class KSHandler: sage: from sage.algebras.fusion_rings.shm_managers import KSHandler sage: #Create shared data structure - sage: f = FMatrix(FusionRing("A1",2), inject_variables=True) + sage: f = FMatrix(FusionRing("A1", 2), inject_variables=True) creating variables fx1..fx14 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 sage: n = f._poly_ring.ngens() sage: f.start_worker_pool() - sage: ks = KSHandler(n,f._field,use_mp=True) + sage: ks = KSHandler(n, f._field, use_mp=True) sage: TestSuite(ks).run() sage: ks.shm.unlink() sage: f.shutdown_worker_pool() @@ -119,9 +119,9 @@ cdef class KSHandler: n = n_slots d = self.field.degree() ks_t = np.dtype([ - ('known', 'bool', (1,)), - ('nums','i8',(d,)), - ('denoms','u8',(d,)) + ('known', 'bool', (1, )), + ('nums', 'i8', (d, )), + ('denoms', 'u8', (d, )) ]) self.obj_cache = [None]*n if use_mp: @@ -129,23 +129,23 @@ cdef class KSHandler: self.shm = shared_memory.SharedMemory(create=True, size=n*ks_t.itemsize) else: self.shm = shared_memory.SharedMemory(name=name) - self.ks_dat = np.ndarray((n,), dtype=ks_t, buffer=self.shm.buf) + self.ks_dat = np.ndarray((n, ), dtype=ks_t, buffer=self.shm.buf) else: - self.ks_dat = np.ndarray((n,), dtype=ks_t) + self.ks_dat = np.ndarray((n, ), dtype=ks_t) if name is None: - self.ks_dat['known'] = np.zeros((n,1), dtype='bool') - self.ks_dat['nums'] = np.zeros((n,d), dtype='i8') - self.ks_dat['denoms'] = np.ones((n,d), dtype='u8') + self.ks_dat['known'] = np.zeros((n, 1), dtype='bool') + self.ks_dat['nums'] = np.zeros((n, d), dtype='i8') + self.ks_dat['denoms'] = np.ones((n, d), dtype='u8') #Populate initializer data for idx, sq in init_data.items(): - self.setitem(idx,sq) + self.setitem(idx, sq) @cython.nonecheck(False) @cython.wraparound(False) @cython.boundscheck(False) cdef NumberFieldElement_absolute get(self, int idx): r""" - Retrieve the known square corresponding to the given index, + Retrieve the known square corresponding to the given index, if it exists. """ if not self.ks_dat['known'][idx]: @@ -155,9 +155,9 @@ cdef class KSHandler: cdef int d cdef list rat cdef Py_ssize_t i - cdef np.ndarray[np.int64_t,ndim=1] nums = self.ks_dat['nums'][idx] + cdef np.ndarray[np.int64_t, ndim=1] nums = self.ks_dat['nums'][idx] # cdef np.int64_t[::1] num_view = nums - cdef np.ndarray[np.uint64_t,ndim=1] denoms = self.ks_dat['denoms'][idx] + cdef np.ndarray[np.uint64_t, ndim=1] denoms = self.ks_dat['denoms'][idx] # cdef np.uint64_t[::1] denom_view = denoms cdef np.int64_t num cdef np.uint64_t denom @@ -182,25 +182,25 @@ cdef class KSHandler: EXAMPLES:: - sage: f = FMatrix(FusionRing("B5",1)) + sage: f = FMatrix(FusionRing("B5", 1)) sage: f._reset_solver_state() sage: for idx, sq in f._ks.items(): ....: k ....: sage: f.get_orthogonality_constraints() - [fx0^2 - 1, - fx1^2 - 1, - fx2^2 - 1, - fx3^2 - 1, - fx4^2 - 1, - fx5^2 - 1, - fx6^2 - 1, - fx7^2 - 1, - fx8^2 - 1, - fx9^2 - 1, - fx10^2 + fx12^2 - 1, - fx10*fx11 + fx12*fx13, - fx10*fx11 + fx12*fx13, + [fx0^2 - 1, + fx1^2 - 1, + fx2^2 - 1, + fx3^2 - 1, + fx4^2 - 1, + fx5^2 - 1, + fx6^2 - 1, + fx7^2 - 1, + fx8^2 - 1, + fx9^2 - 1, + fx10^2 + fx12^2 - 1, + fx10*fx11 + fx12*fx13, + fx10*fx11 + fx12*fx13, fx11^2 + fx13^2 - 1] sage: f.get_orthogonality_constraints(output=False) sage: f._ks.update(f.ideal_basis) @@ -236,7 +236,7 @@ cdef class KSHandler: try: self.setitem(idx, rhs) except OverflowError: - print("KS overflowed on index {} with value {}".format(idx,self.field(rhs))) + print("KS overflowed on index {} with value {}".format(idx, self.field(rhs))) @cython.nonecheck(False) @cython.wraparound(False) @@ -249,8 +249,8 @@ cdef class KSHandler: list/tuple representation. """ cdef Py_ssize_t i - cdef np.ndarray[np.int64_t,ndim=1] nums = self.ks_dat['nums'][idx] - cdef np.ndarray[np.uint64_t,ndim=1] denoms = self.ks_dat['denoms'][idx] + cdef np.ndarray[np.int64_t, ndim=1] nums = self.ks_dat['nums'][idx] + cdef np.ndarray[np.uint64_t, ndim=1] denoms = self.ks_dat['denoms'][idx] cdef np.int64_t num cdef np.uint64_t denom cdef Rational quo @@ -281,16 +281,16 @@ cdef class KSHandler: TESTS:: - sage: f = FMatrix(FusionRing("C2",2)) + sage: f = FMatrix(FusionRing("C2", 2)) sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) sage: from sage.algebras.fusion_rings.shm_managers import KSHandler sage: n = f._poly_ring.ngens() sage: f.start_worker_pool() - sage: ks = KSHandler(n,f._field,use_mp=True,init_data=f._ks) + sage: ks = KSHandler(n, f._field, use_mp=True, init_data=f._ks) sage: #In the same shell or in a different one, attach to shared memory handler sage: name = ks.shm.name - sage: k2 = KSHandler(n,f._field,name=name,use_mp=True) + sage: k2 = KSHandler(n, f._field, name=name, use_mp=True) sage: ks == k2 True sage: ks.shm.unlink() @@ -304,7 +304,7 @@ cdef class KSHandler: TESTS:: - sage: f = FMatrix(FusionRing("A3",1)) + sage: f = FMatrix(FusionRing("A3", 1)) sage: f._reset_solver_state() sage: loads(dumps(f._ks)) == f._ks True @@ -321,12 +321,12 @@ cdef class KSHandler: EXAMPLES:: - sage: f = FMatrix(FusionRing("A3",1)) + sage: f = FMatrix(FusionRing("A3", 1)) sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) sage: f._ks.update(f.ideal_basis) sage: for idx, sq in f._ks.items(): - ....: print("Index: {}, sq: {}".format(idx,sq)) + ....: print("Index: {}, sq: {}".format(idx, sq)) ....: Index: 0, sq: 1 Index: 1, sq: 1 @@ -342,13 +342,13 @@ cdef class KSHandler: if self.ks_dat['known'][i]: yield i, self.get(i) -def make_KSHandler(n_slots,field,init_data): +def make_KSHandler(n_slots, field, init_data): r""" Provide pickling / unpickling support for :class:`KSHandler`. TESTS:: - sage: f = FMatrix(FusionRing("B4",1)) + sage: f = FMatrix(FusionRing("B4", 1)) sage: f._reset_solver_state() sage: loads(dumps(f._ks)) == f._ks # indirect doctest True @@ -384,7 +384,7 @@ cdef class FvarsHandler: entries have been modified before attempting retrieval. The parent process should construct this object without a - ``name`` attribute. Children processes use the ``name`` attribute, + ``name`` attribute. Children processes use the ``name`` attribute, accessed via ``self.shm.name`` to attach to the shared memory block. Multiprocessing requires Python 3.8+, since we must import the @@ -413,7 +413,7 @@ cdef class FvarsHandler: .. NOTE:: - To properly dispose of shared memory resources, + To properly dispose of shared memory resources, ``self.shm.unlink()`` must be called before exiting. .. NOTE:: @@ -424,7 +424,7 @@ cdef class FvarsHandler: .. WARNING:: - The current data structure supports up to `2^16` entries, + The current data structure supports up to `2^16` entries, with each monomial in each entry having at most 254 nonzero terms. On average, each of the ``max_terms`` monomials can have at most 30 terms. @@ -433,7 +433,7 @@ cdef class FvarsHandler: sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: #Create shared data structure - sage: f = FMatrix(FusionRing("A2",1), inject_variables=True) + sage: f = FMatrix(FusionRing("A2", 1), inject_variables=True) creating variables fx1..fx8 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 sage: f.start_worker_pool() @@ -442,7 +442,7 @@ cdef class FvarsHandler: sage: fvars = FvarsHandler(8, f._field, f._idx_to_sextuple, use_mp=n_proc, pids_name=pids_name) sage: #In the same shell or in a different shell, attach to fvars sage: name = fvars.shm.name - sage: fvars2 = FvarsHandler(8, f._field, f._idx_to_sextuple, name=name ,use_mp=n_proc, pids_name=pids_name) + sage: fvars2 = FvarsHandler(8, f._field, f._idx_to_sextuple, name=name , use_mp=n_proc, pids_name=pids_name) sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup sage: rhs = tuple((exp, tuple(c._coefficients())) for exp, c in poly_to_tup(fx5**5)) sage: fvars[f2, f1, f2, f2, f0, f0] = rhs @@ -451,7 +451,7 @@ cdef class FvarsHandler: sage: fvars.shm.unlink() sage: f.shutdown_worker_pool() """ - def __init__(self, n_slots, field, idx_to_sextuple, init_data={}, use_mp=0, + def __init__(self, n_slots, field, idx_to_sextuple, init_data={}, use_mp=0, pids_name=None, name=None, max_terms=20, n_bytes=32): r""" Initialize ``self``. @@ -460,13 +460,13 @@ cdef class FvarsHandler: sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: #Create shared data structure - sage: f = FMatrix(FusionRing("A2",1), inject_variables=True) + sage: f = FMatrix(FusionRing("A2", 1), inject_variables=True) creating variables fx1..fx8 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 sage: f.start_worker_pool() sage: n_proc = f.pool._processes sage: pids_name = f._pid_list.shm.name - sage: fvars = FvarsHandler(8,f._field,f._idx_to_sextuple,use_mp=n_proc,pids_name=pids_name) + sage: fvars = FvarsHandler(8, f._field, f._idx_to_sextuple, use_mp=n_proc, pids_name=pids_name) sage: TestSuite(fvars).run(skip="_test_pickling") sage: fvars.shm.unlink() sage: f.shutdown_worker_pool() @@ -478,11 +478,11 @@ cdef class FvarsHandler: cdef int slots = self.bytes // 8 cdef int n_proc = use_mp + 1 self.fvars_t = np.dtype([ - ('modified',np.int8,(n_proc,)), - ('ticks', 'u1', (max_terms,)), - ('exp_data', 'u2', (max_terms*30,)), - ('coeff_nums',np.int64,(max_terms,d,slots)), - ('coeff_denom',np.uint64,(max_terms,d,slots)) + ('modified', np.int8, (n_proc, )), + ('ticks', 'u1', (max_terms, )), + ('exp_data', 'u2', (max_terms*30, )), + ('coeff_nums', np.int64, (max_terms, d, slots)), + ('coeff_denom', np.uint64, (max_terms, d, slots)) ]) self.sext_to_idx = {s: i for i, s in idx_to_sextuple.items()} self.ngens = n_slots @@ -491,18 +491,18 @@ cdef class FvarsHandler: self.shm = shared_memory.SharedMemory(create=True, size=self.ngens*self.fvars_t.itemsize) else: self.shm = shared_memory.SharedMemory(name=name) - self.fvars = np.ndarray((self.ngens,), dtype=self.fvars_t, buffer=self.shm.buf) + self.fvars = np.ndarray((self.ngens, ), dtype=self.fvars_t, buffer=self.shm.buf) self.pid_list = shared_memory.ShareableList(name=pids_name) self.child_id = -1 else: - self.fvars = np.ndarray((self.ngens,), dtype=self.fvars_t) + self.fvars = np.ndarray((self.ngens, ), dtype=self.fvars_t) self.child_id = 0 #Populate with initialziation data for sextuple, fvar in init_data.items(): if isinstance(fvar, MPolynomial_libsingular): fvar = _flatten_coeffs(poly_to_tup(fvar)) if isinstance(fvar, NumberFieldElement_absolute): - fvar = ((ETuple({},self.ngens), tuple(fvar._coefficients())),) + fvar = ((ETuple({}, self.ngens), tuple(fvar._coefficients())), ) if isinstance(fvar, tuple): transformed = list() for exp, c in fvar: @@ -521,7 +521,7 @@ cdef class FvarsHandler: unflattening its representation and constructing relevant Python objects. - This method returns a tuple of ``(ETuple, coeff)`` pairs, + This method returns a tuple of ``(ETuple, coeff)`` pairs, where ``coeff`` is an element of ``self.field``. EXAMPLES:: @@ -534,7 +534,7 @@ cdef class FvarsHandler: sage: f.start_worker_pool() sage: n_proc = f.pool._processes sage: pids_name = f._pid_list.shm.name - sage: fvars = FvarsHandler(14,f._field,f._idx_to_sextuple,use_mp=n_proc,pids_name=pids_name) + sage: fvars = FvarsHandler(14, f._field, f._idx_to_sextuple, use_mp=n_proc, pids_name=pids_name) sage: rhs = tuple((exp, tuple(c._coefficients())) ....: for exp, c in poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx13**3 - 799/2881*fx1*fx2**5*fx10)) sage: fvars[(f1, f2, f1, f2, f2, f2)] = rhs @@ -569,7 +569,7 @@ cdef class FvarsHandler: if self.child_id < 0: self.child_id = self.pid_list.index(getpid()) if idx in self.obj_cache: - if self.fvars['modified'][idx,self.child_id]: + if self.fvars['modified'][idx, self.child_id]: del self.obj_cache[idx] else: return self.obj_cache[idx] @@ -582,11 +582,11 @@ cdef class FvarsHandler: cdef Rational quo cdef tuple ret #Define memory views to reduce Python overhead and ensure correct typing - cdef np.ndarray[np.uint8_t,ndim=1] ticks = self.fvars['ticks'][idx] - cdef np.ndarray[np.uint16_t,ndim=1] exp_data = self.fvars['exp_data'][idx] - cdef np.ndarray[np.int64_t,ndim=3] nums = self.fvars['coeff_nums'][idx] - cdef np.ndarray[np.uint64_t,ndim=3] denoms = self.fvars['coeff_denom'][idx] - cdef np.ndarray[np.int8_t,ndim=1] modified = self.fvars['modified'][idx] + cdef np.ndarray[np.uint8_t, ndim=1] ticks = self.fvars['ticks'][idx] + cdef np.ndarray[np.uint16_t, ndim=1] exp_data = self.fvars['exp_data'][idx] + cdef np.ndarray[np.int64_t, ndim=3] nums = self.fvars['coeff_nums'][idx] + cdef np.ndarray[np.uint64_t, ndim=3] denoms = self.fvars['coeff_denom'][idx] + cdef np.ndarray[np.int8_t, ndim=1] modified = self.fvars['modified'][idx] e = ETuple({}, self.ngens) poly_tup = list() cum = 0 @@ -606,8 +606,8 @@ cdef class FvarsHandler: #Construct cyclotomic field coefficient rats = list() for k in range(self.field.degree()): - num = Integer(list(nums[i,k]),2**63) - denom = Integer(list(denoms[i,k]),2**64) + num = Integer(list(nums[i, k]), 2**63) + denom = Integer(list(denoms[i, k]), 2**64) quo = num / denom rats.append(quo) cyc_coeff = self.field(rats) @@ -622,7 +622,7 @@ cdef class FvarsHandler: @cython.wraparound(False) def __setitem__(self, sextuple, fvar): r""" - Given a sextuple of labels and a tuple of ``(ETuple, cyc_coeff)`` pairs, + Given a sextuple of labels and a tuple of ``(ETuple, cyc_coeff)`` pairs, create or overwrite an entry in the shared data structure corresponding to the given sextuple. @@ -636,7 +636,7 @@ cdef class FvarsHandler: sage: f.start_worker_pool() sage: n_proc = f.pool._processes sage: pids_name = f._pid_list.shm.name - sage: fvars = FvarsHandler(27,f._field,f._idx_to_sextuple,use_mp=n_proc,pids_name=pids_name) + sage: fvars = FvarsHandler(27, f._field, f._idx_to_sextuple, use_mp=n_proc, pids_name=pids_name) sage: rhs = tuple((exp, tuple(c._coefficients())) ....: for exp, c in poly_to_tup(1/8*fx0**15 - 23/79*fx2*fx21**3 - 799/2881*fx1*fx2**5*fx10)) sage: fvars[(f3, f2, f1, f2, f1, f3)] = rhs @@ -661,16 +661,16 @@ cdef class FvarsHandler: cdef Rational r idx = self.sext_to_idx[sextuple] #Clear entry before inserting - self.fvars[idx] = np.zeros((1,), dtype=self.fvars_t) + self.fvars[idx] = np.zeros((1, ), dtype=self.fvars_t) #Define memory views to reduce Python overhead and ensure correct typing - cdef np.ndarray[np.uint8_t,ndim=1] ticks = self.fvars['ticks'][idx] - cdef np.ndarray[np.uint16_t,ndim=1] exp_data = self.fvars['exp_data'][idx] - cdef np.ndarray[np.int64_t,ndim=3] nums = self.fvars['coeff_nums'][idx] - cdef np.ndarray[np.uint64_t,ndim=3] denoms = self.fvars['coeff_denom'][idx] - cdef np.ndarray[np.int8_t,ndim=1] modified = self.fvars['modified'][idx] + cdef np.ndarray[np.uint8_t, ndim=1] ticks = self.fvars['ticks'][idx] + cdef np.ndarray[np.uint16_t, ndim=1] exp_data = self.fvars['exp_data'][idx] + cdef np.ndarray[np.int64_t, ndim=3] nums = self.fvars['coeff_nums'][idx] + cdef np.ndarray[np.uint64_t, ndim=3] denoms = self.fvars['coeff_denom'][idx] + cdef np.ndarray[np.int8_t, ndim=1] modified = self.fvars['modified'][idx] cdef list digits #Initialize denominators to 1 - denoms[:,:,0] = 1 + denoms[:, :, 0] = 1 cum = 0 i = 0 for exp, coeff_tup in fvar: @@ -688,19 +688,19 @@ cdef class FvarsHandler: if abs(num) > 2**63 or denom > 2**63: print("Large integers encountered in FvarsHandler", num, denom) if abs(num) < 2**63: - nums[i,k,0] = num + nums[i, k, 0] = num else: digits = num.digits(2**63) - # assert len(digits) <= self.bytes // 8, "Numerator {} is too large for shared FvarsHandler. Use at least {} bytes...".format(num,num.nbits()//8+1) + # assert len(digits) <= self.bytes // 8, "Numerator {} is too large for shared FvarsHandler. Use at least {} bytes...".format(num, num.nbits()//8+1) for t in range(len(digits)): - nums[i,k,t] = digits[t] + nums[i, k, t] = digits[t] if denom < 2**64: - denoms[i,k,0] = denom + denoms[i, k, 0] = denom else: digits = denom.digits(2**64) - # assert len(digits) <= self.bytes // 8, "Denominator {} is too large for shared FvarsHandler. Use at least {} bytes...".format(denom,denom.nbits()//8+1) + # assert len(digits) <= self.bytes // 8, "Denominator {} is too large for shared FvarsHandler. Use at least {} bytes...".format(denom, denom.nbits()//8+1) for t in range(len(digits)): - denoms[i,k,t] = digits[t] + denoms[i, k, t] = digits[t] k += 1 i += 1 modified[:] = 1 @@ -711,13 +711,13 @@ cdef class FvarsHandler: TESTS:: - sage: f = FMatrix(FusionRing("F4",1)) + sage: f = FMatrix(FusionRing("F4", 1)) sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() sage: f.start_worker_pool() sage: n_proc = f.pool._processes sage: pids_name = f._pid_list.shm.name - sage: fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars,use_mp=n_proc,pids_name=pids_name) + sage: fvars = FvarsHandler(n, f._field, f._idx_to_sextuple, init_data=f._fvars, use_mp=n_proc, pids_name=pids_name) sage: for s, fvar in loads(dumps(fvars)).items(): ....: assert f._fvars[s] == f._tup_to_fpoly(fvar) ....: @@ -727,7 +727,7 @@ cdef class FvarsHandler: n = self.fvars.size idx_map = {i: s for s, i in self.sext_to_idx.items()} d = {s: fvar for s, fvar in self.items()} - return make_FvarsHandler, (n,self.field,idx_map,d) + return make_FvarsHandler, (n, self.field, idx_map, d) def items(self): r""" @@ -743,7 +743,7 @@ cdef class FvarsHandler: creating variables fx1..fx5 Defining fx0, fx1, fx2, fx3, fx4 sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler - sage: shared_fvars = FvarsHandler(5,f._field,f._idx_to_sextuple,init_data=f._fvars) + sage: shared_fvars = FvarsHandler(5, f._field, f._idx_to_sextuple, init_data=f._fvars) sage: for sextuple, fvar in shared_fvars.items(): ....: if sextuple == (f1, f1, f1, f1, f1, f1): ....: f._tup_to_fpoly(fvar) @@ -753,19 +753,19 @@ cdef class FvarsHandler: for sextuple in self.sext_to_idx: yield sextuple, self[sextuple] -def make_FvarsHandler(n,field,idx_map,init_data): +def make_FvarsHandler(n, field, idx_map, init_data): r""" Provide pickling / unpickling support for :class:`FvarsHandler`. TESTS:: - sage: f = FMatrix(FusionRing("G2",1)) + sage: f = FMatrix(FusionRing("G2", 1)) sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() sage: f.start_worker_pool() sage: n_proc = f.pool._processes sage: pids_name = f._pid_list.shm.name - sage: fvars = FvarsHandler(n,f._field,f._idx_to_sextuple,init_data=f._fvars,use_mp=n_proc,pids_name=pids_name) + sage: fvars = FvarsHandler(n, f._field, f._idx_to_sextuple, init_data=f._fvars, use_mp=n_proc, pids_name=pids_name) sage: for s, fvar in loads(dumps(fvars)).items(): # indirect doctest ....: assert f._fvars[s] == f._tup_to_fpoly(fvar) ....: From d60568b5dd4f66de5698fbe1e223ba26fed12361 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Tue, 18 Oct 2022 17:33:39 -0700 Subject: [PATCH 261/632] space after comment character --- src/sage/algebras/fusion_rings/all.py | 3 +- src/sage/algebras/fusion_rings/f_matrix.py | 126 +++++++++--------- .../fast_parallel_fmats_methods.pyx | 103 +++++++------- .../fast_parallel_fusion_ring_braid_repn.pyx | 61 +++++---- src/sage/algebras/fusion_rings/fusion_ring.py | 48 +++---- .../algebras/fusion_rings/poly_tup_engine.pyx | 20 +-- .../algebras/fusion_rings/shm_managers.pyx | 106 +++++++-------- 7 files changed, 232 insertions(+), 235 deletions(-) diff --git a/src/sage/algebras/fusion_rings/all.py b/src/sage/algebras/fusion_rings/all.py index 041ced4f28d..e1dfaa1f2b2 100644 --- a/src/sage/algebras/fusion_rings/all.py +++ b/src/sage/algebras/fusion_rings/all.py @@ -3,7 +3,7 @@ """ # **************************************************************************** -# Copyright (C) 2022 Guillermo Aboumrad +# Copyright (C) 2022 Guillermo Aboumrad # # 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 @@ -18,4 +18,3 @@ lazy_import('sage.algebras.fusion_rings.f_matrix', ['FMatrix']) del lazy_import - diff --git a/src/sage/algebras/fusion_rings/f_matrix.py b/src/sage/algebras/fusion_rings/f_matrix.py index 9ec23340e66..f7ba23a588e 100644 --- a/src/sage/algebras/fusion_rings/f_matrix.py +++ b/src/sage/algebras/fusion_rings/f_matrix.py @@ -282,7 +282,7 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab self._FR.fusion_labels(fusion_label, inject_variables=True) if not self._FR.is_multiplicity_free(): raise NotImplementedError("FMatrix is only available for multiplicity free FusionRings") - #Set up F-symbols entry by entry + # Set up F-symbols entry by entry n_vars = self.findcases() self._poly_ring = PolynomialRing(self._FR.field(), n_vars, var_prefix) if inject_variables: @@ -290,15 +290,15 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab self._poly_ring.inject_variables(get_main_globals()) self._idx_to_sextuple, self._fvars = self.findcases(output=True) - #Base field attributes + # Base field attributes self._field = self._FR.field() r = self._field.defining_polynomial().roots(ring=QQbar, multiplicities=False)[0] self._qqbar_embedding = self._field.hom([r], QQbar) - #Warm starting + # Warm starting self._chkpt_status = -1 - #Multiprocessing attributes + # Multiprocessing attributes self.mp_thresh = 10000 self.pool = None @@ -407,7 +407,7 @@ def _reset_solver_state(self): self._singles = self.get_fvars_by_size(1, indices=True) self._nnz = self._get_known_nonz() - #Clear relevant caches + # Clear relevant caches [x.q_dimension.clear_cache() for x in self._FR.basis()] self._FR.r_matrix.clear_cache() self._FR.s_ij.clear_cache() @@ -443,7 +443,7 @@ def fmat(self, a, b, c, d, x, y, data=True): or self._FR.Nk_ij(b, c, y) == 0 or self._FR.Nk_ij(a, y, d) == 0): return 0 - #Some known zero F-symbols + # Some known zero F-symbols if a == self._FR.one(): if x == b and y == d: return 1 @@ -460,8 +460,8 @@ def fmat(self, a, b, c, d, x, y, data=True): else: return 0 if data: - #Better to use try/except for speed. Somewhat trivial, but worth - #hours when method is called ~10^11 times + # Better to use try/except for speed. Somewhat trivial, but worth + # hours when method is called ~10^11 times try: return self._fvars[a, b, c, d, x, y] except KeyError: @@ -692,7 +692,7 @@ def get_poly_ring(self): """ return self._poly_ring - #TODO: this method is incredibly slow... improve by keeping track of the cyclotomic polynomials, NOT their roots in QQbar + # TODO: this method is incredibly slow... improve by keeping track of the cyclotomic polynomials, NOT their roots in QQbar def get_non_cyclotomic_roots(self): r""" Return a list of roots that define the extension of the associated @@ -809,11 +809,11 @@ def get_coerce_map_from_fr_cyclotomic_field(self): sage: phi.is_identity() True """ - #If base field is different from associated FusionRing's CyclotomicField, - #return coercion map + # If base field is different from associated FusionRing's CyclotomicField, + # return coercion map try: return self._coerce_map_from_cyc_field - #Otherwise, return identity map CyclotomicField <-> CyclotomicField + # Otherwise, return identity map CyclotomicField <-> CyclotomicField except AttributeError: F = self._FR.field() return F.hom([F.gen()], F) @@ -958,7 +958,7 @@ def get_fvars_by_size(self, n, indices=False): if len(X) == n and len(Y) == n: for x in X: for y in Y: - #Discard trivial 1x1 F-matrix + # Discard trivial 1x1 F-matrix trivial = a == one and x == b and y == d trivial |= b == one and x == a and y == c trivial |= c == one and x == d and y == b @@ -1054,7 +1054,7 @@ def load_fvars(self, filename): """ with open(filename, 'rb') as f: self._fvars, self._non_cyc_roots, self._coerce_map_from_cyc_field, self._qqbar_embedding = pickle.load(f) - #Update state attributes + # Update state attributes self._chkpt_status = 7 self._solved = list(True for v in self._fvars) self._field = self._qqbar_embedding.domain() @@ -1206,7 +1206,7 @@ def _restore_state(self, filename): """ with open(filename, 'rb') as f: state = pickle.load(f) - #Loading saved results pickle + # Loading saved results pickle if len(state) == 4: self.load_fvars(filename) self._chkpt_status = 7 @@ -1281,7 +1281,7 @@ class methods. pass if not hasattr(self, '_nnz'): self._reset_solver_state() - #Set up shared memory resource handlers + # Set up shared memory resource handlers n_proc = cpu_count() if processes is None else processes self._pid_list = shared_memory.ShareableList([0]*(n_proc+1)) pids_name = self._pid_list.shm.name @@ -1294,7 +1294,7 @@ class methods. ks_names = self._ks.shm.name self._shared_fvars = FvarsHandler(n, self._field, self._idx_to_sextuple, use_mp=n_proc, pids_name=pids_name, init_data=self._fvars) fvar_names = self._shared_fvars.shm.name - #Initialize worker pool processes + # Initialize worker pool processes args = (id(self), s_name, vd_name, ks_names, fvar_names, n_proc, pids_name) def init(fmats_id, solved_name, vd_name, ks_names, fvar_names, n_proc, pids_name): """ @@ -1377,7 +1377,7 @@ def _map_triv_reduce(self, mapper, input_iter, worker_pool=None, chunksize=None, """ if mp_thresh is None: mp_thresh = self.mp_thresh - #Compute multiprocessing parameters + # Compute multiprocessing parameters if worker_pool is not None: try: n = len(input_iter) @@ -1386,13 +1386,13 @@ def _map_triv_reduce(self, mapper, input_iter, worker_pool=None, chunksize=None, if chunksize is None: chunksize = n // (worker_pool._processes**2) + 1 no_mp = worker_pool is None or n < mp_thresh - #Map phase + # Map phase input_iter = zip_longest([], input_iter, fillvalue=(mapper, id(self))) if no_mp: mapped = map(executor, input_iter) else: mapped = worker_pool.imap_unordered(executor, input_iter, chunksize=chunksize) - #Reduce phase + # Reduce phase results = set() for child_eqns in mapped: if child_eqns is not None: @@ -1601,7 +1601,7 @@ def _triangular_elim(self, eqns=None, verbose=True): if not linear_terms_exist: break _backward_subs(self) - #Compute new reduction params and update eqns + # Compute new reduction params and update eqns self._update_reduction_params(eqns=eqns) if self.pool is not None and len(eqns) > self.mp_thresh: n = self.pool._processes @@ -1673,13 +1673,13 @@ def equations_graph(self, eqns=None): G = Graph() if not eqns: return G - #Eqns could be a list of poly objects or poly tuples stored in internal repn + # Eqns could be a list of poly objects or poly tuples stored in internal repn if isinstance(eqns[0], tuple): G.add_vertices([x for eq_tup in eqns for x in variables(eq_tup)]) else: G.add_vertices([x for eq in eqns for x in eq.variables()]) for eq in eqns: - #Eqns could be a list of poly objects or poly tuples stored in internal repn + # Eqns could be a list of poly objects or poly tuples stored in internal repn if isinstance(eq, tuple): s = [v for v in variables(eq)] else: @@ -1773,7 +1773,7 @@ def _par_graph_gb(self, eqns=None, term_order="degrevlex", largest_comp=45, verb small_comps = list() temp_eqns = list() for comp, comp_eqns in self._partition_eqns(eqns=eqns, verbose=verbose).items(): - #Check if component is too large to process + # Check if component is too large to process if len(comp) > largest_comp: temp_eqns.extend(comp_eqns) else: @@ -1810,17 +1810,17 @@ def _get_component_variety(self, var, eqns): sage: f._get_component_variety(c, eqns) # long time [{216: -1, 292: -1, 319: 1}] """ - #Define smaller poly ring in component vars + # Define smaller poly ring in component vars R = PolynomialRing(self._FR.field(), len(var), 'a', order='lex') - #Zip tuples into R and compute Groebner basis + # Zip tuples into R and compute Groebner basis idx_map = {old: new for new, old in enumerate(sorted(var))} nvars = len(var) eqns = [_unflatten_coeffs(self._field, eq_tup) for eq_tup in eqns] polys = [_tup_to_poly(resize(eq_tup, idx_map, nvars), parent=R) for eq_tup in eqns] var_in_R = Ideal(sorted(polys)).variety(ring=AA) - #Change back to fmats poly ring and append to temp_eqns + # Change back to fmats poly ring and append to temp_eqns inv_idx_map = {v: k for k, v in idx_map.items()} return [{inv_idx_map[i]: value for i, (key, value) in enumerate(sorted(soln.items()))} for soln in var_in_R] @@ -1829,8 +1829,8 @@ def _get_component_variety(self, var, eqns): ####################### # TODO: this can probably be improved by constructing a set of defining polynomials - # and checking, one by one, if it's irreducible over the current field. - # If it is, we construct an extension. Perhaps it's best to go one by one here... + # and checking, one by one, if it's irreducible over the current field. + # If it is, we construct an extension. Perhaps it's best to go one by one here... def attempt_number_field_computation(self): r""" Based on the ``CartanType`` of ``self`` and data @@ -1866,7 +1866,7 @@ def attempt_number_field_computation(self): """ ct = self._FR.cartan_type() k = self._FR._k - #Don't try when k is large and odd for SU(2)_k + # Don't try when k is large and odd for SU(2)_k if ct.letter == 'A': if ct.n == 1 and k >= 9 and k % 2: return False @@ -1917,7 +1917,7 @@ def _get_explicit_solution(self, eqns=None, verbose=True): """ if eqns is None: eqns = self.ideal_basis - #Don't add square fixers when warm starting from a late-stage checkpoint + # Don't add square fixers when warm starting from a late-stage checkpoint if self._chkpt_status < 5: n = self._poly_ring.ngens() one = self._field.one() @@ -1934,21 +1934,21 @@ def _get_explicit_solution(self, eqns=None, verbose=True): must_change_base_field = False phi = F.hom([F.gen()], F) for comp, part in eqns_partition.items(): - #If component has only one equation in a single variable, get a root + # If component has only one equation in a single variable, get a root if len(comp) == 1 and len(part) == 1: - #Attempt to find cyclotomic root + # Attempt to find cyclotomic root univ_poly = tup_to_univ_poly(part[0], R) roots = univ_poly.roots(multiplicities=False) if roots: numeric_fvars[comp[0]] = roots[0] else: - #A real solution is preferred + # A real solution is preferred roots = univ_poly.roots(ring=AA, multiplicities=False) if not roots: roots = univ_poly.roots(ring=QQbar, multiplicities=False) non_cyclotomic_roots.append((comp[0], roots[0])) must_change_base_field = True - #Otherwise, compute the component variety and select a point to obtain a numerical solution + # Otherwise, compute the component variety and select a point to obtain a numerical solution else: sols = self._get_component_variety(comp, part) for fx, rhs in sols[0].items(): @@ -1956,8 +1956,8 @@ def _get_explicit_solution(self, eqns=None, verbose=True): must_change_base_field = True if must_change_base_field: - #Attempt to compute smallest number field containing all the F-symbols - #If calculation takes too long, we use QQbar as the base field + # Attempt to compute smallest number field containing all the F-symbols + # If calculation takes too long, we use QQbar as the base field if self.attempt_number_field_computation(): if verbose: print("Computing appropriate NumberField...") @@ -1970,30 +1970,30 @@ def _get_explicit_solution(self, eqns=None, verbose=True): self._qqbar_embedding = lambda x : x self._non_cyc_roots = bf_elts[1:] - #Embed cyclotomic field into newly constructed base field + # Embed cyclotomic field into newly constructed base field cyc_gen_as_bf_elt = bf_elts.pop(0) phi = self._FR.field().hom([cyc_gen_as_bf_elt], self._field) self._coerce_map_from_cyc_field = phi numeric_fvars = {k : phi(v) for k, v in numeric_fvars.items()} for i, elt in enumerate(bf_elts): numeric_fvars[non_cyclotomic_roots[i][0]] = elt - #Update polynomial ring + # Update polynomial ring self._poly_ring = self._poly_ring.change_ring(self._field) - #Ensure all F-symbols are known + # Ensure all F-symbols are known for fx in numeric_fvars: self._solved[fx] = True nvars = self._poly_ring.ngens() assert sum(self._solved) == nvars, "Some F-symbols are still missing...{}".format([self._poly_ring.gen(fx) for fx in range(nvars) if not self._solved[fx]]) - #Backward substitution step. Traverse variables in reverse lexicographical order. (System is in triangular form) + # Backward substitution step. Traverse variables in reverse lexicographical order. (System is in triangular form) self._fvars = {sextuple : apply_coeff_map(rhs, phi) for sextuple, rhs in self._fvars.items()} for fx, rhs in numeric_fvars.items(): self._fvars[self._idx_to_sextuple[fx]] = ((ETuple({}, nvars), rhs), ) _backward_subs(self, flatten=False) self._fvars = {sextuple : constant_coeff(rhs, self._field) for sextuple, rhs in self._fvars.items()} - #Update base field attributes + # Update base field attributes self._FR._field = self.field() self._FR._basecoer = self.get_coerce_map_from_fr_cyclotomic_field() if self._FR._basecoer: @@ -2099,29 +2099,29 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start return self._reset_solver_state() - #Resume computation from checkpoint + # Resume computation from checkpoint if warm_start: self._restore_state(warm_start) - #Loading from a pickle with solved F-symbols + # Loading from a pickle with solved F-symbols if self._chkpt_status > 5: return # loads_shared_memory = False # if use_mp: - # loads_shared_memory = self.start_worker_pool() + # loads_shared_memory = self.start_worker_pool() if use_mp: self.start_worker_pool() if verbose: print("Computing F-symbols for {} with {} variables...".format(self._FR, self._poly_ring.ngens())) if self._chkpt_status < 1: - #Set up hexagon equations and orthogonality constraints + # Set up hexagon equations and orthogonality constraints self.get_orthogonality_constraints(output=False) self.get_defining_equations('hexagons', output=False) - #Report progress + # Report progress if verbose: print("Set up {} hex and orthogonality constraints...".format(len(self.ideal_basis))) - #Unzip _fvars and link to shared_memory structure if using multiprocessing + # Unzip _fvars and link to shared_memory structure if using multiprocessing if use_mp:# and loads_shared_memory: self._fvars = self._shared_fvars else: @@ -2130,35 +2130,35 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start self._checkpoint(checkpoint, 1, verbose=verbose) if self._chkpt_status < 2: - #Set up equations graph. Find GB for each component in parallel. Eliminate variables + # Set up equations graph. Find GB for each component in parallel. Eliminate variables self.ideal_basis = self._par_graph_gb(verbose=verbose) self.ideal_basis.sort(key=poly_tup_sortkey) self._triangular_elim(verbose=verbose) - #Report progress + # Report progress if verbose: print("Hex elim step solved for {} / {} variables".format(sum(self._solved), len(self._poly_ring.gens()))) self._checkpoint(checkpoint, 2, verbose=verbose) if self._chkpt_status < 3: - #Set up pentagon equations in parallel + # Set up pentagon equations in parallel self.get_defining_equations('pentagons', output=False) - #Report progress + # Report progress if verbose: print("Set up {} reduced pentagons...".format(len(self.ideal_basis))) self._checkpoint(checkpoint, 3, verbose=verbose) if self._chkpt_status < 4: - #Simplify and eliminate variables + # Simplify and eliminate variables self.ideal_basis.sort(key=poly_tup_sortkey) self._triangular_elim(verbose=verbose) - #Report progress + # Report progress if verbose: print("Pent elim step solved for {} / {} variables".format(sum(self._solved), len(self._poly_ring.gens()))) self._checkpoint(checkpoint, 4, verbose=verbose) - #Try adding degrevlex gb -> elim loop until len(ideal_basis) does not change + # Try adding degrevlex gb -> elim loop until len(ideal_basis) does not change - #Set up new equations graph and compute variety for each component + # Set up new equations graph and compute variety for each component if self._chkpt_status < 5: self.ideal_basis = self._par_graph_gb(term_order="lex", verbose=verbose) self.ideal_basis.sort(key=poly_tup_sortkey) @@ -2166,9 +2166,9 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start self._checkpoint(checkpoint, 5, verbose=verbose) self.shutdown_worker_pool() - #Find numeric values for each F-symbol + # Find numeric values for each F-symbol self._get_explicit_solution(verbose=verbose) - #The calculation was successful, so we may delete checkpoints + # The calculation was successful, so we may delete checkpoints self._chkpt_status = 7 self.clear_equations() if checkpoint: @@ -2203,13 +2203,13 @@ def _fix_gauge(self, algorithm=""): adding equation... fx21 - 1 """ while not all(v for v in self._solved): - #Get a variable that has not been fixed - #In ascending index order, for consistent results + # Get a variable that has not been fixed + # In ascending index order, for consistent results for i, var in enumerate(self._poly_ring.gens()): if not self._solved[i]: break - #Fix var = 1, substitute, and solve equations + # Fix var = 1, substitute, and solve equations self.ideal_basis.add(var-1) print("adding equation...", var-1) self.ideal_basis = set(Ideal(list(self.ideal_basis)).groebner_basis(algorithm=algorithm)) @@ -2244,11 +2244,11 @@ def _substitute_degree_one(self, eqns=None): for eq in eqns: if eq.degree() == 1 and sum(eq.degrees()) <= 2 and eq.lm() not in self._solved: self._fvars[self._var_to_sextuple[eq.lm()]] = -sum(c * m for c, m in zip(eq.coefficients()[1:], eq.monomials()[1:])) / eq.lc() - #Add variable to set of known values and remove this equation + # Add variable to set of known values and remove this equation new_knowns.add(eq.lm()) useless.add(eq) - #Update fvars depending on other variables + # Update fvars depending on other variables for idx, fx in enumerate(self._poly_ring.gens()): if fx in new_knowns: self._solved[idx] = fx diff --git a/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx b/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx index 98983f0e171..f68fca95369 100644 --- a/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx +++ b/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx @@ -10,12 +10,12 @@ Fast F-Matrix methods cimport cython from sage.algebras.fusion_rings.poly_tup_engine cimport ( - compute_known_powers, - get_variables_degrees, variables, - poly_to_tup, _tup_to_poly, - subs, subs_squares, reduce_poly_dict, resize, - _flatten_coeffs, _unflatten_coeffs, - has_appropriate_linear_term, + compute_known_powers, + get_variables_degrees, variables, + poly_to_tup, _tup_to_poly, + subs, subs_squares, reduce_poly_dict, resize, + _flatten_coeffs, _unflatten_coeffs, + has_appropriate_linear_term, resize ) from sage.algebras.fusion_rings.shm_managers cimport KSHandler, FvarsHandler @@ -81,14 +81,14 @@ cpdef _solve_for_linear_terms(factory, list eqns=None): factory._solved[vars[0]] = True linear_terms_exist = True - #TESTS: + # TESTS: # s = factory._idx_to_sextuple[vars[0]] # factory.test_fvars[s] = tuple() # assert factory.test_fvars[s] == fvars[s], "OG value {}, Shared: {}".format(fvars[s], factory.test_fvars[s]) if len(eq_tup) == 2: idx = has_appropriate_linear_term(eq_tup) if idx < 0: continue - #The chosen term is guaranteed to be univariate in the largest variable + # The chosen term is guaranteed to be univariate in the largest variable exp = eq_tup[idx][0] max_var = exp._data[0] if not factory._solved[max_var]: @@ -100,7 +100,7 @@ cpdef _solve_for_linear_terms(factory, list eqns=None): factory._solved[max_var] = True linear_terms_exist = True - #TESTS: + # TESTS: # s = factory._idx_to_sextuple[max_var] # factory.test_fvars[s] = ((rhs_exp, rhs_coeff), ) # assert _unflatten_coeffs(factory._field, factory.test_fvars[s]) == fvars[s], "OG value {}, Shared: {}".format(factory.test_fvars[s], fvars[s]) @@ -175,7 +175,7 @@ cdef _fmat(fvars, _Nk_ij, id_anyon, a, b, c, d, x, y): """ if _Nk_ij(a, b, x) == 0 or _Nk_ij(x, c, d) == 0 or _Nk_ij(b, c, y) == 0 or _Nk_ij(a, y, d) == 0: return 0 - #Some known F-symbols + # Some known F-symbols if a == id_anyon: if x == b and y == d: return 1 @@ -201,19 +201,19 @@ cdef _fmat(fvars, _Nk_ij, id_anyon, a, b, c, d, x, y): # cdef dict _Nk_ij = dict() # cpdef _Nk_ij(factory, proc): -# cdef int coeff -# for a, b, c in product(factory._FR.basis(), repeat=3): -# try: -# coeff = (a*b).monomial_coefficients(copy=False)[c.weight()] -# except: -# coeff = 0 -# _Nk_ij[a, b, c] = coeff +# cdef int coeff +# for a, b, c in product(factory._FR.basis(), repeat=3): +# try: +# coeff = (a*b).monomial_coefficients(copy=False)[c.weight()] +# except: +# coeff = 0 +# _Nk_ij[a, b, c] = coeff # cpdef int _Nk_ij(a, b, c): -# try: -# return (a*b).monomial_coefficients(copy=False)[c.weight()] -# except KeyError: -# return 0 +# try: +# return (a*b).monomial_coefficients(copy=False)[c.weight()] +# except KeyError: +# return 0 # # _Nk_ij = cached_function(_Nk_ij, name='_Nk_ij') @@ -227,8 +227,8 @@ cdef req_cy(tuple basis, r_matrix, dict fvars, Nk_ij, id_anyon, tuple sextuple): as a polynomial object. """ a, b, c, d, e, g = sextuple - #To add typing we need to ensure all fmats.fmat are of the same type? - #Return fmats._poly_ring.zero() and fmats._poly_ring.one() instead of 0 and 1? + # To add typing we need to ensure all fmats.fmat are of the same type? + # Return fmats._poly_ring.zero() and fmats._poly_ring.one() instead of 0 and 1? lhs = r_matrix(a, c, e, base_coercion=False) * _fmat(fvars, Nk_ij, id_anyon, a, c, b, d, e, g) * r_matrix(b, c, g, base_coercion=False) rhs = 0 for f in basis: @@ -242,21 +242,21 @@ cdef get_reduced_hexagons(factory, tuple mp_params): """ Set up and reduce the hexagon equations corresponding to this worker. """ - #Set up multiprocessing parameters + # Set up multiprocessing parameters cdef list worker_results = list() cdef int child_id, n_proc cdef unsigned long i child_id, n_proc, output = mp_params cdef tuple sextuple, red - #Pre-compute common parameters for speed + # Pre-compute common parameters for speed cdef tuple basis = tuple(factory._FR.basis()) cdef dict fvars cdef bint must_zip_up = False if not output: fvars = {s: factory._poly_ring.gen(i) for i, s in factory._idx_to_sextuple.items()} else: - #Handle both cyclotomic and orthogonal solution method + # Handle both cyclotomic and orthogonal solution method for k, v in factory._fvars.items(): must_zip_up = isinstance(v, tuple) break @@ -272,7 +272,7 @@ cdef get_reduced_hexagons(factory, tuple mp_params): cdef ETuple _nnz = factory._nnz _ks = factory._ks - #Computation loop + # Computation loop it = product(basis, repeat=6) for i in range(len(basis)**6): sextuple = next(it) @@ -281,7 +281,7 @@ cdef get_reduced_hexagons(factory, tuple mp_params): if he: red = reduce_poly_dict(he.dict(), _nnz, _ks, one) - #Avoid pickling cyclotomic coefficients + # Avoid pickling cyclotomic coefficients red = _flatten_coeffs(red) worker_results.append(red) @@ -309,7 +309,7 @@ cdef get_reduced_pentagons(factory, tuple mp_params): r""" Set up and reduce the pentagon equations corresponding to this worker. """ - #Set up multiprocessing parameters + # Set up multiprocessing parameters cdef list worker_results = list() cdef int child_id, n_proc child_id, n_proc, output = mp_params @@ -317,9 +317,9 @@ cdef get_reduced_pentagons(factory, tuple mp_params): cdef tuple nonuple, red cdef MPolynomial_libsingular pe - #Pre-compute common parameters for speed + # Pre-compute common parameters for speed cdef tuple basis = tuple(factory._FR.basis()) - #Handle both cyclotomic and orthogonal solution method + # Handle both cyclotomic and orthogonal solution method cdef bint must_zip_up for k, v in factory._fvars.items(): must_zip_up = isinstance(v, tuple) @@ -338,7 +338,7 @@ cdef get_reduced_pentagons(factory, tuple mp_params): factory._nnz = factory._get_known_nonz() cdef ETuple _nnz = factory._nnz - #Computation loop + # Computation loop it = product(basis, repeat=9) for i in range(len(basis)**9): nonuple = next(it) @@ -347,7 +347,7 @@ cdef get_reduced_pentagons(factory, tuple mp_params): if pe: red = reduce_poly_dict(pe.dict(), _nnz, _ks, one) - #Avoid pickling cyclotomic coefficients + # Avoid pickling cyclotomic coefficients red = _flatten_coeffs(red) worker_results.append(red) @@ -361,11 +361,11 @@ cdef list update_reduce(factory, list eqns): cdef tuple eq_tup, red, unflat cdef dict eq_dict - #Pre-compute common parameters for speed + # Pre-compute common parameters for speed _field = factory._field one = _field.one() cdef KSHandler _ks = factory._ks - #Update reduction params + # Update reduction params factory._nnz = factory._get_known_nonz() factory._kp = compute_known_powers(factory._var_degs, factory._get_known_vals(), factory._field.one()) cdef dict _kp = factory._kp @@ -373,13 +373,13 @@ cdef list update_reduce(factory, list eqns): for i in range(len(eqns)): eq_tup = eqns[i] - #Construct cyclotomic field elts from list repn + # Construct cyclotomic field elts from list repn unflat = _unflatten_coeffs(_field, eq_tup) eq_dict = subs(unflat, _kp, one) red = reduce_poly_dict(eq_dict, _nnz, _ks, one) - #Avoid pickling cyclotomic coefficients + # Avoid pickling cyclotomic coefficients red = _flatten_coeffs(red) res.append(red) @@ -392,7 +392,7 @@ cdef list compute_gb(factory, tuple args): cdef list res = list() cdef list eqns, sorted_vars eqns, term_order = args - #Define smaller poly ring in component vars + # Define smaller poly ring in component vars sorted_vars = [] cdef tuple eq_tup cdef int fx @@ -402,7 +402,7 @@ cdef list compute_gb(factory, tuple args): sorted_vars = sorted(set(sorted_vars)) cdef MPolynomialRing_libsingular R = PolynomialRing(factory._FR.field(), len(sorted_vars), 'a', order=term_order) - #Zip tuples into R and compute Groebner basis + # Zip tuples into R and compute Groebner basis cdef idx_map = { old : new for new, old in enumerate(sorted_vars) } nvars = len(sorted_vars) F = factory.field() @@ -412,14 +412,14 @@ cdef list compute_gb(factory, tuple args): polys.append(_tup_to_poly(resize(eq_tup, idx_map, nvars), parent=R)) gb = Ideal(sorted(polys)).groebner_basis(algorithm="libsingular:slimgb") - #Change back to fmats poly ring and append to temp_eqns + # Change back to fmats poly ring and append to temp_eqns cdef dict inv_idx_map = { v : k for k, v in idx_map.items() } cdef tuple t nvars = factory._poly_ring.ngens() for p in gb: t = resize(poly_to_tup(p), inv_idx_map, nvars) - #Avoid pickling cyclotomic coefficients + # Avoid pickling cyclotomic coefficients t = _flatten_coeffs(t) res.append(t) @@ -437,7 +437,7 @@ cdef inline list collect_eqns(list eqns): This method is only useful when called after :meth:`executor`, whose function argument appends output to the ``worker_results`` list. """ - #Discard the zero polynomial + # Discard the zero polynomial reduced = set(eqns) - set([tuple()]) return list(reduced) @@ -445,12 +445,12 @@ cdef inline list collect_eqns(list eqns): ### Parallel code executor ### ############################## -#Hard-coded module __dict__-style attribute with visible cdef methods +# Hard-coded module __dict__-style attribute with visible cdef methods cdef dict mappers = { - "get_reduced_hexagons": get_reduced_hexagons, - "get_reduced_pentagons": get_reduced_pentagons, - "update_reduce": update_reduce, - "compute_gb": compute_gb, + "get_reduced_hexagons": get_reduced_hexagons, + "get_reduced_pentagons": get_reduced_pentagons, + "update_reduce": update_reduce, + "compute_gb": compute_gb, "pent_verify": pent_verify } @@ -492,9 +492,9 @@ cpdef executor(tuple params): True """ (fn_name, fmats_id), args = params - #Construct a reference to global FMatrix object in this worker's memory + # Construct a reference to global FMatrix object in this worker's memory fmats_obj = cast(fmats_id, py_object).value - #Bind module method to FMatrix object in worker process, and call the method + # Bind module method to FMatrix object in worker process, and call the method return mappers[fn_name](fmats_obj, args) #################### @@ -521,7 +521,7 @@ cdef feq_verif(factory, worker_results, fvars, Nk_ij, id_anyon, tuple nonuple, f @cython.cdivision(True) cdef pent_verify(factory, tuple mp_params): r""" - Generate all the pentagon equations assigned to this process, + Generate all the pentagon equations assigned to this process, and reduce them. """ child_id, n_proc, verbose = mp_params @@ -530,7 +530,7 @@ cdef pent_verify(factory, tuple mp_params): cdef unsigned long long i cdef list worker_results = list() - #Pre-compute common parameters for speed + # Pre-compute common parameters for speed Nk_ij = factory._FR.Nk_ij cdef dict fvars = factory._fvars id_anyon = factory._FR.one() @@ -539,4 +539,3 @@ cdef pent_verify(factory, tuple mp_params): feq_verif(factory, worker_results, fvars, Nk_ij, id_anyon, nonuple) if i % 50000000 == 0 and i and verbose: print("{:5d}m equations checked... {} potential misses so far...".format(i // 1000000, len(worker_results))) - diff --git a/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx b/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx index 7d60bdb0d6e..1b0ae45e6d0 100644 --- a/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx +++ b/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx @@ -28,7 +28,7 @@ cdef mid_sig_ij(fusion_ring, row, col, a, b): This method assumes F-matrices are orthogonal. """ - #Pre-compute common parameters for efficiency + # Pre-compute common parameters for efficiency _fvars = fusion_ring.fmats._fvars _Nk_ij = fusion_ring.Nk_ij one = fusion_ring.one() @@ -39,7 +39,7 @@ cdef mid_sig_ij(fusion_ring, row, col, a, b): cdef list basis = list(fusion_ring.basis()) for c in basis: for d in basis: - ##Warning: We assume F-matrices are orthogonal!!! (using transpose for inverse) + # #Warning: We assume F-matrices are orthogonal!!! (using transpose for inverse) f1 = _fmat(_fvars, _Nk_ij, one, a, a, yi, b, xi, c) f2 = _fmat(_fvars, _Nk_ij, one, a, a, a, c, d, yi) f3 = _fmat(_fvars, _Nk_ij, one, a, a, a, c, d, yj) @@ -58,21 +58,21 @@ cdef odd_one_out_ij(fusion_ring, xi, xj, a, b): This method assumes F-matrices are orthogonal. """ - #Pre-compute common parameters for efficiency + # Pre-compute common parameters for efficiency _fvars = fusion_ring.fmats._fvars _Nk_ij = fusion_ring.Nk_ij one = fusion_ring.one() entry = 0 for c in fusion_ring.basis(): - ##Warning: We assume F-matrices are orthogonal!!! (using transpose for inverse) + # #Warning: We assume F-matrices are orthogonal!!! (using transpose for inverse) f1 = _fmat(_fvars, _Nk_ij, one, a, a, a, b, xi, c) f2 = _fmat(_fvars, _Nk_ij, one, a, a, a, b, xj, c) r = fusion_ring.r_matrix(a, a, c) entry += f1 * r * f2 return entry -#Cache methods (manually for cdef methods) +# Cache methods (manually for cdef methods) cdef odd_one_out_ij_cache = dict() cdef mid_sig_ij_cache = dict() @@ -102,7 +102,7 @@ cdef sig_2k(fusion_ring, tuple args): r""" Compute entries of the `2k`-th braid generator """ - #Pre-compute common parameters for efficiency + # Pre-compute common parameters for efficiency _fvars = fusion_ring.fmats._fvars _Nk_ij = fusion_ring.Nk_ij one = fusion_ring.one() @@ -112,37 +112,37 @@ cdef sig_2k(fusion_ring, tuple args): k, a, b, n_strands = fn_args cdef int ctr = -1 cdef list worker_results = list() - #Get computational basis + # Get computational basis cdef list comp_basis = fusion_ring.get_computational_basis(a, b, n_strands) cdef dict basis_dict = {elt: i for i, elt in enumerate(comp_basis)} cdef int dim = len(comp_basis) cdef set coords = set() cdef int i - #Avoid pickling cyclotomic field element objects + # Avoid pickling cyclotomic field element objects cdef bint must_flatten_coeff = fusion_ring.fvars_field() != QQbar cdef list basis = list(fusion_ring.basis()) for i in range(dim): for f in basis: for e in basis: for q in basis: - #Distribute work amongst processes + # Distribute work amongst processes ctr += 1 if ctr % n_proc != child_id: continue - #Compute appropriate possible nonzero row index + # Compute appropriate possible nonzero row index nnz_pos = list(comp_basis[i]) nnz_pos[k-1] = f nnz_pos[k] = e - #Handle the special case k = 1 + # Handle the special case k = 1 if k > 1: nnz_pos[n_strands//2+k-2] = q nnz_pos = tuple(nnz_pos) - #Skip repeated entries when k = 1 + # Skip repeated entries when k = 1 if nnz_pos in comp_basis and (basis_dict[nnz_pos], i) not in coords: m, l = comp_basis[i][:n_strands//2], comp_basis[i][n_strands//2:] - #A few special cases + # A few special cases top_left = m[0] if k >= 3: top_left = l[k-3] @@ -150,11 +150,11 @@ cdef sig_2k(fusion_ring, tuple args): if k - 1 < len(l): root = l[k-1] - #Handle the special case k = 1 + # Handle the special case k = 1 if k == 1: entry = cached_mid_sig_ij(fusion_ring, m[:2], (f, e), a, root) - #Avoid pickling cyclotomic field element objects + # Avoid pickling cyclotomic field element objects if must_flatten_coeff: entry = entry.list() @@ -168,7 +168,7 @@ cdef sig_2k(fusion_ring, tuple args): f2 = _fmat(_fvars, _Nk_ij, one, top_left, f, e, root, q, p) entry += f1 * cached_mid_sig_ij(fusion_ring, (m[k-1], m[k]), (f, e), a, p) * f2 - #Avoid pickling cyclotomic field element objects + # Avoid pickling cyclotomic field element objects if must_flatten_coeff: entry = entry.list() @@ -182,7 +182,7 @@ cdef odd_one_out(fusion_ring, tuple args): Compute entries of the rightmost braid generator, in case we have an odd number of strands. """ - #Pre-compute common parameters for efficiency + # Pre-compute common parameters for efficiency _fvars = fusion_ring.fmats._fvars _Nk_ij = fusion_ring.Nk_ij one = fusion_ring.one() @@ -194,27 +194,27 @@ cdef odd_one_out(fusion_ring, tuple args): child_id, n_proc, fn_args = args a, b, n_strands = fn_args cdef int ctr = -1 - #Get computational basis + # Get computational basis cdef list comp_basis = fusion_ring.get_computational_basis(a, b, n_strands) cdef dict basis_dict = {elt: i for i, elt in enumerate(comp_basis)} cdef int dim = len(comp_basis) - #Avoid pickling cyclotomic field element objects + # Avoid pickling cyclotomic field element objects cdef bint must_flatten_coeff = fusion_ring.fvars_field() != QQbar cdef list basis = list(fusion_ring.basis()) for i in range(dim): for f in basis: for q in basis: - #Distribute work amongst processes + # Distribute work amongst processes ctr += 1 if ctr % n_proc != child_id: continue - #Compute appropriate possible nonzero row index + # Compute appropriate possible nonzero row index nnz_pos_temp = list(comp_basis[i]) nnz_pos_temp[n_strands//2-1] = f - #Handle small special case + # Handle small special case if n_strands > 3: nnz_pos_temp[-1] = q nnz_pos = tuple(nnz_pos_temp) @@ -222,11 +222,11 @@ cdef odd_one_out(fusion_ring, tuple args): if nnz_pos in comp_basis: m, l = comp_basis[i][:n_strands//2], comp_basis[i][n_strands//2:] - #Handle a couple of small special cases + # Handle a couple of small special cases if n_strands == 3: entry = cached_odd_one_out_ij(fusion_ring, m[-1], f, a, b) - #Avoid pickling cyclotomic field element objects + # Avoid pickling cyclotomic field element objects if must_flatten_coeff: entry = entry.list() @@ -237,14 +237,14 @@ cdef odd_one_out(fusion_ring, tuple args): top_left = l[-2] root = b - #Compute relevant entry + # Compute relevant entry entry = 0 for p in basis: f1 = _fmat(_fvars, _Nk_ij, one, top_left, m[-1], a, root, l[-1], p) f2 = _fmat(_fvars, _Nk_ij, one, top_left, f, a, root, q, p) entry += f1 * cached_odd_one_out_ij(fusion_ring, m[-1], f, a, p) * f2 - #Avoid pickling cyclotomic field element objects + # Avoid pickling cyclotomic field element objects if must_flatten_coeff: entry = entry.list() @@ -255,9 +255,9 @@ cdef odd_one_out(fusion_ring, tuple args): ### Parallel code executor ### ############################## -#Hard-coded module __dict__-style attribute with visible cdef methods +# Hard-coded module __dict__-style attribute with visible cdef methods cdef dict mappers = { - "sig_2k": sig_2k, + "sig_2k": sig_2k, "odd_one_out": odd_one_out } @@ -294,9 +294,9 @@ cpdef executor(tuple params): True """ (fn_name, fr_id), args = params - #Construct a reference to global FMatrix object in this worker's memory + # Construct a reference to global FMatrix object in this worker's memory fusion_ring_obj = cast(fr_id, py_object).value - #Bind module method to FMatrix object in worker process, and call the method + # Bind module method to FMatrix object in worker process, and call the method return mappers[fn_name](fusion_ring_obj, args) ###################################### @@ -327,4 +327,3 @@ cpdef _unflatten_entries(fusion_ring, list entries): if F != QQbar: for i, (coord, entry) in enumerate(entries): entries[i] = (coord, F(entry)) - diff --git a/src/sage/algebras/fusion_rings/fusion_ring.py b/src/sage/algebras/fusion_rings/fusion_ring.py index be067e64ece..029300b5d1f 100644 --- a/src/sage/algebras/fusion_rings/fusion_ring.py +++ b/src/sage/algebras/fusion_rings/fusion_ring.py @@ -2,13 +2,13 @@ Fusion Rings """ # **************************************************************************** -# Copyright (C) 2019 Daniel Bump -# Guillermo Aboumrad -# Travis Scrimshaw -# Nicolas Thiery +# Copyright (C) 2019 Daniel Bump +# Guillermo Aboumrad +# Travis Scrimshaw +# Nicolas Thiery # -# Distributed under the terms of the GNU General Public License (GPL) -# https://www.gnu.org/licenses/ +# Distributed under the terms of the GNU General Public License (GPL) +# https://www.gnu.org/licenses/ # **************************************************************************** from itertools import product, zip_longest @@ -408,10 +408,10 @@ def test_braid_representation(self, max_strands=6, anyon=None): raise NotImplementedError("only implemented for multiplicity free fusion rings") b = self.basis() results = [] - #Test with different numbers of strands + # Test with different numbers of strands for n_strands in range(3, max_strands+1): - #Randomly select a fusing anyon. Skip the identity element, since - #its braiding matrices are trivial + # Randomly select a fusing anyon. Skip the identity element, since + # its braiding matrices are trivial if anyon is not None: a = anyon else: @@ -421,7 +421,7 @@ def test_braid_representation(self, max_strands=6, anyon=None): break pow = a ** n_strands d = pow.monomials()[0] - #Try to find 'interesting' braid group reps i.e. skip 1-d reps + # Try to find 'interesting' braid group reps i.e. skip 1-d reps for k, v in pow.monomial_coefficients().items(): if v > 1: d = self(k) @@ -515,7 +515,7 @@ def field(self): Cyclotomic Field of order 40 and degree 16 """ # if self._field is None: - # self._field = CyclotomicField(4 * self._cyclotomic_order) + # self._field = CyclotomicField(4 * self._cyclotomic_order) # return self._field return CyclotomicField(4 * self._cyclotomic_order) @@ -1160,7 +1160,7 @@ def _get_trees(fr, top_row, root): comp_basis = list() for top in product((a*a).monomials(), repeat=n_strands//2): - #If the n_strands is odd, we must extend the top row by a fusing anyon + # If the n_strands is odd, we must extend the top row by a fusing anyon top_row = list(top)+[a]*(n_strands%2) comp_basis.extend(tuple([*top, *levels]) for levels in _get_trees(self, top_row, b)) return comp_basis @@ -1222,14 +1222,14 @@ def _emap(self, mapper, input_args, worker_pool=None): n_proc = worker_pool._processes if worker_pool is not None else 1 input_iter = [(child_id, n_proc, input_args) for child_id in range(n_proc)] no_mp = worker_pool is None - #Map phase + # Map phase input_iter = zip_longest([], input_iter, fillvalue=(mapper, id(self))) results = list() if no_mp: mapped = map(executor, input_iter) else: mapped = worker_pool.imap_unordered(executor, input_iter, chunksize=1) - #Reduce phase + # Reduce phase for worker_results in mapped: results.extend(worker_results) return results @@ -1317,7 +1317,7 @@ def get_braid_generators(self, """ if n_strands < 3: raise ValueError("the number of strands must be an integer at least 3") - #Construct associated FMatrix object and solve for F-symbols + # Construct associated FMatrix object and solve for F-symbols if self.fmats._chkpt_status < 7: self.fmats.find_orthogonal_solution(checkpoint=checkpoint, save_results=save_results, @@ -1325,37 +1325,37 @@ def get_braid_generators(self, use_mp=use_mp, verbose=verbose) - #Set multiprocessing parameters. Context can only be set once, so we try to set it + # Set multiprocessing parameters. Context can only be set once, so we try to set it try: set_start_method('fork') except RuntimeError: pass - #Turn off multiprocessing when field is QQbar due to pickling issues introduced by PARI upgrade in trac ticket #30537 + # Turn off multiprocessing when field is QQbar due to pickling issues introduced by PARI upgrade in trac ticket #30537 pool = Pool() if use_mp and self.fvars_field() != QQbar else None - #Set up computational basis and compute generators one at a time + # Set up computational basis and compute generators one at a time a, b = fusing_anyon, total_charge_anyon comp_basis = self.get_computational_basis(a, b, n_strands) d = len(comp_basis) if verbose: print("Computing an {}-dimensional representation of the Artin braid group on {} strands...".format(d, n_strands)) - #Compute diagonal odd-indexed generators using the 3j-symbols + # Compute diagonal odd-indexed generators using the 3j-symbols gens = {2*i+1: diagonal_matrix(self.r_matrix(a, a, c[i]) for c in comp_basis) for i in range(n_strands//2)} - #Compute even-indexed generators using F-matrices + # Compute even-indexed generators using F-matrices for k in range(1, n_strands//2): entries = self._emap('sig_2k', (k, a, b, n_strands), pool) - #Build cyclotomic field element objects from tuple of rationals repn + # Build cyclotomic field element objects from tuple of rationals repn _unflatten_entries(self, entries) gens[2*k] = matrix(dict(entries)) - #If n_strands is odd, we compute the final generator + # If n_strands is odd, we compute the final generator if n_strands % 2: entries = self._emap('odd_one_out', (a, b, n_strands), pool) - #Build cyclotomic field element objects from tuple of rationals repn + # Build cyclotomic field element objects from tuple of rationals repn _unflatten_entries(self, entries) gens[n_strands-1] = matrix(dict(entries)) @@ -1467,7 +1467,7 @@ def twist(self, reduced=True): P = self.parent() rho = P.space().rho() # We copy self.weight() to skip the test (which was already done - # by self.is_simple_object()). + # by self.is_simple_object()). lam = next(iter(self._monomial_coefficients)) inner = lam.inner_product(lam + 2*rho) twist = P._conj * P._nf * inner / P.fusion_l() diff --git a/src/sage/algebras/fusion_rings/poly_tup_engine.pyx b/src/sage/algebras/fusion_rings/poly_tup_engine.pyx index ac077f376ae..e14be4106d6 100644 --- a/src/sage/algebras/fusion_rings/poly_tup_engine.pyx +++ b/src/sage/algebras/fusion_rings/poly_tup_engine.pyx @@ -222,7 +222,7 @@ cdef inline ETuple degrees(tuple poly_tup): r""" Return the maximal degree of each variable in the polynomial. """ - #Deal with the empty tuple, representing the zero polynomial + # Deal with the empty tuple, representing the zero polynomial if not poly_tup: return ETuple() cdef ETuple max_degs, exp @@ -329,7 +329,7 @@ cdef inline bint tup_fixes_sq(tuple eq_tup): """ if len(eq_tup) != 2: return False - #To access _attributes, we must cdef ETuple + # To access _attributes, we must cdef ETuple cdef ETuple lm = eq_tup[0][0] if lm._nonzero != 1 or lm._data[1] != 2: return False @@ -367,13 +367,13 @@ cdef dict subs_squares(dict eq_dict, KSHandler known_sq): for idx, power in exp.sparse_iter(): if known_sq.contains(idx): coeff *= pow(known_sq.get(idx), power // 2) - #New power is 1 if power is odd + # New power is 1 if power is odd if power & True: new_e[idx] = 1 else: new_e[idx] = power exp = ETuple(new_e, len(exp)) - #If exponent tuple is already present in dictionary, coefficients are added + # If exponent tuple is already present in dictionary, coefficients are added if exp in subbed: subbed[exp] += coeff else: @@ -389,7 +389,7 @@ cdef dict remove_gcf(dict eq_dict, ETuple nonz): variables known to be nonzero. The entries of ``nonz`` are assumed to be some relatively large number, like 100. """ - #Find common variables, filtered according to known nonzeros + # Find common variables, filtered according to known nonzeros cdef ETuple common_powers, exp cdef NumberFieldElement_absolute c common_powers = nonz @@ -474,7 +474,7 @@ cpdef dict compute_known_powers(max_degs, dict val_dict, one): cdef ETuple max_deg = ETuple(list(max_degs)) max_deg = max_deg.emin(ETuple({idx: 100 for idx in val_dict}, len(max_deg))) cdef dict known_powers - #Get polynomial unit as tuple to initialize list elements + # Get polynomial unit as tuple to initialize list elements cdef tuple one_tup = ((max_deg._new(), one), ) cdef int d, power, var_idx known_powers = {var_idx: [one_tup]*(d+1) for var_idx, d in max_deg.sparse_iter()} @@ -492,7 +492,7 @@ cdef dict subs(tuple poly_tup, dict known_powers, one): cdef int var_idx, power cdef tuple temp for exp, coeff in poly_tup: - #Get polynomial unit as tuple + # Get polynomial unit as tuple temp = ((exp._new(), one), ) for var_idx, power in exp.sparse_iter(): if var_idx in known_powers: @@ -532,7 +532,7 @@ cdef tuple monom_sortkey(ETuple exp): """ cdef int deg = exp.unweighted_degree() # for i in range(exp._nonzero): - # exp._data[2*i+1] = -exp._data[2*i+1] + # exp._data[2*i+1] = -exp._data[2*i+1] cdef ETuple rev = exp.reversed().emul(-1) return (deg, rev) @@ -568,9 +568,9 @@ cpdef tuple poly_tup_sortkey(tuple eq_tup): cdef int i, l, nnz cdef list key = [] for exp, c in eq_tup: - #Compare by term degree + # Compare by term degree key.append(exp.unweighted_degree()) - #Next compare by term w.r.t. lex order + # Next compare by term w.r.t. lex order for i in range(exp._nonzero): # key.append(exp._length-1-exp._data[2*(nnz-i-1)]) # key.append(-exp._data[2*(nnz-i-1)+1]) diff --git a/src/sage/algebras/fusion_rings/shm_managers.pyx b/src/sage/algebras/fusion_rings/shm_managers.pyx index 76095a3242d..5a90595f6e6 100644 --- a/src/sage/algebras/fusion_rings/shm_managers.pyx +++ b/src/sage/algebras/fusion_rings/shm_managers.pyx @@ -47,7 +47,7 @@ cdef class KSHandler: whether the structure contains an entry corresponding to the given index. The parent process should construct this object without a - ``name`` attribute. Children processes use the ``name`` attribute, + ``name`` attribute. Children processes use the ``name`` attribute, accessed via ``self.shm.name`` to attach to the shared memory block. INPUT: @@ -64,7 +64,7 @@ cdef class KSHandler: .. NOTE:: - To properly dispose of shared memory resources, + To properly dispose of shared memory resources, ``self.shm.unlink()`` must be called before exiting. .. WARNING:: @@ -75,14 +75,14 @@ cdef class KSHandler: EXAMPLES:: sage: from sage.algebras.fusion_rings.shm_managers import KSHandler - sage: #Create shared data structure + sage: # Create shared data structure sage: f = FMatrix(FusionRing("A1", 2), inject_variables=True) creating variables fx1..fx14 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 sage: n = f._poly_ring.ngens() sage: f.start_worker_pool() sage: ks = KSHandler(n, f._field, use_mp=True) - sage: #In the same shell or in a different shell, attach to fvars + sage: # In the same shell or in a different shell, attach to fvars sage: name = ks.shm.name sage: ks2 = KSHandler(n, f._field, name=name, use_mp=True) sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup @@ -103,7 +103,7 @@ cdef class KSHandler: EXAMPLES:: sage: from sage.algebras.fusion_rings.shm_managers import KSHandler - sage: #Create shared data structure + sage: # Create shared data structure sage: f = FMatrix(FusionRing("A1", 2), inject_variables=True) creating variables fx1..fx14 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 @@ -119,8 +119,8 @@ cdef class KSHandler: n = n_slots d = self.field.degree() ks_t = np.dtype([ - ('known', 'bool', (1, )), - ('nums', 'i8', (d, )), + ('known', 'bool', (1, )), + ('nums', 'i8', (d, )), ('denoms', 'u8', (d, )) ]) self.obj_cache = [None]*n @@ -136,7 +136,7 @@ cdef class KSHandler: self.ks_dat['known'] = np.zeros((n, 1), dtype='bool') self.ks_dat['nums'] = np.zeros((n, d), dtype='i8') self.ks_dat['denoms'] = np.ones((n, d), dtype='u8') - #Populate initializer data + # Populate initializer data for idx, sq in init_data.items(): self.setitem(idx, sq) @@ -145,7 +145,7 @@ cdef class KSHandler: @cython.boundscheck(False) cdef NumberFieldElement_absolute get(self, int idx): r""" - Retrieve the known square corresponding to the given index, + Retrieve the known square corresponding to the given index, if it exists. """ if not self.ks_dat['known'][idx]: @@ -188,19 +188,19 @@ cdef class KSHandler: ....: k ....: sage: f.get_orthogonality_constraints() - [fx0^2 - 1, - fx1^2 - 1, - fx2^2 - 1, - fx3^2 - 1, - fx4^2 - 1, - fx5^2 - 1, - fx6^2 - 1, - fx7^2 - 1, - fx8^2 - 1, - fx9^2 - 1, - fx10^2 + fx12^2 - 1, - fx10*fx11 + fx12*fx13, - fx10*fx11 + fx12*fx13, + [fx0^2 - 1, + fx1^2 - 1, + fx2^2 - 1, + fx3^2 - 1, + fx4^2 - 1, + fx5^2 - 1, + fx6^2 - 1, + fx7^2 - 1, + fx8^2 - 1, + fx9^2 - 1, + fx10^2 + fx12^2 - 1, + fx10*fx11 + fx12*fx13, + fx10*fx11 + fx12*fx13, fx11^2 + fx13^2 - 1] sage: f.get_orthogonality_constraints(output=False) sage: f._ks.update(f.ideal_basis) @@ -230,7 +230,7 @@ cdef class KSHandler: eq_tup = eqns[i] if tup_fixes_sq(eq_tup): rhs = [-v for v in eq_tup[-1][1]] - #eq_tup is guaranteed univariate, so we extract variable idx from lm + # eq_tup is guaranteed univariate, so we extract variable idx from lm lm = eq_tup[0][0] idx = lm._data[0] try: @@ -288,7 +288,7 @@ cdef class KSHandler: sage: n = f._poly_ring.ngens() sage: f.start_worker_pool() sage: ks = KSHandler(n, f._field, use_mp=True, init_data=f._ks) - sage: #In the same shell or in a different one, attach to shared memory handler + sage: # In the same shell or in a different one, attach to shared memory handler sage: name = ks.shm.name sage: k2 = KSHandler(n, f._field, name=name, use_mp=True) sage: ks == k2 @@ -308,7 +308,7 @@ cdef class KSHandler: sage: f._reset_solver_state() sage: loads(dumps(f._ks)) == f._ks True - sage: f.find_orthogonal_solution(verbose=False) #long time + sage: f.find_orthogonal_solution(verbose=False) # long time sage: loads(dumps(f._ks)) == f._ks True """ @@ -384,11 +384,11 @@ cdef class FvarsHandler: entries have been modified before attempting retrieval. The parent process should construct this object without a - ``name`` attribute. Children processes use the ``name`` attribute, + ``name`` attribute. Children processes use the ``name`` attribute, accessed via ``self.shm.name`` to attach to the shared memory block. Multiprocessing requires Python 3.8+, since we must import the - ``multiprocessing.shared_memory`` module. + ``multiprocessing.shared_memory`` module. INPUT: @@ -413,7 +413,7 @@ cdef class FvarsHandler: .. NOTE:: - To properly dispose of shared memory resources, + To properly dispose of shared memory resources, ``self.shm.unlink()`` must be called before exiting. .. NOTE:: @@ -424,7 +424,7 @@ cdef class FvarsHandler: .. WARNING:: - The current data structure supports up to `2^16` entries, + The current data structure supports up to `2^16` entries, with each monomial in each entry having at most 254 nonzero terms. On average, each of the ``max_terms`` monomials can have at most 30 terms. @@ -432,7 +432,7 @@ cdef class FvarsHandler: EXAMPLES:: sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler - sage: #Create shared data structure + sage: # Create shared data structure sage: f = FMatrix(FusionRing("A2", 1), inject_variables=True) creating variables fx1..fx8 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 @@ -440,7 +440,7 @@ cdef class FvarsHandler: sage: n_proc = f.pool._processes sage: pids_name = f._pid_list.shm.name sage: fvars = FvarsHandler(8, f._field, f._idx_to_sextuple, use_mp=n_proc, pids_name=pids_name) - sage: #In the same shell or in a different shell, attach to fvars + sage: # In the same shell or in a different shell, attach to fvars sage: name = fvars.shm.name sage: fvars2 = FvarsHandler(8, f._field, f._idx_to_sextuple, name=name , use_mp=n_proc, pids_name=pids_name) sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup @@ -451,7 +451,7 @@ cdef class FvarsHandler: sage: fvars.shm.unlink() sage: f.shutdown_worker_pool() """ - def __init__(self, n_slots, field, idx_to_sextuple, init_data={}, use_mp=0, + def __init__(self, n_slots, field, idx_to_sextuple, init_data={}, use_mp=0, pids_name=None, name=None, max_terms=20, n_bytes=32): r""" Initialize ``self``. @@ -459,7 +459,7 @@ cdef class FvarsHandler: EXAMPLES:: sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler - sage: #Create shared data structure + sage: # Create shared data structure sage: f = FMatrix(FusionRing("A2", 1), inject_variables=True) creating variables fx1..fx8 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 @@ -478,10 +478,10 @@ cdef class FvarsHandler: cdef int slots = self.bytes // 8 cdef int n_proc = use_mp + 1 self.fvars_t = np.dtype([ - ('modified', np.int8, (n_proc, )), - ('ticks', 'u1', (max_terms, )), - ('exp_data', 'u2', (max_terms*30, )), - ('coeff_nums', np.int64, (max_terms, d, slots)), + ('modified', np.int8, (n_proc, )), + ('ticks', 'u1', (max_terms, )), + ('exp_data', 'u2', (max_terms*30, )), + ('coeff_nums', np.int64, (max_terms, d, slots)), ('coeff_denom', np.uint64, (max_terms, d, slots)) ]) self.sext_to_idx = {s: i for i, s in idx_to_sextuple.items()} @@ -497,7 +497,7 @@ cdef class FvarsHandler: else: self.fvars = np.ndarray((self.ngens, ), dtype=self.fvars_t) self.child_id = 0 - #Populate with initialziation data + # Populate with initialziation data for sextuple, fvar in init_data.items(): if isinstance(fvar, MPolynomial_libsingular): fvar = _flatten_coeffs(poly_to_tup(fvar)) @@ -521,7 +521,7 @@ cdef class FvarsHandler: unflattening its representation and constructing relevant Python objects. - This method returns a tuple of ``(ETuple, coeff)`` pairs, + This method returns a tuple of ``(ETuple, coeff)`` pairs, where ``coeff`` is an element of ``self.field``. EXAMPLES:: @@ -562,10 +562,10 @@ cdef class FvarsHandler: if not sextuple in self.sext_to_idx: raise KeyError('invalid sextuple {}'.format(sextuple)) cdef Py_ssize_t idx = self.sext_to_idx[sextuple] - #Each process builds its own cache, so each process must know - #whether the entry it wants to retrieve has been modified. - #Each process needs to know where to look, so we use an index - #every process agrees on. The pid_list[0] belongs to the main process. + # Each process builds its own cache, so each process must know + # whether the entry it wants to retrieve has been modified. + # Each process needs to know where to look, so we use an index + # every process agrees on. The pid_list[0] belongs to the main process. if self.child_id < 0: self.child_id = self.pid_list.index(getpid()) if idx in self.obj_cache: @@ -581,7 +581,7 @@ cdef class FvarsHandler: cdef Py_ssize_t cum, i, j, k cdef Rational quo cdef tuple ret - #Define memory views to reduce Python overhead and ensure correct typing + # Define memory views to reduce Python overhead and ensure correct typing cdef np.ndarray[np.uint8_t, ndim=1] ticks = self.fvars['ticks'][idx] cdef np.ndarray[np.uint16_t, ndim=1] exp_data = self.fvars['exp_data'][idx] cdef np.ndarray[np.int64_t, ndim=3] nums = self.fvars['coeff_nums'][idx] @@ -592,9 +592,9 @@ cdef class FvarsHandler: cum = 0 count = np.count_nonzero(ticks) for i in range(count): - #Construct new ETuple for each monomial + # Construct new ETuple for each monomial exp = e._new() - #Handle constant coeff + # Handle constant coeff nnz = ticks[i] if ticks[i] < 255 else 0 exp._nonzero = nnz if nnz: @@ -603,7 +603,7 @@ cdef class FvarsHandler: exp._data[j] = exp_data[cum] cum += 1 - #Construct cyclotomic field coefficient + # Construct cyclotomic field coefficient rats = list() for k in range(self.field.degree()): num = Integer(list(nums[i, k]), 2**63) @@ -613,7 +613,7 @@ cdef class FvarsHandler: cyc_coeff = self.field(rats) poly_tup.append((exp, cyc_coeff)) ret = tuple(poly_tup) - #Cache object and reset modified + # Cache object and reset modified self.obj_cache[idx] = ret modified[self.child_id] = 0 return ret @@ -622,7 +622,7 @@ cdef class FvarsHandler: @cython.wraparound(False) def __setitem__(self, sextuple, fvar): r""" - Given a sextuple of labels and a tuple of ``(ETuple, cyc_coeff)`` pairs, + Given a sextuple of labels and a tuple of ``(ETuple, cyc_coeff)`` pairs, create or overwrite an entry in the shared data structure corresponding to the given sextuple. @@ -660,21 +660,21 @@ cdef class FvarsHandler: cdef Py_ssize_t cum, i, idx, j, k, t cdef Rational r idx = self.sext_to_idx[sextuple] - #Clear entry before inserting + # Clear entry before inserting self.fvars[idx] = np.zeros((1, ), dtype=self.fvars_t) - #Define memory views to reduce Python overhead and ensure correct typing + # Define memory views to reduce Python overhead and ensure correct typing cdef np.ndarray[np.uint8_t, ndim=1] ticks = self.fvars['ticks'][idx] cdef np.ndarray[np.uint16_t, ndim=1] exp_data = self.fvars['exp_data'][idx] cdef np.ndarray[np.int64_t, ndim=3] nums = self.fvars['coeff_nums'][idx] cdef np.ndarray[np.uint64_t, ndim=3] denoms = self.fvars['coeff_denom'][idx] cdef np.ndarray[np.int8_t, ndim=1] modified = self.fvars['modified'][idx] cdef list digits - #Initialize denominators to 1 + # Initialize denominators to 1 denoms[:, :, 0] = 1 cum = 0 i = 0 for exp, coeff_tup in fvar: - #Handle constant coefficient + # Handle constant coefficient if exp._nonzero > 0: ticks[i] = exp._nonzero else: From d68678a1406970cc13011ab0dac577a28b76e7f7 Mon Sep 17 00:00:00 2001 From: Johann Birnick Date: Wed, 19 Oct 2022 20:00:19 +0100 Subject: [PATCH 262/632] Corrected doctring formatting. --- .../polynomial/multi_polynomial_ring_base.pyx | 37 ++++++++++--------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx index f59c521a0bb..2a9c0ac5004 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx @@ -355,30 +355,30 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): This function can be called in two ways: - 1. multivariate_interpolation(bound, points, values) + 1. multivariate_interpolation(bound, points, values) - 2. multivariate_interpolation(bound, function) + 2. multivariate_interpolation(bound, function) INPUT: - * "bound" -- either an integer bounding the total degree or a + * `bound` -- either an integer bounding the total degree or a list/tuple of integers bounding the degree of the variables - * "points" -- a list/tuple containing the evaluation points + * `points` -- list/tuple containing the evaluation points - * "values" -- a list/tuple containing the desired values at "points" + * `values` -- list/tuple containing the desired values at `points` - * "function" -- a evaluable function in n variables, where n is the - number of variables of the polynomial ring + * `function` -- evaluable function in n variables, where n is the number + of variables of the polynomial ring OUTPUT: - 1. A polynomial respecting the bounds and having "values" as values - when evaluated at "points". + 1. A polynomial respecting the bounds and having `values` as values + when evaluated at `points`. - 2. A polynomial respecting the bounds and having the same values as - "function" at exactly so many points so that the polynomial is - unique. + 2. A polynomial respecting the bounds and having the same values as + `function` at exactly so many points so that the polynomial is + unique. EXAMPLES:: @@ -411,17 +411,17 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): ALGORITHM: - Solves a linear system of equations with the linear algebra module. - If the points are not specified, it samples exactly as many points - as needed for a unique solution. + Solves a linear system of equations with the linear algebra module. If + the points are not specified, it samples exactly as many points as + needed for a unique solution. - NOTE: + .. NOTE:: It will only run if the base ring is a field, even though it might work otherwise as well. If your base ring is an integral domain, let it run over the fraction field. - WARNING:: + .. WARNING:: If you don't provide point/value pairs but just a function, it will only use as many points as needed for a unique solution with @@ -437,7 +437,8 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): sage: R.multivariate_interpolation(3,F) 1/2*x^3 + x*y + z^2 - 1/2*x + y + 25 - SEEALSO: + .. SEEALSO:: + :meth:`lagrange_polynomial` """ # get ring and number of variables From fce1d8a11f3aa84b3fe8a05e77e202e31ae16a2a Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 20 Oct 2022 10:38:49 +0200 Subject: [PATCH 263/632] Address Sebastien's comments --- src/sage/rings/morphism.pyx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/morphism.pyx b/src/sage/rings/morphism.pyx index cc6b6e9efc8..41f5e7f57ce 100644 --- a/src/sage/rings/morphism.pyx +++ b/src/sage/rings/morphism.pyx @@ -2941,7 +2941,9 @@ cdef class FrobeniusEndomorphism_generic(RingHomomorphism): cdef _update_slots(self, dict _slots): """ - Helper for copying and pickling. + Update information with the given slots. + + Helper function for copying or pickling. EXAMPLES:: @@ -2951,14 +2953,17 @@ cdef class FrobeniusEndomorphism_generic(RingHomomorphism): sage: phi == psi True """ - self._p = _slots['prime'] + self._p = _slots['p'] self._power = _slots['power'] self._q = self._p ** self._power RingHomomorphism._update_slots(self, _slots) cdef dict _extra_slots(self): """ - Helper for copying and pickling. + Return additional information about this morphism + as a dictionary. + + Helper function for copying or pickling. EXAMPLES:: @@ -2973,7 +2978,7 @@ cdef class FrobeniusEndomorphism_generic(RingHomomorphism): True """ slots = RingHomomorphism._extra_slots(self) - slots['prime'] = self._p + slots['p'] = self._p slots['power'] = self._power return slots From 3c7685643bef6f9dbc40a00fa5a4efa735887285 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 20 Oct 2022 11:01:56 +0200 Subject: [PATCH 264/632] retrieve p as domain.characteristic() --- .../rings/finite_rings/hom_finite_field.pyx | 26 ------------------- src/sage/rings/morphism.pyx | 3 +-- 2 files changed, 1 insertion(+), 28 deletions(-) diff --git a/src/sage/rings/finite_rings/hom_finite_field.pyx b/src/sage/rings/finite_rings/hom_finite_field.pyx index 81c7620d42d..86d2401ceec 100644 --- a/src/sage/rings/finite_rings/hom_finite_field.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field.pyx @@ -826,31 +826,6 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): """ return Morphism.__hash__(self) - cdef dict _extra_slots(self): - r""" - Helper function for copying and pickling - - TESTS:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism(2) - sage: Frob.__reduce__() # indirect doctest - (, - (, - Automorphism group of Finite Field in t of size 5^3, - {}, - {'_codomain': Finite Field in t of size 5^3, - '_domain': Finite Field in t of size 5^3, - '_is_coercion': False, - '_lift': None, - '_power': 2, - '_repr_type_str': None})) - """ - cdef dict slots - slots = FrobeniusEndomorphism_generic._extra_slots(self) - slots['_power'] = self._power - return slots - cdef _update_slots(self, dict slots): r""" Helper function for copying and pickling @@ -869,7 +844,6 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): True """ FrobeniusEndomorphism_generic._update_slots(self, slots) - self._power = slots['_power'] domain = self.domain() self._degree = domain.degree() self._degree_fixed = domain.degree().gcd(self._power) diff --git a/src/sage/rings/morphism.pyx b/src/sage/rings/morphism.pyx index 41f5e7f57ce..b4f38dcc8ec 100644 --- a/src/sage/rings/morphism.pyx +++ b/src/sage/rings/morphism.pyx @@ -2953,7 +2953,7 @@ cdef class FrobeniusEndomorphism_generic(RingHomomorphism): sage: phi == psi True """ - self._p = _slots['p'] + self._p = _slots['_domain'].characteristic() self._power = _slots['power'] self._q = self._p ** self._power RingHomomorphism._update_slots(self, _slots) @@ -2978,7 +2978,6 @@ cdef class FrobeniusEndomorphism_generic(RingHomomorphism): True """ slots = RingHomomorphism._extra_slots(self) - slots['p'] = self._p slots['power'] = self._power return slots From a9a4183e3d1d2f27407948d7636b7adc8cb4c6ff Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 20 Oct 2022 11:17:20 +0200 Subject: [PATCH 265/632] power -> _power --- src/sage/rings/morphism.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/morphism.pyx b/src/sage/rings/morphism.pyx index b4f38dcc8ec..eb54dc4a900 100644 --- a/src/sage/rings/morphism.pyx +++ b/src/sage/rings/morphism.pyx @@ -2954,7 +2954,7 @@ cdef class FrobeniusEndomorphism_generic(RingHomomorphism): True """ self._p = _slots['_domain'].characteristic() - self._power = _slots['power'] + self._power = _slots['_power'] self._q = self._p ** self._power RingHomomorphism._update_slots(self, _slots) @@ -2978,7 +2978,7 @@ cdef class FrobeniusEndomorphism_generic(RingHomomorphism): True """ slots = RingHomomorphism._extra_slots(self) - slots['power'] = self._power + slots['_power'] = self._power return slots def _repr_(self): From 61150df051de5c6841fe44a8cd4fc1af9f0d5b82 Mon Sep 17 00:00:00 2001 From: Johann Birnick Date: Thu, 20 Oct 2022 19:37:29 +0100 Subject: [PATCH 266/632] improved documentation, removed warning for non-unique solution --- .../polynomial/multi_polynomial_ring_base.pyx | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx index 2a9c0ac5004..6e66b079a1e 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx @@ -361,23 +361,23 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): INPUT: - * `bound` -- either an integer bounding the total degree or a + * ``bound`` -- either an integer bounding the total degree or a list/tuple of integers bounding the degree of the variables - * `points` -- list/tuple containing the evaluation points + * ``points`` -- list/tuple containing the evaluation points - * `values` -- list/tuple containing the desired values at `points` + * ``values`` -- list/tuple containing the desired values at ``points`` - * `function` -- evaluable function in n variables, where n is the number - of variables of the polynomial ring + * ``function`` -- evaluable function in `n` variables, where `n` is the + number of variables of the polynomial ring OUTPUT: - 1. A polynomial respecting the bounds and having `values` as values - when evaluated at `points`. + 1. A polynomial respecting the bounds and having ``values`` as values + when evaluated at ``points``. 2. A polynomial respecting the bounds and having the same values as - `function` at exactly so many points so that the polynomial is + ``function`` at exactly so many points so that the polynomial is unique. EXAMPLES:: @@ -421,6 +421,9 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): work otherwise as well. If your base ring is an integral domain, let it run over the fraction field. + Also, if the solution is not unique, it spits out one solution, + without any notice that there are more. + .. WARNING:: If you don't provide point/value pairs but just a function, it @@ -428,7 +431,7 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): the given bounds. In particular it will *not* notice or check whether the result yields the correct evaluation for other points as well. So if you give wrong bounds, you will get a wrong answer - without a warning. + without any warning. sage: def F(a,b,c): ....: return a^3*b + b + c^2 + 25 @@ -497,10 +500,6 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): raise ValueError('Could not find a solution.') solution = sum(coeff[i] * self.monomial(*exponents_space[i]) for i in range(len(exponents_space))) - # warn the user if the solution is not unique - if M.left_kernel().dimension() > 0: - warnings.warn('The solution is not unique.') - return solution def _coerce_map_from_base_ring(self): From eb5833f39ec177f501092470abddf7a74ffc60a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gonzalo=20Tornar=C3=ADa?= Date: Thu, 20 Oct 2022 18:03:19 -0300 Subject: [PATCH 267/632] #34681: fix multiplication of points over Integers(n) --- src/sage/schemes/elliptic_curves/ell_point.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/sage/schemes/elliptic_curves/ell_point.py b/src/sage/schemes/elliptic_curves/ell_point.py index 9c9038e0eb5..cac55f7f546 100644 --- a/src/sage/schemes/elliptic_curves/ell_point.py +++ b/src/sage/schemes/elliptic_curves/ell_point.py @@ -641,6 +641,13 @@ def _add_(self, right): ... ZeroDivisionError: Inverse of 28 does not exist (characteristic = 35 = 7*5) + + Checks that :trac:`34681` is fixed:: + + sage: P+P + (15 : 14 : 1) + sage: 2*P + (15 : 14 : 1) """ # Use Prop 7.1.7 of Cohen "A Course in Computational Algebraic # Number Theory" @@ -3505,7 +3512,7 @@ def _acted_upon_(self, other, side): vQ = 0 else: assert len(pariQ) == 2 - vQ = Sequence(tuple(pariQ) + (1,), E.base_field()) + vQ = Sequence(tuple(pariQ) + (1,), E.base_ring()) Q = EllipticCurvePoint_finite_field(E, vQ, check=False) else: From 9bbacfe4e0232a26b2e50c93e8eed1c01c073ae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gonzalo=20Tornar=C3=ADa?= Date: Thu, 20 Oct 2022 18:04:45 -0300 Subject: [PATCH 268/632] #34681: do not redo multiplication when pari fails --- src/sage/schemes/elliptic_curves/ell_point.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_point.py b/src/sage/schemes/elliptic_curves/ell_point.py index cac55f7f546..fbb0614c802 100644 --- a/src/sage/schemes/elliptic_curves/ell_point.py +++ b/src/sage/schemes/elliptic_curves/ell_point.py @@ -85,7 +85,7 @@ sage: LCM([2..60])*P Traceback (most recent call last): ... - ZeroDivisionError: Inverse of 1520944668 does not exist (characteristic = 1715761513 = 26927*63719) + ZeroDivisionError: Inverse of 26927 does not exist (characteristic = 1715761513 = 26927*63719) AUTHORS: @@ -630,7 +630,7 @@ def _add_(self, right): sage: LCM([2..60])*P Traceback (most recent call last): ... - ZeroDivisionError: Inverse of 1520944668 does not exist + ZeroDivisionError: Inverse of 26927 does not exist (characteristic = 1715761513 = 26927*63719) sage: N = 35 @@ -639,7 +639,7 @@ def _add_(self, right): sage: 4*P Traceback (most recent call last): ... - ZeroDivisionError: Inverse of 28 does not exist + ZeroDivisionError: Inverse of 7 does not exist (characteristic = 35 = 7*5) Checks that :trac:`34681` is fixed:: @@ -3504,7 +3504,16 @@ def _acted_upon_(self, other, side): try: pariQ = pari.ellmul(E, self, k) - except PariError: + except PariError as err: + if str(err.errdata().component(1)) == "Fp_inv": + val = err.errdata().component(2) + a = val.lift() + N = val.mod() + N1 = N.gcd(a) + N2 = N//N1 + raise ZeroDivisionError( + f"Inverse of {a} does not exist" + f" (characteristic = {N} = {N1}*{N2})") pariQ = None if pariQ is not None: From e5e8cd54b96bcc01beedee0e7ead7404e0dccb33 Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Fri, 21 Oct 2022 12:57:19 -0600 Subject: [PATCH 269/632] trac 34662: create permutation from a generator --- src/sage/combinat/permutation.py | 72 +++++++++++++++----------------- 1 file changed, 33 insertions(+), 39 deletions(-) diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index f6d802e2beb..0fa32de70ee 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -7478,57 +7478,51 @@ def from_cycles(n, cycles, parent=None): sage: Permutation("(-12,2)(3,4)") Traceback (most recent call last): ... - ValueError: All elements should be strictly positive integers, and I just found a non-positive one. + ValueError: all elements should be strictly positive integers, but I found -12 sage: Permutation("(1,2)(2,4)") Traceback (most recent call last): ... - ValueError: an element appears twice in the input + ValueError: the element 2 appears more than once in the input sage: permutation.from_cycles(4, [[1,18]]) Traceback (most recent call last): ... - ValueError: You claimed that this was a permutation on 1...4 but it contains 18 + ValueError: you claimed that this is a permutation on 1...4, but it contains 18 + + TESTS: + + Verify that :trac:`34662` has been fixed:: + + sage: permutation.from_cycles(6, (c for c in [[1,2,3], [4,5,6]])) + [2, 3, 1, 5, 6, 4] """ if parent is None: parent = Permutations(n) - p = list(range(1, n+1)) - - # Is it really a permutation on 1...n ? - flattened_and_sorted = [] - for c in cycles: - flattened_and_sorted.extend(c) - flattened_and_sorted.sort() - - # Empty input - if not flattened_and_sorted: - return parent(p, check_input=False) - - # Only positive elements - if int(flattened_and_sorted[0]) < 1: - raise ValueError("All elements should be strictly positive " - "integers, and I just found a non-positive one.") - - # Really smaller or equal to n ? - if flattened_and_sorted[-1] > n: - raise ValueError("You claimed that this was a permutation on 1..."+ - str(n)+" but it contains "+str(flattened_and_sorted[-1])) - - # Disjoint cycles ? - previous = flattened_and_sorted[0] - 1 - for i in flattened_and_sorted: - if i == previous: - raise ValueError("an element appears twice in the input") - else: - previous = i + # None represents a value of the permutation that has not yet been specified + p = n * [None] for cycle in cycles: - if not cycle: - continue - first = cycle[0] - for i in range(len(cycle)-1): - p[cycle[i]-1] = cycle[i+1] - p[cycle[-1]-1] = first - + for i in range(len(cycle)): + # two consecutive terms in the cycle represent k and p(k) + k = cycle[i] + pk = cycle[(i + 1) % len(cycle)] + + # check that the values are valid + if (int(k) < 1) or (int(pk) < 1): + raise ValueError("all elements should be strictly positive " + f"integers, but I found {min(k, pk)}") + if (k > n) or (pk > n): + raise ValueError("you claimed that this is a permutation on " + f"1...{n}, but it contains {max(k, pk)}") + if p[k - 1] is not None: + raise ValueError(f"the element {k} appears more than once" + " in the input") + + p[k - 1] = pk + # unspecified values are fixed points of the permutation + for i in range(n): + if p[i] is None: + p[i] = i + 1 return parent(p, check_input=False) def from_lehmer_code(lehmer, parent=None): From ab935ed1cbb46fda2561364134cbce20b6968d41 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 13 Oct 2022 23:31:57 -0700 Subject: [PATCH 270/632] build/pkgs/numpy: Update to 1.23.4 --- build/pkgs/numpy/checksums.ini | 6 +++--- build/pkgs/numpy/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/numpy/checksums.ini b/build/pkgs/numpy/checksums.ini index 544f35fbfb8..74a09aeafe1 100644 --- a/build/pkgs/numpy/checksums.ini +++ b/build/pkgs/numpy/checksums.ini @@ -1,5 +1,5 @@ tarball=numpy-VERSION.tar.gz -sha1=570c995d7b155c7e4ac43bc46594172cedf1e4fa -md5=6efc60a3f6c1b74c849d53fbcc07807b -cksum=3973735135 +sha1=8cb1e6b72cbb84e5892889bf7b09cb341d1708ec +md5=d9ffd2c189633486ec246e61d4b947a0 +cksum=1174837624 upstream_url=https://pypi.io/packages/source/n/numpy/numpy-VERSION.tar.gz diff --git a/build/pkgs/numpy/package-version.txt b/build/pkgs/numpy/package-version.txt index ac1df3fce34..27ddcc14da0 100644 --- a/build/pkgs/numpy/package-version.txt +++ b/build/pkgs/numpy/package-version.txt @@ -1 +1 @@ -1.23.3 +1.23.4 From fcc7087f16fd9c7f3015d5a40c51089b934f3834 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 21 Oct 2022 23:15:30 -0700 Subject: [PATCH 271/632] build/pkgs/scipy: Update to 1.9.3 --- build/pkgs/scipy/checksums.ini | 6 +++--- build/pkgs/scipy/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/scipy/checksums.ini b/build/pkgs/scipy/checksums.ini index 086694d7214..24c26b4981b 100644 --- a/build/pkgs/scipy/checksums.ini +++ b/build/pkgs/scipy/checksums.ini @@ -1,5 +1,5 @@ tarball=scipy-VERSION.tar.gz -sha1=f93163946802657fcd1c10821a1330497045ef6b -md5=ee6db269d03b2d47d04e876d38515d0d -cksum=3886027417 +sha1=55fb286ab1a0b66a7439c5cc76e3c80e9de409ec +md5=83b0d9eab2ce79b7fe5888f119adee64 +cksum=1605676871 upstream_url=https://pypi.io/packages/source/s/scipy/scipy-VERSION.tar.gz diff --git a/build/pkgs/scipy/package-version.txt b/build/pkgs/scipy/package-version.txt index 8fdcf386946..77fee73a8cf 100644 --- a/build/pkgs/scipy/package-version.txt +++ b/build/pkgs/scipy/package-version.txt @@ -1 +1 @@ -1.9.2 +1.9.3 From a2b5da4528b59fbbbc74bcaeee3acead6e7cd654 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 21 Oct 2022 23:15:47 -0700 Subject: [PATCH 272/632] build/pkgs/networkx: Update to 2.8.7 --- build/pkgs/networkx/checksums.ini | 6 +++--- build/pkgs/networkx/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/networkx/checksums.ini b/build/pkgs/networkx/checksums.ini index fd005a4d478..fbcdd6b3771 100644 --- a/build/pkgs/networkx/checksums.ini +++ b/build/pkgs/networkx/checksums.ini @@ -1,5 +1,5 @@ tarball=networkx-VERSION.tar.gz -sha1=757ea87972243f1dab99687cd94ed51377efa34b -md5=91fdb77298a54c6e7cfb2d5305d23d23 -cksum=2430190254 +sha1=ca2bfcee5662a59e44a8398feacbeee4ac3f86d6 +md5=d7e62d70785d92f523e7e317a1f70f0a +cksum=1611585872 upstream_url=https://pypi.io/packages/source/n/networkx/networkx-VERSION.tar.gz diff --git a/build/pkgs/networkx/package-version.txt b/build/pkgs/networkx/package-version.txt index 2701a226a2f..bcd0f91fe45 100644 --- a/build/pkgs/networkx/package-version.txt +++ b/build/pkgs/networkx/package-version.txt @@ -1 +1 @@ -2.8.4 +2.8.7 From ba5c196223aa96014e0e138711f26d8238f3841b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 21 Oct 2022 23:24:13 -0700 Subject: [PATCH 273/632] build/pkgs/pythran: Update to 0.12.0 --- build/pkgs/pythran/checksums.ini | 6 +++--- build/pkgs/pythran/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/pythran/checksums.ini b/build/pkgs/pythran/checksums.ini index 5c295d3da45..0789ef48f67 100644 --- a/build/pkgs/pythran/checksums.ini +++ b/build/pkgs/pythran/checksums.ini @@ -1,5 +1,5 @@ tarball=pythran-VERSION.tar.gz -sha1=d2a227e14244a9afaeb4732a0299103b6f7ecabe -md5=e09e90484771937ab499380858bdb18d -cksum=1753405982 +sha1=ed5630b0879be9c59885d83c5a24fcd5dfbca5af +md5=d2961ece35b4b9f44a84ef31df1b21ff +cksum=399652957 upstream_url=https://pypi.io/packages/source/p/pythran/pythran-VERSION.tar.gz diff --git a/build/pkgs/pythran/package-version.txt b/build/pkgs/pythran/package-version.txt index d9df1bbc0c7..ac454c6a1fc 100644 --- a/build/pkgs/pythran/package-version.txt +++ b/build/pkgs/pythran/package-version.txt @@ -1 +1 @@ -0.11.0 +0.12.0 From 93cf36d06796b1065cdd659989b31b47779cf25e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 22 Oct 2022 10:33:18 +0200 Subject: [PATCH 274/632] some suggested details --- src/sage/combinat/triangles_FHM.py | 56 ++++++++++++++++++++++--- src/sage/topology/simplicial_complex.py | 6 +++ 2 files changed, 57 insertions(+), 5 deletions(-) diff --git a/src/sage/combinat/triangles_FHM.py b/src/sage/combinat/triangles_FHM.py index 6e962476433..2a9b08b0791 100644 --- a/src/sage/combinat/triangles_FHM.py +++ b/src/sage/combinat/triangles_FHM.py @@ -143,6 +143,21 @@ def __init__(self, poly, variables=None): self._poly = poly self._n = max(self._poly.degree(v) for v in self._vars) + def _ascii_art_(self): + """ + Return the ascii-art representation (as a matrix). + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: ht = H_triangle(1+2*x*y) + sage: ascii_art(ht) + [0 2] + [1 0] + """ + return self.matrix()._ascii_art_() + def _unicode_art_(self): """ Return the unicode representation (as a matrix). @@ -170,7 +185,24 @@ def _repr_(self) -> str: sage: ht H: 2*x*y + 1 """ - return self._prefix + repr(self._poly) + return self._prefix + ": " + repr(self._poly) + + def _latex_(self): + r""" + Return the LaTeX representation (as a matrix). + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: ht = H_triangle(1+2*x*y) + sage: latex(ht) + \left(\begin{array}{rr} + 0 & 2 \\ + 1 & 0 + \end{array}\right) + """ + return self.matrix()._latex_() def __eq__(self, other) -> bool: """ @@ -266,6 +298,20 @@ def matrix(self): """ return _matrix_display(self._poly, variables=self._vars) + def polynomial(self): + """ + Return the triangle as a bare polynomial. + + EXAMPLES:: + + sage: from sage.combinat.triangles_FHM import H_triangle + sage: x, y = polygens(ZZ, 'x,y') + sage: h = H_triangle(1+2*x*y) + sage: h.polynomial() + 2*x*y + 1 + """ + return self._poly + def truncate(self, d): """ Return the truncated triangle. @@ -304,7 +350,7 @@ class M_triangle(Triangle): sage: P.M_triangle() M: x*y - y + 1 """ - _prefix = 'M: ' + _prefix = 'M' def dual(self): """ @@ -407,7 +453,7 @@ class H_triangle(Triangle): """ Class for the H-triangles. """ - _prefix = 'H: ' + _prefix = 'H' def transpose(self): """ @@ -544,7 +590,7 @@ class F_triangle(Triangle): """ Class for the F-triangles. """ - _prefix = 'F: ' + _prefix = 'F' def h(self): """ @@ -641,7 +687,7 @@ class Gamma_triangle(Triangle): """ Class for the Gamma-triangles. """ - _prefix = 'Γ: ' + _prefix = 'Γ' def h(self): r""" diff --git a/src/sage/topology/simplicial_complex.py b/src/sage/topology/simplicial_complex.py index 84026ddb17e..53b1919ac66 100644 --- a/src/sage/topology/simplicial_complex.py +++ b/src/sage/topology/simplicial_complex.py @@ -1604,6 +1604,12 @@ def F_triangle(self, S): sage: cs.F_triangle(cs.facets()[0]) F: x^3 + 9*x^2*y + 3*x*y^2 + y^3 + 6*x^2 + 12*x*y + 3*y^2 + 4*x + 3*y + 1 + + TESTS:: + + sage: S = SimplicialComplex([]) + sage: S.F_triangle(S.facets()[0]) + F: 1 """ x, y = polygens(ZZ, 'x, y') from sage.combinat.triangles_FHM import F_triangle From 415670ca7dd9c2789805dbd9d1c8c2cd24c68721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 23 Oct 2022 20:27:46 +0200 Subject: [PATCH 275/632] make Compositions() an additive monoid. --- src/sage/combinat/composition.py | 47 ++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/src/sage/combinat/composition.py b/src/sage/combinat/composition.py index ee711e52f9f..ab51d83ecfd 100644 --- a/src/sage/combinat/composition.py +++ b/src/sage/combinat/composition.py @@ -32,8 +32,8 @@ from itertools import accumulate from collections.abc import Sequence -from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets -from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets +from sage.categories.enumerated_sets import EnumeratedSets +from sage.categories.additive_monoids import AdditiveMonoids from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent from sage.sets.finite_enumerated_set import FiniteEnumeratedSet @@ -1386,6 +1386,7 @@ def count(self, n): """ return sum(i == n for i in self) + Sequence.register(Composition) ############################################################## @@ -1547,14 +1548,14 @@ class Compositions(UniqueRepresentation, Parent): results. It is up to the user to ensure that the inner and outer compositions themselves satisfy the parts and slope constraints. - Note that if you specify ``min_part=0``, then the objects produced may - have parts equal to zero. This violates the internal assumptions - that the composition class makes. Use at your own risk, or - preferably consider using ``IntegerVectors`` instead:: + Note that setting ``min_part=0`` is not allowed:: - sage: Compositions(2, length=3, min_part=0).list() - doctest:...: RuntimeWarning: Currently, setting min_part=0 produces Composition objects which violate internal assumptions. Calling methods on these objects may produce errors or WRONG results! - [[2, 0, 0], [1, 1, 0], [1, 0, 1], [0, 2, 0], [0, 1, 1], [0, 0, 2]] + sage: Compositions(2, length=3, min_part=0) + Traceback (most recent call last): + ... + ValueError: setting min_part=0 is not allowed for Compositions + + Preferably consider using ``IntegerVectors`` instead:: sage: list(IntegerVectors(2, 3)) [[2, 0, 0], [1, 1, 0], [1, 0, 1], [0, 2, 0], [0, 1, 1], [0, 0, 2]] @@ -1651,8 +1652,7 @@ def __classcall_private__(self, n=None, **kwargs): if 'min_part' not in kwargs: kwargs['min_part'] = 1 elif kwargs['min_part'] == 0: - from warnings import warn - warn("Currently, setting min_part=0 produces Composition objects which violate internal assumptions. Calling methods on these objects may produce errors or WRONG results!", RuntimeWarning) + raise ValueError("setting min_part=0 is not allowed for Compositions") if 'outer' in kwargs: kwargs['ceiling'] = list(kwargs['outer']) @@ -1673,7 +1673,7 @@ def __classcall_private__(self, n=None, **kwargs): kwargs['min_length'] = len(inner) return IntegerListsLex(n, **kwargs) - def __init__(self, is_infinite=False): + def __init__(self, is_infinite=False, category=None): """ Initialize ``self``. @@ -1682,10 +1682,12 @@ def __init__(self, is_infinite=False): sage: C = Compositions() sage: TestSuite(C).run() """ + if category is None: + category = EnumeratedSets() if is_infinite: - Parent.__init__(self, category=InfiniteEnumeratedSets()) + Parent.__init__(self, category=category.Infinite()) else: - Parent.__init__(self, category=FiniteEnumeratedSets()) + Parent.__init__(self, category=category.Finite()) Element = Composition @@ -1878,7 +1880,8 @@ def __init__(self): sage: C = Compositions() sage: TestSuite(C).run() """ - Compositions.__init__(self, True) + cat = AdditiveMonoids() + Compositions.__init__(self, True, category=cat) def _repr_(self) -> str: """ @@ -1907,6 +1910,20 @@ def subset(self, size=None): return self return Compositions(size) + def zero(self): + """ + Return the zero of the additive monoid. + + This is the empty composition. + + EXAMPLES:: + + sage: C = Compositions() + sage: C.zero() + [] + """ + return Composition([]) + def __iter__(self): """ Iterate over all compositions. From 57a2d1dbe644b7ed4e34f3e3944d2e0297908825 Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Tue, 18 Oct 2022 15:34:10 +0100 Subject: [PATCH 276/632] Add test failing when matrix can be generated by paleyI --- src/sage/combinat/matrices/hadamard_matrix.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 9ca538ac482..e6826780305 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -411,6 +411,8 @@ def hadamard_matrix(n,existence=False, check=True): Traceback (most recent call last): ... ValueError: The Hadamard matrix of order 10 does not exist + sage: matrix.hadamard(312, existence=True) + True """ if not(n % 4 == 0) and (n > 2): if existence: From 822b99a16841fda42d0123b424dae1076f7b2b47 Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Wed, 19 Oct 2022 15:56:46 +0100 Subject: [PATCH 277/632] Try recursive method in hadamard_matrix only if it will be successful --- src/sage/combinat/matrices/hadamard_matrix.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index e6826780305..058d39879be 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -430,9 +430,9 @@ def hadamard_matrix(n,existence=False, check=True): if existence: return True M = hadamard_matrix_paleyII(n) - elif n == 4 or n % 8 == 0: + elif n == 4 or n % 8 == 0 and hadamard_matrix(n//2,existence=True) is True: if existence: - return hadamard_matrix(n//2,existence=True) + return True had = hadamard_matrix(n//2,check=False) chad1 = matrix([list(r) + list(r) for r in had.rows()]) mhad = (-1) * had From c1a371e91e505c1abccdf1bdf7d3f12678ad5efa Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Wed, 19 Oct 2022 18:00:33 +0100 Subject: [PATCH 278/632] Add tests for matrices created by skew_hadamard_matrix and regular_symmetric_hadamard_matrix_with_constant_diagonal --- src/sage/combinat/matrices/hadamard_matrix.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 058d39879be..9655c326ccc 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -413,6 +413,10 @@ def hadamard_matrix(n,existence=False, check=True): ValueError: The Hadamard matrix of order 10 does not exist sage: matrix.hadamard(312, existence=True) True + sage: matrix.hadamard(1904, existence=True) + True + sage: matrix.hadamard(324, existence=True) + True """ if not(n % 4 == 0) and (n > 2): if existence: From a411049f1df98af9d26281e2f62e72ac375e49ed Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Wed, 19 Oct 2022 18:17:52 +0100 Subject: [PATCH 279/632] Add skew_hadamard_matrix and regular_symmetric_hadamard_matrix_with_constant_diagonal to hadamard_matrix function --- src/sage/combinat/matrices/hadamard_matrix.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 9655c326ccc..e56722c50b3 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -448,6 +448,14 @@ def hadamard_matrix(n,existence=False, check=True): if existence: return True M = hadamard_matrix_paleyI(n) + elif skew_hadamard_matrix(n, existence=True) is True: + if existence: + return True + M = skew_hadamard_matrix(n, check=False) + elif regular_symmetric_hadamard_matrix_with_constant_diagonal(n, 1, existence=True) is True: + if existence: + return True + M = regular_symmetric_hadamard_matrix_with_constant_diagonal(n, 1) else: if existence: return Unknown From 86506ceecdec2c6e2b843409625b74aa741bae5b Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Wed, 19 Oct 2022 18:20:10 +0100 Subject: [PATCH 280/632] Fix failed test returning Unknown in hadamard_matrix --- src/sage/combinat/matrices/hadamard_matrix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index e56722c50b3..839ba63fceb 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -405,7 +405,7 @@ def hadamard_matrix(n,existence=False, check=True): False sage: matrix.hadamard(12,existence=True) True - sage: matrix.hadamard(92,existence=True) + sage: matrix.hadamard(116,existence=True) Unknown sage: matrix.hadamard(10) Traceback (most recent call last): From ddc1f62c6d3dc34ab991d09ad227039d0dfdc632 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Sun, 23 Oct 2022 16:45:22 -0700 Subject: [PATCH 281/632] fusion ring updates to fmatrix, tests passing --- src/sage/algebras/fusion_rings/all.py | 2 +- src/sage/algebras/fusion_rings/fusion_ring.py | 28 +++++++++++-------- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/sage/algebras/fusion_rings/all.py b/src/sage/algebras/fusion_rings/all.py index e1dfaa1f2b2..e9125c55696 100644 --- a/src/sage/algebras/fusion_rings/all.py +++ b/src/sage/algebras/fusion_rings/all.py @@ -15,6 +15,6 @@ from sage.misc.lazy_import import lazy_import lazy_import('sage.algebras.fusion_rings.fusion_ring', ['FusionRing']) -lazy_import('sage.algebras.fusion_rings.f_matrix', ['FMatrix']) +# lazy_import('sage.algebras.fusion_rings.f_matrix', ['FMatrix']) del lazy_import diff --git a/src/sage/algebras/fusion_rings/fusion_ring.py b/src/sage/algebras/fusion_rings/fusion_ring.py index 029300b5d1f..becb5f4490e 100644 --- a/src/sage/algebras/fusion_rings/fusion_ring.py +++ b/src/sage/algebras/fusion_rings/fusion_ring.py @@ -571,7 +571,7 @@ def fvars_field(self): computed :func:`NumberField`. """ if self.is_multiplicity_free(): - return self.fmats.field() + return self.get_fmatrix().field() else: raise ValueError("Method is only available for multiplicity free fusion rings.") @@ -859,14 +859,16 @@ def s_ijconj(self, elt_i, elt_j, base_coercion=True): sage: E62.s_ij(e8, e1).conjugate() == E62.s_ijconj(e8, e1) True sage: F41 = FusionRing("F4", 1) - sage: F41.fmats.find_orthogonal_solution(verbose=False) + sage: fmats = F41.get_fmatrix() + sage: fmats.find_orthogonal_solution(verbose=False) sage: b = F41.basis() sage: all(F41.s_ijconj(x, y) == F41._basecoer(F41.s_ij(x, y, base_coercion=False).conjugate()) for x in b for y in b) True sage: G22 = FusionRing("G2", 2) - sage: G22.fmats.find_orthogonal_solution(verbose=False) # long time (~11 s) + sage: fmats = G22.get_fmatrix() + sage: fmats.find_orthogonal_solution(verbose=False) # long time (~11 s) sage: b = G22.basis() # long time - sage: all(G22.s_ijconj(x, y) == G22.fmats.field()(G22.s_ij(x, y, base_coercion=False).conjugate()) for x in b for y in b) # long time + sage: all(G22.s_ijconj(x, y) == fmats.field()(G22.s_ij(x, y, base_coercion=False).conjugate()) for x in b for y in b) # long time True """ ret = self.s_ij(elt_i, elt_j, base_coercion=False).conjugate() @@ -1165,8 +1167,7 @@ def _get_trees(fr, top_row, root): comp_basis.extend(tuple([*top, *levels]) for levels in _get_trees(self, top_row, b)) return comp_basis - @lazy_attribute - def fmats(self): + def get_fmatrix(self, *args, **kwargs): r""" Construct an :class:`FMatrix` factory to solve the pentagon relations and organize the resulting F-symbols. @@ -1176,11 +1177,13 @@ def fmats(self): EXAMPLES:: sage: A15 = FusionRing("A1", 5) - sage: A15.fmats + sage: A15.get_fmatrix() F-Matrix factory for The Fusion Ring of Type A1 and level 5 with Integer Ring coefficients """ - from sage.algebras.fusion_rings.f_matrix import FMatrix - return FMatrix(self) + if not hasattr(self, 'fmats'): + from sage.algebras.fusion_rings.f_matrix import FMatrix + self.fmats = FMatrix(self, *args, **kwargs) + return self.fmats def _emap(self, mapper, input_args, worker_pool=None): r""" @@ -1210,12 +1213,14 @@ def _emap(self, mapper, input_args, worker_pool=None): sage: FR = FusionRing("A1", 4) sage: FR.fusion_labels(['idd', 'one', 'two', 'three', 'four'], inject_variables=True) - sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time + sage: fmats = FR.get_fmatrix() + sage: fmats.find_orthogonal_solution(verbose=False) # long time sage: len(FR._emap('sig_2k', (1, one, one, 5))) # long time 13 sage: FR = FusionRing("A1", 2) sage: FR.fusion_labels("a", inject_variables=True) - sage: FR.fmats.find_orthogonal_solution(verbose=False) + sage: fmats = FR.get_fmatrix() + sage: fmats.find_orthogonal_solution(verbose=False) sage: len(FR._emap('odd_one_out', (a1, a1, 7))) 16 """ @@ -1318,6 +1323,7 @@ def get_braid_generators(self, if n_strands < 3: raise ValueError("the number of strands must be an integer at least 3") # Construct associated FMatrix object and solve for F-symbols + self.get_fmatrix() if self.fmats._chkpt_status < 7: self.fmats.find_orthogonal_solution(checkpoint=checkpoint, save_results=save_results, From ddcfb48ffaf1cf0c5f79119bc9d9047bbbe2036c Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Sun, 23 Oct 2022 18:16:26 -0700 Subject: [PATCH 282/632] avoid importing FMatrix into global namespace... FMatrix accessible through FusionRing.get_fmatrix --- src/sage/algebras/fusion_rings/all.py | 1 - src/sage/algebras/fusion_rings/f_matrix.py | 154 +++++++++--------- src/sage/algebras/fusion_rings/fusion_ring.py | 6 +- 3 files changed, 84 insertions(+), 77 deletions(-) diff --git a/src/sage/algebras/fusion_rings/all.py b/src/sage/algebras/fusion_rings/all.py index e9125c55696..946524a7f43 100644 --- a/src/sage/algebras/fusion_rings/all.py +++ b/src/sage/algebras/fusion_rings/all.py @@ -15,6 +15,5 @@ from sage.misc.lazy_import import lazy_import lazy_import('sage.algebras.fusion_rings.fusion_ring', ['FusionRing']) -# lazy_import('sage.algebras.fusion_rings.f_matrix', ['FMatrix']) del lazy_import diff --git a/src/sage/algebras/fusion_rings/f_matrix.py b/src/sage/algebras/fusion_rings/f_matrix.py index f7ba23a588e..f63041abc63 100644 --- a/src/sage/algebras/fusion_rings/f_matrix.py +++ b/src/sage/algebras/fusion_rings/f_matrix.py @@ -152,7 +152,7 @@ class FMatrix(SageObject): sage: I = FusionRing("E8", 2, conjugate=True) sage: I.fusion_labels(["i0", "p", "s"], inject_variables=True) - sage: f = FMatrix(I, inject_variables=True); f + sage: f = I.get_fmatrix(inject_variables=True); f creating variables fx1..fx14 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 F-Matrix factory for The Fusion Ring of Type E8 and level 2 with Integer Ring coefficients @@ -258,12 +258,12 @@ class FMatrix(SageObject): :: - sage: f = FMatrix(FusionRing("B3", 2)) + sage: f = FusionRing("B3", 2).get_fmatrix() sage: f.find_orthogonal_solution(verbose=False, checkpoint=True) # not tested (~100 s) - sage: all(v in CyclotomicField(56) for v in f.get_fvars().values()) # not tested + sage: all(v in CyclotomicField(56) for v in f.get_fvars().values()) # not tested True - sage: f = FMatrix(FusionRing("G2", 2)) + sage: f = FusionRing("G2", 2).get_fmatrix() sage: f.find_orthogonal_solution(verbose=False) # long time (~11 s) sage: f.field() # long time Algebraic Field @@ -274,7 +274,7 @@ def __init__(self, fusion_ring, fusion_label="f", var_prefix='fx', inject_variab EXAMPLES:: - sage: f = FMatrix(FusionRing("B3", 2)) + sage: f = FusionRing("B3", 2).get_fmatrix() sage: TestSuite(f).run(skip="_test_pickling") """ self._FR = fusion_ring @@ -312,7 +312,7 @@ def _repr_(self): EXAMPLES:: - sage: FMatrix(FusionRing("B2", 1)) + sage: FusionRing("B2", 1).get_fmatrix() F-Matrix factory for The Fusion Ring of Type B2 and level 1 with Integer Ring coefficients """ return "F-Matrix factory for %s"%self._FR @@ -323,7 +323,7 @@ def clear_equations(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("E6", 1)) + sage: f = FusionRing("E6", 1).get_fmatrix() sage: f.get_defining_equations('hexagons', output=False) sage: len(f.ideal_basis) 6 @@ -339,7 +339,7 @@ def clear_vars(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("C4", 1)) + sage: f = FusionRing("C4", 1).get_fmatrix() sage: fvars = f.get_fvars() sage: some_key = sorted(fvars)[0] sage: fvars[some_key] @@ -363,7 +363,7 @@ def _reset_solver_state(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("G2", 1)) + sage: f = FusionRing("G2", 1).get_fmatrix() sage: f._reset_solver_state() sage: K = f.field() sage: len(f._nnz.nonzero_positions()) @@ -418,8 +418,9 @@ def fmat(self, a, b, c, d, x, y, data=True): EXAMPLES:: - sage: f=FMatrix(FusionRing("G2", 1, fusion_labels=("i0", "t"), inject_variables=True)) - sage: [f.fmat(t, t, t, t, x, y) for x in f._FR.basis() for y in f._FR.basis()] + sage: fr = FusionRing("G2", 1, fusion_labels=("i0", "t"), inject_variables=True) + sage: f = fr.get_fmatrix() + sage: [f.fmat(t, t, t, t, x, y) for x in fr.basis() for y in fr.basis()] [fx1, fx2, fx3, fx4] sage: f.find_cyclotomic_solution(output=True) Setting up hexagons and pentagons... @@ -479,7 +480,8 @@ def fmatrix(self, a, b, c, d): EXAMPLES:: - sage: f = FMatrix(FusionRing("A1", 2, fusion_labels="c", inject_variables=True)) + sage: fr = FusionRing("A1", 2, fusion_labels="c", inject_variables=True) + sage: f = fr.get_fmatrix(new=True) sage: f.fmatrix(c1, c1, c1, c1) [fx0 fx1] [fx2 fx3] @@ -526,7 +528,7 @@ def field(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("G2", 1)) + sage: f = FusionRing("G2", 1).get_fmatrix() sage: f.field() Cyclotomic Field of order 60 and degree 16 sage: f.find_orthogonal_solution(verbose=False) @@ -551,7 +553,7 @@ def FR(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("D3", 1)) + sage: f = FusionRing("D3", 1).get_fmatrix() sage: f.FR() The Fusion Ring of Type D3 and level 1 with Integer Ring coefficients """ @@ -567,7 +569,7 @@ def findcases(self, output=False): EXAMPLES:: - sage: f = FMatrix(FusionRing("G2", 1, fusion_labels=("i0", "t"))) + sage: f = FusionRing("G2", 1, fusion_labels=("i0", "t")).get_fmatrix() sage: f.findcases() 5 sage: f.findcases(output=True) @@ -614,7 +616,7 @@ def f_from(self, a, b, c, d): EXAMPLES:: sage: fr = FusionRing("A1", 3, fusion_labels="a", inject_variables=True) - sage: f = FMatrix(fr) + sage: f = fr.get_fmatrix() sage: f.fmatrix(a1, a1, a2, a2) [fx6 fx7] [fx8 fx9] @@ -639,7 +641,7 @@ def f_to(self, a, b, c, d): sage: b22 = FusionRing("B2", 2) sage: b22.fusion_labels("b", inject_variables=True) - sage: B=FMatrix(b22) + sage: B = b22.get_fmatrix() sage: B.fmatrix(b2, b4, b2, b4) [fx266 fx267 fx268] [fx269 fx270 fx271] @@ -668,7 +670,7 @@ def get_fvars(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("A2", 1), inject_variables=True) + sage: f = FusionRing("A2", 1).get_fmatrix(inject_variables=True) creating variables fx1..fx8 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 sage: f.get_fvars()[(f1, f1, f1, f0, f2, f2)] @@ -685,7 +687,7 @@ def get_poly_ring(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("B6", 1)) + sage: f = FusionRing("B6", 1).get_fmatrix() sage: f.get_poly_ring() Multivariate Polynomial Ring in fx0, ..., fx13 over Cyclotomic Field of order 96 and degree 32 @@ -710,13 +712,13 @@ def get_non_cyclotomic_roots(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("E6", 1)) + sage: f = FusionRing("E6", 1).get_fmatrix() sage: f.find_orthogonal_solution(verbose=False) sage: f.field() == f.FR().field() True sage: f.get_non_cyclotomic_roots() [] - sage: f = FMatrix(FusionRing("G2", 1)) + sage: f = FusionRing("G2", 1).get_fmatrix() sage: f.find_orthogonal_solution(verbose=False) sage: f.field() == f.FR().field() False @@ -744,7 +746,8 @@ def get_qqbar_embedding(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("G2", 1), fusion_label="g", inject_variables=True) + sage: fr = FusionRing("G2", 1) + sage: f = fr.get_fmatrix(fusion_label="g", inject_variables=True, new=True) creating variables fx1..fx5 Defining fx0, fx1, fx2, fx3, fx4 sage: f.find_orthogonal_solution() @@ -777,7 +780,7 @@ def get_coerce_map_from_fr_cyclotomic_field(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("G2", 1)) + sage: f = FusionRing("G2", 1).get_fmatrix() sage: f.find_orthogonal_solution(verbose=False) sage: f.FR().field() Cyclotomic Field of order 60 and degree 16 @@ -797,7 +800,7 @@ def get_coerce_map_from_fr_cyclotomic_field(self): :: - sage: f = FMatrix(FusionRing("A2", 1)) + sage: f = FusionRing("A2", 1).get_fmatrix() sage: f.find_orthogonal_solution(verbose=False) sage: phi = f.get_coerce_map_from_fr_cyclotomic_field() sage: f.field() @@ -828,7 +831,8 @@ def get_fvars_in_alg_field(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("G2", 1), fusion_label="g", inject_variables=True) + sage: fr = FusionRing("G2", 1) + sage: f = fr.get_fmatrix(fusion_label="g", inject_variables=True, new=True) creating variables fx1..fx5 Defining fx0, fx1, fx2, fx3, fx4 sage: f.find_orthogonal_solution(verbose=False) @@ -849,7 +853,7 @@ def get_radical_expression(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("G2", 1)) + sage: f = FusionRing("G2", 1).get_fmatrix() sage: f.FR().fusion_labels("g", inject_variables=True) sage: f.find_orthogonal_solution(verbose=False) sage: radical_fvars = f.get_radical_expression() # long time (~1.5s) @@ -869,7 +873,7 @@ def _get_known_vals(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("D4", 1)) + sage: f = FusionRing("D4", 1).get_fmatrix() sage: f._reset_solver_state() sage: len(f._get_known_vals()) == 0 True @@ -890,7 +894,7 @@ def _get_known_nonz(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("D5", 1)) # indirect doctest + sage: f = FusionRing("D5", 1).get_fmatrix() # indirect doctest sage: f._reset_solver_state() sage: f._nnz (100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, @@ -911,7 +915,7 @@ def largest_fmat_size(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("B3", 2)) + sage: f = FusionRing("B3", 2).get_fmatrix() sage: f.largest_fmat_size() 4 """ @@ -938,7 +942,7 @@ def get_fvars_by_size(self, n, indices=False): EXAMPLES:: - sage: f = FMatrix(FusionRing("A2", 2), inject_variables=True) + sage: f = FusionRing("A2", 2).get_fmatrix(inject_variables=True) creating variables fx1..fx287 Defining fx0, ..., fx286 sage: f.largest_fmat_size() @@ -997,14 +1001,14 @@ def save_fvars(self, filename): EXAMPLES:: - sage: f = FMatrix(FusionRing("A2", 1)) + sage: f = FusionRing("A2", 1).get_fmatrix(new=True) sage: f.find_orthogonal_solution(verbose=False) sage: fvars = f.get_fvars() sage: K = f.field() sage: filename = f.get_fr_str() + "_solver_results.pickle" sage: f.save_fvars(filename) sage: del f - sage: f2 = FMatrix(FusionRing("A2", 1)) + sage: f2 = FusionRing("A2", 1).get_fmatrix(new=True) sage: f2.load_fvars(filename) sage: fvars == f2.get_fvars() True @@ -1029,14 +1033,14 @@ def load_fvars(self, filename): EXAMPLES:: - sage: f = FMatrix(FusionRing("A2", 1)) + sage: f = FusionRing("A2", 1).get_fmatrix(new=True) sage: f.find_orthogonal_solution(verbose=False) sage: fvars = f.get_fvars() sage: K = f.field() sage: filename = f.get_fr_str() + "_solver_results.pickle" sage: f.save_fvars(filename) sage: del f - sage: f2 = FMatrix(FusionRing("A2", 1)) + sage: f2 = FusionRing("A2", 1).get_fmatrix(new=True) sage: f2.load_fvars(filename) sage: fvars == f2.get_fvars() True @@ -1065,7 +1069,7 @@ def get_fr_str(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("B3", 1)) + sage: f = FusionRing("B3", 1).get_fmatrix() sage: f.get_fr_str() 'B31' """ @@ -1078,7 +1082,7 @@ def _checkpoint(self, do_chkpt, status, verbose=True): EXAMPLES:: - sage: f = FMatrix(FusionRing("A1", 3)) + sage: f = FusionRing("A1", 3).get_fmatrix(new=True) sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) sage: f.get_defining_equations('hexagons', output=False) @@ -1093,7 +1097,7 @@ def _checkpoint(self, do_chkpt, status, verbose=True): sage: f._checkpoint(do_chkpt=True, status=2) Checkpoint 2 reached! sage: del f - sage: f = FMatrix(FusionRing("A1", 3)) + sage: f = FusionRing("A1", 3).get_fmatrix(new=True) sage: f.find_orthogonal_solution(warm_start="fmatrix_solver_checkpoint_A13.pickle") Computing F-symbols for The Fusion Ring of Type A1 and level 3 with Integer Ring coefficients with 71 variables... Set up 121 reduced pentagons... @@ -1111,7 +1115,7 @@ def _checkpoint(self, do_chkpt, status, verbose=True): sage: sum(f._solved) == f._poly_ring.ngens() True sage: os.remove("fmatrix_solver_checkpoint_A13.pickle") - sage: f = FMatrix(FusionRing("A1", 2)) + sage: f = FusionRing("A1", 2).get_fmatrix(new=True) sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) sage: f.get_defining_equations('hexagons', output=False) @@ -1129,7 +1133,7 @@ def _checkpoint(self, do_chkpt, status, verbose=True): sage: f._checkpoint(do_chkpt=True, status=4) Checkpoint 4 reached! sage: del f - sage: f = FMatrix(FusionRing("A1", 2)) + sage: f = FusionRing("A1", 2).get_fmatrix(new=True) sage: f.find_orthogonal_solution(warm_start="fmatrix_solver_checkpoint_A12.pickle") Computing F-symbols for The Fusion Ring of Type A1 and level 2 with Integer Ring coefficients with 14 variables... Partitioned 0 equations into 0 components of size: @@ -1157,7 +1161,7 @@ def _restore_state(self, filename): EXAMPLES:: - sage: f = FMatrix(FusionRing("A1", 2)) + sage: f = FusionRing("A1", 2).get_fmatrix(new=True) sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) sage: f.get_defining_equations('hexagons', output=False) @@ -1177,7 +1181,7 @@ def _restore_state(self, filename): sage: f._checkpoint(do_chkpt=True, status=2) Checkpoint 2 reached! sage: del f - sage: f = FMatrix(FusionRing("A1", 2)) + sage: f = FusionRing("A1", 2).get_fmatrix(new=True) sage: f._reset_solver_state() sage: f._restore_state("fmatrix_solver_checkpoint_A12.pickle") sage: for sextuple, fvar in fvars.items(): @@ -1195,10 +1199,10 @@ def _restore_state(self, filename): TESTS:: - sage: f = FMatrix(FusionRing("A1", 3)) + sage: f = FusionRing("A1", 3).get_fmatrix(new=True) sage: f.find_orthogonal_solution(save_results="test.pickle", verbose=False) # long time sage: del f - sage: f = FMatrix(FusionRing("A1", 3)) + sage: f = FusionRing("A1", 3).get_fmatrix(new=True) sage: f.find_orthogonal_solution(warm_start="test.pickle") # long time sage: f._chkpt_status == 7 # long time True @@ -1250,7 +1254,7 @@ def start_worker_pool(self, processes=None): EXAMPLES:: - sage: f = FMatrix(FusionRing("G2", 1)) + sage: f = FusionRing("G2", 1).get_fmatrix(new=True) sage: f.start_worker_pool() sage: he = f.get_defining_equations('hexagons') sage: sorted(he) @@ -1328,7 +1332,7 @@ def shutdown_worker_pool(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("A1", 3)) + sage: f = FusionRing("A1", 3).get_fmatrix(new=True) sage: f.start_worker_pool() sage: he = f.get_defining_equations('hexagons') sage: f.shutdown_worker_pool() @@ -1365,7 +1369,7 @@ def _map_triv_reduce(self, mapper, input_iter, worker_pool=None, chunksize=None, EXAMPLES:: - sage: f = FMatrix(FusionRing("A1", 2)) + sage: f = FusionRing("A1", 2).get_fmatrix() sage: f._reset_solver_state() sage: len(f._map_triv_reduce('get_reduced_hexagons', [(0, 1, False)])) 11 @@ -1424,7 +1428,7 @@ def get_orthogonality_constraints(self, output=True): EXAMPLES:: - sage: f = FMatrix(FusionRing("B4", 1)) + sage: f = FusionRing("B4", 1).get_fmatrix() sage: f.get_orthogonality_constraints() [fx0^2 - 1, fx1^2 - 1, @@ -1480,7 +1484,7 @@ def get_defining_equations(self, option, output=True): EXAMPLES:: - sage: f = FMatrix(FusionRing("B2", 1)) + sage: f = FusionRing("B2", 1).get_fmatrix() sage: sorted(f.get_defining_equations('hexagons')) [fx7 + 1, fx6 - 1, @@ -1528,10 +1532,10 @@ def _tup_to_fpoly(self, eq_tup): EXAMPLES:: - sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup - sage: f = FMatrix(FusionRing("C3", 1)) + sage: f = FusionRing("C3", 1).get_fmatrix() sage: f.start_worker_pool() sage: he = f.get_defining_equations('hexagons') + sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup sage: all(f._tup_to_fpoly(poly_to_tup(h)) for h in he) True sage: f.shutdown_worker_pool() @@ -1544,7 +1548,7 @@ def _update_reduction_params(self, eqns=None): EXAMPLES:: - sage: f = FMatrix(FusionRing("A1", 3)) + sage: f = FusionRing("A1", 3).get_fmatrix() sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) sage: f.start_worker_pool() @@ -1579,7 +1583,7 @@ def _triangular_elim(self, eqns=None, verbose=True): EXAMPLES:: - sage: f = FMatrix(FusionRing("D3", 1)) + sage: f = FusionRing("D3", 1).get_fmatrix() sage: f.get_defining_equations('hexagons', output=False) sage: f.get_orthogonality_constraints(output=False) sage: gb = f._par_graph_gb(verbose=False) @@ -1659,7 +1663,7 @@ def equations_graph(self, eqns=None): EXAMPLES:: - sage: f = FMatrix(FusionRing("A3", 1)) + sage: f = FusionRing("A3", 1).get_fmatrix() sage: f.get_poly_ring().ngens() 27 sage: he = f.get_defining_equations('hexagons') @@ -1703,7 +1707,7 @@ def _partition_eqns(self, eqns=None, verbose=True): EXAMPLES:: - sage: f = FMatrix(FusionRing("C2", 1)) + sage: f = FusionRing("C2", 1).get_fmatrix() sage: f.get_defining_equations('hexagons', output=False) sage: partition = f._partition_eqns() Partitioned 11 equations into 5 components of size: @@ -1751,7 +1755,7 @@ def _par_graph_gb(self, eqns=None, term_order="degrevlex", largest_comp=45, verb EXAMPLES:: - sage: f = FMatrix(FusionRing("F4", 1)) + sage: f = FusionRing("F4", 1).get_fmatrix() sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) sage: f.start_worker_pool() @@ -1796,7 +1800,7 @@ def _get_component_variety(self, var, eqns): EXAMPLES:: - sage: f = FMatrix(FusionRing("G2", 2)) + sage: f = FusionRing("G2", 2).get_fmatrix(new=True) sage: f.start_worker_pool() sage: f.get_defining_equations('hexagons', output=False) # long time sage: f.shutdown_worker_pool() @@ -1848,10 +1852,10 @@ def attempt_number_field_computation(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("F4", 2)) + sage: f = FusionRing("F4", 2).get_fmatrix() sage: f.attempt_number_field_computation() False - sage: f = FMatrix(FusionRing("G2", 1)) + sage: f = FusionRing("G2", 1).get_fmatrix() sage: f.attempt_number_field_computation() True @@ -1895,8 +1899,8 @@ def _get_explicit_solution(self, eqns=None, verbose=True): EXAMPLES:: - sage: f = FMatrix(FusionRing("A1", 3)) # indirect doctest - sage: f.find_orthogonal_solution() # long time + sage: f = FusionRing("A1", 3).get_fmatrix() # indirect doctest + sage: f.find_orthogonal_solution() # long time Computing F-symbols for The Fusion Ring of Type A1 and level 3 with Integer Ring coefficients with 71 variables... Set up 134 hex and orthogonality constraints... Partitioned 134 equations into 17 components of size: @@ -2052,7 +2056,7 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start EXAMPLES:: - sage: f = FMatrix(FusionRing("B5", 1), fusion_label="b", inject_variables=True) + sage: f = FusionRing("B5", 1).get_fmatrix(fusion_label="b", inject_variables=True) creating variables fx1..fx14 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 sage: f.find_orthogonal_solution() @@ -2105,9 +2109,6 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start # Loading from a pickle with solved F-symbols if self._chkpt_status > 5: return - # loads_shared_memory = False - # if use_mp: - # loads_shared_memory = self.start_worker_pool() if use_mp: self.start_worker_pool() if verbose: @@ -2191,10 +2192,10 @@ def _fix_gauge(self, algorithm=""): EXAMPLES:: - sage: f = FMatrix(FusionRing("A3", 1)) - sage: f._reset_solver_state() # long time + sage: f = FusionRing("A3", 1).get_fmatrix() + sage: f._reset_solver_state() # long time sage: f._var_to_sextuple = {f._poly_ring.gen(i): s for i, s in f._idx_to_sextuple.items()} # long time - sage: eqns = f.get_defining_equations("hexagons")+f.get_defining_equations("pentagons") # long time + sage: eqns = f.get_defining_equations("hexagons")+f.get_defining_equations("pentagons") # long time sage: f.ideal_basis = set(Ideal(eqns).groebner_basis()) # long time sage: _, _ = f._substitute_degree_one() # long time sage: f._fix_gauge() # long time @@ -2224,7 +2225,8 @@ def _substitute_degree_one(self, eqns=None): EXAMPLES:: - sage: f = FMatrix(FusionRing("D3", 1), inject_variables=True) + sage: fr = FusionRing("D3", 1) + sage: f = fr.get_fmatrix(inject_variables=True, new=True) creating variables fx1..fx27 Defining fx0, ..., fx26 sage: f._reset_solver_state() @@ -2264,7 +2266,8 @@ def _update_equations(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("D3", 1), inject_variables=True) + sage: fr = FusionRing("D3", 1) + sage: f = fr.get_fmatrix(inject_variables=True, new=True) creating variables fx1..fx27 Defining fx0, ..., fx26 sage: f._reset_solver_state() @@ -2301,7 +2304,8 @@ def find_cyclotomic_solution(self, equations=None, algorithm="", verbose=True, o EXAMPLES:: - sage: f = FMatrix(FusionRing("A2", 1, fusion_labels="a", inject_variables=True), inject_variables=True) + sage: fr = FusionRing("A2", 1, fusion_labels="a", inject_variables=True) + sage: f = fr.get_fmatrix(inject_variables=True) creating variables fx1..fx8 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 sage: f.find_cyclotomic_solution(output=True) @@ -2369,7 +2373,7 @@ def fmats_are_orthogonal(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("D4", 1)) + sage: f = FusionRing("D4", 1).get_fmatrix() sage: f.find_orthogonal_solution(verbose=False) sage: f.fmats_are_orthogonal() True @@ -2386,7 +2390,7 @@ def fvars_are_real(self): EXAMPLES:: - sage: f = FMatrix(FusionRing("A1", 3)) + sage: f = FusionRing("A1", 3).get_fmatrix() sage: f.find_orthogonal_solution(verbose=False) # long time sage: f.fvars_are_real() # not tested (cypari issue in doctesting framework) True @@ -2415,8 +2419,8 @@ def certify_pentagons(self, use_mp=True, verbose=False): EXAMPLES:: - sage: f = FMatrix(FusionRing("C3", 1)) - sage: f.find_orthogonal_solution() # long time + sage: f = FusionRing("C3", 1).get_fmatrix() + sage: f.find_orthogonal_solution() # long time Computing F-symbols for The Fusion Ring of Type C3 and level 1 with Integer Ring coefficients with 71 variables... Set up 134 hex and orthogonality constraints... Partitioned 134 equations into 17 components of size: @@ -2434,7 +2438,7 @@ def certify_pentagons(self, use_mp=True, verbose=False): Partitioned 6 equations into 6 components of size: [1, 1, 1, 1, 1, 1] Computing appropriate NumberField... - sage: f.certify_pentagons() is None # not tested (long time ~1.5s, cypari issue in doctesting framework) + sage: f.certify_pentagons() is None # not tested (long time ~1.5s, cypari issue in doctesting framework) True """ fvars_copy = deepcopy(self._fvars) diff --git a/src/sage/algebras/fusion_rings/fusion_ring.py b/src/sage/algebras/fusion_rings/fusion_ring.py index becb5f4490e..f392e8b42c2 100644 --- a/src/sage/algebras/fusion_rings/fusion_ring.py +++ b/src/sage/algebras/fusion_rings/fusion_ring.py @@ -1180,7 +1180,11 @@ def get_fmatrix(self, *args, **kwargs): sage: A15.get_fmatrix() F-Matrix factory for The Fusion Ring of Type A1 and level 5 with Integer Ring coefficients """ - if not hasattr(self, 'fmats'): + # Initialize fresh FMatrix object. Useful if you need to reset + # FMatrix properties and there are various FusionRing objects (unique) + # associated to same level and algebra. + if not hasattr(self, 'fmats') or kwargs.get('new', False): + kwargs.pop('new', None) from sage.algebras.fusion_rings.f_matrix import FMatrix self.fmats = FMatrix(self, *args, **kwargs) return self.fmats From ec342042b933ed457cf22246d77cdd7a10f724b2 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Mon, 24 Oct 2022 09:38:53 +0200 Subject: [PATCH 283/632] docstring fixes for Payley constructions --- src/sage/combinat/matrices/hadamard_matrix.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 839ba63fceb..0344c4ade32 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -93,8 +93,8 @@ def hadamard_matrix_paleyI(n, normalize=True): r""" Implement the Paley type I construction. - The Paley type I case corresponds to the case `p \cong 3 \mod{4}` for a - prime `p` (see [Hora]_). + The Paley type I case corresponds to the case `p=n-1 \cong 3 \mod{4}` for a + prime power `p` (see [Hora]_). INPUT: @@ -160,8 +160,8 @@ def hadamard_matrix_paleyII(n): r""" Implement the Paley type II construction. - The Paley type II case corresponds to the case `p \cong 1 \mod{4}` for a - prime `p` (see [Hora]_). + The Paley type II case corresponds to the case `p=n/2-1 \cong 1 \mod{4}` for a + prime power `p` (see [Hora]_). EXAMPLES:: From aa6105d9bc4d1e51857110cd8147878e1800caf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 24 Oct 2022 10:18:38 +0200 Subject: [PATCH 284/632] minor tweaks in the doc --- src/sage/combinat/triangles_FHM.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/triangles_FHM.py b/src/sage/combinat/triangles_FHM.py index 2a9b08b0791..ad9fa36891c 100644 --- a/src/sage/combinat/triangles_FHM.py +++ b/src/sage/combinat/triangles_FHM.py @@ -6,6 +6,9 @@ possibly with other parameters. The conversion methods amount to specific invertible rational change-of-variables involving `x` and `y`. +These polynomial are called triangles because their supports, the sets +of exponents where their coefficients can be non-zero, have a triangular shape. + The M-triangle class is motivated by the generating series of Möbius numbers for graded posets. A typical example is:: @@ -697,8 +700,8 @@ def h(self): .. MATH:: - H(x,y) = (1+x)^d \sum_{0\leq i; 0\leq j \leq d-2i} gamma_{i,j} - (\frac{x}{(1+x)^2})^i (\frac{1+xy}{1+x})^j + H(x,y) = (1+x)^d \sum_{0\leq i; 0\leq j \leq d-2i} \gamma_{i,j} + \left(\frac{x}{(1+x)^2}\right)^i \left(\frac{1+xy}{1+x}\right)^j EXAMPLES:: From f5be12f4e5c0ee462c0036c674d4e87fb2f52e89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 24 Oct 2022 10:30:27 +0200 Subject: [PATCH 285/632] provide "an_element" for Compositions --- src/sage/combinat/composition.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/sage/combinat/composition.py b/src/sage/combinat/composition.py index ab51d83ecfd..5cb2cf73281 100644 --- a/src/sage/combinat/composition.py +++ b/src/sage/combinat/composition.py @@ -1924,6 +1924,18 @@ def zero(self): """ return Composition([]) + def _an_element_(self): + """ + Return an element of ``self``. + + EXAMPLES:: + + sage: C = Compositions() + sage: C.an_element() + [] + """ + return self.zero() + def __iter__(self): """ Iterate over all compositions. From 9acf7643c3999d616ae100df9e4aa86b9cc1cb23 Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Mon, 24 Oct 2022 15:35:17 +0200 Subject: [PATCH 286/632] =?UTF-8?q?fix=20parent=20of=200=C3=970=20companio?= =?UTF-8?q?n=20matrix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sage/matrix/special.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/sage/matrix/special.py b/src/sage/matrix/special.py index 619d5d7c892..5d3bb13445b 100644 --- a/src/sage/matrix/special.py +++ b/src/sage/matrix/special.py @@ -67,6 +67,7 @@ import sage.matrix.matrix_space as matrix_space from sage.modules.free_module_element import vector from sage.structure.element import is_Matrix +from sage.structure.sequence import Sequence from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ from sage.rings.integer import Integer @@ -2332,6 +2333,9 @@ def companion_matrix(poly, format='right'): ... ValueError: polynomial cannot be specified by an empty list + sage: companion_matrix([QQ.one()]).parent() + Full MatrixSpace of 0 by 0 dense matrices over Rational Field + AUTHOR: - Rob Beezer (2011-05-19) @@ -2340,7 +2344,7 @@ def companion_matrix(poly, format='right'): if format not in ['right', 'left', 'top', 'bottom']: raise ValueError("format must be 'right', 'left', 'top' or 'bottom', not {0}".format(format)) try: - poly = list(poly) + poly = Sequence(poly) except TypeError: raise TypeError('input must be a polynomial (not a symbolic expression, see docstring), or other iterable, not {0}'.format(poly)) n = len(poly) - 1 @@ -2348,31 +2352,30 @@ def companion_matrix(poly, format='right'): raise ValueError('polynomial cannot be specified by an empty list') if not poly[n] == 1: raise ValueError('polynomial (or the polynomial implied by coefficients) must be monic, not a leading coefficient of {0}'.format(poly[n])) - entries = [0] * (n * n) + try: + M = sage.matrix.constructor.matrix(poly.universe(), n, n) + except TypeError: + raise TypeError("unable to find common ring for coefficients from polynomial") # 1's below diagonal, or above diagonal if format in ['right', 'top']: for i in range(n - 1): - entries[(i+1)*n + i] = 1 + M[i+1, i] = 1 else: for i in range(n-1): - entries[i*n + i+1] = 1 + M[i, i+1] = 1 # right side, left side (reversed), bottom edge, top edge (reversed) if format == 'right': for i in range(n): - entries[i*n + n-1] = -poly[i] + M[i, n-1] = -poly[i] elif format == 'left': for i in range(n): - entries[(n-1-i)*n + 0] = -poly[i] + M[n-1-i, 0] = -poly[i] elif format == 'bottom': for i in range(n): - entries[(n-1)*n + i] = -poly[i] + M[n-1, i] = -poly[i] elif format == 'top': for i in range(n): - entries[0*n + n-1-i] = -poly[i] - try: - M = sage.matrix.constructor.matrix(n, n, entries) - except TypeError: - raise TypeError("unable to find common ring for coefficients from polynomial") + M[0, n-1-i] = -poly[i] return M From 79eccefc7a488e2f42cfbd2c5676c6b5ad52f6cb Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 24 Oct 2022 14:39:54 -0700 Subject: [PATCH 287/632] build/make/Makefile.in: Only warn if meson_python testsuite fails --- build/make/Makefile.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/make/Makefile.in b/build/make/Makefile.in index 9148f3d7577..e971def2416 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -443,8 +443,9 @@ $(foreach pkgname, $(NORMAL_PACKAGES) $(SCRIPT_PACKAGES),\ # # Since Python's self-tests seem to fail on all platforms, we disable # its test suite by default. +# meson_python 0.10.0 fails on some platforms, so we reduce it to warnings. # However, if SAGE_CHECK=warn, we do not do that. -SAGE_CHECK_PACKAGES_DEFAULT_yes := !python3 +SAGE_CHECK_PACKAGES_DEFAULT_yes := !python3,?meson_python SAGE_CHECK_PACKAGES_DEFAULT_warn := SAGE_CHECK_PACKAGES_DEFAULT_no := comma := , From 4645a0d0c10f9f1599aa01362593195af6d27a16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Bissey?= Date: Tue, 25 Oct 2022 14:27:01 +1300 Subject: [PATCH 288/632] Removing faulty plot --- src/sage/plot/plot.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/sage/plot/plot.py b/src/sage/plot/plot.py index 5301a05c3dc..cef7876cddb 100644 --- a/src/sage/plot/plot.py +++ b/src/sage/plot/plot.py @@ -1744,11 +1744,6 @@ def b(n): return lambda x: bessel_J(n, x) + 0.5*(n-1) sage: plot(2*x+1,(x,0,5),ticks=[[0,1,e,pi,sqrt(20)],2],tick_formatter="latex") # not tested (broken with matplotlib 3.6) Graphics object consisting of 1 graphics primitive - .. PLOT:: - - g = plot(2*x+1,(x,0,5),ticks=[[0,1,e,pi,sqrt(20)],2],tick_formatter="latex") - sphinx_plot(g) - This is particularly useful when setting custom ticks in multiples of `pi`. :: From 8139c367dba1e9ac88703289feb4910e47b9dc7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 25 Oct 2022 08:18:40 +0200 Subject: [PATCH 289/632] rephrase to "must use" --- src/sage/combinat/composition.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/composition.py b/src/sage/combinat/composition.py index 5cb2cf73281..97b6bf58a2c 100644 --- a/src/sage/combinat/composition.py +++ b/src/sage/combinat/composition.py @@ -1555,7 +1555,7 @@ class Compositions(UniqueRepresentation, Parent): ... ValueError: setting min_part=0 is not allowed for Compositions - Preferably consider using ``IntegerVectors`` instead:: + Instead you must use ``IntegerVectors``:: sage: list(IntegerVectors(2, 3)) [[2, 0, 0], [1, 1, 0], [1, 0, 1], [0, 2, 0], [0, 1, 1], [0, 0, 2]] From af2f7625c5f5a79020f3039bc546a79d42049f47 Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Tue, 25 Oct 2022 21:29:03 -0600 Subject: [PATCH 290/632] improvements suggested by reviewer --- src/sage/combinat/permutation.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/permutation.py b/src/sage/combinat/permutation.py index 0fa32de70ee..4a43a727b3f 100644 --- a/src/sage/combinat/permutation.py +++ b/src/sage/combinat/permutation.py @@ -7498,17 +7498,18 @@ def from_cycles(n, cycles, parent=None): if parent is None: parent = Permutations(n) - # None represents a value of the permutation that has not yet been specified + # None represents a value of the permutation that has not been specified yet p = n * [None] for cycle in cycles: - for i in range(len(cycle)): + cycle_length = len(cycle) + for i in range(cycle_length): # two consecutive terms in the cycle represent k and p(k) - k = cycle[i] - pk = cycle[(i + 1) % len(cycle)] + k = ZZ(cycle[i]) + pk = ZZ(cycle[(i + 1) % cycle_length]) # check that the values are valid - if (int(k) < 1) or (int(pk) < 1): + if (k < 1) or (pk < 1): raise ValueError("all elements should be strictly positive " f"integers, but I found {min(k, pk)}") if (k > n) or (pk > n): @@ -7519,10 +7520,10 @@ def from_cycles(n, cycles, parent=None): " in the input") p[k - 1] = pk - # unspecified values are fixed points of the permutation + # values that are not in any cycle are fixed points of the permutation for i in range(n): if p[i] is None: - p[i] = i + 1 + p[i] = ZZ(i + 1) return parent(p, check_input=False) def from_lehmer_code(lehmer, parent=None): From 0300962bf65a087a765ac6c897ed0811a314d15b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 27 Oct 2022 08:47:35 +0200 Subject: [PATCH 291/632] minor fixes in doc in 2 pyx files --- src/sage/rings/finite_rings/finite_field_base.pyx | 2 +- src/sage/rings/tate_algebra_ideal.pyx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/finite_rings/finite_field_base.pyx b/src/sage/rings/finite_rings/finite_field_base.pyx index c91126649f1..1ab1ac08eaa 100644 --- a/src/sage/rings/finite_rings/finite_field_base.pyx +++ b/src/sage/rings/finite_rings/finite_field_base.pyx @@ -457,7 +457,7 @@ cdef class FiniteField(Field): ``self.0`` to the unique element of ``im_gens`` is a valid field homomorphism. Otherwise, return ``False``. - EXAMPLES:: + EXAMPLES: Between prime fields:: diff --git a/src/sage/rings/tate_algebra_ideal.pyx b/src/sage/rings/tate_algebra_ideal.pyx index 8826f67d000..e0b40cd1e7a 100644 --- a/src/sage/rings/tate_algebra_ideal.pyx +++ b/src/sage/rings/tate_algebra_ideal.pyx @@ -292,7 +292,7 @@ class TateAlgebraIdeal(Ideal_generic): All ideals are saturated when `\pi` is invertible. - EXAMPLES:: + EXAMPLES: Over classical Tate algebras (where `\pi` is invertible), this method always returns ``True``:: @@ -350,7 +350,7 @@ class TateAlgebraIdeal(Ideal_generic): When `\pi` is invertible in `A`, all ideals are saturated. - EXAMPLES:: + EXAMPLES: Over classical Tate algebras (where `\pi` is invertible), this method always returns the same ideal:: From 01e18432b8c98c57b6c82b11de97bc3ba099b39d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 27 Oct 2022 08:54:49 +0200 Subject: [PATCH 292/632] more fixes in doc --- src/sage/geometry/polyhedron/base5.py | 4 ++-- src/sage/graphs/distances_all_pairs.pyx | 2 +- src/sage/graphs/generators/world_map.py | 2 +- src/sage/matrix/matrix_sparse.pyx | 2 +- src/sage/rings/polynomial/ore_polynomial_element.pyx | 2 +- src/sage/rings/tate_algebra_element.pyx | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/geometry/polyhedron/base5.py b/src/sage/geometry/polyhedron/base5.py index 7a77f9685a3..04d1fa0314b 100644 --- a/src/sage/geometry/polyhedron/base5.py +++ b/src/sage/geometry/polyhedron/base5.py @@ -190,9 +190,9 @@ def polar(self, in_affine_span=False): sage: (P*point).polar(in_affine_span=True) == P.polar()*point True - TESTS:: + TESTS: - Check that :trac:`25081` is fixed:: + Check that :trac:`25081` is fixed:: sage: C = polytopes.hypercube(4,backend='cdd') sage: C.polar().backend() diff --git a/src/sage/graphs/distances_all_pairs.pyx b/src/sage/graphs/distances_all_pairs.pyx index 7da899e88b8..87e83b4f5ce 100644 --- a/src/sage/graphs/distances_all_pairs.pyx +++ b/src/sage/graphs/distances_all_pairs.pyx @@ -1441,7 +1441,7 @@ cdef uint32_t diameter_DiFUB(short_digraph sd, - ``source`` -- starting node of the first BFS - TESTS:: + TESTS: The diameter of a weakly connected digraph is infinity :: diff --git a/src/sage/graphs/generators/world_map.py b/src/sage/graphs/generators/world_map.py index 2b0e30686cc..42d0a95372f 100644 --- a/src/sage/graphs/generators/world_map.py +++ b/src/sage/graphs/generators/world_map.py @@ -311,7 +311,7 @@ def WorldMap(): sage: sorted(g.connected_component_containing_vertex('Ireland')) ['Ireland', 'United Kingdom'] - TESTS:: + TESTS: :trac:`24488`:: diff --git a/src/sage/matrix/matrix_sparse.pyx b/src/sage/matrix/matrix_sparse.pyx index cd5daef1903..dcd9c2e1550 100644 --- a/src/sage/matrix/matrix_sparse.pyx +++ b/src/sage/matrix/matrix_sparse.pyx @@ -1005,7 +1005,7 @@ cdef class Matrix_sparse(matrix.Matrix): [ 0 1 2 3] [ 4 5 6 7] - TESTS:: + TESTS: One can stack matrices over different rings (:trac:`16399`). :: diff --git a/src/sage/rings/polynomial/ore_polynomial_element.pyx b/src/sage/rings/polynomial/ore_polynomial_element.pyx index 858ec02cbfa..a5cade9325f 100644 --- a/src/sage/rings/polynomial/ore_polynomial_element.pyx +++ b/src/sage/rings/polynomial/ore_polynomial_element.pyx @@ -2735,7 +2735,7 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): sage: P * Q == Q * P False - TESTS:: + TESTS: We check associativity and distributivity:: diff --git a/src/sage/rings/tate_algebra_element.pyx b/src/sage/rings/tate_algebra_element.pyx index 3f09e3722d3..02b8305cd17 100644 --- a/src/sage/rings/tate_algebra_element.pyx +++ b/src/sage/rings/tate_algebra_element.pyx @@ -3502,7 +3502,7 @@ cdef class TateAlgebraElement(CommutativeAlgebraElement): - ``other`` -- a Tate series - TESTS:: + TESTS: We check that the S-polynomial of two monomials vanishes:: From 5d2135ccd84c3106c752a54a6f1dc6554e6d8eb4 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Thu, 27 Oct 2022 15:22:15 -0400 Subject: [PATCH 293/632] initial fix --- src/sage/modular/quasimodform/element.py | 24 ++++++++++++++---------- src/sage/modular/quasimodform/ring.py | 19 +++++++++++-------- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/sage/modular/quasimodform/element.py b/src/sage/modular/quasimodform/element.py index 340d8b0320e..4173e969b79 100644 --- a/src/sage/modular/quasimodform/element.py +++ b/src/sage/modular/quasimodform/element.py @@ -344,16 +344,16 @@ def is_modular_form(self): """ return self._polynomial.degree() <= 0 and self._polynomial[0].is_modular_form() - def polynomial(self, names='E2, E4, E6'): + #def polynomial(self, names='E2, E4, E6'): + def polynomial(self, names='g'): r""" - Return a multivariate polynomial `P(E_2, E_4, E_6)` corresponding to the - given form where `E_2`, `E_4` and `E_6` are the generators of the - quasimodular form ring given by the following method: + Return a multivariate polynomial `P(g_0,..., g_n)` where `g_i` + correspond to the `i`-th generator of the ring given by the method: :meth:`~sage.modular.quasimodform.ring.QuasiModularForms.gens`. INPUT: - - ``names`` (str, default: ``'E2, E4, E6'``) -- a list or tuple of names + - ``names`` (str, default: ``'g'``) -- a list or tuple of names (strings), or a comma separated string. Correspond to the names of the variables; @@ -362,15 +362,19 @@ def polynomial(self, names='E2, E4, E6'): EXAMPLES:: sage: QM = QuasiModularForms(1) - sage: (QM.0 + QM.1).polynomial() + sage: (QM.0 + QM.1).polynomial('E2, E4, E6') E4 + E2 - sage: (1/2 + QM.0 + 2*QM.1^2 + QM.0*QM.2).polynomial() + sage: (1/2 + QM.0 + 2*QM.1^2 + QM.0*QM.2).polynomial('E2, E4, E6') E2*E6 + 2*E4^2 + E2 + 1/2 """ P = self.parent().polynomial_ring(names) - g0, g1 = self.parent().modular_forms_subring().polynomial_ring(names='x').gens() - E2, E4, E6 = P.gens() - return sum(f.to_polynomial().subs({g0:E4, g1:E6}) * E2 ** exp for exp, f in enumerate(self._polynomial.coefficients(sparse=False))) + P_gens = P.gens()[1:] + E2 = P_gens[0] + modform_poly_gens = self.parent().modular_forms_subring().polynomial_ring(names='x').gens() + subs_dictionnary = {} + for idx, g in enumerate(modform_poly_gens): + subs_dictionnary[g] = P_gens[idx] + return sum(f.to_polynomial().subs(subs_dictionnary) * E2 ** exp for exp, f in enumerate(self._polynomial.coefficients(sparse=False))) to_polynomial = polynomial # alias diff --git a/src/sage/modular/quasimodform/ring.py b/src/sage/modular/quasimodform/ring.py index 8f0d8baed62..f16130f263a 100644 --- a/src/sage/modular/quasimodform/ring.py +++ b/src/sage/modular/quasimodform/ring.py @@ -526,18 +526,18 @@ def polygen(self): """ return self.__polynomial_subring.gen() - def polynomial_ring(self, names='E2, E4, E6'): + def polynomial_ring(self, names='g0'): r""" Return a multivariate polynomial ring isomorphic to the given graded quasimodular forms ring. In the case of the full modular group, this - ring is `R[E_2, E_4, E_6]` where `E_2`, `E_4` and `E_6` have degrees 2, + ring is `R[g_0, g_1, g_2]` where `g_0`, `g_4` and `g_6` have degrees 2, 4 and 6 respectively. INPUT: - - ``names`` (str, default: ``'E2, E4, E6'``) -- a list or tuple of names + - ``names`` (str, default: ``'g'``) -- a list or tuple of names (strings), or a comma separated string. Correspond to the names of the variables. @@ -560,19 +560,22 @@ def polynomial_ring(self, names='E2, E4, E6'): ... ValueError: the number of variables (4) of the given polynomial cannot exceed the number of generators (3) of the quasimodular forms ring """ - return PolynomialRing(self.base_ring(), 3, names, order=TermOrder('wdeglex', [ZZ(2), ZZ(4), ZZ(6)])) + weights = [2] + for f in self.__modular_forms_subring.gen_forms(): + weights.append(f.weight()) + return PolynomialRing(self.base_ring(), len(weights), names, order=TermOrder('wdeglex', weights)) def from_polynomial(self, polynomial): r""" - Convert the given polynomial `P(X, Y, Z)` to the graded quasiform - `P(E_2, E_4, E_6)` where `E_2`, `E_4` and `E_6` are the generators given + Convert the given polynomial `P(x,\ldots, y)` to the graded quasiform + `P(g_0, \ldots, g_n)` where the `g_i` are the generators given by :meth:`~sage.modular.quasimodform.ring.QuasiModularForms.gens`. INPUT: - - ``plynomial`` -- A multivariate polynomial + - ``polynomial`` -- A multivariate polynomial - OUTPUT: the graded quasimodular forms `P(E_2, E_4, E_6)` + OUTPUT: the graded quasimodular forms `P(g_0, \ldots, g_n)` EXAMPLES:: From c0d61cf3ba8e3d1f3c1eb0c50bb24e66a07debe1 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Thu, 27 Oct 2022 15:53:37 -0400 Subject: [PATCH 294/632] src/sage/modular/quasimodform/element.py: fix polynomial method --- src/sage/modular/quasimodform/element.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/modular/quasimodform/element.py b/src/sage/modular/quasimodform/element.py index 4173e969b79..31f93ee4fc4 100644 --- a/src/sage/modular/quasimodform/element.py +++ b/src/sage/modular/quasimodform/element.py @@ -368,12 +368,13 @@ def polynomial(self, names='g'): E2*E6 + 2*E4^2 + E2 + 1/2 """ P = self.parent().polynomial_ring(names) - P_gens = P.gens()[1:] - E2 = P_gens[0] + poly_gens = P.gens() + E2 = poly_gens[0] + poly_gens = poly_gens[1:] modform_poly_gens = self.parent().modular_forms_subring().polynomial_ring(names='x').gens() subs_dictionnary = {} for idx, g in enumerate(modform_poly_gens): - subs_dictionnary[g] = P_gens[idx] + subs_dictionnary[g] = poly_gens[idx] return sum(f.to_polynomial().subs(subs_dictionnary) * E2 ** exp for exp, f in enumerate(self._polynomial.coefficients(sparse=False))) to_polynomial = polynomial # alias From ffd27a4aa44e9b02bc77755ab71a33dab960481d Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Thu, 27 Oct 2022 16:09:02 -0400 Subject: [PATCH 295/632] src/sage/modular/quasimodform/element.py: remove comment line --- src/sage/modular/quasimodform/element.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/modular/quasimodform/element.py b/src/sage/modular/quasimodform/element.py index 31f93ee4fc4..8b796b374bf 100644 --- a/src/sage/modular/quasimodform/element.py +++ b/src/sage/modular/quasimodform/element.py @@ -344,7 +344,6 @@ def is_modular_form(self): """ return self._polynomial.degree() <= 0 and self._polynomial[0].is_modular_form() - #def polynomial(self, names='E2, E4, E6'): def polynomial(self, names='g'): r""" Return a multivariate polynomial `P(g_0,..., g_n)` where `g_i` From d55eb7e93d3f3b95aec701bddfa20009b8b5265e Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Thu, 27 Oct 2022 18:11:09 -0400 Subject: [PATCH 296/632] add various doctests --- src/sage/modular/quasimodform/element.py | 67 ++++++++++++++- src/sage/modular/quasimodform/ring.py | 101 +++++++++++++++++++++-- 2 files changed, 155 insertions(+), 13 deletions(-) diff --git a/src/sage/modular/quasimodform/element.py b/src/sage/modular/quasimodform/element.py index 8b796b374bf..8d16834e8a9 100644 --- a/src/sage/modular/quasimodform/element.py +++ b/src/sage/modular/quasimodform/element.py @@ -16,6 +16,8 @@ # https://www.gnu.org/licenses/ # **************************************************************************** +from sage.modular.arithgroup.congroup_sl2z import is_SL2Z +from sage.modular.modform.constructor import EisensteinForms from sage.modular.modform.eis_series import eisenstein_series_qexp from sage.modular.modform.element import GradedModularFormElement @@ -273,6 +275,9 @@ def is_zero(self): False sage: (QM.0).is_zero() False + sage: QM = QuasiModularForms(Gamma0(2)) + sage: QM(0).is_zero() + True """ return not self @@ -290,6 +295,9 @@ def is_one(self): True sage: (QM.0).is_one() False + sage: QM = QuasiModularForms(Gamma0(2)) + sage: QM(1).is_one() + True """ return self._polynomial.is_one() @@ -317,6 +325,13 @@ def is_graded_modular_form(self): True sage: QM.zero().is_graded_modular_form() True + sage: QM = QuasiModularForms(Gamma0(6)) + sage: (QM.0).is_graded_modular_form() + False + sage: (QM.0 + QM.1*QM.2 + QM.3).is_graded_modular_form() + False + sage: (QM.1*QM.2 + QM.3).is_graded_modular_form() + True .. NOTE:: @@ -341,12 +356,17 @@ def is_modular_form(self): False sage: QM.zero().is_modular_form() True + sage: QM = QuasiModularForms(Gamma0(4)) + sage: (QM.0).is_modular_form() + False + sage: (QM.1).is_modular_form() + True """ return self._polynomial.degree() <= 0 and self._polynomial[0].is_modular_form() def polynomial(self, names='g'): r""" - Return a multivariate polynomial `P(g_0,..., g_n)` where `g_i` + Return a multivariate polynomial `P(g_0,\ldots, g_n)` where `g_i` correspond to the `i`-th generator of the ring given by the method: :meth:`~sage.modular.quasimodform.ring.QuasiModularForms.gens`. @@ -365,6 +385,15 @@ def polynomial(self, names='g'): E4 + E2 sage: (1/2 + QM.0 + 2*QM.1^2 + QM.0*QM.2).polynomial('E2, E4, E6') E2*E6 + 2*E4^2 + E2 + 1/2 + + Check that :trac:`34569` is fixed:: + + sage: QM = QuasiModularForms(Gamma1(3)) + sage: QM.ngens() + 5 + sage: (QM.0 + QM.1 + QM.2*QM.1 + QM.3*QM.4).polynomial() + g3*g4 + g1*g2 + g0 + g1 + """ P = self.parent().polynomial_ring(names) poly_gens = P.gens() @@ -394,6 +423,9 @@ def weights_list(self): [6] sage: QM(1/2).weights_list() [0] + sage: QM = QuasiModularForms(Gamma1(3)) + sage: (QM.0 + QM.1 + QM.2*QM.1 + QM.3*QM.4).weights_list() + [2, 5, 7] """ return sorted(list(self.to_polynomial().homogeneous_components())) @@ -416,6 +448,16 @@ def is_homogeneous(self): True sage: (1 + QM.0).is_homogeneous() False + sage: QM = QuasiModularForms(Gamma0(4)) + sage: (QM.0).is_homogeneous() + True + sage: (QM.0 + QM.1).is_homogeneous() + True + sage: (QM.0 + QM.1 + QM.2).is_homogeneous() + True + sage: (QM.0 + QM.1^3).is_homogeneous() + False + """ return len(self.weights_list()) == 1 @@ -456,10 +498,16 @@ def homogeneous_components(self): {2: 1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6)} sage: (QM.0 + QM.1 + QM.2).homogeneous_components() {2: 1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6), - 4: 1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + 30240*q^5 + O(q^6), - 6: 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6)} + 4: 1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + 30240*q^5 + O(q^6), + 6: 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6)} sage: (1 + QM.0).homogeneous_components() {0: 1, 2: 1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6)} + sage: QM = QuasiModularForms(Gamma0(5)) + sage: F = QM.0 + QM.1 * QM.0 + QM.3^2*QM.0 + sage: F.homogeneous_components() + {2: 1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6), + 4: 1 - 18*q - 198*q^2 - 936*q^3 - 2574*q^4 - 5610*q^5 + O(q^6), + 10: q^2 - 24*q^3 - 52*q^4 - 520*q^5 + O(q^6)} """ QM = self.parent() poly_self = self.to_polynomial() @@ -497,12 +545,23 @@ def serre_derivative(self): 6 sage: F.serre_derivative().weight() 8 + + Check that :trac:`34569` is fixed:: + + sage: QM = QuasiModularForms(Gamma1(3)) + sage: E2 = QM.weight_2_eisenstein_series() + sage: E2.serre_derivative() + -1/6 - 16*q - 216*q^2 - 832*q^3 - 2248*q^4 - 4320*q^5 + O(q^6) + sage: F = QM.0 + QM.1*QM.2 """ # initial variables: QM = self.parent() R = QM.base_ring() E2 = QM.gen(0) - E4 = QM.gen(1) + if is_SL2Z(QM.group()): + E4 = QM.gen(1) + else: + E4 = QM(EisensteinForms(group=1, weight=4, base_ring=R).gen(0)) # compute the derivative of E2: q*dE2/dq E2deriv = R(12).inverse_of_unit() * (E2 ** 2 - E4) diff --git a/src/sage/modular/quasimodform/ring.py b/src/sage/modular/quasimodform/ring.py index f16130f263a..118794d6546 100644 --- a/src/sage/modular/quasimodform/ring.py +++ b/src/sage/modular/quasimodform/ring.py @@ -44,8 +44,8 @@ Ring of Quasimodular Forms for Modular Group SL(2,Z) over Rational Field sage: QM.gens() [1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6), - 1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + 30240*q^5 + O(q^6), - 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6)] + 1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + 30240*q^5 + O(q^6), + 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6)] sage: E2 = QM.0; E4 = QM.1; E6 = QM.2 sage: E2 * E4 + E6 2 - 288*q - 20304*q^2 - 185472*q^3 - 855216*q^4 - 2697408*q^5 + O(q^6) @@ -74,6 +74,71 @@ sage: F == Delta + E4 * E2 + (Delta + E4) * E2^2 True +One may also create rings of quasimodular forms for certain congruence subgroups:: + + sage: QM = QuasiModularForms(Gamma0(5)); QM + Ring of Quasimodular Forms for Congruence Subgroup Gamma0(5) over Rational Field + sage: QM.ngens() + 4 + +The first generators is the weight 2 Eisenstein serie:: + + sage: E2 = QM.0; E2 + 1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6) + +The other generators corresponds to the generators given by the method +:meth:`sage.modular.modform.ring.ModularFormsRing.gens`:: + + sage: QM.gens() + [1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6), + 1 + 6*q + 18*q^2 + 24*q^3 + 42*q^4 + 6*q^5 + O(q^6), + 1 + 240*q^5 + O(q^6), + q + 10*q^3 + 28*q^4 + 35*q^5 + O(q^6)] + sage: QM.modular_forms_subring().gens() + [1 + 6*q + 18*q^2 + 24*q^3 + 42*q^4 + 6*q^5 + O(q^6), + 1 + 240*q^5 + O(q^6), + q + 10*q^3 + 28*q^4 + 35*q^5 + O(q^6)] + +It is possible to convert a graded quasimodular form into a polynomial where +each variable corresponds to a generator of the ring:: + + sage: QM = QuasiModularForms(1) + sage: E2, E4, E6 = QM.gens() + sage: F = E2*E4*E6 + E6^2; F + 2 - 1296*q + 91584*q^2 + 14591808*q^3 + 464670432*q^4 + 6160281120*q^5 + O(q^6) + sage: p = F.polynomial('E2, E4, E6'); p + E2*E4*E6 + E6^2 + sage: P = p.parent(); P + Multivariate Polynomial Ring in E2, E4, E6 over Rational Field + +The generators of the polynomial ring have degree equal to the weight of the +corresponding form:: + + sage: P.inject_variables() + Defining E2, E4, E6 + sage: E2.degree() + 2 + sage: E4.degree() + 4 + sage: E6.degree() + 6 + +This works also for congruence subgroup:: + + sage: QM = QuasiModularForms(Gamma1(4)) + sage: QM.ngens() + 5 + sage: QM.polynomial_ring() + Multivariate Polynomial Ring in g0, g1, g2, g3, g4 over Rational Field + sage: (QM.0 + QM.1*QM.0^2 + QM.3 + QM.4^3).polynomial() + g4^3 + g0^2*g1 + g3 + g0 + +One can also convert a multivariate polynomial into a quasimodular form:: + + sage: QM.polynomial_ring().inject_variables() + Defining g0, g1, g2, g3, g4 + sage: QM.from_polynomial(g4^3 + g0^2*g1 + g3 + g0) + 3 - 72*q + 396*q^2 + 2081*q^3 + 19752*q^4 + 98712*q^5 + O(q^6) .. NOTE:: @@ -134,8 +199,8 @@ class QuasiModularForms(Parent, UniqueRepresentation): Ring of Quasimodular Forms for Modular Group SL(2,Z) over Rational Field sage: QM.gens() [1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6), - 1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + 30240*q^5 + O(q^6), - 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6)] + 1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + 30240*q^5 + O(q^6), + 1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6)] It is possible to access the weight 2 Eisenstein series:: @@ -526,7 +591,7 @@ def polygen(self): """ return self.__polynomial_subring.gen() - def polynomial_ring(self, names='g0'): + def polynomial_ring(self, names='g'): r""" Return a multivariate polynomial ring isomorphic to the given graded quasimodular forms ring. @@ -555,10 +620,9 @@ def polynomial_ring(self, names='g0'): sage: E6.degree() 6 sage: P. = QQ[] - sage: QM.from_polynomial(x+y+z+w) - Traceback (most recent call last): - ... - ValueError: the number of variables (4) of the given polynomial cannot exceed the number of generators (3) of the quasimodular forms ring + sage: QM = QuasiModularForms(Gamma0(13)) + sage: QM.polynomial_ring() + Multivariate Polynomial Ring in g0, g1, g2, g3, g4, g5, g6, g7 over Rational Field """ weights = [2] for f in self.__modular_forms_subring.gen_forms(): @@ -591,6 +655,25 @@ def from_polynomial(self, polynomial): True sage: QM.from_polynomial(x^2 + y + x*z + 1) 4 - 336*q - 2016*q^2 + 322368*q^3 + 3691392*q^4 + 21797280*q^5 + O(q^6) + sage: QM = QuasiModularForms(Gamma0(2)) + sage: P = QM.polynomial_ring() + sage: P.inject_variables() + Defining g0, g1, g2 + sage: QM.from_polynomial(g0) + 1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6) + sage: QM.from_polynomial(g0 + g2*g1) == QM.0 + QM.2*QM.1 + True + + Naturally, the number of variable must not exceed the number of generators:: + + sage: P = PolynomialRing(QQ, 'g', 4) + sage: P.inject_variables() + Defining g0, g1, g2, g3 + sage: QM.from_polynomial(g0 + g1 + g2 + g3) + Traceback (most recent call last): + ... + ValueError: the number of variables (4) of the given polynomial cannot exceed the number of generators (3) of the quasimodular forms ring + TESTS:: From 9a6e65cc83fbbe393895f1db777910c7f11d8412 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Thu, 27 Oct 2022 18:44:51 -0400 Subject: [PATCH 297/632] src/sage/modular/modform/ring.py: fix conversion bug --- src/sage/modular/modform/ring.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/sage/modular/modform/ring.py b/src/sage/modular/modform/ring.py index 4c8865fd745..83b70d0d11f 100644 --- a/src/sage/modular/modform/ring.py +++ b/src/sage/modular/modform/ring.py @@ -511,6 +511,10 @@ def _element_constructor_(self, forms_datum): Traceback (most recent call last): ... NotImplementedError: conversion from q-expansion not yet implemented + sage: M = ModularFormsRing(Gamma0(2)) + sage: E4 = ModularForms(1, 4).0 + sage: M(E4)[4].parent() + Modular Forms space of dimension 2 for Congruence Subgroup Gamma0(2) of weight 4 over Rational Field """ if isinstance(forms_datum, (dict, list)): forms_dictionary = forms_datum @@ -518,7 +522,8 @@ def _element_constructor_(self, forms_datum): forms_dictionary = forms_datum._forms_dictionary elif is_ModularFormElement(forms_datum): if self.group().is_subgroup(forms_datum.group()) and self.base_ring().has_coerce_map_from(forms_datum.base_ring()): - forms_dictionary = {forms_datum.weight():forms_datum} + M = ModularForms(self.group(), forms_datum.weight(), self.base_ring()) + forms_dictionary = {forms_datum.weight() : M(forms_datum)} else: raise ValueError('the group (%s) and/or the base ring (%s) of the given modular form is not consistant with the base space: %s'%(forms_datum.group(), forms_datum.base_ring(), self)) elif forms_datum in self.base_ring(): From 8386a3ed7f24e9ae72aaf63012cc57490e82cc56 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Thu, 27 Oct 2022 19:38:15 -0400 Subject: [PATCH 298/632] src/sage/modular/quasimodform/ring.py: fix comments 1) and 2) --- src/sage/modular/quasimodform/ring.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/modular/quasimodform/ring.py b/src/sage/modular/quasimodform/ring.py index 118794d6546..22221eab823 100644 --- a/src/sage/modular/quasimodform/ring.py +++ b/src/sage/modular/quasimodform/ring.py @@ -81,12 +81,12 @@ sage: QM.ngens() 4 -The first generators is the weight 2 Eisenstein serie:: +The first generators is the weight 2 Eisenstein series:: sage: E2 = QM.0; E2 1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6) -The other generators corresponds to the generators given by the method +The other generators correspond to the generators given by the method :meth:`sage.modular.modform.ring.ModularFormsRing.gens`:: sage: QM.gens() From 1b5c14efea6649ccec10257c43f0c5a80ea56853 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Thu, 27 Oct 2022 19:40:18 -0400 Subject: [PATCH 299/632] src/sage/modular/quasimodform/ring.py: fix comment 1) --- src/sage/modular/quasimodform/ring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/modular/quasimodform/ring.py b/src/sage/modular/quasimodform/ring.py index 22221eab823..b47cf1fdf37 100644 --- a/src/sage/modular/quasimodform/ring.py +++ b/src/sage/modular/quasimodform/ring.py @@ -81,7 +81,7 @@ sage: QM.ngens() 4 -The first generators is the weight 2 Eisenstein series:: +The first generator is the weight 2 Eisenstein series:: sage: E2 = QM.0; E2 1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6) From 247faf7ee4a5a2a0848d77b6765e327e5116e636 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Fri, 28 Oct 2022 16:04:01 +0900 Subject: [PATCH 300/632] Being more careful about passing the coefficients. --- src/sage/rings/lazy_series_ring.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index b7b1953f52f..c505d1417df 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -1535,6 +1535,14 @@ def q_pochhammer(self, q=None): sage: phi[:20] == M.euler()[:20] True + TESTS:: + + sage: R = ZZ['q'].fraction_field() + sage: q = R.gen() + sage: L. = LazyLaurentSeriesRing(LazyDirichletSeriesRing(R, "s")) + sage: z.q_pochhammer(q) + 1 + ((1/(q-1)))*z + ((q/(q^3-q^2-q+1)))*z^2 + ... + O(z^7) + REFERENCES: - :wikipedia:`Q-Pochhammer_symbol` @@ -1550,7 +1558,7 @@ def q_pochhammer(self, q=None): one = qP.one() def coeff(n): return (-1)**n * q**binomial(n, 2) / qP.prod(one - q**i for i in range(1, n+1)) - return self(coeff, valuation=0) + return self(coefficients=coeff, valuation=0) def euler(self): r""" @@ -1578,6 +1586,12 @@ def euler(self): sage: P[:20] == [Partitions(n).cardinality() for n in range(20)] True + TESTS:: + + sage: L. = LazyLaurentSeriesRing(LazyDirichletSeriesRing(QQ, "s")) + sage: q.euler() + 1 - q - q^2 + q^5 + O(q^7) + REFERENCES: - :wikipedia:`Euler_function` @@ -1588,7 +1602,8 @@ def coeff(n): if rem: return ZZ.zero() return (-1) ** ((m + 1) // 6) - return self(coeff, valuation=0) + return self(coefficients=coeff, valuation=0) + ###################################################################### From 87b4d8ec4a315a31bfa51a7a40eb5d52b603217d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 28 Oct 2022 11:51:33 +0200 Subject: [PATCH 301/632] some details about OUTPUT and INPUT in the doc --- src/sage/algebras/free_algebra_quotient.py | 12 +++-- .../algebras/steenrod/steenrod_algebra.py | 37 +++++++++---- .../steenrod/steenrod_algebra_bases.py | 6 ++- .../steenrod/steenrod_algebra_misc.py | 52 +++++++++++++------ .../covariant_functorial_construction.py | 1 + src/sage/categories/coxeter_groups.py | 6 ++- .../examples/finite_coxeter_groups.py | 5 +- .../examples/finite_enumerated_sets.py | 2 + src/sage/categories/finite_monoids.py | 4 +- src/sage/categories/groups.py | 2 +- src/sage/categories/magmas.py | 1 + src/sage/categories/poor_man_map.py | 2 + .../combinat/fully_commutative_elements.py | 4 +- src/sage/combinat/matrices/latin.py | 14 ++--- src/sage/combinat/root_system/cartan_type.py | 21 +++++--- .../root_system/root_lattice_realizations.py | 6 ++- src/sage/combinat/words/words.py | 7 +-- src/sage/crypto/lattice.py | 6 ++- src/sage/geometry/lattice_polytope.py | 27 +++++----- src/sage/groups/generic.py | 6 ++- src/sage/probability/random_variable.py | 7 ++- .../rings/multi_power_series_ring_element.py | 6 ++- src/sage/rings/polynomial/toy_buchberger.py | 4 +- .../hyperelliptic_curves/jacobian_morphism.py | 12 +++-- src/sage/structure/formal_sum.py | 15 +++--- src/sage/topology/simplicial_set.py | 8 ++- 26 files changed, 179 insertions(+), 94 deletions(-) diff --git a/src/sage/algebras/free_algebra_quotient.py b/src/sage/algebras/free_algebra_quotient.py index d5f8050a1c9..a338123e9a6 100644 --- a/src/sage/algebras/free_algebra_quotient.py +++ b/src/sage/algebras/free_algebra_quotient.py @@ -169,9 +169,9 @@ def _element_constructor_(self, x): sage: a = H._element_constructor_([1,2,3,4]); a 1 + 2*i + 3*j + 4*k """ - return self.element_class(self,x) + return self.element_class(self, x) - def _coerce_map_from_(self,S): + def _coerce_map_from_(self,S ): """ EXAMPLES:: @@ -331,11 +331,13 @@ def hamilton_quatalg(R): constructed as a free algebra quotient. INPUT: - - R -- a commutative ring + + - R -- a commutative ring OUTPUT: - - Q -- quaternion algebra - - gens -- generators for Q + + - Q -- quaternion algebra + - gens -- generators for Q EXAMPLES:: diff --git a/src/sage/algebras/steenrod/steenrod_algebra.py b/src/sage/algebras/steenrod/steenrod_algebra.py index 8feec0d6d98..ea2fb868c52 100644 --- a/src/sage/algebras/steenrod/steenrod_algebra.py +++ b/src/sage/algebras/steenrod/steenrod_algebra.py @@ -525,7 +525,9 @@ def __init__(self, p=2, basis='milnor', **kwds): - ``precision`` - (optional, default ``None``) - ``generic`` - (optional, default 'auto') - OUTPUT: mod `p` Steenrod algebra with basis, or a sub-Hopf + OUTPUT: + + mod `p` Steenrod algebra with basis, or a sub-Hopf algebra of the mod `p` Steenrod algebra defined by the given profile function. @@ -1036,8 +1038,9 @@ def homogeneous_component(self, n): - `n` - integer - OUTPUT: a vector space spanned by the basis for this algebra - in dimension `n` + OUTPUT: + + a vector space spanned by the basis for this algebra in dimension `n` EXAMPLES:: @@ -1128,7 +1131,9 @@ def product_on_basis(self, t1, t2): - ``t1``, ``t2`` -- tuples, the indices of two basis elements of self - OUTPUT: the product of the two corresponding basis elements, + OUTPUT: + + the product of the two corresponding basis elements, as an element of self ALGORITHM: If the two elements are represented in the Milnor @@ -1251,7 +1256,9 @@ def coproduct_on_basis(self, t, algorithm=None): Which of these methods is used is controlled by whether ``algorithm`` is 'milnor' or 'serre-cartan'. - OUTPUT: the coproduct of the corresponding basis element, + OUTPUT: + + the coproduct of the corresponding basis element, as an element of ``self`` tensor ``self``. EXAMPLES:: @@ -1433,7 +1440,9 @@ def antipode_on_basis(self, t): - ``t`` -- tuple, the index of a basis element of self - OUTPUT: the antipode of the corresponding basis element, + OUTPUT: + + the antipode of the corresponding basis element, as an element of self. ALGORITHM: according to a result of Milnor's, the antipode of @@ -2184,7 +2193,9 @@ def basis(self, d=None): - `d` -- integer or ``None``, optional (default ``None``) - OUTPUT: If `d` is ``None``, then return a basis of the algebra. + OUTPUT: + + If `d` is ``None``, then return a basis of the algebra. Otherwise, return the basis in degree `d`. EXAMPLES:: @@ -2301,7 +2312,9 @@ def P(self, *nums): - ``a, b, c, ...`` - non-negative integers - OUTPUT: element of the Steenrod algebra given by the Milnor + OUTPUT: + + element of the Steenrod algebra given by the Milnor single basis element `P(a, b, c, ...)` Note that at the prime 2, this is the same element as @@ -3794,7 +3807,9 @@ def SteenrodAlgebra(p=2, basis='milnor', generic='auto', **kwds): - ``precision`` - integer or ``None`` (optional, default ``None``) - ``generic`` - (optional, default 'auto') - OUTPUT: mod `p` Steenrod algebra or one of its sub-Hopf algebras, + OUTPUT: + + mod `p` Steenrod algebra or one of its sub-Hopf algebras, elements of which are printed using ``basis`` See below for information about ``basis``, ``profile``, etc. @@ -4166,7 +4181,9 @@ def AA(n=None, p=2): - `n` - non-negative integer, optional (default ``None``) - `p` - prime number, optional (default 2) - OUTPUT: If `n` is ``None``, then return the full Steenrod algebra. + OUTPUT: + + If `n` is ``None``, then return the full Steenrod algebra. Otherwise, return `A(n)`. When `p=2`, `A(n)` is the sub-Hopf algebra generated by the diff --git a/src/sage/algebras/steenrod/steenrod_algebra_bases.py b/src/sage/algebras/steenrod/steenrod_algebra_bases.py index 4cfbe2f83e4..f028e6d5df5 100644 --- a/src/sage/algebras/steenrod/steenrod_algebra_bases.py +++ b/src/sage/algebras/steenrod/steenrod_algebra_bases.py @@ -193,8 +193,9 @@ def convert_from_milnor_matrix(n, basis, p=2, generic='auto'): - ``p`` - positive prime number (optional, default 2) - OUTPUT: ``matrix`` - change-of-basis matrix, a square matrix over - GF(p) + OUTPUT: + + ``matrix`` - change-of-basis matrix, a square matrix over GF(p) .. note:: @@ -1082,6 +1083,7 @@ def sorting_pair(s,t,basis): # pair used for sorting the basis result.append((tuple(q_mono), tuple(p_mono))) return tuple(result) + ############################################################################# def steenrod_basis_error_check(dim, p, **kwds): """ diff --git a/src/sage/algebras/steenrod/steenrod_algebra_misc.py b/src/sage/algebras/steenrod/steenrod_algebra_misc.py index 850081f6e67..d8fedad70b8 100644 --- a/src/sage/algebras/steenrod/steenrod_algebra_misc.py +++ b/src/sage/algebras/steenrod/steenrod_algebra_misc.py @@ -311,7 +311,9 @@ def normalize_profile(profile, precision=None, truncation_type='auto', p=2, gene - `p` - prime, optional, default 2 - `generic` - boolean, optional, default ``None`` - OUTPUT: a triple ``profile, precision, truncation_type``, in + OUTPUT: + + a triple ``profile, precision, truncation_type``, in standard form as described below. The "standard form" is as follows: ``profile`` should be a tuple @@ -638,6 +640,7 @@ def milnor_mono_to_string(mono, latex=False, generic=False): string = string + ")" return string.strip(" ") + def serre_cartan_mono_to_string(mono, latex=False, generic=False): r""" String representation of element of the Serre-Cartan basis. @@ -730,7 +733,9 @@ def wood_mono_to_string(mono, latex=False): - ``latex`` - boolean (optional, default False), if true, output LaTeX string - OUTPUT: ``string`` - concatenation of strings of the form + OUTPUT: + + ``string`` - concatenation of strings of the form ``Sq^{2^s (2^{t+1}-1)}`` for each pair (s,t) EXAMPLES:: @@ -773,8 +778,9 @@ def wall_mono_to_string(mono, latex=False): - ``latex`` - boolean (optional, default False), if true, output LaTeX string - OUTPUT: ``string`` - concatenation of strings ``Q^{m}_{k}`` for - each pair (m,k) + OUTPUT: + + ``string`` - concatenation of strings ``Q^{m}_{k}`` for each pair (m,k) EXAMPLES:: @@ -812,8 +818,9 @@ def wall_long_mono_to_string(mono, latex=False): - ``latex`` - boolean (optional, default False), if true, output LaTeX string - OUTPUT: ``string`` - concatenation of strings of the form - ``Sq^(2^m)`` + OUTPUT: + + ``string`` - concatenation of strings of the form ``Sq^(2^m)`` EXAMPLES:: @@ -855,8 +862,10 @@ def arnonA_mono_to_string(mono, latex=False, p=2): - ``latex`` - boolean (optional, default False), if true, output LaTeX string - OUTPUT: ``string`` - concatenation of strings of the form - ``X^{m}_{k}`` for each pair (m,k) + OUTPUT: + + ``string`` - concatenation of strings of the form ``X^{m}_{k}`` + for each pair (m,k) EXAMPLES:: @@ -894,8 +903,9 @@ def arnonA_long_mono_to_string(mono, latex=False, p=2): - ``latex`` - boolean (optional, default False), if true, output LaTeX string - OUTPUT: ``string`` - concatenation of strings of the form - ``Sq(2^m)`` + OUTPUT: + + ``string`` - concatenation of strings of the form ``Sq(2^m)`` EXAMPLES:: @@ -939,8 +949,10 @@ def pst_mono_to_string(mono, latex=False, generic=False): - ``generic`` - whether to format generically, or for the prime 2 (default) - OUTPUT: ``string`` - concatenation of strings of the form - ``P^{s}_{t}`` for each pair (s,t) + OUTPUT: + + ``string`` - concatenation of strings of the form ``P^{s}_{t}`` + for each pair (s,t) EXAMPLES:: @@ -999,8 +1011,10 @@ def comm_mono_to_string(mono, latex=False, generic=False): - ``generic`` - whether to format generically, or for the prime 2 (default) - OUTPUT: ``string`` - concatenation of strings of the form - ``c_{s,t}`` for each pair (s,t) + OUTPUT: + + ``string`` - concatenation of strings of the form ``c_{s,t}`` + for each pair (s,t) EXAMPLES:: @@ -1059,8 +1073,10 @@ def comm_long_mono_to_string(mono, p, latex=False, generic=False): - ``generic`` - whether to format generically, or for the prime 2 (default) - OUTPUT: ``string`` - concatenation of strings of the form ``s_{2^s - ... 2^(s+t-1)}`` for each pair (s,t) + OUTPUT: + + ``string`` - concatenation of strings of the form ``s_{2^s... 2^(s+t-1)}`` + for each pair (s,t) EXAMPLES:: @@ -1121,7 +1137,9 @@ def convert_perm(m): - ``m`` - tuple of non-negative integers with no repetitions - OUTPUT: ``list`` - conversion of ``m`` to a permutation of the set + OUTPUT: + + ``list`` - conversion of ``m`` to a permutation of the set 1,2,...,len(m) If ``m=(3,7,4)``, then one can view ``m`` as representing the diff --git a/src/sage/categories/covariant_functorial_construction.py b/src/sage/categories/covariant_functorial_construction.py index 4ed3a678986..813b66e8afd 100644 --- a/src/sage/categories/covariant_functorial_construction.py +++ b/src/sage/categories/covariant_functorial_construction.py @@ -205,6 +205,7 @@ def __call__(self, args, **kwargs): Functorial construction application INPUT: + - ``self``: a covariant functorial construction `F` - ``args``: a tuple (or iterable) of parents or elements diff --git a/src/sage/categories/coxeter_groups.py b/src/sage/categories/coxeter_groups.py index c4f4cdab349..b7ee2ac9fe9 100644 --- a/src/sage/categories/coxeter_groups.py +++ b/src/sage/categories/coxeter_groups.py @@ -263,8 +263,10 @@ def braid_orbit(self, word): - ``word``: a list (or iterable) of indices in ``self.index_set()`` - OUTPUT: a list of all lists that can be obtained from - ``word`` by replacements of braid relations + OUTPUT: + + a list of all lists that can be obtained from + ``word`` by replacements of braid relations See :meth:`braid_relations` for the definition of braid relations. diff --git a/src/sage/categories/examples/finite_coxeter_groups.py b/src/sage/categories/examples/finite_coxeter_groups.py index c135c877b70..de49a789fa0 100644 --- a/src/sage/categories/examples/finite_coxeter_groups.py +++ b/src/sage/categories/examples/finite_coxeter_groups.py @@ -87,10 +87,11 @@ class DihedralGroup(UniqueRepresentation, Parent): def __init__(self, n=5): r""" + Construct the `n`-th DihedralGroup of order `2 n` + INPUT: - - ``n`` - an integer with `n>=2` - Construct the n-th DihedralGroup of order 2*n + - `n` -- an integer with `n>=2` EXAMPLES:: diff --git a/src/sage/categories/examples/finite_enumerated_sets.py b/src/sage/categories/examples/finite_enumerated_sets.py index 91896c77d25..24222b4fa91 100644 --- a/src/sage/categories/examples/finite_enumerated_sets.py +++ b/src/sage/categories/examples/finite_enumerated_sets.py @@ -146,6 +146,7 @@ def ambient(self): def lift(self, x): """ INPUT: + - ``x`` -- an element of ``self`` Lifts ``x`` to the ambient space for ``self``, as per @@ -164,6 +165,7 @@ def lift(self, x): def retract(self, x): """ INPUT: + - ``x`` -- an element of the ambient space for ``self`` Retracts ``x`` from the ambient space to ``self``, as per diff --git a/src/sage/categories/finite_monoids.py b/src/sage/categories/finite_monoids.py index c58cfc3e4e8..a300339ae22 100644 --- a/src/sage/categories/finite_monoids.py +++ b/src/sage/categories/finite_monoids.py @@ -35,7 +35,9 @@ def nerve(self): r""" The nerve (classifying space) of this monoid. - OUTPUT: the nerve `BG` (if `G` denotes this monoid), as a + OUTPUT: + + the nerve `BG` (if `G` denotes this monoid), as a simplicial set. The `k`-dimensional simplices of this object are indexed by products of `k` elements in the monoid: diff --git a/src/sage/categories/groups.py b/src/sage/categories/groups.py index ef681dbb07a..97d57799f8c 100644 --- a/src/sage/categories/groups.py +++ b/src/sage/categories/groups.py @@ -233,11 +233,11 @@ def cayley_table(self, names='letters', elements=None): this can be used when the base set is infinite. OUTPUT: + An object representing the multiplication table. This is an :class:`~sage.matrix.operation_table.OperationTable` object and even more documentation can be found there. - EXAMPLES: Permutation groups, matrix groups and abelian groups diff --git a/src/sage/categories/magmas.py b/src/sage/categories/magmas.py index 801d3700707..936ada2a40e 100644 --- a/src/sage/categories/magmas.py +++ b/src/sage/categories/magmas.py @@ -857,6 +857,7 @@ def multiplication_table(self, names='letters', elements=None): this can be used when the base set is infinite. OUTPUT: + The multiplication table as an object of the class :class:`~sage.matrix.operation_table.OperationTable` which defines several methods for manipulating and diff --git a/src/sage/categories/poor_man_map.py b/src/sage/categories/poor_man_map.py index 507705fb9f9..13f8322f8d3 100644 --- a/src/sage/categories/poor_man_map.py +++ b/src/sage/categories/poor_man_map.py @@ -14,6 +14,7 @@ # **************************************************************************** import sage.structure.sage_object + class PoorManMap(sage.structure.sage_object.SageObject): """ A class for maps between sets which are not (yet) modeled by parents @@ -184,6 +185,7 @@ def __mul__(self, other): Composition INPUT: + - ``self`` -- a map `f` - ``other`` -- a map `g` diff --git a/src/sage/combinat/fully_commutative_elements.py b/src/sage/combinat/fully_commutative_elements.py index b8f26d8ba8b..bb46692a8b7 100644 --- a/src/sage/combinat/fully_commutative_elements.py +++ b/src/sage/combinat/fully_commutative_elements.py @@ -214,7 +214,9 @@ def heap(self, **kargs): - ``display_labeling`` -- boolean (default: False). Setting the value to True will display the label `s_i` for each element `i` of the poset - OUTPUT: A labeled poset where the underlying set is `\{0,1,...,k-1\}` + OUTPUT: + + A labeled poset where the underlying set is `\{0,1,...,k-1\}` and where each element `i` carries `s_i` as its label. The partial order `\prec` on the poset is defined by declaring `i\prec j` if `i = ` diff --git a/src/sage/schemes/hyperelliptic_curves/jacobian_morphism.py b/src/sage/schemes/hyperelliptic_curves/jacobian_morphism.py index 7167fe2208a..2ba280b545a 100644 --- a/src/sage/schemes/hyperelliptic_curves/jacobian_morphism.py +++ b/src/sage/schemes/hyperelliptic_curves/jacobian_morphism.py @@ -339,6 +339,7 @@ def cantor_composition(D1,D2,f,h,genus): a = a.monic() return (a, b) + class JacobianMorphism_divisor_class_field(AdditiveGroupElement, SchemeMorphism): r""" An element of a Jacobian defined over a field, i.e. in @@ -348,10 +349,13 @@ def __init__(self, parent, polys, check=True): r""" Create a new Jacobian element in Mumford representation. - INPUT: parent: the parent Homset polys: Mumford's `u` and - `v` polynomials check (default: True): if True, ensure that - polynomials define a divisor on the appropriate curve and are - reduced + INPUT: + + - parent -- the parent Homset + - polys -- Mumford's `u` and `v` polynomials + - check (default: ``True``) -- if ``True``, ensure that + polynomials define a divisor on the appropriate curve and are + reduced .. warning:: diff --git a/src/sage/structure/formal_sum.py b/src/sage/structure/formal_sum.py index 0e14ab6dd1e..a96d3bb3351 100644 --- a/src/sage/structure/formal_sum.py +++ b/src/sage/structure/formal_sum.py @@ -85,13 +85,14 @@ class FormalSum(ModuleElement): def __init__(self, x, parent=None, check=True, reduce=True): """ INPUT: - - ``x`` -- object - - ``parent`` -- FormalSums(R) module (default: FormalSums(ZZ)) - - ``check`` -- bool (default: True) if False, might not coerce - coefficients into base ring, which can speed - up constructing a formal sum. - - ``reduce`` -- reduce (default: True) if False, do not - combine common terms + + - ``x`` -- object + - ``parent`` -- FormalSums(R) module (default: FormalSums(ZZ)) + - ``check`` -- bool (default: ``True``) if ``False``, might not coerce + coefficients into base ring, which can speed + up constructing a formal sum. + - ``reduce`` -- reduce (default: ``True``) if ``False``, do not + combine common terms EXAMPLES:: diff --git a/src/sage/topology/simplicial_set.py b/src/sage/topology/simplicial_set.py index 836a13da05d..0ca2ff5bcb2 100644 --- a/src/sage/topology/simplicial_set.py +++ b/src/sage/topology/simplicial_set.py @@ -3819,7 +3819,9 @@ def standardize_degeneracies(*L): - ``L`` -- list of integers, representing a composition of degeneracies in a simplicial set. - OUTPUT: an equivalent list of degeneracies, standardized to be + OUTPUT: + + an equivalent list of degeneracies, standardized to be written in decreasing order, using the simplicial identity .. MATH:: @@ -3923,7 +3925,9 @@ def standardize_face_maps(*L): - ``L`` -- list of integers, representing a composition of face maps in a simplicial set. - OUTPUT: an equivalent list of face maps, standardized to be + OUTPUT: + + an equivalent list of face maps, standardized to be written in non-increasing order, using the simplicial identity .. MATH:: From d8949bb373bafc22c1b5e285fab5b3a7234c9af5 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Fri, 28 Oct 2022 05:55:35 -0400 Subject: [PATCH 302/632] src/sage/modular/quasimodform/ring.py: fix names for polynomial_ring method --- src/sage/modular/quasimodform/ring.py | 28 ++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/sage/modular/quasimodform/ring.py b/src/sage/modular/quasimodform/ring.py index b47cf1fdf37..73e26350e7a 100644 --- a/src/sage/modular/quasimodform/ring.py +++ b/src/sage/modular/quasimodform/ring.py @@ -165,6 +165,8 @@ # https://www.gnu.org/licenses/ # **************************************************************************** +from itertools import product, chain + from sage.categories.graded_algebras import GradedAlgebras from sage.modular.arithgroup.congroup_gamma0 import Gamma0_constructor as Gamma0 @@ -591,7 +593,7 @@ def polygen(self): """ return self.__polynomial_subring.gen() - def polynomial_ring(self, names='g'): + def polynomial_ring(self, names=None): r""" Return a multivariate polynomial ring isomorphic to the given graded quasimodular forms ring. @@ -624,10 +626,26 @@ def polynomial_ring(self, names='g'): sage: QM.polynomial_ring() Multivariate Polynomial Ring in g0, g1, g2, g3, g4, g5, g6, g7 over Rational Field """ - weights = [2] - for f in self.__modular_forms_subring.gen_forms(): - weights.append(f.weight()) - return PolynomialRing(self.base_ring(), len(weights), names, order=TermOrder('wdeglex', weights)) + weights = [f.weight() for f in self.__modular_forms_subring.gen_forms()] + if names is None: + if self.group() == Gamma0(1): + names = ["E2", "E4", "E6"] + else: + names = ["E2"] + letters = "ABCDFGH" + for unique_weight in set(weights): + same_weights = [k for k in weights if k == unique_weight] + # create all the names of the form: + # A, B, C, D, F, G, H, AA, AB, AC, AD,..., AAA, AAB, AAC,... + # the letter E is reserved for the weight 2 Eisenstein series + iter_names = (product(letters, repeat=r) + for r in range(1, len(same_weights)//7 + 2)) + iter_names = chain(*iter_names) + for k in same_weights: + names.append("".join(next(iter_names)) + str(k)) + weights.insert(0, 2) # add the weight 2 Eisenstein series + return PolynomialRing(self.base_ring(), len(weights), names, + order=TermOrder('wdeglex', weights)) def from_polynomial(self, polynomial): r""" From ea69eb36358623d8e2de5a4bf75d67e6e3107891 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Fri, 28 Oct 2022 06:34:46 -0400 Subject: [PATCH 303/632] src/sage/modular/quasimodform/ring.py: fix documentation --- src/sage/modular/quasimodform/ring.py | 47 +++++++++++++++------------ 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/src/sage/modular/quasimodform/ring.py b/src/sage/modular/quasimodform/ring.py index 73e26350e7a..d1f278dd130 100644 --- a/src/sage/modular/quasimodform/ring.py +++ b/src/sage/modular/quasimodform/ring.py @@ -129,15 +129,15 @@ sage: QM.ngens() 5 sage: QM.polynomial_ring() - Multivariate Polynomial Ring in g0, g1, g2, g3, g4 over Rational Field + Multivariate Polynomial Ring in E2, A2, B2, A3, B3 over Rational Field sage: (QM.0 + QM.1*QM.0^2 + QM.3 + QM.4^3).polynomial() g4^3 + g0^2*g1 + g3 + g0 One can also convert a multivariate polynomial into a quasimodular form:: sage: QM.polynomial_ring().inject_variables() - Defining g0, g1, g2, g3, g4 - sage: QM.from_polynomial(g4^3 + g0^2*g1 + g3 + g0) + Defining E2, A2, B2, A3, B3 + sage: QM.from_polynomial(B3^3 + E2^2*A2 + A3 + E2) 3 - 72*q + 396*q^2 + 2081*q^3 + 19752*q^4 + 98712*q^5 + O(q^6) .. NOTE:: @@ -595,36 +595,41 @@ def polygen(self): def polynomial_ring(self, names=None): r""" - Return a multivariate polynomial ring isomorphic to the given graded - quasimodular forms ring. + Return a multivariate polynomial ring of which the quasimodular forms + ring is a quotient. - In the case of the full modular group, this - ring is `R[g_0, g_1, g_2]` where `g_0`, `g_4` and `g_6` have degrees 2, - 4 and 6 respectively. + In the case of the full modular group, this ring is `R[E_2, E_4, E_6]` + where `E_2`, `E_4` and `E_6` have degrees 2, 4 and 6 respectively. INPUT: - - ``names`` (str, default: ``'g'``) -- a list or tuple of names - (strings), or a comma separated string. Correspond to the names of the - variables. + - ``names`` (str, default: ``None``) -- a list or tuple of names + (strings), or a comma separated string. Defines the names for the + generators of the multivariate polynomial ring. The default names are + of the form ``ABCk`` where ``k`` is a number corresponding to the + weight of the form ``ABC``. OUTPUT: A multivariate polynomial ring in the variables ``names`` EXAMPLES:: sage: QM = QuasiModularForms(1) - sage: P. = QM.polynomial_ring(); P + sage: P = QM.polynomial_ring(); P Multivariate Polynomial Ring in E2, E4, E6 over Rational Field + sage: P.inject_variables() + Defining E2, E4, E6 sage: E2.degree() 2 sage: E4.degree() 4 sage: E6.degree() 6 - sage: P. = QQ[] - sage: QM = QuasiModularForms(Gamma0(13)) + sage: QM = QuasiModularForms(Gamma1(9)) sage: QM.polynomial_ring() - Multivariate Polynomial Ring in g0, g1, g2, g3, g4, g5, g6, g7 over Rational Field + Multivariate Polynomial Ring in E2, A2, B2, C2, D2, F2, G2, H2, A3, B3, C3, D3, F3, G3, H3, AA3, AB3, AC3 over Rational Field + sage: QM = QuasiModularForms(Gamma0(6)) + sage: QM.polynomial_ring(names="F") + Multivariate Polynomial Ring in F0, F1, F2, F3 over Rational Field """ weights = [f.weight() for f in self.__modular_forms_subring.gen_forms()] if names is None: @@ -676,18 +681,18 @@ def from_polynomial(self, polynomial): sage: QM = QuasiModularForms(Gamma0(2)) sage: P = QM.polynomial_ring() sage: P.inject_variables() - Defining g0, g1, g2 - sage: QM.from_polynomial(g0) + Defining E2, A2, A4 + sage: QM.from_polynomial(E2) 1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6) - sage: QM.from_polynomial(g0 + g2*g1) == QM.0 + QM.2*QM.1 + sage: QM.from_polynomial(E2 + A4*A2) == QM.0 + QM.2*QM.1 True Naturally, the number of variable must not exceed the number of generators:: - sage: P = PolynomialRing(QQ, 'g', 4) + sage: P = PolynomialRing(QQ, 'F', 4) sage: P.inject_variables() - Defining g0, g1, g2, g3 - sage: QM.from_polynomial(g0 + g1 + g2 + g3) + Defining F0, F1, F2, F3 + sage: QM.from_polynomial(F0 + F1 + F2 + F3) Traceback (most recent call last): ... ValueError: the number of variables (4) of the given polynomial cannot exceed the number of generators (3) of the quasimodular forms ring From 2e8855f516475ae1f92c971ae0e1be148491b9a3 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Fri, 28 Oct 2022 07:05:25 -0400 Subject: [PATCH 304/632] src/sage/modular/quasimodform/element.py: fix doc --- src/sage/modular/quasimodform/element.py | 39 ++++++++++++++++++------ src/sage/modular/quasimodform/ring.py | 2 +- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/sage/modular/quasimodform/element.py b/src/sage/modular/quasimodform/element.py index 8d16834e8a9..0fde12cc248 100644 --- a/src/sage/modular/quasimodform/element.py +++ b/src/sage/modular/quasimodform/element.py @@ -64,6 +64,23 @@ class QuasiModularFormsElement(ModuleElement): sage: M = QM.modular_forms_subring() sage: QM(M.0 * E2 + M.1 * E2^2) 2 - 336*q + 4320*q^2 + 398400*q^3 - 3772992*q^4 - 89283168*q^5 + O(q^6) + + One may convert a quasimodular form into a multivariate polynomial in the + generators of the ring by calling + :meth:`~sage.modular.quasimodform.element.QuasiModularFormsElement.polynomial`:: + + sage: QM = QuasiModularForms(1) + sage: F = QM.0^2 + QM.1^2 + QM.0*QM.1*QM.2 + sage: F.polynomial() + E2*E4*E6 + E4^2 + E2^2 + + If the group is not the full modular group, the names of the generators will + of the form ``ABCk`` where ``k`` is the weight of the form ``ABC``:: + + sage: QM = QuasiModularForms(Gamma1(4)) + sage: F = (QM.0^4)*(QM.1^3) + QM.3 + sage: F.polynomial() + -512*E2^4*B2^3 + E2^4*A3^2 + 48*E2^4*B3^2 + A3 """ def __init__(self, parent, polynomial): r""" @@ -364,26 +381,30 @@ def is_modular_form(self): """ return self._polynomial.degree() <= 0 and self._polynomial[0].is_modular_form() - def polynomial(self, names='g'): + def polynomial(self, names=None): r""" - Return a multivariate polynomial `P(g_0,\ldots, g_n)` where `g_i` - correspond to the `i`-th generator of the ring given by the method: + Return a multivariate polynomial such that every variable corresponds to + a generator of the ring, ordered by the method: :meth:`~sage.modular.quasimodform.ring.QuasiModularForms.gens`. + An alias of this method is ``to_polynomial``. + INPUT: - - ``names`` (str, default: ``'g'``) -- a list or tuple of names - (strings), or a comma separated string. Correspond to the names of the - variables; + - ``names`` (str, default: ``None``) -- a list or tuple of names + (strings), or a comma separated string. Defines the names for the + generators of the multivariate polynomial ring. The default names are + of the form ``ABCk`` where ``k`` is a number corresponding to the + weight of the form ``ABC``. OUTPUT: A multivariate polynomial in the variables ``names`` EXAMPLES:: sage: QM = QuasiModularForms(1) - sage: (QM.0 + QM.1).polynomial('E2, E4, E6') + sage: (QM.0 + QM.1).polynomial() E4 + E2 - sage: (1/2 + QM.0 + 2*QM.1^2 + QM.0*QM.2).polynomial('E2, E4, E6') + sage: (1/2 + QM.0 + 2*QM.1^2 + QM.0*QM.2).polynomial() E2*E6 + 2*E4^2 + E2 + 1/2 Check that :trac:`34569` is fixed:: @@ -392,7 +413,7 @@ def polynomial(self, names='g'): sage: QM.ngens() 5 sage: (QM.0 + QM.1 + QM.2*QM.1 + QM.3*QM.4).polynomial() - g3*g4 + g1*g2 + g0 + g1 + B3*A4 + A2*A3 + E2 + A2 """ P = self.parent().polynomial_ring(names) diff --git a/src/sage/modular/quasimodform/ring.py b/src/sage/modular/quasimodform/ring.py index d1f278dd130..80417c12ced 100644 --- a/src/sage/modular/quasimodform/ring.py +++ b/src/sage/modular/quasimodform/ring.py @@ -131,7 +131,7 @@ sage: QM.polynomial_ring() Multivariate Polynomial Ring in E2, A2, B2, A3, B3 over Rational Field sage: (QM.0 + QM.1*QM.0^2 + QM.3 + QM.4^3).polynomial() - g4^3 + g0^2*g1 + g3 + g0 + B3^3 + E2^2*A2 + A3 + E2 One can also convert a multivariate polynomial into a quasimodular form:: From fdfa523d3b6444cd92e751f0d2f90ae2981276f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 28 Oct 2022 16:49:23 +0200 Subject: [PATCH 305/632] a few more details --- src/sage/groups/generic.py | 4 ++-- src/sage/rings/polynomial/omega.py | 3 +-- src/sage/rings/polynomial/polynomial_fateman.py | 12 +++++++----- src/sage/schemes/elliptic_curves/period_lattice.py | 1 + 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/sage/groups/generic.py b/src/sage/groups/generic.py index e1c64f4159d..d5823ebf9e0 100644 --- a/src/sage/groups/generic.py +++ b/src/sage/groups/generic.py @@ -693,11 +693,11 @@ def discrete_log(a, base, ord=None, bounds=None, operation='*', identity=None, i OUTPUT: - Returns an integer `n` such that `b^n = a` (or `nb = a`), + This returns an integer `n` such that `b^n = a` (or `nb = a`), assuming that ``ord`` is a multiple of the order of the base `b`. If ``ord`` is not specified, an attempt is made to compute it. - If no such `n` exists, this function raises a ValueError exception. + If no such `n` exists, this function raises a ``ValueError`` exception. .. warning:: diff --git a/src/sage/rings/polynomial/omega.py b/src/sage/rings/polynomial/omega.py index 97b97d4fe8c..3421ef04126 100644 --- a/src/sage/rings/polynomial/omega.py +++ b/src/sage/rings/polynomial/omega.py @@ -358,8 +358,7 @@ def _simplify_(numerator, terms): OUTPUT: A pair of a Laurent polynomial and a tuple of Laurent polynomials - representing numerator and denominator as described in the - INPUT-section. + representing numerator and denominator as described in the INPUT-section. EXAMPLES:: diff --git a/src/sage/rings/polynomial/polynomial_fateman.py b/src/sage/rings/polynomial/polynomial_fateman.py index 3b3246f2d47..0b21f42169d 100644 --- a/src/sage/rings/polynomial/polynomial_fateman.py +++ b/src/sage/rings/polynomial/polynomial_fateman.py @@ -19,7 +19,9 @@ def _mul_fateman_to_int2(f_list, g_list): """ Convert a polynomial to an integer by evaluating it + INPUT: p, a list of integers + OUTPUT: padding """ max_coeff_f = max([abs(i) for i in f_list]) @@ -28,13 +30,13 @@ def _mul_fateman_to_int2(f_list, g_list): return int(pyceil(pylog(b, 2))) -def _mul_fateman_to_poly(number,padding): +def _mul_fateman_to_poly(number, padding): """ - Converts a number to a polynomial, according - to a padding - OUTPUT: a list containing the coefficient of - a polynomial of degree len(list) + Converts a number to a polynomial, according to a padding + + OUTPUT: + a list containing the coefficient of a polynomial of degree len(list) """ coeffs = [] flag=0 diff --git a/src/sage/schemes/elliptic_curves/period_lattice.py b/src/sage/schemes/elliptic_curves/period_lattice.py index 85c76603092..7206ea74a7a 100644 --- a/src/sage/schemes/elliptic_curves/period_lattice.py +++ b/src/sage/schemes/elliptic_curves/period_lattice.py @@ -1941,6 +1941,7 @@ def normalise_periods(w1, w2): def extended_agm_iteration(a, b, c): r""" Internal function for the extended AGM used in elliptic logarithm computation. + INPUT: - ``a``, ``b``, ``c`` (real or complex) -- three real or complex numbers. From 80c84f743abadd42f956a7514f4255f0cd060699 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Fri, 28 Oct 2022 14:18:19 -0700 Subject: [PATCH 306/632] updating shm_managers to new FMatrix interface --- .../algebras/fusion_rings/shm_managers.pyx | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/sage/algebras/fusion_rings/shm_managers.pyx b/src/sage/algebras/fusion_rings/shm_managers.pyx index 5a90595f6e6..223f4925761 100644 --- a/src/sage/algebras/fusion_rings/shm_managers.pyx +++ b/src/sage/algebras/fusion_rings/shm_managers.pyx @@ -76,7 +76,7 @@ cdef class KSHandler: sage: from sage.algebras.fusion_rings.shm_managers import KSHandler sage: # Create shared data structure - sage: f = FMatrix(FusionRing("A1", 2), inject_variables=True) + sage: f = FusionRing("A1", 2).f_matrix(inject_variables=True) creating variables fx1..fx14 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 sage: n = f._poly_ring.ngens() @@ -104,7 +104,7 @@ cdef class KSHandler: sage: from sage.algebras.fusion_rings.shm_managers import KSHandler sage: # Create shared data structure - sage: f = FMatrix(FusionRing("A1", 2), inject_variables=True) + sage: f = FusionRing("A1", 2).f_matrix(inject_variables=True) creating variables fx1..fx14 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 sage: n = f._poly_ring.ngens() @@ -182,7 +182,7 @@ cdef class KSHandler: EXAMPLES:: - sage: f = FMatrix(FusionRing("B5", 1)) + sage: f = FusionRing("B5", 1).f_matrix() sage: f._reset_solver_state() sage: for idx, sq in f._ks.items(): ....: k @@ -281,7 +281,7 @@ cdef class KSHandler: TESTS:: - sage: f = FMatrix(FusionRing("C2", 2)) + sage: f = FusionRing("C2", 2).f_matrix() sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) sage: from sage.algebras.fusion_rings.shm_managers import KSHandler @@ -304,7 +304,7 @@ cdef class KSHandler: TESTS:: - sage: f = FMatrix(FusionRing("A3", 1)) + sage: f = FusionRing("A3", 1).f_matrix() sage: f._reset_solver_state() sage: loads(dumps(f._ks)) == f._ks True @@ -321,7 +321,7 @@ cdef class KSHandler: EXAMPLES:: - sage: f = FMatrix(FusionRing("A3", 1)) + sage: f = FusionRing("A3", 1).f_matrix() sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) sage: f._ks.update(f.ideal_basis) @@ -348,7 +348,7 @@ def make_KSHandler(n_slots, field, init_data): TESTS:: - sage: f = FMatrix(FusionRing("B4", 1)) + sage: f = FusionRing("B4", 1).f_matrix() sage: f._reset_solver_state() sage: loads(dumps(f._ks)) == f._ks # indirect doctest True @@ -433,7 +433,7 @@ cdef class FvarsHandler: sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: # Create shared data structure - sage: f = FMatrix(FusionRing("A2", 1), inject_variables=True) + sage: f = FusionRing("A2", 1).f_matrix(inject_variables=True) creating variables fx1..fx8 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 sage: f.start_worker_pool() @@ -460,7 +460,7 @@ cdef class FvarsHandler: sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: # Create shared data structure - sage: f = FMatrix(FusionRing("A2", 1), inject_variables=True) + sage: f = FusionRing("A2", 1).f_matrix(inject_variables=True) creating variables fx1..fx8 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 sage: f.start_worker_pool() @@ -528,7 +528,7 @@ cdef class FvarsHandler: sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup - sage: f = FMatrix(FusionRing("B7", 1), inject_variables=True) + sage: f = FusionRing("B7", 1).f_matrix(inject_variables=True) creating variables fx1..fx14 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 sage: f.start_worker_pool() @@ -630,7 +630,7 @@ cdef class FvarsHandler: sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup - sage: f = FMatrix(FusionRing("A3", 1), inject_variables=True) + sage: f = FusionRing("A3", 1).f_matrix(inject_variables=True) creating variables fx1..fx27 Defining fx0, ..., fx26 sage: f.start_worker_pool() @@ -711,7 +711,7 @@ cdef class FvarsHandler: TESTS:: - sage: f = FMatrix(FusionRing("F4", 1)) + sage: f = FusionRing("F4", 1).f_matrix() sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() sage: f.start_worker_pool() @@ -739,7 +739,7 @@ cdef class FvarsHandler: EXAMPLES:: - sage: f = FMatrix(FusionRing("G2", 1), inject_variables=True) + sage: f = FusionRing("G2", 1).f_matrix(inject_variables=True) creating variables fx1..fx5 Defining fx0, fx1, fx2, fx3, fx4 sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler @@ -759,7 +759,7 @@ def make_FvarsHandler(n, field, idx_map, init_data): TESTS:: - sage: f = FMatrix(FusionRing("G2", 1)) + sage: f = FusionRing("G2", 1).f_matrix() sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() sage: f.start_worker_pool() From 10b5e117de06e1700a304192743bbb72ab970de0 Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Fri, 28 Oct 2022 14:21:30 -0700 Subject: [PATCH 307/632] updating pyx files to use new FMatrix interface --- .../algebras/fusion_rings/fast_parallel_fmats_methods.pyx | 8 ++++---- src/sage/algebras/fusion_rings/poly_tup_engine.pyx | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx b/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx index f68fca95369..a768e49a24b 100644 --- a/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx +++ b/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx @@ -39,7 +39,7 @@ cpdef _solve_for_linear_terms(factory, list eqns=None): EXAMPLES:: - sage: f = FMatrix(FusionRing("D3", 1), inject_variables=True) + sage: f = FusionRing("D3", 1).f_matrix(inject_variables=True) creating variables fx1..fx27 Defining fx0, ..., fx26 sage: f._reset_solver_state() @@ -113,7 +113,7 @@ cpdef _backward_subs(factory, bint flatten=True): EXAMPLES:: - sage: f = FMatrix(FusionRing("D3", 1), inject_variables=True) + sage: f = FusionRing("D3", 1).f_matrix(inject_variables=True) creating variables fx1..fx27 Defining fx0, ..., fx26 sage: f._reset_solver_state() @@ -480,12 +480,12 @@ cpdef executor(tuple params): TESTS:: sage: from sage.algebras.fusion_rings.fast_parallel_fmats_methods import executor - sage: fmats = FMatrix(FusionRing("A1", 3)) + sage: fmats = FusionRing("A1", 3).f_matrix() sage: fmats._reset_solver_state() sage: params = (('get_reduced_hexagons', id(fmats)), (0, 1, True)) sage: len(executor(params)) == 63 True - sage: fmats = FMatrix(FusionRing("E6", 1)) + sage: fmats = FusionRing("E6", 1).f_matrix() sage: fmats._reset_solver_state() sage: params = (('get_reduced_hexagons', id(fmats)), (0, 1, False)) sage: len(executor(params)) == 6 diff --git a/src/sage/algebras/fusion_rings/poly_tup_engine.pyx b/src/sage/algebras/fusion_rings/poly_tup_engine.pyx index e14be4106d6..0817c14799f 100644 --- a/src/sage/algebras/fusion_rings/poly_tup_engine.pyx +++ b/src/sage/algebras/fusion_rings/poly_tup_engine.pyx @@ -99,7 +99,7 @@ cpdef tuple _unflatten_coeffs(field, tuple eq_tup): EXAMPLES:: sage: from sage.algebras.fusion_rings.poly_tup_engine import _unflatten_coeffs - sage: fm = FMatrix(FusionRing("A2", 2)) + sage: fm = FusionRing("A2", 2).f_matrix() sage: p = fm._poly_ring.random_element() sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup sage: flat_poly_tup = list() From 696742cd60aa43ff158417a0ef09cb7e000b4f3b Mon Sep 17 00:00:00 2001 From: Willie Aboumrad Date: Fri, 28 Oct 2022 14:51:08 -0700 Subject: [PATCH 308/632] pyx files now use updated FMatrix interface --- .../fast_parallel_fmats_methods.pyx | 8 +++--- .../fast_parallel_fusion_ring_braid_repn.pyx | 14 +++++----- .../algebras/fusion_rings/poly_tup_engine.pyx | 2 +- .../algebras/fusion_rings/shm_managers.pyx | 28 +++++++++---------- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx b/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx index a768e49a24b..bc1ad78e826 100644 --- a/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx +++ b/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx @@ -39,7 +39,7 @@ cpdef _solve_for_linear_terms(factory, list eqns=None): EXAMPLES:: - sage: f = FusionRing("D3", 1).f_matrix(inject_variables=True) + sage: f = FusionRing("D3", 1).get_fmatrix(inject_variables=True, new=True) creating variables fx1..fx27 Defining fx0, ..., fx26 sage: f._reset_solver_state() @@ -113,7 +113,7 @@ cpdef _backward_subs(factory, bint flatten=True): EXAMPLES:: - sage: f = FusionRing("D3", 1).f_matrix(inject_variables=True) + sage: f = FusionRing("D3", 1).get_fmatrix(inject_variables=True, new=True) creating variables fx1..fx27 Defining fx0, ..., fx26 sage: f._reset_solver_state() @@ -480,12 +480,12 @@ cpdef executor(tuple params): TESTS:: sage: from sage.algebras.fusion_rings.fast_parallel_fmats_methods import executor - sage: fmats = FusionRing("A1", 3).f_matrix() + sage: fmats = FusionRing("A1", 3).get_fmatrix() sage: fmats._reset_solver_state() sage: params = (('get_reduced_hexagons', id(fmats)), (0, 1, True)) sage: len(executor(params)) == 63 True - sage: fmats = FusionRing("E6", 1).f_matrix() + sage: fmats = FusionRing("E6", 1).get_fmatrix() sage: fmats._reset_solver_state() sage: params = (('get_reduced_hexagons', id(fmats)), (0, 1, False)) sage: len(executor(params)) == 6 diff --git a/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx b/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx index 1b0ae45e6d0..c68bcd2e88c 100644 --- a/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx +++ b/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx @@ -29,7 +29,7 @@ cdef mid_sig_ij(fusion_ring, row, col, a, b): This method assumes F-matrices are orthogonal. """ # Pre-compute common parameters for efficiency - _fvars = fusion_ring.fmats._fvars + _fvars = fusion_ring.get_fmatrix()._fvars _Nk_ij = fusion_ring.Nk_ij one = fusion_ring.one() @@ -59,7 +59,7 @@ cdef odd_one_out_ij(fusion_ring, xi, xj, a, b): This method assumes F-matrices are orthogonal. """ # Pre-compute common parameters for efficiency - _fvars = fusion_ring.fmats._fvars + _fvars = fusion_ring.get_fmatrix()._fvars _Nk_ij = fusion_ring.Nk_ij one = fusion_ring.one() @@ -103,7 +103,7 @@ cdef sig_2k(fusion_ring, tuple args): Compute entries of the `2k`-th braid generator """ # Pre-compute common parameters for efficiency - _fvars = fusion_ring.fmats._fvars + _fvars = fusion_ring.get_fmatrix()._fvars _Nk_ij = fusion_ring.Nk_ij one = fusion_ring.one() @@ -183,7 +183,7 @@ cdef odd_one_out(fusion_ring, tuple args): odd number of strands. """ # Pre-compute common parameters for efficiency - _fvars = fusion_ring.fmats._fvars + _fvars = fusion_ring.get_fmatrix()._fvars _Nk_ij = fusion_ring.Nk_ij one = fusion_ring.one() @@ -281,14 +281,14 @@ cpdef executor(tuple params): sage: from sage.algebras.fusion_rings.fast_parallel_fusion_ring_braid_repn import executor sage: FR = FusionRing("A1", 4) sage: FR.fusion_labels(['idd', 'one', 'two', 'three', 'four'], inject_variables=True) - sage: FR.fmats.find_orthogonal_solution(verbose=False) # long time + sage: FR.get_fmatrix().find_orthogonal_solution(verbose=False) # long time sage: params = (('sig_2k', id(FR)), (0, 1, (1, one, one, 5))) # long time sage: len(executor(params)) == 13 # long time True sage: from sage.algebras.fusion_rings.fast_parallel_fusion_ring_braid_repn import executor sage: FR = FusionRing("A1", 2) sage: FR.fusion_labels("a", inject_variables=True) - sage: FR.fmats.find_orthogonal_solution(verbose=False) + sage: FR.get_fmatrix().find_orthogonal_solution(verbose=False) sage: params = (('odd_one_out', id(FR)), (0, 1, (a2, a2, 5))) sage: len(executor(params)) == 1 True @@ -323,7 +323,7 @@ cpdef _unflatten_entries(fusion_ring, list entries): True """ F = fusion_ring.fvars_field() - fm = fusion_ring.fmats + fm = fusion_ring.get_fmatrix() if F != QQbar: for i, (coord, entry) in enumerate(entries): entries[i] = (coord, F(entry)) diff --git a/src/sage/algebras/fusion_rings/poly_tup_engine.pyx b/src/sage/algebras/fusion_rings/poly_tup_engine.pyx index 0817c14799f..8255e463492 100644 --- a/src/sage/algebras/fusion_rings/poly_tup_engine.pyx +++ b/src/sage/algebras/fusion_rings/poly_tup_engine.pyx @@ -99,7 +99,7 @@ cpdef tuple _unflatten_coeffs(field, tuple eq_tup): EXAMPLES:: sage: from sage.algebras.fusion_rings.poly_tup_engine import _unflatten_coeffs - sage: fm = FusionRing("A2", 2).f_matrix() + sage: fm = FusionRing("A2", 2).get_fmatrix() sage: p = fm._poly_ring.random_element() sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup sage: flat_poly_tup = list() diff --git a/src/sage/algebras/fusion_rings/shm_managers.pyx b/src/sage/algebras/fusion_rings/shm_managers.pyx index 223f4925761..b5c443d7cc2 100644 --- a/src/sage/algebras/fusion_rings/shm_managers.pyx +++ b/src/sage/algebras/fusion_rings/shm_managers.pyx @@ -76,7 +76,7 @@ cdef class KSHandler: sage: from sage.algebras.fusion_rings.shm_managers import KSHandler sage: # Create shared data structure - sage: f = FusionRing("A1", 2).f_matrix(inject_variables=True) + sage: f = FusionRing("A1", 2).get_fmatrix(inject_variables=True, new=True) creating variables fx1..fx14 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 sage: n = f._poly_ring.ngens() @@ -104,7 +104,7 @@ cdef class KSHandler: sage: from sage.algebras.fusion_rings.shm_managers import KSHandler sage: # Create shared data structure - sage: f = FusionRing("A1", 2).f_matrix(inject_variables=True) + sage: f = FusionRing("A1", 2).get_fmatrix(inject_variables=True, new=True) creating variables fx1..fx14 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 sage: n = f._poly_ring.ngens() @@ -182,7 +182,7 @@ cdef class KSHandler: EXAMPLES:: - sage: f = FusionRing("B5", 1).f_matrix() + sage: f = FusionRing("B5", 1).get_fmatrix() sage: f._reset_solver_state() sage: for idx, sq in f._ks.items(): ....: k @@ -281,7 +281,7 @@ cdef class KSHandler: TESTS:: - sage: f = FusionRing("C2", 2).f_matrix() + sage: f = FusionRing("C2", 2).get_fmatrix() sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) sage: from sage.algebras.fusion_rings.shm_managers import KSHandler @@ -304,7 +304,7 @@ cdef class KSHandler: TESTS:: - sage: f = FusionRing("A3", 1).f_matrix() + sage: f = FusionRing("A3", 1).get_fmatrix() sage: f._reset_solver_state() sage: loads(dumps(f._ks)) == f._ks True @@ -321,7 +321,7 @@ cdef class KSHandler: EXAMPLES:: - sage: f = FusionRing("A3", 1).f_matrix() + sage: f = FusionRing("A3", 1).get_fmatrix() sage: f._reset_solver_state() sage: f.get_orthogonality_constraints(output=False) sage: f._ks.update(f.ideal_basis) @@ -348,7 +348,7 @@ def make_KSHandler(n_slots, field, init_data): TESTS:: - sage: f = FusionRing("B4", 1).f_matrix() + sage: f = FusionRing("B4", 1).get_fmatrix() sage: f._reset_solver_state() sage: loads(dumps(f._ks)) == f._ks # indirect doctest True @@ -433,7 +433,7 @@ cdef class FvarsHandler: sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: # Create shared data structure - sage: f = FusionRing("A2", 1).f_matrix(inject_variables=True) + sage: f = FusionRing("A2", 1).get_fmatrix(inject_variables=True, new=True) creating variables fx1..fx8 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 sage: f.start_worker_pool() @@ -460,7 +460,7 @@ cdef class FvarsHandler: sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: # Create shared data structure - sage: f = FusionRing("A2", 1).f_matrix(inject_variables=True) + sage: f = FusionRing("A2", 1).get_fmatrix(inject_variables=True, new=True) creating variables fx1..fx8 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7 sage: f.start_worker_pool() @@ -528,7 +528,7 @@ cdef class FvarsHandler: sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup - sage: f = FusionRing("B7", 1).f_matrix(inject_variables=True) + sage: f = FusionRing("B7", 1).get_fmatrix(inject_variables=True, new=True) creating variables fx1..fx14 Defining fx0, fx1, fx2, fx3, fx4, fx5, fx6, fx7, fx8, fx9, fx10, fx11, fx12, fx13 sage: f.start_worker_pool() @@ -630,7 +630,7 @@ cdef class FvarsHandler: sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: from sage.algebras.fusion_rings.poly_tup_engine import poly_to_tup - sage: f = FusionRing("A3", 1).f_matrix(inject_variables=True) + sage: f = FusionRing("A3", 1).get_fmatrix(inject_variables=True, new=True) creating variables fx1..fx27 Defining fx0, ..., fx26 sage: f.start_worker_pool() @@ -711,7 +711,7 @@ cdef class FvarsHandler: TESTS:: - sage: f = FusionRing("F4", 1).f_matrix() + sage: f = FusionRing("F4", 1).get_fmatrix() sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() sage: f.start_worker_pool() @@ -739,7 +739,7 @@ cdef class FvarsHandler: EXAMPLES:: - sage: f = FusionRing("G2", 1).f_matrix(inject_variables=True) + sage: f = FusionRing("G2", 1).get_fmatrix(inject_variables=True, new=True) creating variables fx1..fx5 Defining fx0, fx1, fx2, fx3, fx4 sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler @@ -759,7 +759,7 @@ def make_FvarsHandler(n, field, idx_map, init_data): TESTS:: - sage: f = FusionRing("G2", 1).f_matrix() + sage: f = FusionRing("G2", 1).get_fmatrix() sage: from sage.algebras.fusion_rings.shm_managers import FvarsHandler sage: n = f._poly_ring.ngens() sage: f.start_worker_pool() From 7fdda638f1b51472be81d25a66d7c9d70615eebe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 29 Oct 2022 11:30:25 +0200 Subject: [PATCH 309/632] fix suggested details --- src/sage/categories/examples/finite_coxeter_groups.py | 11 +++++------ .../categories/examples/finite_enumerated_sets.py | 4 ++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/sage/categories/examples/finite_coxeter_groups.py b/src/sage/categories/examples/finite_coxeter_groups.py index de49a789fa0..ad37c96e667 100644 --- a/src/sage/categories/examples/finite_coxeter_groups.py +++ b/src/sage/categories/examples/finite_coxeter_groups.py @@ -1,14 +1,13 @@ r""" Examples of finite Coxeter groups """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2008 Nicolas M. Thiery # Copyright (C) 2009 Nicolas Borie # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#****************************************************************************** - +# https://www.gnu.org/licenses/ +# ***************************************************************************** from sage.misc.cachefunc import cached_method from sage.structure.parent import Parent from sage.structure.element_wrapper import ElementWrapper @@ -87,11 +86,11 @@ class DihedralGroup(UniqueRepresentation, Parent): def __init__(self, n=5): r""" - Construct the `n`-th DihedralGroup of order `2 n` + Construct the `n`-th DihedralGroup of order `2 n`. INPUT: - - `n` -- an integer with `n>=2` + - `n` -- an integer with `n>=2` EXAMPLES:: diff --git a/src/sage/categories/examples/finite_enumerated_sets.py b/src/sage/categories/examples/finite_enumerated_sets.py index 24222b4fa91..6a606f7b9e3 100644 --- a/src/sage/categories/examples/finite_enumerated_sets.py +++ b/src/sage/categories/examples/finite_enumerated_sets.py @@ -147,7 +147,7 @@ def lift(self, x): """ INPUT: - - ``x`` -- an element of ``self`` + - ``x`` -- an element of ``self`` Lifts ``x`` to the ambient space for ``self``, as per :meth:`Sets.Subquotients.ParentMethods.lift() @@ -166,7 +166,7 @@ def retract(self, x): """ INPUT: - - ``x`` -- an element of the ambient space for ``self`` + - ``x`` -- an element of the ambient space for ``self`` Retracts ``x`` from the ambient space to ``self``, as per :meth:`Sets.Subquotients.ParentMethods.retract() From 7fc01deff4eb6c2d17bc3785126e07d7ed88c3e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 29 Oct 2022 17:47:32 +0200 Subject: [PATCH 310/632] more fixes --- src/sage/algebras/free_algebra_quotient.py | 16 +++++++++------- .../categories/examples/finite_coxeter_groups.py | 9 +++++---- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/sage/algebras/free_algebra_quotient.py b/src/sage/algebras/free_algebra_quotient.py index a338123e9a6..583eb5f9ae8 100644 --- a/src/sage/algebras/free_algebra_quotient.py +++ b/src/sage/algebras/free_algebra_quotient.py @@ -171,7 +171,7 @@ def _element_constructor_(self, x): """ return self.element_class(self, x) - def _coerce_map_from_(self,S ): + def _coerce_map_from_(self, S): """ EXAMPLES:: @@ -183,7 +183,7 @@ def _coerce_map_from_(self,S ): sage: H._coerce_map_from_(GF(7)) False """ - return S==self or self.__free_algebra.has_coerce_map_from(S) + return S == self or self.__free_algebra.has_coerce_map_from(S) def _repr_(self): """ @@ -197,7 +197,7 @@ def _repr_(self): n = self.__ngens r = self.__module.dimension() x = self.variable_names() - return "Free algebra quotient on %s generators %s and dimension %s over %s"%(n,x,r,R) + return "Free algebra quotient on %s generators %s and dimension %s over %s" % (n, x, r, R) def gen(self, i): """ @@ -361,8 +361,10 @@ def hamilton_quatalg(R): A = FreeAlgebra(R, n, 'i') F = A.monoid() i, j, k = F.gens() - mons = [ F(1), i, j, k ] - M = MatrixSpace(R,4) - mats = [M([0,1,0,0, -1,0,0,0, 0,0,0,-1, 0,0,1,0]), M([0,0,1,0, 0,0,0,1, -1,0,0,0, 0,-1,0,0]), M([0,0,0,1, 0,0,-1,0, 0,1,0,0, -1,0,0,0]) ] - H3 = FreeAlgebraQuotient(A,mons,mats, names=('i','j','k')) + mons = [F(1), i, j, k] + M = MatrixSpace(R, 4) + mats = [M([0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0]), + M([0, 0, 1, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, -1, 0, 0]), + M([0, 0, 0, 1, 0, 0, -1, 0, 0, 1, 0, 0, -1, 0, 0, 0])] + H3 = FreeAlgebraQuotient(A, mons, mats, names=('i', 'j', 'k')) return H3, H3.gens() diff --git a/src/sage/categories/examples/finite_coxeter_groups.py b/src/sage/categories/examples/finite_coxeter_groups.py index ad37c96e667..71ca54c8a5b 100644 --- a/src/sage/categories/examples/finite_coxeter_groups.py +++ b/src/sage/categories/examples/finite_coxeter_groups.py @@ -90,7 +90,7 @@ def __init__(self, n=5): INPUT: - - `n` -- an integer with `n>=2` + - `n` -- an integer with `n \geq 2` EXAMPLES:: @@ -238,12 +238,13 @@ def apply_simple_reflection_right(self, i): return self.parent()(reduced_word[:-1]) else: return self.parent()(reduced_word[1:]) - elif (len(reduced_word) == n-1 and (not self.has_descent(i))) and (reduced_word[0] == 2): - return self.parent()((1,)+reduced_word) + elif (len(reduced_word) == n - 1 and (not self.has_descent(i))) and (reduced_word[0] == 2): + return self.parent()((1,) + reduced_word) else: if self.has_descent(i): return self.parent()(reduced_word[:-1]) else: - return self.parent()(reduced_word+(i,)) + return self.parent()(reduced_word + (i,)) + Example = DihedralGroup From ed9fb97c733c1c3e3cf7cc01526aebc06990a378 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 30 Oct 2022 14:52:28 +0900 Subject: [PATCH 311/632] Fixing some last little details. --- src/sage/algebras/fusion_rings/f_matrix.py | 11 +++++++---- src/sage/algebras/fusion_rings/fusion_ring.py | 16 ++++++++-------- src/sage/combinat/root_system/weyl_characters.py | 2 +- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/sage/algebras/fusion_rings/f_matrix.py b/src/sage/algebras/fusion_rings/f_matrix.py index f63041abc63..618bf40812a 100644 --- a/src/sage/algebras/fusion_rings/f_matrix.py +++ b/src/sage/algebras/fusion_rings/f_matrix.py @@ -1300,6 +1300,7 @@ class methods. fvar_names = self._shared_fvars.shm.name # Initialize worker pool processes args = (id(self), s_name, vd_name, ks_names, fvar_names, n_proc, pids_name) + def init(fmats_id, solved_name, vd_name, ks_names, fvar_names, n_proc, pids_name): """ Connect worker process to shared memory resources @@ -1380,12 +1381,12 @@ def _map_triv_reduce(self, mapper, input_iter, worker_pool=None, chunksize=None, sage: f.shutdown_worker_pool() """ if mp_thresh is None: - mp_thresh = self.mp_thresh + mp_thresh = self.mp_thresh # Compute multiprocessing parameters if worker_pool is not None: try: n = len(input_iter) - except: + except (TypeError, ValueError, AttributeError): n = mp_thresh + 1 if chunksize is None: chunksize = n // (worker_pool._processes**2) + 1 @@ -1675,7 +1676,8 @@ def equations_graph(self, eqns=None): eqns = self.ideal_basis G = Graph() - if not eqns: return G + if not eqns: + return G # Eqns could be a list of poly objects or poly tuples stored in internal repn if isinstance(eqns[0], tuple): @@ -1773,7 +1775,8 @@ def _par_graph_gb(self, eqns=None, term_order="degrevlex", largest_comp=45, verb fx3^2 + (zeta80^24 - zeta80^16)] sage: f.shutdown_worker_pool() """ - if eqns is None: eqns = self.ideal_basis + if eqns is None: + eqns = self.ideal_basis small_comps = list() temp_eqns = list() for comp, comp_eqns in self._partition_eqns(eqns=eqns, verbose=verbose).items(): diff --git a/src/sage/algebras/fusion_rings/fusion_ring.py b/src/sage/algebras/fusion_rings/fusion_ring.py index f392e8b42c2..a4e27c3534a 100644 --- a/src/sage/algebras/fusion_rings/fusion_ring.py +++ b/src/sage/algebras/fusion_rings/fusion_ring.py @@ -2,13 +2,14 @@ Fusion Rings """ # **************************************************************************** -# Copyright (C) 2019 Daniel Bump -# Guillermo Aboumrad -# Travis Scrimshaw -# Nicolas Thiery +# Copyright (C) 2019 Daniel Bump +# Guillermo Aboumrad +# Travis Scrimshaw +# Nicolas Thiery +# 2022 Guillermo Aboumrad # -# Distributed under the terms of the GNU General Public License (GPL) -# https://www.gnu.org/licenses/ +# Distributed under the terms of the GNU General Public License (GPL) +# https://www.gnu.org/licenses/ # **************************************************************************** from itertools import product, zip_longest @@ -22,7 +23,6 @@ from sage.matrix.constructor import matrix from sage.matrix.special import diagonal_matrix from sage.misc.cachefunc import cached_method -from sage.misc.lazy_attribute import lazy_attribute from sage.misc.misc import inject_variable from sage.rings.integer_ring import ZZ from sage.rings.number_field.number_field import CyclotomicField @@ -405,7 +405,7 @@ def test_braid_representation(self, max_strands=6, anyon=None): True """ if not self.is_multiplicity_free(): # Braid group representation is not available if self is not multiplicity free - raise NotImplementedError("only implemented for multiplicity free fusion rings") + raise NotImplementedError("only implemented for multiplicity free fusion rings") b = self.basis() results = [] # Test with different numbers of strands diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index 56163c5ca85..036f8a58614 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -133,7 +133,7 @@ def __init__(self, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, conju self._basecoer = None self._k = k if k is not None: - self._k = Integer(k) + self._k = Integer(k) if ct.is_irreducible(): self._opposition = ct.opposition_automorphism() self._highest = self._space.highest_root() From 589a5b86e6d67a3738ba595f7b62a0a2af004b6a Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 30 Oct 2022 15:07:46 +0900 Subject: [PATCH 312/632] Just a few more polish. --- src/sage/algebras/fusion_rings/all.py | 1 + src/sage/algebras/fusion_rings/f_matrix.py | 36 +++++++++---------- .../fast_parallel_fmats_methods.pyx | 3 +- .../fast_parallel_fusion_ring_braid_repn.pyx | 2 +- src/sage/algebras/fusion_rings/fusion_ring.py | 28 +++++++-------- .../algebras/fusion_rings/poly_tup_engine.pxd | 1 + .../algebras/fusion_rings/poly_tup_engine.pyx | 2 +- .../algebras/fusion_rings/shm_managers.pyx | 7 ++-- 8 files changed, 39 insertions(+), 41 deletions(-) diff --git a/src/sage/algebras/fusion_rings/all.py b/src/sage/algebras/fusion_rings/all.py index 946524a7f43..4a7b2196938 100644 --- a/src/sage/algebras/fusion_rings/all.py +++ b/src/sage/algebras/fusion_rings/all.py @@ -17,3 +17,4 @@ lazy_import('sage.algebras.fusion_rings.fusion_ring', ['FusionRing']) del lazy_import + diff --git a/src/sage/algebras/fusion_rings/f_matrix.py b/src/sage/algebras/fusion_rings/f_matrix.py index 618bf40812a..47f04a5e125 100644 --- a/src/sage/algebras/fusion_rings/f_matrix.py +++ b/src/sage/algebras/fusion_rings/f_matrix.py @@ -1,5 +1,5 @@ r""" -F-Matrices of Fusion Rings +The F-Matrix of a Fusion Ring """ # **************************************************************************** # Copyright (C) 2019 Daniel Bump @@ -45,7 +45,7 @@ class FMatrix(SageObject): r""" - Return an F-Matrix factory for a :class:`FusionRing`. + An F-matrix for a :class:`FusionRing`. INPUT: @@ -222,9 +222,7 @@ class FMatrix(SageObject): smaller examples. For example the :class:`FusionRing` for `G_2` at level 2 is too large. When it is available, this method produces an F-matrix whose entries are in the same - cyclotomic field as the underlying :class:`FusionRing`. - - :: + cyclotomic field as the underlying :class:`FusionRing`. :: sage: f.find_cyclotomic_solution() Setting up hexagons and pentagons... @@ -254,9 +252,7 @@ class FMatrix(SageObject): modified, adding an attribute ``_basecoer`` that is a coercion from the cyclotomic field to the field containing the F-matrix. The field containing the F-matrix - is available through :meth:`field`. - - :: + is available through :meth:`field`. :: sage: f = FusionRing("B3", 2).get_fmatrix() sage: f.find_orthogonal_solution(verbose=False, checkpoint=True) # not tested (~100 s) @@ -795,10 +791,8 @@ def get_coerce_map_from_fr_cyclotomic_field(self): When F-symbols are computed as elements of the associated :class:`FusionRing`'s base :class:`Cyclotomic field`, - we have ``self.field() == self.FR().field()`` and this method - returns the identity map on ``self.field()``. - - :: + we have ``self.field() == self.FR().field()`` and this + returns the identity map on ``self.field()``. :: sage: f = FusionRing("A2", 1).get_fmatrix() sage: f.find_orthogonal_solution(verbose=False) @@ -849,7 +843,7 @@ def get_fvars_in_alg_field(self): def get_radical_expression(self): """ - Return radical expression of F-symbols for easy visualization + Return a radical expression of F-symbols. EXAMPLES:: @@ -885,7 +879,8 @@ def _get_known_vals(self): def _get_known_nonz(self): r""" - Construct an ETuple indicating positions of known nonzero variables. + Construct an :class:`ETuple` indicating positions of + known nonzero variables. .. NOTE:: @@ -1930,7 +1925,7 @@ def _get_explicit_solution(self, eqns=None, verbose=True): one = self._field.one() for fx, rhs in self._ks.items(): if not self._solved[fx]: - lt = (ETuple({fx : 2}, n), one) + lt = (ETuple({fx: 2}, n), one) eqns.append(((lt, (ETuple({}, n), -rhs)))) eqns_partition = self._partition_eqns(verbose=verbose) @@ -1994,11 +1989,11 @@ def _get_explicit_solution(self, eqns=None, verbose=True): assert sum(self._solved) == nvars, "Some F-symbols are still missing...{}".format([self._poly_ring.gen(fx) for fx in range(nvars) if not self._solved[fx]]) # Backward substitution step. Traverse variables in reverse lexicographical order. (System is in triangular form) - self._fvars = {sextuple : apply_coeff_map(rhs, phi) for sextuple, rhs in self._fvars.items()} + self._fvars = {sextuple: apply_coeff_map(rhs, phi) for sextuple, rhs in self._fvars.items()} for fx, rhs in numeric_fvars.items(): self._fvars[self._idx_to_sextuple[fx]] = ((ETuple({}, nvars), rhs), ) _backward_subs(self, flatten=False) - self._fvars = {sextuple : constant_coeff(rhs, self._field) for sextuple, rhs in self._fvars.items()} + self._fvars = {sextuple: constant_coeff(rhs, self._field) for sextuple, rhs in self._fvars.items()} # Update base field attributes self._FR._field = self.field() @@ -2186,7 +2181,7 @@ def find_orthogonal_solution(self, checkpoint=False, save_results="", warm_start def _fix_gauge(self, algorithm=""): r""" - Fix the gauge by forcing F-symbols not already fixed to equal 1. + Fix the gauge by forcing F-symbols not already fixed to equal `1`. .. NOTE:: @@ -2221,7 +2216,7 @@ def _fix_gauge(self, algorithm=""): self._update_equations() def _substitute_degree_one(self, eqns=None): - """ + r""" Substitute known value from linear univariate polynomial and solve, following [Bond2007]_ p.37, for two-term linear equation for one of the variables. @@ -2287,7 +2282,7 @@ def _update_equations(self): def find_cyclotomic_solution(self, equations=None, algorithm="", verbose=True, output=False): r""" - Solve the the hexagon and pentagon relations to evaluate the F-matrix. + Solve the hexagon and pentagon relations to evaluate the F-matrix. This method (omitting the orthogonality constraints) produces output in the cyclotomic field, but it is very limited in the size @@ -2462,3 +2457,4 @@ def certify_pentagons(self, use_mp=True, verbose=False): print("Something went wrong. Pentagons remain.") self._fvars = fvars_copy return pe + diff --git a/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx b/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx index bc1ad78e826..64b8254f13a 100644 --- a/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx +++ b/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx @@ -1,5 +1,5 @@ """ -Fast F-Matrix methods +Fast F-Matrix Methods """ # **************************************************************************** # Copyright (C) 2021 Guillermo Aboumrad @@ -539,3 +539,4 @@ cdef pent_verify(factory, tuple mp_params): feq_verif(factory, worker_results, fvars, Nk_ij, id_anyon, nonuple) if i % 50000000 == 0 and i and verbose: print("{:5d}m equations checked... {} potential misses so far...".format(i // 1000000, len(worker_results))) + diff --git a/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx b/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx index c68bcd2e88c..9fcb4408c21 100644 --- a/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx +++ b/src/sage/algebras/fusion_rings/fast_parallel_fusion_ring_braid_repn.pyx @@ -1,5 +1,5 @@ """ -Fast FusionRing methods for computing braid group representations +Fast Fusion Ring Methods for Computing Braid Group Representations """ # **************************************************************************** # Copyright (C) 2021 Guillermo Aboumrad diff --git a/src/sage/algebras/fusion_rings/fusion_ring.py b/src/sage/algebras/fusion_rings/fusion_ring.py index a4e27c3534a..a9bc2eed23a 100644 --- a/src/sage/algebras/fusion_rings/fusion_ring.py +++ b/src/sage/algebras/fusion_rings/fusion_ring.py @@ -572,8 +572,7 @@ def fvars_field(self): """ if self.is_multiplicity_free(): return self.get_fmatrix().field() - else: - raise ValueError("Method is only available for multiplicity free fusion rings.") + raise NotImplementedError("method is only available for multiplicity free fusion rings") def root_of_unity(self, r, base_coercion=True): r""" @@ -592,13 +591,12 @@ def root_of_unity(self, r, base_coercion=True): [1, -1, zeta24^4 - 1, zeta24^6, None, zeta24^4, None] """ n = 2 * r * self._cyclotomic_order - if n in ZZ: - ret = self.field().gen() ** n - if (not base_coercion) or (self._basecoer is None): - return ret - return self._basecoer(ret) - else: - return None + if n not in ZZ: + raise ValueError("not an integer root of unity") + ret = self.field().gen() ** n + if (not base_coercion) or (self._basecoer is None): + return ret + return self._basecoer(ret) def get_order(self): r""" @@ -908,11 +906,11 @@ def s_matrix(self, unitary=False, base_coercion=True): [0 0 0 1] """ b = self.basis() - S = matrix([[self.s_ij(b[x], b[y], base_coercion=base_coercion) for x in self.get_order()] for y in self.get_order()]) + S = matrix([[self.s_ij(b[x], b[y], base_coercion=base_coercion) + for x in self.get_order()] for y in self.get_order()]) if unitary: return S / self.total_q_order(base_coercion=base_coercion) - else: - return S + return S @cached_method def r_matrix(self, i, j, k, base_coercion=True): @@ -987,7 +985,7 @@ def global_q_dimension(self, base_coercion=True): sage: FusionRing("E6", 1).global_q_dimension() 3 """ - ret = sum(x.q_dimension(base_coercion=False)**2 for x in self.basis()) + ret = sum(x.q_dimension(base_coercion=False) ** 2 for x in self.basis()) if (not base_coercion) or (self._basecoer is None): return ret return self._basecoer(ret) @@ -1486,8 +1484,7 @@ def twist(self, reduced=True): f = twist.floor() twist -= f return twist + (f % 2) - else: - return twist + return twist def ribbon(self, base_coercion=True): r""" @@ -1565,3 +1562,4 @@ def q_dimension(self, base_coercion=True): if (not base_coercion) or (self.parent()._basecoer is None): return ret return self.parent()._basecoer(ret) + diff --git a/src/sage/algebras/fusion_rings/poly_tup_engine.pxd b/src/sage/algebras/fusion_rings/poly_tup_engine.pxd index a4ddf9b92ae..22c6449385a 100644 --- a/src/sage/algebras/fusion_rings/poly_tup_engine.pxd +++ b/src/sage/algebras/fusion_rings/poly_tup_engine.pxd @@ -21,3 +21,4 @@ cdef tuple reduce_poly_dict(dict eq_dict, ETuple nonz, KSHandler known_sq, Numbe cdef tuple _flatten_coeffs(tuple eq_tup) cpdef tuple _unflatten_coeffs(field, tuple eq_tup) cdef int has_appropriate_linear_term(tuple eq_tup) + diff --git a/src/sage/algebras/fusion_rings/poly_tup_engine.pyx b/src/sage/algebras/fusion_rings/poly_tup_engine.pyx index 8255e463492..47e403c6d2c 100644 --- a/src/sage/algebras/fusion_rings/poly_tup_engine.pyx +++ b/src/sage/algebras/fusion_rings/poly_tup_engine.pyx @@ -350,7 +350,6 @@ cdef dict subs_squares(dict eq_dict, KSHandler known_sq): - ``eq_dict`` -- a dictionary of ``(ETuple, coeff)`` pairs representing a polynomial - - ``known_sq`` -- a dictionary of ``(int i, NumberFieldElement a)`` pairs such that `x_i^2 - a = 0` @@ -577,3 +576,4 @@ cpdef tuple poly_tup_sortkey(tuple eq_tup): key.append(-exp._data[2*i]) key.append(exp._data[2*i+1]) return tuple(key) + diff --git a/src/sage/algebras/fusion_rings/shm_managers.pyx b/src/sage/algebras/fusion_rings/shm_managers.pyx index b5c443d7cc2..5d7ec652613 100644 --- a/src/sage/algebras/fusion_rings/shm_managers.pyx +++ b/src/sage/algebras/fusion_rings/shm_managers.pyx @@ -1,11 +1,11 @@ r""" -Shared memory managers for F-symbol attributes. +Shared Memory Managers for F-Symbol Attributes This module provides an implementation for shared dictionary like state attributes required by the orthogonal F-matrix solver. -Currently, the attributes only work when the base field of the FMatrix -factory is a cyclotomic field. +Currently, the attributes only work when the base field of the +:class:`FMatrix` factory is a cyclotomic field. """ # **************************************************************************** @@ -773,3 +773,4 @@ def make_FvarsHandler(n, field, idx_map, init_data): sage: f.shutdown_worker_pool() """ return FvarsHandler(n, field, idx_map, init_data=init_data) + From d9de90ace8082a5ac80421e2f57c7898bc4dd991 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 30 Oct 2022 13:48:17 +0100 Subject: [PATCH 313/632] fix deprecated use of PyEval_Call --- src/sage/modular/arithgroup/farey.cpp | 2 +- src/sage/symbolic/ginac/function.cpp | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/modular/arithgroup/farey.cpp b/src/sage/modular/arithgroup/farey.cpp index a98ade699d0..b999728b57e 100644 --- a/src/sage/modular/arithgroup/farey.cpp +++ b/src/sage/modular/arithgroup/farey.cpp @@ -256,7 +256,7 @@ bool is_element_general::is_member(const SL2Z& m) const { PyObject* arg = convert_to_SL2Z(m); PyObject* tuple = PyTuple_New(1); PyTuple_SetItem(tuple, 0, arg); - PyObject *result = PyEval_CallObject(method, tuple); + PyObject *result = PyObject_Call(method, tuple); Py_DECREF(tuple); if( not PyBool_Check(result) ) { cerr << "__contains__ does not return bool." << endl; diff --git a/src/sage/symbolic/ginac/function.cpp b/src/sage/symbolic/ginac/function.cpp index 689e2b8a22f..67d608c6240 100644 --- a/src/sage/symbolic/ginac/function.cpp +++ b/src/sage/symbolic/ginac/function.cpp @@ -976,7 +976,7 @@ ex function::evalf(int level, PyObject* kwds) const // convert seq to a PyTuple of Expressions PyObject* args = py_funcs.exvector_to_PyTuple(eseq); // call opt.evalf_f with this list - PyObject* pyresult = PyEval_CallObjectWithKeywords( + PyObject* pyresult = PyObject_Call( PyObject_GetAttrString(reinterpret_cast(opt.evalf_f), "_evalf_"), args, kwds); Py_DECREF(args); @@ -1056,7 +1056,7 @@ ex function::series(const relational & r, int order, unsigned options) const // add the point of expansion as a keyword argument PyDict_SetItemString(kwds, "at", py_funcs.ex_to_pyExpression(r.rhs())); // call opt.series_f with this list - PyObject* pyresult = PyEval_CallObjectWithKeywords( + PyObject* pyresult = PyObject_Call( PyObject_GetAttrString(reinterpret_cast(opt.series_f), "_series_"), args, kwds); Py_DECREF(args); @@ -1321,7 +1321,7 @@ ex function::derivative(const symbol & s) const PyObject* kwds = Py_BuildValue("{s:O}","diff_param", symb); // call opt.derivative_f with this list - PyObject* pyresult = PyEval_CallObjectWithKeywords( + PyObject* pyresult = PyObject_Call( PyObject_GetAttrString( reinterpret_cast(opt.derivative_f), "_tderivative_"), args, kwds); @@ -1478,7 +1478,7 @@ ex function::pderivative(unsigned diff_param) const // partial differentiation // create a dictionary {'diff_param': diff_param} PyObject* kwds = Py_BuildValue("{s:I}","diff_param",diff_param); // call opt.derivative_f with this list - PyObject* pyresult = PyEval_CallObjectWithKeywords( + PyObject* pyresult = PyObject_Call( PyObject_GetAttrString(reinterpret_cast(opt.derivative_f), "_derivative_"), args, kwds); Py_DECREF(args); @@ -1557,7 +1557,7 @@ ex function::power(const ex & power_param) const // power of function PyObject* kwds = PyDict_New(); PyDict_SetItemString(kwds, "power_param", py_funcs.ex_to_pyExpression(power_param)); // call opt.power_f with this list - PyObject* pyresult = PyEval_CallObjectWithKeywords( + PyObject* pyresult = PyObject_Call( PyObject_GetAttrString(reinterpret_cast(opt.power_f), "_power_"), args, kwds); Py_DECREF(args); From 285c58f75903c835647cf1ecbaa5cc220890368d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 30 Oct 2022 14:28:02 +0100 Subject: [PATCH 314/632] fix detail --- src/sage/modular/arithgroup/farey.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/modular/arithgroup/farey.cpp b/src/sage/modular/arithgroup/farey.cpp index b999728b57e..34f5e5727db 100644 --- a/src/sage/modular/arithgroup/farey.cpp +++ b/src/sage/modular/arithgroup/farey.cpp @@ -256,7 +256,7 @@ bool is_element_general::is_member(const SL2Z& m) const { PyObject* arg = convert_to_SL2Z(m); PyObject* tuple = PyTuple_New(1); PyTuple_SetItem(tuple, 0, arg); - PyObject *result = PyObject_Call(method, tuple); + PyObject *result = PyObject_CallObject(method, tuple); Py_DECREF(tuple); if( not PyBool_Check(result) ) { cerr << "__contains__ does not return bool." << endl; From f38224119f28f3439c46eb7ba1b403ee510ad181 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Mon, 6 Jun 2022 13:53:39 +0200 Subject: [PATCH 315/632] trac #33562: better error message --- src/sage/graphs/generic_graph.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index f2c94041c51..a9378efbdf3 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -2386,6 +2386,19 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None, *, base_ring=Non [0 0 0] [1 0 0] [1 0 0] + + Check error message for non numerical edge weights (:trac:`33562`):: + + sage: G = Graph([(0, 1)]) + sage: G.weighted_adjacency_matrix() + Traceback (most recent call last): + ... + ValueError: the weight function cannot find the weight of (0, 1, None) + sage: G = Graph([(0, 1, 'a')]) + sage: G.weighted_adjacency_matrix() + Traceback (most recent call last): + ... + ValueError: the weight function cannot find the weight of (0, 1, 'a') """ if self.has_multiple_edges(): raise NotImplementedError("don't know how to represent weights for a multigraph") @@ -2396,6 +2409,15 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None, *, base_ring=Non set(vertices) != set(self.vertex_iterator())): raise ValueError("``vertices`` must be a permutation of the vertices") + # Check the edge weights + if base_ring is None: + def weight_function(e): + return e[2] + else: + def weight_function(e): + return base_ring(e[2]) + self._check_weight_function(weight_function) + new_indices = {v: i for i,v in enumerate(vertices)} D = {} From 7dee54304ef6799886b8677bc6f9b0c09be4148f Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sun, 30 Oct 2022 14:00:35 +0100 Subject: [PATCH 316/632] trac #33562: add paramter default_weight --- src/sage/graphs/generic_graph.py | 43 +++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index a9378efbdf3..965404177fa 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -2316,7 +2316,8 @@ def distance_matrix(self, vertices=None, *, base_ring=None, **kwds): ret.set_immutable() return ret - def weighted_adjacency_matrix(self, sparse=True, vertices=None, *, base_ring=None, **kwds): + def weighted_adjacency_matrix(self, sparse=True, vertices=None, + default_weight=None, *, base_ring=None, **kwds): """ Return the weighted adjacency matrix of the graph. @@ -2333,6 +2334,10 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None, *, base_ring=Non each vertex is represented by its position in the list returned by method :meth:`vertices` + - ``default_weight`` -- (default: ``None``); specifies the weight to + replace any ``None`` edge label. When not specified an error is raised + if the label of an edge is ``None``. + - ``base_ring`` -- a ring (default: determined from the weights); the base ring of the matrix space to use. @@ -2393,12 +2398,15 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None, *, base_ring=Non sage: G.weighted_adjacency_matrix() Traceback (most recent call last): ... - ValueError: the weight function cannot find the weight of (0, 1, None) + ValueError: cannot find the weight of (0, 1, None). Consider setting parameter 'default_weight' + sage: G.weighted_adjacency_matrix(default_weight=3) + [0 3] + [3 0] sage: G = Graph([(0, 1, 'a')]) sage: G.weighted_adjacency_matrix() Traceback (most recent call last): ... - ValueError: the weight function cannot find the weight of (0, 1, 'a') + TypeError: Cannot convert NoneType to sage.structure.parent.Parent """ if self.has_multiple_edges(): raise NotImplementedError("don't know how to represent weights for a multigraph") @@ -2409,29 +2417,34 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None, *, base_ring=Non set(vertices) != set(self.vertex_iterator())): raise ValueError("``vertices`` must be a permutation of the vertices") - # Check the edge weights - if base_ring is None: - def weight_function(e): - return e[2] + # Method for checking edge weights and setting default weight + if default_weight is None: + def func(u, v, label): + if label is None: + raise ValueError(f"cannot find the weight of ({u}, {v}, None). " + "Consider setting parameter 'default_weight'") + return label else: - def weight_function(e): - return base_ring(e[2]) - self._check_weight_function(weight_function) + def func(u, v, label): + if label is None: + return default_weight + return label new_indices = {v: i for i,v in enumerate(vertices)} D = {} if self._directed: - for u, v, l in self.edge_iterator(): + for u, v, label in self.edge_iterator(): i = new_indices[u] j = new_indices[v] - D[i,j] = l + D[i,j] = func(u, v, label) else: - for u, v, l in self.edge_iterator(): + for u, v, label in self.edge_iterator(): i = new_indices[u] j = new_indices[v] - D[i,j] = l - D[j,i] = l + label = func(u, v, label) + D[i,j] = label + D[j,i] = label from sage.matrix.constructor import matrix if base_ring is None: M = matrix(self.num_verts(), D, sparse=sparse, **kwds) From 88733f9402fa27c280430dc1009610d263b67ed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 30 Oct 2022 17:37:24 +0100 Subject: [PATCH 317/632] remove traces of lgtm --- .lgtm.yml | 23 ---------------------- src/doc/en/developer/tools.rst | 11 ----------- src/sage/algebras/lie_algebras/examples.py | 17 ++++++++++------ 3 files changed, 11 insertions(+), 40 deletions(-) delete mode 100644 .lgtm.yml diff --git a/.lgtm.yml b/.lgtm.yml deleted file mode 100644 index 0650ffa1eee..00000000000 --- a/.lgtm.yml +++ /dev/null @@ -1,23 +0,0 @@ -queries: - - exclude: py/call/wrong-named-class-argument - - exclude: py/call/wrong-number-class-arguments - - exclude: py/similar-function - - exclude: py/unsafe-cyclic-import -path_classifiers: - imports_only: - - "**/all.py" - - "**/all_*.py" - - "**/*catalog*.py" - - "**/species/library.py" - - "**/categories/basic.py" - - "**/combinat/ribbon.py" - - "**/combinat/family.py" - - "**/interacts/geometry.py" - - "**/matroids/advanced.py" - - "**/matroids/named_matroids.py" - - "**/modular/congroup.py" - - "**/quadratic_forms/quadratic_form__mass.py" -extraction: - python: - python_setup: - version: 3 diff --git a/src/doc/en/developer/tools.rst b/src/doc/en/developer/tools.rst index becaad7f829..e2ac3ef82dd 100644 --- a/src/doc/en/developer/tools.rst +++ b/src/doc/en/developer/tools.rst @@ -319,14 +319,3 @@ Pyright Pyflakes ======== `Pyflakes `_ checks for common coding errors. - - -.. _section-tools-lgtm: - -LGTM -==== -The website ``lgtm.com`` offers a detailed diagnostic about the global code quality and its evolution. - -The reports can be found `here `_. - -Our choice of configuration is made in ``.lgtm.yml``. diff --git a/src/sage/algebras/lie_algebras/examples.py b/src/sage/algebras/lie_algebras/examples.py index d466856a7b9..99106f8a1ec 100644 --- a/src/sage/algebras/lie_algebras/examples.py +++ b/src/sage/algebras/lie_algebras/examples.py @@ -41,7 +41,7 @@ from sage.algebras.lie_algebras.classical_lie_algebra import ClassicalMatrixLieAlgebra as ClassicalMatrix -# the next 6 lines are here to silent pyflakes and lgtm warnings +# the next 6 lines are here to silent pyflakes warnings assert VirasoroAlgebra assert RankTwoHeisenbergVirasoro assert OnsagerAlgebra @@ -97,6 +97,7 @@ def three_dimensional(R, a, b, c, d, names=['X', 'Y', 'Z']): s_coeff = {(X,Y): {Z:a, Y:d}, (Y,Z): {X:b}, (Z,X): {Y:c, Z:d}} return LieAlgebraWithStructureCoefficients(R, s_coeff, tuple(names)) + def cross_product(R, names=['X', 'Y', 'Z']): r""" The Lie algebra of `\RR^3` defined by the usual cross product @@ -160,7 +161,7 @@ def three_dimensional_by_rank(R, n, a=None, names=['X', 'Y', 'Z']): return AbelianLieAlgebra(R, names=names) if n == 1: - L = three_dimensional(R, 0, 1, 0, 0, names=names) # Strictly upper triangular matrices + L = three_dimensional(R, 0, 1, 0, 0, names=names) # Strictly upper triangular matrices L.rename("Lie algebra of 3x3 strictly upper triangular matrices over {}".format(R)) return L @@ -184,18 +185,19 @@ def three_dimensional_by_rank(R, n, a=None, names=['X', 'Y', 'Z']): return L if n == 3: - #return sl(R, 2) + # return sl(R, 2) from sage.algebras.lie_algebras.structure_coefficients import LieAlgebraWithStructureCoefficients E = names[0] F = names[1] H = names[2] - s_coeff = { (E,F): {H:R.one()}, (H,E): {E:R(2)}, (H,F): {F:R(-2)} } + s_coeff = {(E, F): {H: R.one()}, (H, E): {E: R(2)}, (H, F): {F: R(-2)}} L = LieAlgebraWithStructureCoefficients(R, s_coeff, tuple(names)) L.rename("sl2 over {}".format(R)) return L raise ValueError("Invalid rank") + def affine_transformations_line(R, names=['X', 'Y'], representation='bracket'): """ The Lie algebra of affine transformations of the line. @@ -233,6 +235,7 @@ def affine_transformations_line(R, names=['X', 'Y'], representation='bracket'): L.rename("Lie algebra of affine transformations of a line over {}".format(R)) return L + def abelian(R, names=None, index_set=None): """ Return the abelian Lie algebra generated by ``names``. @@ -365,6 +368,7 @@ def upper_triangular_matrices(R, n): L.rename("Lie algebra of {}-dimensional upper triangular matrices over {}".format(n, L.base_ring())) return L + def strictly_upper_triangular_matrices(R, n): r""" Return the Lie algebra `\mathfrak{n}_k` of strictly `k \times k` upper @@ -401,13 +405,13 @@ def strictly_upper_triangular_matrices(R, n): MS = MatrixSpace(R, n, sparse=True) one = R.one() names = tuple('n{}'.format(i) for i in range(n-1)) - gens = tuple(MS({(i,i+1):one}) for i in range(n-1)) + gens = tuple(MS({(i,i+1): one}) for i in range(n-1)) L = LieAlgebraFromAssociative(MS, gens, names=names) L.rename("Lie algebra of {}-dimensional strictly upper triangular matrices over {}".format(n, L.base_ring())) return L ##################################################################### -## Classical Lie algebras +# Classical Lie algebras def sl(R, n, representation='bracket'): @@ -511,6 +515,7 @@ def su(R, n, representation='matrix'): return MatrixCompactRealForm(R, CartanType(['A', n-1])) raise ValueError("invalid representation") + def so(R, n, representation='bracket'): r""" The Lie algebra `\mathfrak{so}_n`. From 52d539b1e0a8b9d13797168a8cf0d1e4fafef8f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 30 Oct 2022 18:34:13 +0100 Subject: [PATCH 318/632] fix typo --- src/sage/algebras/lie_algebras/examples.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/algebras/lie_algebras/examples.py b/src/sage/algebras/lie_algebras/examples.py index 99106f8a1ec..2017ad3c6d2 100644 --- a/src/sage/algebras/lie_algebras/examples.py +++ b/src/sage/algebras/lie_algebras/examples.py @@ -41,7 +41,7 @@ from sage.algebras.lie_algebras.classical_lie_algebra import ClassicalMatrixLieAlgebra as ClassicalMatrix -# the next 6 lines are here to silent pyflakes warnings +# the next 6 lines are here to silence pyflakes warnings assert VirasoroAlgebra assert RankTwoHeisenbergVirasoro assert OnsagerAlgebra From ae432e3ffc5e2464021f4e0d96e6d060d57468ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 30 Oct 2022 20:55:10 +0100 Subject: [PATCH 319/632] clean W293 in py files (but one) and some pyx files --- src/sage/calculus/var.pyx | 4 ++-- src/sage/databases/knotinfo_db.py | 8 +++---- src/sage/doctest/fixtures.py | 1 - src/sage/features/__init__.py | 3 +-- src/sage/geometry/fan_morphism.py | 10 ++++---- .../geometry/hyperplane_arrangement/plot.py | 12 +++++----- .../triangulation/point_configuration.py | 4 ++-- src/sage/interfaces/gap.py | 2 -- src/sage/interfaces/gap3.py | 12 ++++++---- src/sage/interfaces/interface.py | 2 -- src/sage/interfaces/lisp.py | 4 +--- src/sage/interfaces/macaulay2.py | 2 -- src/sage/interfaces/magma.py | 6 +---- src/sage/interfaces/mathematica.py | 4 +--- src/sage/interfaces/mathics.py | 2 -- src/sage/interfaces/maxima_abstract.py | 2 -- src/sage/interfaces/octave.py | 2 -- src/sage/interfaces/primecount.py | 2 +- src/sage/interfaces/r.py | 4 +--- src/sage/libs/pari/convert_gmp.pyx | 2 +- src/sage/manifolds/catalog.py | 4 ++-- src/sage/matrix/matrix_gap.pyx | 2 +- src/sage/misc/fast_methods.pyx | 2 +- src/sage/misc/unknown.py | 2 -- .../modular/btquotients/pautomorphicform.py | 2 -- .../numerical/backends/generic_backend.pyx | 4 ++-- src/sage/numerical/gauss_legendre.pyx | 4 ++-- src/sage/numerical/linear_tensor.py | 10 ++++---- .../numerical/linear_tensor_constraints.py | 8 +++---- src/sage/rings/function_field/element.pyx | 4 ++-- src/sage/rings/noncommutative_ideals.pyx | 2 +- .../number_field/number_field_morphisms.pyx | 4 ++-- .../small_primes_of_degree_one.py | 2 +- .../polynomial/laurent_polynomial_ideal.py | 24 +++++++++---------- .../polynomial/laurent_polynomial_ring.py | 2 +- .../rings/semirings/tropical_semiring.pyx | 2 +- src/sage/rings/tate_algebra_ideal.pyx | 6 ++--- src/sage/sat/solvers/cryptominisat.py | 9 +++---- src/sage/sat/solvers/picosat.py | 9 +++---- src/sage/sat/solvers/satsolver.pyx | 2 +- src/sage/schemes/elliptic_curves/padics.py | 6 ++--- .../product_projective/rational_point.py | 2 +- .../projective/projective_rational_point.py | 2 +- .../judson-abstract-algebra/boolean-sage.py | 2 -- .../judson-abstract-algebra/rings-sage.py | 1 - 45 files changed, 89 insertions(+), 116 deletions(-) diff --git a/src/sage/calculus/var.pyx b/src/sage/calculus/var.pyx index 85359ac098d..ed7a4305188 100644 --- a/src/sage/calculus/var.pyx +++ b/src/sage/calculus/var.pyx @@ -343,11 +343,11 @@ def function(s, **kwds): TESTS: Make sure that :trac:`15860` is fixed and whitespaces are removed:: - + sage: function('A, B') (A, B) sage: B - B + B """ G = globals() # this is the reason the code must be in Cython. v = new_function(s, **kwds) diff --git a/src/sage/databases/knotinfo_db.py b/src/sage/databases/knotinfo_db.py index 759ded16cac..d70e6be6bac 100644 --- a/src/sage/databases/knotinfo_db.py +++ b/src/sage/databases/knotinfo_db.py @@ -603,16 +603,16 @@ def _create_data_sobj(self, sobj_path=None): val_list.append(knot_list[i][col.name]) - if col.column_type() != col.types.OnlyKnots: - for i in range(1 , len_links): + if col.column_type() != col.types.OnlyKnots: + for i in range(1, len_links): if col.name == self._names_column: link_name = link_list[i][col.name] link_name = link_name.replace('{', '_') link_name = link_name.replace(',', '_') link_name = link_name.replace('}', '') - + num_comp = int(link_list[i][self._components_column]) - row_dict[link_name] = [i + len_knots - 2 , num_comp] + row_dict[link_name] = [i + len_knots - 2, num_comp] val_list.append(link_list[i][col.name]) diff --git a/src/sage/doctest/fixtures.py b/src/sage/doctest/fixtures.py index 24d0bf9ae01..e6f8fc45f8e 100644 --- a/src/sage/doctest/fixtures.py +++ b/src/sage/doctest/fixtures.py @@ -340,7 +340,6 @@ def trace_method(obj, meth, **kwds): - ``reads`` -- (default: ``True``) whether to trace read access as well. - EXAMPLES:: diff --git a/src/sage/features/__init__.py b/src/sage/features/__init__.py index fd899aa4770..ed1059cecbe 100644 --- a/src/sage/features/__init__.py +++ b/src/sage/features/__init__.py @@ -371,8 +371,6 @@ def __bool__(self): """ return bool(self.is_present) - - def __repr__(self): r""" TESTS:: @@ -386,6 +384,7 @@ def __repr__(self): _cache_package_systems = None + def package_systems(): """ Return a list of :class:`~sage.features.pkg_systems.PackageSystem` objects diff --git a/src/sage/geometry/fan_morphism.py b/src/sage/geometry/fan_morphism.py index d7ba7ac7ea6..e94e2cce8c0 100644 --- a/src/sage/geometry/fan_morphism.py +++ b/src/sage/geometry/fan_morphism.py @@ -1477,7 +1477,7 @@ def is_dominant(self): If the fan morphism is dominant, then the associated morphism of toric varieties is dominant in the algebraic-geometric sense (that is, surjective onto a dense subset). - + OUTPUT: Boolean. @@ -1878,13 +1878,13 @@ def factor(self): def relative_star_generators(self, domain_cone): """ Return the relative star generators of ``domain_cone``. - + INPUT: - + - ``domain_cone`` -- a cone of the :meth:`domain_fan` of ``self``. - + OUTPUT: - + - :meth:`~RationalPolyhedralFan.star_generators` of ``domain_cone`` viewed as a cone of :meth:`preimage_fan` of :meth:`image_cone` of ``domain_cone``. diff --git a/src/sage/geometry/hyperplane_arrangement/plot.py b/src/sage/geometry/hyperplane_arrangement/plot.py index 875fa0de46e..9f89585578e 100644 --- a/src/sage/geometry/hyperplane_arrangement/plot.py +++ b/src/sage/geometry/hyperplane_arrangement/plot.py @@ -139,7 +139,7 @@ def plot(hyperplane_arrangement, **kwds): :mod:`sage.geometry.hyperplane_arrangement.plot`. OUTPUT: - + A graphics object of the plot. EXAMPLES:: @@ -338,7 +338,6 @@ def plot_hyperplane(hyperplane, **kwds): sage: a.plot(point_size=100,hyperplane_label='hello') # optional - sage.plot Graphics object consisting of 3 graphics primitives - sage: H2. = HyperplaneArrangements(QQ) sage: b = 3*x + 4*y + 5 sage: b.plot() # optional - sage.plot @@ -469,14 +468,15 @@ def plot_hyperplane(hyperplane, **kwds): def legend_3d(hyperplane_arrangement, hyperplane_colors, length): r""" - Create plot of a 3d legend for an arrangement of planes in 3-space. The - ``length`` parameter determines whether short or long labels are used in - the legend. + Create plot of a 3d legend for an arrangement of planes in 3-space. + + The ``length`` parameter determines whether short or long labels + are used in the legend. INPUT: - ``hyperplane_arrangement`` -- a hyperplane arrangement - + - ``hyperplane_colors`` -- list of colors - ``length`` -- either ``'short'`` or ``'long'`` diff --git a/src/sage/geometry/triangulation/point_configuration.py b/src/sage/geometry/triangulation/point_configuration.py index 4e86b8fc3e3..1bf933700c9 100644 --- a/src/sage/geometry/triangulation/point_configuration.py +++ b/src/sage/geometry/triangulation/point_configuration.py @@ -1832,7 +1832,7 @@ def contained_simplex(self, large=True, initial_point=None, point_order=None): :class:`~sage.geometry.triangulation.base.Point` or ``None`` (default). A specific point to start with when picking the simplex vertices. - + - ``point_order`` -- a list or tuple of (some or all) :class:`~sage.geometry.triangulation.base.Point` s or ``None`` (default). @@ -1871,7 +1871,7 @@ def contained_simplex(self, large=True, initial_point=None, point_order=None): sage: pc.contained_simplex(point_order = [pc[0],pc[3],pc[4]]) (P(0, 0), P(1, 1)) - + TESTS:: sage: pc = PointConfiguration([[0,0],[0,1],[1,0]]) diff --git a/src/sage/interfaces/gap.py b/src/sage/interfaces/gap.py index ba175d4e340..c80a847d7e9 100644 --- a/src/sage/interfaces/gap.py +++ b/src/sage/interfaces/gap.py @@ -952,8 +952,6 @@ def __bool__(self): P = self._check_valid() return self != P(0) and repr(self) != 'false' - - def __len__(self): """ EXAMPLES:: diff --git a/src/sage/interfaces/gap3.py b/src/sage/interfaces/gap3.py index fc21ee46d06..78d05035cbf 100644 --- a/src/sage/interfaces/gap3.py +++ b/src/sage/interfaces/gap3.py @@ -12,7 +12,7 @@ The experimental package for GAP3 is Jean Michel's pre-packaged GAP3, which is a minimal GAP3 distribution containing packages that have - no equivalent in GAP4, see :trac:`20107` and also + no equivalent in GAP4, see :trac:`20107` and also https://webusers.imj-prg.fr/~jean.michel/gap3/ @@ -212,7 +212,7 @@ ... """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2010 Franco Saliola # # Distributed under the terms of the GNU General Public License (GPL) @@ -224,8 +224,8 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.cachefunc import cached_method from sage.interfaces.expect import Expect @@ -236,6 +236,7 @@ # gap3_cmd should point to the gap3 executable gap3_cmd = 'gap3' + class Gap3(Gap_generic): r""" A simple Expect interface to GAP3. @@ -624,9 +625,10 @@ def _tab_completion(self): """ return [] - + gap3 = Gap3() + class GAP3Element(GapElement_generic): r""" A GAP3 element diff --git a/src/sage/interfaces/interface.py b/src/sage/interfaces/interface.py index 7e612f049f2..90d4ba6d1d0 100644 --- a/src/sage/interfaces/interface.py +++ b/src/sage/interfaces/interface.py @@ -1340,8 +1340,6 @@ def __bool__(self): P._false_symbol()) return P.eval(cmd) != P._true_symbol() - - def __float__(self): """ EXAMPLES:: diff --git a/src/sage/interfaces/lisp.py b/src/sage/interfaces/lisp.py index 0f9d09605bf..7b057a4de3c 100644 --- a/src/sage/interfaces/lisp.py +++ b/src/sage/interfaces/lisp.py @@ -434,8 +434,6 @@ def __bool__(self): """ return self != 0 and repr(self) != 'NIL' - - def _add_(self, right): """ EXAMPLES:: @@ -445,7 +443,7 @@ def _add_(self, right): 3 """ P = self._check_valid() - return P.new('(+ %s %s)'%(self._name, right._name)) + return P.new('(+ %s %s)' % (self._name, right._name)) def _sub_(self, right): """ diff --git a/src/sage/interfaces/macaulay2.py b/src/sage/interfaces/macaulay2.py index 14d217cff9e..b5bcac4003f 100644 --- a/src/sage/interfaces/macaulay2.py +++ b/src/sage/interfaces/macaulay2.py @@ -1179,8 +1179,6 @@ def __bool__(self): P = self.parent() return P.eval('{0}===false or {0}==0'.format(self._name)) != 'true' - - def sage_polystring(self): """ If this Macaulay2 element is a polynomial, return a string diff --git a/src/sage/interfaces/magma.py b/src/sage/interfaces/magma.py index e34e5a13aa3..89dc1aa8140 100644 --- a/src/sage/interfaces/magma.py +++ b/src/sage/interfaces/magma.py @@ -2652,17 +2652,13 @@ def __bool__(self): pass return True - - def sub(self, gens): """ Return the sub-object of self with given gens. INPUT: - - - ``gens`` - object or list/tuple of generators - + - ``gens`` -- object or list/tuple of generators EXAMPLES:: diff --git a/src/sage/interfaces/mathematica.py b/src/sage/interfaces/mathematica.py index 6d0558d8717..9c6e3ca86e9 100644 --- a/src/sage/interfaces/mathematica.py +++ b/src/sage/interfaces/mathematica.py @@ -377,7 +377,7 @@ sage: FL=[sin, cos, tan, csc, sec, cot, # optional - mathematica ....: sinh, cosh, tanh, csch, sech, coth] sage: IFL=[arcsin, arccos, arctan, arccsc, # optional - mathematica - ....: arcsec, arccot, arcsinh, arccosh, + ....: arcsec, arccot, arcsinh, arccosh, ....: arctanh, arccsch, arcsech, arccoth] sage: [mathematica.TrigToExp(u(x)).sage() # optional - mathematica ....: for u in FL] @@ -1079,8 +1079,6 @@ def __bool__(self): cmd = '%s===%s' % (self._name, P._false_symbol()) return P.eval(cmd).strip() != P._true_symbol() - - def n(self, *args, **kwargs): r""" Numerical approximation by converting to Sage object first diff --git a/src/sage/interfaces/mathics.py b/src/sage/interfaces/mathics.py index aa86c956635..0bf0bc78829 100644 --- a/src/sage/interfaces/mathics.py +++ b/src/sage/interfaces/mathics.py @@ -1242,8 +1242,6 @@ def __bool__(self): cmd = '%s===%s' % (self._name, P._false_symbol()) return not str(P(cmd)) == P._true_symbol() - - def n(self, *args, **kwargs): r""" Numerical approximation by converting to Sage object first diff --git a/src/sage/interfaces/maxima_abstract.py b/src/sage/interfaces/maxima_abstract.py index 3fb1a53a893..cc1d88160bb 100644 --- a/src/sage/interfaces/maxima_abstract.py +++ b/src/sage/interfaces/maxima_abstract.py @@ -1148,8 +1148,6 @@ def __bool__(self): # but be careful, since for relations things like is(equal(a,b)) are # what Maxima needs - - def _richcmp_(self, other, op): """ Compare this Maxima object with ``other``. diff --git a/src/sage/interfaces/octave.py b/src/sage/interfaces/octave.py index b22c2fb1d3a..1f3c2cbd3b2 100644 --- a/src/sage/interfaces/octave.py +++ b/src/sage/interfaces/octave.py @@ -665,8 +665,6 @@ def __bool__(self): """ return str(self) != ' [](0x0)' and any(x != '0' for x in str(self).split()) - - def _matrix_(self, R=None): r""" Return Sage matrix from this octave element. diff --git a/src/sage/interfaces/primecount.py b/src/sage/interfaces/primecount.py index e647bf9b980..518378e1fe2 100644 --- a/src/sage/interfaces/primecount.py +++ b/src/sage/interfaces/primecount.py @@ -1,5 +1,5 @@ from sage.misc.superseded import deprecation deprecation(32894, "the module sage.interfaces.primecount is deprecated - use primecountpy.primecount instead") from sage.misc.lazy_import import lazy_import -lazy_import("primecountpy.primecount", ['phi', 'nth_prime', 'prime_pi', 'prime_pi_128'], +lazy_import("primecountpy.primecount", ['phi', 'nth_prime', 'prime_pi', 'prime_pi_128'], deprecation=(32894, "the module sage.interfaces.primecount is deprecated - use primecountpy.primecount instead")) diff --git a/src/sage/interfaces/r.py b/src/sage/interfaces/r.py index 72481d3e1ef..19b8a7df600 100644 --- a/src/sage/interfaces/r.py +++ b/src/sage/interfaces/r.py @@ -1606,9 +1606,7 @@ def __bool__(self): sage: bool(r(1)) # optional - rpy2 True """ - return "FALSE" in repr(self==0) - - + return "FALSE" in repr(self == 0) def _comparison(self, other, symbol): """ diff --git a/src/sage/libs/pari/convert_gmp.pyx b/src/sage/libs/pari/convert_gmp.pyx index fe23a00cdc7..a39caa379b4 100644 --- a/src/sage/libs/pari/convert_gmp.pyx +++ b/src/sage/libs/pari/convert_gmp.pyx @@ -159,7 +159,7 @@ cdef Gen rational_matrix(mpq_t** B, long nr, long nc): """ Create a new PARI matrix of type ``t_MAT`` from a given array of GMP rationals ``mpq_t``. - + INPUT: - ``B`` -- a 2-dimensional array of ``mpq_t`` values. This array is diff --git a/src/sage/manifolds/catalog.py b/src/sage/manifolds/catalog.py index 7dcdad7b113..e71faca3072 100644 --- a/src/sage/manifolds/catalog.py +++ b/src/sage/manifolds/catalog.py @@ -364,9 +364,9 @@ def RealProjectiveSpace(dim=2): charts = {0: U0.chart(''.join(names[1:]))} # create the charts - for j in range(1, dim+1): + for j in range(1, dim + 1): U = P.open_subset(name=f'U{j}', latex_name=f'U_{j}') - + # The chart where we assert that x_i == 1 Cj = U.chart(''.join(names[:j] + names[j+1:])) gj = Cj[:] diff --git a/src/sage/matrix/matrix_gap.pyx b/src/sage/matrix/matrix_gap.pyx index 0102d045e6c..8485760e9cb 100644 --- a/src/sage/matrix/matrix_gap.pyx +++ b/src/sage/matrix/matrix_gap.pyx @@ -327,7 +327,7 @@ cdef class Matrix_gap(Matrix_dense): def transpose(self): r""" Return the transpose of this matrix. - + EXAMPLES:: sage: M = MatrixSpace(QQ, 2, implementation='gap') diff --git a/src/sage/misc/fast_methods.pyx b/src/sage/misc/fast_methods.pyx index eb73cb6bc3e..800bd798fb6 100644 --- a/src/sage/misc/fast_methods.pyx +++ b/src/sage/misc/fast_methods.pyx @@ -343,7 +343,7 @@ class Singleton(WithEqualityById, metaclass=ClasscallMetaclass): sage: __main__.C = C # ... in doctests sage: loads(dumps(c)) is copy(c) is C() # indirect doctest True - + The pickle data mainly consist of the class of the unique instance, which may be a subclass of the original class used to create the instance.If the class is replaced by a sub-sub-class after creation diff --git a/src/sage/misc/unknown.py b/src/sage/misc/unknown.py index 39f7fed8f41..b87a3e67506 100644 --- a/src/sage/misc/unknown.py +++ b/src/sage/misc/unknown.py @@ -143,8 +143,6 @@ def __bool__(self): """ raise UnknownError('Unknown does not evaluate in boolean context') - - def __and__(self, other): """ The ``&`` logical operation. diff --git a/src/sage/modular/btquotients/pautomorphicform.py b/src/sage/modular/btquotients/pautomorphicform.py index 890cdc3cd4c..6ba6bb78bbe 100644 --- a/src/sage/modular/btquotients/pautomorphicform.py +++ b/src/sage/modular/btquotients/pautomorphicform.py @@ -1667,8 +1667,6 @@ def __bool__(self): """ return any(not o.is_zero() for o in self._value) - - def __getitem__(self, e1): r""" Evaluate a `p`-adic automorphic form on a matrix in `GL_2(\QQ_p)`. diff --git a/src/sage/numerical/backends/generic_backend.pyx b/src/sage/numerical/backends/generic_backend.pyx index ac167aefa8d..f07a4d05f78 100644 --- a/src/sage/numerical/backends/generic_backend.pyx +++ b/src/sage/numerical/backends/generic_backend.pyx @@ -878,7 +878,7 @@ cdef class GenericBackend: tester = self._tester(**options) p = self tester.assertGreaterEqual(self.ncols(), 0) - + cpdef int nrows(self): """ Return the number of rows/constraints. @@ -1265,7 +1265,7 @@ cdef class GenericBackend: "{}({}) does not match".format(method, i)) for method in ("row_bounds", "row", "row_name"): assert_equal_row_data(method) - + def _test_copy(self, **options): """ Test whether the backend can be copied diff --git a/src/sage/numerical/gauss_legendre.pyx b/src/sage/numerical/gauss_legendre.pyx index 175513687df..0b8dd4361e3 100644 --- a/src/sage/numerical/gauss_legendre.pyx +++ b/src/sage/numerical/gauss_legendre.pyx @@ -216,7 +216,7 @@ def nodes(degree, prec): def estimate_error(results, prec, epsilon): r""" Routine to estimate the error in a list of quadrature approximations. - + The method used is based on Borwein, Bailey, and Girgensohn. As mentioned in mpmath: Although not very conservative, this method seems to be very robust in practice. @@ -226,7 +226,7 @@ def estimate_error(results, prec, epsilon): of the maximum norm of the error in the last approximation. INPUT: - + - ``results`` -- list. List of approximations to estimate the error from. Should be at least length 2. - ``prec`` -- integer. Binary precision at which computations are happening. diff --git a/src/sage/numerical/linear_tensor.py b/src/sage/numerical/linear_tensor.py index a210ff52fd7..26d94b12cc6 100644 --- a/src/sage/numerical/linear_tensor.py +++ b/src/sage/numerical/linear_tensor.py @@ -150,7 +150,7 @@ def LinearTensorParent(free_module_parent, linear_functions_parent): for a given base ring. INPUT: - + - ``free_module_parent`` -- module. A free module, like vector or matrix space. @@ -267,7 +267,7 @@ def is_vector_space(self): sage: LF = mip.linear_functions_parent() sage: LF.tensor(RDF^2).is_vector_space() True - sage: LF.tensor(RDF^(2,2)).is_vector_space() + sage: LF.tensor(RDF^(2,2)).is_vector_space() False """ from sage.modules.free_module import is_FreeModule @@ -319,7 +319,7 @@ def linear_functions(self): def _repr_(self): """ Return a string representation - + OUTPUT: String. @@ -364,7 +364,7 @@ def _convert_constant(self, m): return m_vector else: return M(m) - + def _element_constructor_(self, x): """ Construct a :class:`LinearTensor` from ``x``. @@ -393,7 +393,7 @@ def _element_constructor_(self, x): sage: LT(123) # indirect doctest (123.0, 123.0) - + Similar, over ``QQ`` and with matrices instead of vectors:: sage: p_QQ = MixedIntegerLinearProgram(solver='ppl') diff --git a/src/sage/numerical/linear_tensor_constraints.py b/src/sage/numerical/linear_tensor_constraints.py index 3da0cd4719a..6535867f8c8 100644 --- a/src/sage/numerical/linear_tensor_constraints.py +++ b/src/sage/numerical/linear_tensor_constraints.py @@ -317,8 +317,8 @@ class LinearTensorConstraintsParent_class(Parent): Field and Linear functions over Real Double Field sage: from sage.numerical.linear_tensor_constraints import LinearTensorConstraintsParent sage: LTC = LinearTensorConstraintsParent(LT); LTC - Linear constraints in the tensor product of Vector space of - dimension 2 over Real Double Field and Linear functions over + Linear constraints in the tensor product of Vector space of + dimension 2 over Real Double Field and Linear functions over Real Double Field sage: type(LTC) @@ -330,10 +330,10 @@ def __init__(self, linear_tensor_parent): The Python constructor INPUT: - + - ``linear_tensor_parent`` -- instance of :class:`LinearTensorParent_class`. - + TESTS:: sage: from sage.numerical.linear_functions import LinearFunctionsParent diff --git a/src/sage/rings/function_field/element.pyx b/src/sage/rings/function_field/element.pyx index 41a78f11846..233e8c55d78 100644 --- a/src/sage/rings/function_field/element.pyx +++ b/src/sage/rings/function_field/element.pyx @@ -281,13 +281,13 @@ cdef class FunctionFieldElement(FieldElement): Return the max degree between the denominator and numerator. EXAMPLES:: - + sage: FF. = FunctionField(QQ) sage: f = (t^2 + 3) / (t^3 - 1/3); f (t^2 + 3)/(t^3 - 1/3) sage: f.degree() 3 - + sage: FF. = FunctionField(QQ) sage: f = (t+8); f t + 8 diff --git a/src/sage/rings/noncommutative_ideals.pyx b/src/sage/rings/noncommutative_ideals.pyx index 29599c2ac0f..ea3d3c43f56 100644 --- a/src/sage/rings/noncommutative_ideals.pyx +++ b/src/sage/rings/noncommutative_ideals.pyx @@ -402,7 +402,7 @@ class Ideal_nc(Ideal_generic): gens = [z for z in (x * y for x in self.gens() for y in other.gens()) if z] return self.ring().ideal(gens, side='twosided') raise NotImplementedError("cannot multiply non-commutative ideals") - + if not isinstance(other, Ideal_nc): # Perhaps other is a ring and thus has its own # multiplication. diff --git a/src/sage/rings/number_field/number_field_morphisms.pyx b/src/sage/rings/number_field/number_field_morphisms.pyx index 9f77c9e3949..104603f4a73 100644 --- a/src/sage/rings/number_field/number_field_morphisms.pyx +++ b/src/sage/rings/number_field/number_field_morphisms.pyx @@ -695,7 +695,7 @@ cdef class CyclotomicFieldEmbedding(NumberFieldEmbedding): def section(self): """ Return the section of ``self``. - + EXAMPLES:: sage: from sage.rings.number_field.number_field_morphisms import CyclotomicFieldEmbedding @@ -740,7 +740,7 @@ cdef class CyclotomicFieldConversion(Map): def __init__(self, K, L): """ Construct a conversion map between cyclotomic fields. - + EXAMPLES:: sage: from sage.rings.number_field.number_field_morphisms import CyclotomicFieldEmbedding diff --git a/src/sage/rings/number_field/small_primes_of_degree_one.py b/src/sage/rings/number_field/small_primes_of_degree_one.py index 93cb7d97f70..facd5207a1c 100644 --- a/src/sage/rings/number_field/small_primes_of_degree_one.py +++ b/src/sage/rings/number_field/small_primes_of_degree_one.py @@ -227,7 +227,7 @@ def __next__(self): [29, 41, 61, 89, 101, 109, 149, 181, 229, 241] sage: ids[9] == N.ideal(3*a + 1/2*b + 5/2) True - + We test that :trac:`23468` is fixed:: sage: R. = QQ[] diff --git a/src/sage/rings/polynomial/laurent_polynomial_ideal.py b/src/sage/rings/polynomial/laurent_polynomial_ideal.py index f205371d822..76d1b495274 100644 --- a/src/sage/rings/polynomial/laurent_polynomial_ideal.py +++ b/src/sage/rings/polynomial/laurent_polynomial_ideal.py @@ -2,7 +2,7 @@ r""" Ideals in Laurent polynomial rings. -For `R` a commutative ring, ideals in the Laurent polynomial ring +For `R` a commutative ring, ideals in the Laurent polynomial ring `R[x_1^{\pm 1}, x_2^{\pm 1}, \ldots, x_n^{\pm 1}]` are implemented as ideals in the ordinary polynomial ring `R[x_1, \ldots, x_n]` which are saturated with respect to the ideal `(x_1 \cdots x_n)`. @@ -35,7 +35,7 @@ def __init__(self, ring, gens, coerce=True, hint=None): associated ordinary polynomial ring `R[x_1,\ldots,x_n]` which is saturated with respect to the ideal `(x_1 \cdots x_n)`. Since computing the saturation can be expensive, we employ some strategies to reduce the need for it. - + - We only create the polynomial ideal as needed. - For some operations, we try some superficial tests first. E.g., for @@ -98,14 +98,14 @@ def set_hint(self, hint): """ Set the hint of this ideal. - The hint is an ideal of the associated polynomial ring, which is + The hint is an ideal of the associated polynomial ring, which is assumed to be contained in the associated ideal. It is used internally to speed up computation of the associated ideal in some cases; normally the end user will have no need to work with it directly. sage: P. = LaurentPolynomialRing(QQ, 3) sage: I = P.ideal([x^2*y + 3*x*y^2]) - sage: I.hint() + sage: I.hint() Ideal (0) of Multivariate Polynomial Ring in x, y, z over Rational Field sage: I.set_hint(P.polynomial_ring().ideal([x + 3*y])) sage: I.hint() @@ -117,7 +117,7 @@ def hint(self): """ Return the hint of this ideal. - The hint is an ideal of the associated polynomial ring, which is + The hint is an ideal of the associated polynomial ring, which is assumed to be contained in the associated ideal. It is used internally to speed up computation of the associated ideal in some cases; normally the end user will have no need to work with it directly. @@ -126,7 +126,7 @@ def hint(self): sage: P. = LaurentPolynomialRing(QQ, 3) sage: I = P.ideal([x^2*y + 3*x*y^2]) - sage: I.hint() + sage: I.hint() Ideal (0) of Multivariate Polynomial Ring in x, y, z over Rational Field """ return self._hint @@ -198,8 +198,8 @@ def __contains__(self, f): def change_ring(self, R, hint=None): """ Coerce an ideal into a new ring. - - This operation does not forward hints, but a new hint can be + + This operation does not forward hints, but a new hint can be specified manually. EXAMPLES:: @@ -319,7 +319,7 @@ def __add__(self, other): Return the sum of two ideals in the same ring. Currently this operation does not support coercion. - + This operation forwards hints. EXAMPLES:: @@ -367,10 +367,10 @@ def normalize_gens(self): def polynomial_ideal(self, saturate=True): """ Return the associated polynomial ideal. - + By default, the ideal is saturated with respect to the product of the polynomial ring generators; this is necessary for testing equality and inclusion. - As saturation can be quite time-consuming, it can be disabled by setting + As saturation can be quite time-consuming, it can be disabled by setting ``saturate=False``; however, the result will then depend not just on the original ideal but also on the choice of generators. @@ -441,7 +441,7 @@ def is_binomial(self, groebner_basis=False): """ Determine whether every generator of ``self`` is a binomial. - If ``groebner_basis`` is True, this becomes intrinsic (for a choice of + If ``groebner_basis`` is True, this becomes intrinsic (for a choice of term order). EXAMPLES:: diff --git a/src/sage/rings/polynomial/laurent_polynomial_ring.py b/src/sage/rings/polynomial/laurent_polynomial_ring.py index 9f9948bb84b..c10faf46568 100644 --- a/src/sage/rings/polynomial/laurent_polynomial_ring.py +++ b/src/sage/rings/polynomial/laurent_polynomial_ring.py @@ -730,7 +730,7 @@ def ideal(self, *args, **kwds): Ideal (1) of Multivariate Laurent Polynomial Ring in x0, x1 over Rational Field TESTS: - + check that :trac:`26421` is fixed: sage: R. = LaurentPolynomialRing(ZZ) diff --git a/src/sage/rings/semirings/tropical_semiring.pyx b/src/sage/rings/semirings/tropical_semiring.pyx index 17e25091f39..f0776b79a0a 100644 --- a/src/sage/rings/semirings/tropical_semiring.pyx +++ b/src/sage/rings/semirings/tropical_semiring.pyx @@ -189,7 +189,7 @@ cdef class TropicalSemiringElement(Element): cdef TropicalSemiringElement self, x self = left x = right - + if self._val is None: if x._val is None: return rich_to_bool(op, 0) diff --git a/src/sage/rings/tate_algebra_ideal.pyx b/src/sage/rings/tate_algebra_ideal.pyx index 8826f67d000..5ce909911b9 100644 --- a/src/sage/rings/tate_algebra_ideal.pyx +++ b/src/sage/rings/tate_algebra_ideal.pyx @@ -874,7 +874,7 @@ def groebner_basis_pote(I, prec, verbose=0): # TODO: this should probably be a single function call f = f.monic() << f.valuation() - + if verbose > 0: print("---") print("new generator: %s + ..." % f.leading_term()) @@ -1019,7 +1019,7 @@ def groebner_basis_pote(I, prec, verbose=0): gb.sort(reverse=True) return gb - + def groebner_basis_vapote(I, prec, verbose=0, interrupt_red_with_val=False, interrupt_interred_with_val=False): r""" Run the VaPoTe algorithm to compute the Groebner basis of ``I`` @@ -1158,7 +1158,7 @@ def groebner_basis_vapote(I, prec, verbose=0, interrupt_red_with_val=False, inte continue f = f.monic() << f.valuation() - + if f and f.valuation() > val: if verbose > 0: print("reduction increases the valuation") diff --git a/src/sage/sat/solvers/cryptominisat.py b/src/sage/sat/solvers/cryptominisat.py index eb12e9fbbb3..d2a8ae9e5a6 100644 --- a/src/sage/sat/solvers/cryptominisat.py +++ b/src/sage/sat/solvers/cryptominisat.py @@ -94,10 +94,11 @@ def var(self, decision=None): def nvars(self): r""" - Return the number of variables. Note that for compatibility with DIMACS - convention, the number of variables corresponds to the maximal index of - the variables used. - + Return the number of variables. + + Note that for compatibility with DIMACS convention, the number + of variables corresponds to the maximal index of the variables used. + EXAMPLES:: sage: from sage.sat.solvers.cryptominisat import CryptoMiniSat diff --git a/src/sage/sat/solvers/picosat.py b/src/sage/sat/solvers/picosat.py index daeef620669..a1a80e71794 100644 --- a/src/sage/sat/solvers/picosat.py +++ b/src/sage/sat/solvers/picosat.py @@ -85,10 +85,11 @@ def var(self, decision=None): def nvars(self): r""" - Return the number of variables. Note that for compatibility with DIMACS - convention, the number of variables corresponds to the maximal index of - the variables used. - + Return the number of variables. + + Note that for compatibility with DIMACS convention, the number + of variables corresponds to the maximal index of the variables used. + EXAMPLES:: sage: from sage.sat.solvers.picosat import PicoSAT diff --git a/src/sage/sat/solvers/satsolver.pyx b/src/sage/sat/solvers/satsolver.pyx index d93181c142f..5baec390395 100644 --- a/src/sage/sat/solvers/satsolver.pyx +++ b/src/sage/sat/solvers/satsolver.pyx @@ -328,7 +328,7 @@ def SAT(solver=None, *args, **kwds): - ``"picosat"`` -- note that the pycosat package must be installed. - ``"glucose"`` -- note that the glucose package must be installed. - + - ``"glucose-syrup"`` -- note that the glucose package must be installed. - ``"LP"`` -- use :class:`~sage.sat.solvers.sat_lp.SatLP` to solve the diff --git a/src/sage/schemes/elliptic_curves/padics.py b/src/sage/schemes/elliptic_curves/padics.py index d4b38156669..afe9d6c52d1 100644 --- a/src/sage/schemes/elliptic_curves/padics.py +++ b/src/sage/schemes/elliptic_curves/padics.py @@ -178,10 +178,10 @@ def padic_lseries(self, p, normalize=None, implementation='eclib', sage: L = e.padic_lseries(3, implementation = 'sage') sage: L.series(5,prec=10) 2 + 3 + 3^2 + 2*3^3 + 2*3^5 + 3^6 + O(3^7) + (1 + 3 + 2*3^2 + 3^3 + O(3^4))*T + (1 + 2*3 + O(3^4))*T^2 + (3 + 2*3^2 + O(3^3))*T^3 + (2*3 + 3^2 + O(3^3))*T^4 + (2 + 2*3 + 2*3^2 + O(3^3))*T^5 + (1 + 3^2 + O(3^3))*T^6 + (2 + 3^2 + O(3^3))*T^7 + (2 + 2*3 + 2*3^2 + O(3^3))*T^8 + (2 + O(3^2))*T^9 + O(T^10) - + Also the numerical modular symbols can be used. - This may allow for much larger conductor in some instances:: - + This may allow for much larger conductor in some instances:: + sage: E = EllipticCurve([101,103]) sage: L = E.padic_lseries(5, implementation="num") sage: L.series(2) diff --git a/src/sage/schemes/product_projective/rational_point.py b/src/sage/schemes/product_projective/rational_point.py index 8c737f14e0b..3bd717ee39e 100644 --- a/src/sage/schemes/product_projective/rational_point.py +++ b/src/sage/schemes/product_projective/rational_point.py @@ -189,7 +189,7 @@ def enum_product_projective_number_field(X, **kwds): This is an implementation of the revised algorithm (Algorithm 4) in [DK2013]_. Algorithm 5 is used for imaginary quadratic fields. - + INPUT: kwds: diff --git a/src/sage/schemes/projective/projective_rational_point.py b/src/sage/schemes/projective/projective_rational_point.py index cb12e30c873..738f28cb993 100644 --- a/src/sage/schemes/projective/projective_rational_point.py +++ b/src/sage/schemes/projective/projective_rational_point.py @@ -167,7 +167,7 @@ def enum_projective_number_field(X, **kwds): This is an implementation of the revised algorithm (Algorithm 4) in [DK2013]_. Algorithm 5 is used for imaginary quadratic fields. - + INPUT: kwds: diff --git a/src/sage/tests/books/judson-abstract-algebra/boolean-sage.py b/src/sage/tests/books/judson-abstract-algebra/boolean-sage.py index daa0f3f6954..460403b3efb 100644 --- a/src/sage/tests/books/judson-abstract-algebra/boolean-sage.py +++ b/src/sage/tests/books/judson-abstract-algebra/boolean-sage.py @@ -56,7 +56,6 @@ sage: D = Poset([X, R]) sage: D.plot() # not tested - ~~~~~~~~~~~~~~~~~~~~~~ :: @@ -73,7 +72,6 @@ sage: Q = Posets.PentagonPoset() sage: Q.plot() # not tested - ~~~~~~~~~~~~~~~~~~~~~~ :: diff --git a/src/sage/tests/books/judson-abstract-algebra/rings-sage.py b/src/sage/tests/books/judson-abstract-algebra/rings-sage.py index 67ed4823924..6d9571d00cd 100644 --- a/src/sage/tests/books/judson-abstract-algebra/rings-sage.py +++ b/src/sage/tests/books/judson-abstract-algebra/rings-sage.py @@ -90,7 +90,6 @@ sage: F. = FiniteField(3^2) sage: P. = Z7[] sage: S. = QuaternionAlgebra(-7, 3) - ~~~~~~~~~~~~~~~~~~~~~~ :: From fda90859c17b01922d18086b64ad86bbca8f6948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 30 Oct 2022 21:38:41 +0100 Subject: [PATCH 320/632] fix and activate E306 and W605 in pyx files --- src/sage/graphs/strongly_regular_db.pyx | 1 + .../rings/padics/local_generic_element.pyx | 4 ++-- .../rings/padics/padic_ZZ_pX_CA_element.pyx | 8 +++---- .../rings/padics/padic_ZZ_pX_CR_element.pyx | 6 +++--- .../padics/padic_capped_absolute_element.pyx | 8 +++---- .../rings/padics/padic_fixed_mod_element.pyx | 8 +++---- .../rings/padics/padic_generic_element.pyx | 21 ++++++++++--------- src/sage/rings/padics/pow_computer_ext.pyx | 3 ++- src/sage/rings/padics/qadic_flint_CA.pyx | 4 ++-- src/sage/rings/padics/qadic_flint_CR.pyx | 4 ++-- src/sage/rings/padics/qadic_flint_FM.pyx | 4 ++-- src/sage/rings/padics/qadic_flint_FP.pyx | 4 ++-- src/tox.ini | 2 +- 13 files changed, 40 insertions(+), 37 deletions(-) diff --git a/src/sage/graphs/strongly_regular_db.pyx b/src/sage/graphs/strongly_regular_db.pyx index 1e01738662d..8cf2c73c957 100644 --- a/src/sage/graphs/strongly_regular_db.pyx +++ b/src/sage/graphs/strongly_regular_db.pyx @@ -1213,6 +1213,7 @@ def SRG_from_RSHCD(v, k, l, mu, existence=False, check=True): ValueError: I do not know how to build a (784, 0, 14, 38)-SRG from a RSHCD """ from sage.combinat.matrices.hadamard_matrix import regular_symmetric_hadamard_matrix_with_constant_diagonal + def sgn(x): return 1 if x >= 0 else -1 n = v diff --git a/src/sage/rings/padics/local_generic_element.pyx b/src/sage/rings/padics/local_generic_element.pyx index 5923228bb69..0733e0a483f 100644 --- a/src/sage/rings/padics/local_generic_element.pyx +++ b/src/sage/rings/padics/local_generic_element.pyx @@ -408,8 +408,8 @@ cdef class LocalGenericElement(CommutativeRingElement): return ans def _latex_(self): - """ - Returns a latex representation of self. + r""" + Return a latex representation of self. EXAMPLES:: diff --git a/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx index 731d27ec33c..2b12b36cbed 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx @@ -4,7 +4,7 @@ # distutils: library_dirs = NTL_LIBDIR # distutils: extra_link_args = NTL_LIBEXTRA # distutils: language = c++ -""" +r""" `p`-adic ``ZZ_pX`` CA Element This file implements elements of Eisenstein and unramified extensions @@ -1546,7 +1546,7 @@ cdef class pAdicZZpXCAElement(pAdicZZpXElement): return self.to_fraction_field() * (~right) def _integer_(self, Z=None): - """ + r""" Returns an integer congruent to this element modulo `\pi`^``self.absolute_precision()``, if possible. @@ -1951,8 +1951,8 @@ cdef class pAdicZZpXCAElement(pAdicZZpXElement): return [zero] * ordp + ulist def matrix_mod_pn(self): - """ - Returns the matrix of right multiplication by the element on + r""" + Return the matrix of right multiplication by the element on the power basis `1, x, x^2, \ldots, x^{d-1}` for this extension field. Thus the *rows* of this matrix give the images of each of the `x^i`. The entries of the matrices are diff --git a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx index 463cc371031..116e8a3a1db 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx @@ -4,7 +4,7 @@ # distutils: library_dirs = NTL_LIBDIR # distutils: extra_link_args = NTL_LIBEXTRA # distutils: language = c++ -""" +r""" `p`-adic ``ZZ_pX`` CR Element This file implements elements of Eisenstein and unramified extensions @@ -230,7 +230,7 @@ cdef inline int check_ordp(long a) except -1: cdef class pAdicZZpXCRElement(pAdicZZpXElement): def __init__(self, parent, x, absprec = infinity, relprec = infinity, empty = False): - """ + r""" Creates an element of a capped relative precision, unramified or Eisenstein extension of `\ZZ_p` or `\QQ_p`. @@ -2833,7 +2833,7 @@ cdef class pAdicZZpXCRElement(pAdicZZpXElement): return ulist def matrix_mod_pn(self): - """ + r""" Return the matrix of right multiplication by the element on the power basis `1, x, x^2, \ldots, x^{d-1}` for this extension field. Thus the *rows* of this matrix give the diff --git a/src/sage/rings/padics/padic_capped_absolute_element.pyx b/src/sage/rings/padics/padic_capped_absolute_element.pyx index 91e973ccf7e..e48abd895ae 100644 --- a/src/sage/rings/padics/padic_capped_absolute_element.pyx +++ b/src/sage/rings/padics/padic_capped_absolute_element.pyx @@ -383,7 +383,7 @@ cdef class pAdicCappedAbsoluteElement(CAElement): return ans def _exp_binary_splitting(self, aprec): - """ + r""" Compute the exponential power series of this element This is a helper method for :meth:`exp`. @@ -393,7 +393,7 @@ cdef class pAdicCappedAbsoluteElement(CAElement): - ``aprec`` -- an integer, the precision to which to compute the exponential - NOTE:: + .. NOTE:: The function does not check that its argument ``self`` is the disk of convergence of ``exp``. If this assumption is not @@ -443,7 +443,7 @@ cdef class pAdicCappedAbsoluteElement(CAElement): return ans def _exp_newton(self, aprec, log_algorithm=None): - """ + r""" Compute the exponential power series of this element This is a helper method for :meth:`exp`. @@ -458,7 +458,7 @@ cdef class pAdicCappedAbsoluteElement(CAElement): method. See :meth:`log` for more details about the possible algorithms. - NOTE:: + .. NOTE:: The function does not check that its argument ``self`` is the disk of convergence of ``exp``. If this assumption is not diff --git a/src/sage/rings/padics/padic_fixed_mod_element.pyx b/src/sage/rings/padics/padic_fixed_mod_element.pyx index 8b45f6cea71..a53b0da7525 100644 --- a/src/sage/rings/padics/padic_fixed_mod_element.pyx +++ b/src/sage/rings/padics/padic_fixed_mod_element.pyx @@ -157,7 +157,7 @@ cdef class pAdicFixedModElement(FMElement): return self.lift_c() cdef lift_c(self): - """ + r""" Returns an integer congruent to this element modulo the precision. .. WARNING:: @@ -227,7 +227,7 @@ cdef class pAdicFixedModElement(FMElement): holder.value) def _integer_(self, Z=None): - """ + r""" Return an integer congruent to ``self`` modulo the precision. .. WARNING:: @@ -449,7 +449,7 @@ cdef class pAdicFixedModElement(FMElement): return ans def _exp_binary_splitting(self, aprec): - """ + r""" Compute the exponential power series of this element This is a helper method for :meth:`exp`. @@ -508,7 +508,7 @@ cdef class pAdicFixedModElement(FMElement): return ans def _exp_newton(self, aprec, log_algorithm=None): - """ + r""" Compute the exponential power series of this element This is a helper method for :meth:`exp`. diff --git a/src/sage/rings/padics/padic_generic_element.pyx b/src/sage/rings/padics/padic_generic_element.pyx index 5cf7d8a04f3..4248994b88e 100644 --- a/src/sage/rings/padics/padic_generic_element.pyx +++ b/src/sage/rings/padics/padic_generic_element.pyx @@ -544,7 +544,7 @@ cdef class pAdicGenericElement(LocalGenericElement): return self._repr_(mode=mode) def _repr_(self, mode=None, do_latex=False): - """ + r""" Returns a string representation of this element. INPUT: @@ -565,7 +565,6 @@ cdef class pAdicGenericElement(LocalGenericElement): sage: K. = Qp(2).extension(x^3 - 2) sage: latex(pi) \pi + O(\pi^{61}) - """ return self.parent()._printer.repr_gen(self, do_latex, mode=mode) @@ -3028,7 +3027,7 @@ cdef class pAdicGenericElement(LocalGenericElement): return series_unit*nfactorial_unit.inverse_of_unit()<<(series_val-nfactorial_val) def _exp_binary_splitting(self, aprec): - """ + r""" Compute the exponential power series of this element This is a helper method for :meth:`exp`. @@ -3038,7 +3037,7 @@ cdef class pAdicGenericElement(LocalGenericElement): - ``aprec`` -- an integer, the precision to which to compute the exponential - NOTE:: + .. NOTE:: The function does not check that its argument ``self`` is the disk of convergence of ``exp``. If this assumption is not @@ -3074,7 +3073,7 @@ cdef class pAdicGenericElement(LocalGenericElement): raise NotImplementedError("the binary splitting algorithm is not implemented for the parent: %s" % self.parent()) def _exp_newton(self, aprec, log_algorithm=None): - """ + r""" Compute the exponential power series of this element This is a helper method for :meth:`exp`. @@ -3089,7 +3088,7 @@ cdef class pAdicGenericElement(LocalGenericElement): method. See :meth:`log` for more details about the possible algorithms. - NOTE:: + .. NOTE:: The function does not check that its argument ``self`` is the disk of convergence of ``exp``. If this assumption is not @@ -3783,7 +3782,7 @@ cdef class pAdicGenericElement(LocalGenericElement): return parent(root) def _inverse_pth_root(self, twist=None, hint=None): - """ + r""" In its simplest form, computes the inverse of ``p``-th root of this element. @@ -4409,8 +4408,9 @@ def _polylog_c(n, p): """ return p/(p-1) - (n-1)/p.log().n() + (n-1)*(n*(p-1)/p.log().n()).log(p).n() + (2*p*(p-1)*n/p.log().n()).log(p).n() + def _findprec(c_1, c_2, c_3, p): - """ + r""" Return an integer k such that c_1*k - c_2*log_p(k) > c_3. This is an internal function, used by :meth:`polylog`. @@ -4439,16 +4439,17 @@ def _findprec(c_1, c_2, c_3, p): return k k += 1 + def _compute_g(p, n, prec, terms): - """ + r""" Return the list of power series `g_i = \int(-g_{i-1}/(v-v^2))` used in the computation of polylogarithms. + This is an internal function, used by :meth:`polylog`. EXAMPLES:: sage: sage.rings.padics.padic_generic_element._compute_g(7, 3, 3, 3)[0] O(7^3)*v^2 + (1 + O(7^3))*v + O(7^3) - """ from sage.rings.power_series_ring import PowerSeriesRing from sage.functions.other import ceil diff --git a/src/sage/rings/padics/pow_computer_ext.pyx b/src/sage/rings/padics/pow_computer_ext.pyx index 7dc2887337d..fe60e6bde33 100644 --- a/src/sage/rings/padics/pow_computer_ext.pyx +++ b/src/sage/rings/padics/pow_computer_ext.pyx @@ -1214,8 +1214,9 @@ cdef class PowComputer_ZZ_pX(PowComputer_ext): ZZ_pX_add(xnew_q, xnew_q, x[0]) return 0 + cdef class PowComputer_ZZ_pX_FM(PowComputer_ZZ_pX): - """ + r""" This class only caches a context and modulus for p^prec_cap. Designed for use with fixed modulus p-adic rings, in Eisenstein diff --git a/src/sage/rings/padics/qadic_flint_CA.pyx b/src/sage/rings/padics/qadic_flint_CA.pyx index d10e4b3eaff..3e4d01fcad1 100644 --- a/src/sage/rings/padics/qadic_flint_CA.pyx +++ b/src/sage/rings/padics/qadic_flint_CA.pyx @@ -27,8 +27,8 @@ cdef class qAdicCappedAbsoluteElement(CAElement): norm = norm_unram def matrix_mod_pn(self): - """ - Returns the matrix of right multiplication by the element on + r""" + Return the matrix of right multiplication by the element on the power basis `1, x, x^2, \ldots, x^{d-1}` for this extension field. Thus the *rows* of this matrix give the images of each of the `x^i`. The entries of the matrices are diff --git a/src/sage/rings/padics/qadic_flint_CR.pyx b/src/sage/rings/padics/qadic_flint_CR.pyx index dfadaa2f053..c20fa087424 100644 --- a/src/sage/rings/padics/qadic_flint_CR.pyx +++ b/src/sage/rings/padics/qadic_flint_CR.pyx @@ -27,8 +27,8 @@ cdef class qAdicCappedRelativeElement(CRElement): norm = norm_unram def matrix_mod_pn(self): - """ - Returns the matrix of right multiplication by the element on + r""" + Return the matrix of right multiplication by the element on the power basis `1, x, x^2, \ldots, x^{d-1}` for this extension field. Thus the *rows* of this matrix give the images of each of the `x^i`. The entries of the matrices are diff --git a/src/sage/rings/padics/qadic_flint_FM.pyx b/src/sage/rings/padics/qadic_flint_FM.pyx index 4f3afe77e81..0a5e0563101 100644 --- a/src/sage/rings/padics/qadic_flint_FM.pyx +++ b/src/sage/rings/padics/qadic_flint_FM.pyx @@ -27,8 +27,8 @@ cdef class qAdicFixedModElement(FMElement): norm = norm_unram def matrix_mod_pn(self): - """ - Returns the matrix of right multiplication by the element on + r""" + Return the matrix of right multiplication by the element on the power basis `1, x, x^2, \ldots, x^{d-1}` for this extension field. Thus the *rows* of this matrix give the images of each of the `x^i`. The entries of the matrices are diff --git a/src/sage/rings/padics/qadic_flint_FP.pyx b/src/sage/rings/padics/qadic_flint_FP.pyx index 1e250990c4e..020b03b722a 100644 --- a/src/sage/rings/padics/qadic_flint_FP.pyx +++ b/src/sage/rings/padics/qadic_flint_FP.pyx @@ -29,8 +29,8 @@ cdef class qAdicFloatingPointElement(FPElement): norm = norm_unram def matrix_mod_pn(self): - """ - Returns the matrix of right multiplication by the element on + r""" + Return the matrix of right multiplication by the element on the power basis `1, x, x^2, \ldots, x^{d-1}` for this extension field. Thus the *rows* of this matrix give the images of each of the `x^i`. The entries of the matrices are diff --git a/src/tox.ini b/src/tox.ini index 06ab0b18a0d..1fdf5230aae 100644 --- a/src/tox.ini +++ b/src/tox.ini @@ -111,7 +111,7 @@ description = # See https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes deps = pycodestyle commands = pycodestyle --select E111,E306,E401,E701,E702,E703,W391,W605,E711,E712,E713,E721,E722 {posargs:{toxinidir}/sage/} - pycodestyle --select E111,E401,E703,E712,E713,E721,E722 --filename *.pyx {posargs:{toxinidir}/sage/} + pycodestyle --select E111,E306,E401,E703,W605,E712,E713,E721,E722 --filename *.pyx {posargs:{toxinidir}/sage/} [pycodestyle] max-line-length = 160 From f6faca25071257804593d44278ab6c50a9aa51cc Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 23:34:39 -0700 Subject: [PATCH 321/632] build/pkgs/pip: Update to 22.3 --- build/pkgs/pip/checksums.ini | 6 +++--- build/pkgs/pip/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/pip/checksums.ini b/build/pkgs/pip/checksums.ini index a13af9996c7..cc58418441f 100644 --- a/build/pkgs/pip/checksums.ini +++ b/build/pkgs/pip/checksums.ini @@ -1,5 +1,5 @@ tarball=pip-VERSION.tar.gz -sha1=ed6e6d191a686b4f989a1dbb2640737a1123f24f -md5=05bb8c0607721d171e9eecf22a8c5cc6 -cksum=4023376185 +sha1=a6d9d6b04f17acb11f214db9835f4b66e12a1fe2 +md5=f0dd02265e7ccd2f8758c840fba64810 +cksum=1176625017 upstream_url=https://pypi.io/packages/source/p/pip/pip-VERSION.tar.gz diff --git a/build/pkgs/pip/package-version.txt b/build/pkgs/pip/package-version.txt index 637c2a16439..937387f33bb 100644 --- a/build/pkgs/pip/package-version.txt +++ b/build/pkgs/pip/package-version.txt @@ -1 +1 @@ -22.2.2 +22.3 From 89a7f5248e004ea84f68989cb9126b545f69da25 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 23:35:38 -0700 Subject: [PATCH 322/632] build/pkgs/certifi: Update to 2022.9.24 --- build/pkgs/certifi/checksums.ini | 6 +++--- build/pkgs/certifi/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/certifi/checksums.ini b/build/pkgs/certifi/checksums.ini index 87d8378aa08..cab6201a644 100644 --- a/build/pkgs/certifi/checksums.ini +++ b/build/pkgs/certifi/checksums.ini @@ -1,5 +1,5 @@ tarball=certifi-VERSION.tar.gz -sha1=b13e22d55867e2ca5f92e5289cfdc21ba6e343aa -md5=880ed9e5d04aff8f46f5ff82a3a3e395 -cksum=613361382 +sha1=4a6fb9ae2afe62b33bab98ae21c0853d026d64c2 +md5=ff9c8d5c7e7fb083de6b874609c5ca68 +cksum=726096582 upstream_url=https://pypi.io/packages/source/c/certifi/certifi-VERSION.tar.gz diff --git a/build/pkgs/certifi/package-version.txt b/build/pkgs/certifi/package-version.txt index 6b1fb396ceb..e4b8493d095 100644 --- a/build/pkgs/certifi/package-version.txt +++ b/build/pkgs/certifi/package-version.txt @@ -1 +1 @@ -2021.10.8 +2022.9.24 From 773f2eb51bd9d2acb60fde850a582bc65c495ca5 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 23:36:18 -0700 Subject: [PATCH 323/632] build/pkgs/filelock: Update to 3.8.0 --- build/pkgs/filelock/checksums.ini | 6 +++--- build/pkgs/filelock/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/filelock/checksums.ini b/build/pkgs/filelock/checksums.ini index 24a5e470bef..48a18055930 100644 --- a/build/pkgs/filelock/checksums.ini +++ b/build/pkgs/filelock/checksums.ini @@ -1,5 +1,5 @@ tarball=filelock-VERSION.tar.gz -sha1=e0340015dcb7bbe19b5bf33ff8b9c94670994585 -md5=b1032075ddada92874377426337c38a6 -cksum=3903629392 +sha1=1de304add05b7e3e8874aa9f86202204f8042e30 +md5=9bd8d33d5d7dc95012981ccbfb2d2a0f +cksum=2335245752 upstream_url=https://pypi.io/packages/source/f/filelock/filelock-VERSION.tar.gz diff --git a/build/pkgs/filelock/package-version.txt b/build/pkgs/filelock/package-version.txt index 40c341bdcdb..19811903a7f 100644 --- a/build/pkgs/filelock/package-version.txt +++ b/build/pkgs/filelock/package-version.txt @@ -1 +1 @@ -3.6.0 +3.8.0 From 2b9c26fc5d779c51af12e943b3e15f8df121dec6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 23:36:37 -0700 Subject: [PATCH 324/632] build/pkgs/charset_normalizer: Update to 2.1.1 --- build/pkgs/charset_normalizer/checksums.ini | 6 +++--- build/pkgs/charset_normalizer/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/charset_normalizer/checksums.ini b/build/pkgs/charset_normalizer/checksums.ini index 90d2c47aba4..714de114bec 100644 --- a/build/pkgs/charset_normalizer/checksums.ini +++ b/build/pkgs/charset_normalizer/checksums.ini @@ -1,5 +1,5 @@ tarball=charset-normalizer-VERSION.tar.gz -sha1=6824bfae6dec62d93887b53468ea36124db5ecc8 -md5=f6664e0e90dbb3cc9cfc154a980f9864 -cksum=2680691552 +sha1=f976f0ee784273ee6bc06e996fbc192cbb718d18 +md5=a70f9fc85b6b8265c982eca6fe51381f +cksum=1911107732 upstream_url=https://pypi.io/packages/source/c/charset_normalizer/charset-normalizer-VERSION.tar.gz diff --git a/build/pkgs/charset_normalizer/package-version.txt b/build/pkgs/charset_normalizer/package-version.txt index 280a1e3368b..3e3c2f1e5ed 100644 --- a/build/pkgs/charset_normalizer/package-version.txt +++ b/build/pkgs/charset_normalizer/package-version.txt @@ -1 +1 @@ -2.0.12 +2.1.1 From 8de5b419b659e4821bbdb2d7dc072269cd97353a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 23:36:46 -0700 Subject: [PATCH 325/632] build/pkgs/idna: Update to 3.4 --- build/pkgs/idna/checksums.ini | 6 +++--- build/pkgs/idna/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/idna/checksums.ini b/build/pkgs/idna/checksums.ini index d0e20f2098a..47b585b69cd 100644 --- a/build/pkgs/idna/checksums.ini +++ b/build/pkgs/idna/checksums.ini @@ -1,5 +1,5 @@ tarball=idna-VERSION.tar.gz -sha1=08c0449533fc94462f78652dea209099754d9ee4 -md5=5856306eac5f25db8249e37a4c6ee3e7 -cksum=2700709091 +sha1=c01a061b5ace87f662049d205d5d15e7f8a3a533 +md5=13ea24e076212b6baae1135a116d1e0e +cksum=1497605198 upstream_url=https://pypi.io/packages/source/i/idna/idna-VERSION.tar.gz diff --git a/build/pkgs/idna/package-version.txt b/build/pkgs/idna/package-version.txt index eb39e5382f4..2f4b60750dc 100644 --- a/build/pkgs/idna/package-version.txt +++ b/build/pkgs/idna/package-version.txt @@ -1 +1 @@ -3.3 +3.4 From 48747df5c665b3aae7bb45018a550ed4a4613eed Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 23:37:47 -0700 Subject: [PATCH 326/632] build/pkgs/importlib_metadata: Update to 5.0.0 --- build/pkgs/importlib_metadata/checksums.ini | 6 +++--- build/pkgs/importlib_metadata/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/importlib_metadata/checksums.ini b/build/pkgs/importlib_metadata/checksums.ini index ea39c54a4a3..c6d66d9ee23 100644 --- a/build/pkgs/importlib_metadata/checksums.ini +++ b/build/pkgs/importlib_metadata/checksums.ini @@ -1,5 +1,5 @@ tarball=importlib_metadata-VERSION.tar.gz -sha1=ec68de1ec1800048de8656b9d211e22b7fe7c53e -md5=cfcf29185e13439c76d09c94bc8d81f4 -cksum=2134804316 +sha1=38794db2afb90ed0be04bd8b5996a5b5fd45acc2 +md5=ccd58a387cc2bab6cf72fdf21e403749 +cksum=758044022 upstream_url=https://pypi.io/packages/source/i/importlib_metadata/importlib_metadata-VERSION.tar.gz diff --git a/build/pkgs/importlib_metadata/package-version.txt b/build/pkgs/importlib_metadata/package-version.txt index 815588ef140..0062ac97180 100644 --- a/build/pkgs/importlib_metadata/package-version.txt +++ b/build/pkgs/importlib_metadata/package-version.txt @@ -1 +1 @@ -4.12.0 +5.0.0 From 92f544fc726aa162ed688e0977b73654ad1de2fb Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 23:37:52 -0700 Subject: [PATCH 327/632] build/pkgs/importlib_resources: Update to 5.10.0 --- build/pkgs/importlib_resources/checksums.ini | 6 +++--- build/pkgs/importlib_resources/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/importlib_resources/checksums.ini b/build/pkgs/importlib_resources/checksums.ini index 99a4cdf7908..9885db7ffb4 100644 --- a/build/pkgs/importlib_resources/checksums.ini +++ b/build/pkgs/importlib_resources/checksums.ini @@ -1,5 +1,5 @@ tarball=importlib_resources-VERSION.tar.gz -sha1=3b4b20fa0399e2fa21c7506be27a4b943495d3ad -md5=3b6d98270d40b2ba7af1f8d09188f0c2 -cksum=2401793228 +sha1=a8c7a6a976fffb9841c548230cb633eda3111c4f +md5=8afc48c5f3a7c4ba63cb38163340d78b +cksum=196052500 upstream_url=https://pypi.io/packages/source/i/importlib_resources/importlib_resources-VERSION.tar.gz diff --git a/build/pkgs/importlib_resources/package-version.txt b/build/pkgs/importlib_resources/package-version.txt index b3d91f9cfc0..509b0b618ad 100644 --- a/build/pkgs/importlib_resources/package-version.txt +++ b/build/pkgs/importlib_resources/package-version.txt @@ -1 +1 @@ -5.9.0 +5.10.0 From a8e50429efe1dc91ee5a37178d89c66d020870e4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 23:38:36 -0700 Subject: [PATCH 328/632] build/pkgs/jsonschema: Update to 4.16.0 --- build/pkgs/jsonschema/checksums.ini | 6 +++--- build/pkgs/jsonschema/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/jsonschema/checksums.ini b/build/pkgs/jsonschema/checksums.ini index 955e0401faa..b25e78e04fa 100644 --- a/build/pkgs/jsonschema/checksums.ini +++ b/build/pkgs/jsonschema/checksums.ini @@ -1,5 +1,5 @@ tarball=jsonschema-VERSION.tar.gz -sha1=5f90a208235152dc4f0b8ae51ca2860c0771ff51 -md5=c3f7a29c187bf1d038c66a5d5763eab1 -cksum=2329724463 +sha1=912d562c1394408dca582e14843e3245df2f3827 +md5=3bc1f63a74fc61bf2847f22cadb0dfff +cksum=2200261176 upstream_url=https://pypi.io/packages/source/j/jsonschema/jsonschema-VERSION.tar.gz diff --git a/build/pkgs/jsonschema/package-version.txt b/build/pkgs/jsonschema/package-version.txt index 4404a17baed..ecbc3b03079 100644 --- a/build/pkgs/jsonschema/package-version.txt +++ b/build/pkgs/jsonschema/package-version.txt @@ -1 +1 @@ -4.5.1 +4.16.0 From a0039dbc67cdf1528960ec57c493e220befb5d05 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 18 Oct 2022 12:41:17 -0700 Subject: [PATCH 329/632] build/pkgs/hatch_fancy_pypi_readme: New (jsonschema build dep) --- build/pkgs/hatch_fancy_pypi_readme/SPKG.rst | 18 ++++++++++++++++++ .../pkgs/hatch_fancy_pypi_readme/checksums.ini | 5 +++++ .../pkgs/hatch_fancy_pypi_readme/dependencies | 4 ++++ .../install-requires.txt | 1 + .../package-version.txt | 1 + .../hatch_fancy_pypi_readme/spkg-install.in | 2 ++ build/pkgs/hatch_fancy_pypi_readme/type | 1 + build/pkgs/jsonschema/dependencies | 2 +- 8 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 build/pkgs/hatch_fancy_pypi_readme/SPKG.rst create mode 100644 build/pkgs/hatch_fancy_pypi_readme/checksums.ini create mode 100644 build/pkgs/hatch_fancy_pypi_readme/dependencies create mode 100644 build/pkgs/hatch_fancy_pypi_readme/install-requires.txt create mode 100644 build/pkgs/hatch_fancy_pypi_readme/package-version.txt create mode 100644 build/pkgs/hatch_fancy_pypi_readme/spkg-install.in create mode 100644 build/pkgs/hatch_fancy_pypi_readme/type diff --git a/build/pkgs/hatch_fancy_pypi_readme/SPKG.rst b/build/pkgs/hatch_fancy_pypi_readme/SPKG.rst new file mode 100644 index 00000000000..4e076e4e3cb --- /dev/null +++ b/build/pkgs/hatch_fancy_pypi_readme/SPKG.rst @@ -0,0 +1,18 @@ +hatch_fancy_pypi_readme: Fancy PyPI READMEs with Hatch +====================================================== + +Description +----------- + +Fancy PyPI READMEs with Hatch + +License +------- + +MIT + +Upstream Contact +---------------- + +https://pypi.org/project/hatch-fancy-pypi-readme/ + diff --git a/build/pkgs/hatch_fancy_pypi_readme/checksums.ini b/build/pkgs/hatch_fancy_pypi_readme/checksums.ini new file mode 100644 index 00000000000..6728a45be5c --- /dev/null +++ b/build/pkgs/hatch_fancy_pypi_readme/checksums.ini @@ -0,0 +1,5 @@ +tarball=hatch_fancy_pypi_readme-VERSION.tar.gz +sha1=2cdf215fdd13de69f5de09c7ef0e2ceff4a03666 +md5=588776ea8e3608714d4cbba16dffa92b +cksum=613442646 +upstream_url=https://pypi.io/packages/source/h/hatch_fancy_pypi_readme/hatch_fancy_pypi_readme-VERSION.tar.gz diff --git a/build/pkgs/hatch_fancy_pypi_readme/dependencies b/build/pkgs/hatch_fancy_pypi_readme/dependencies new file mode 100644 index 00000000000..0738c2d7777 --- /dev/null +++ b/build/pkgs/hatch_fancy_pypi_readme/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/hatch_fancy_pypi_readme/install-requires.txt b/build/pkgs/hatch_fancy_pypi_readme/install-requires.txt new file mode 100644 index 00000000000..6d9a1f85903 --- /dev/null +++ b/build/pkgs/hatch_fancy_pypi_readme/install-requires.txt @@ -0,0 +1 @@ +hatch-fancy-pypi-readme diff --git a/build/pkgs/hatch_fancy_pypi_readme/package-version.txt b/build/pkgs/hatch_fancy_pypi_readme/package-version.txt new file mode 100644 index 00000000000..9d673278df8 --- /dev/null +++ b/build/pkgs/hatch_fancy_pypi_readme/package-version.txt @@ -0,0 +1 @@ +22.8.0 diff --git a/build/pkgs/hatch_fancy_pypi_readme/spkg-install.in b/build/pkgs/hatch_fancy_pypi_readme/spkg-install.in new file mode 100644 index 00000000000..37ac1a53437 --- /dev/null +++ b/build/pkgs/hatch_fancy_pypi_readme/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install . diff --git a/build/pkgs/hatch_fancy_pypi_readme/type b/build/pkgs/hatch_fancy_pypi_readme/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/hatch_fancy_pypi_readme/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/jsonschema/dependencies b/build/pkgs/jsonschema/dependencies index f487bdbbe91..e488e4e4422 100644 --- a/build/pkgs/jsonschema/dependencies +++ b/build/pkgs/jsonschema/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) vcversioner attrs importlib_metadata pyrsistent | $(PYTHON_TOOLCHAIN) hatchling +$(PYTHON) vcversioner attrs importlib_metadata pyrsistent | $(PYTHON_TOOLCHAIN) hatchling hatch_fancy_pypi_readme ---------- All lines of this file are ignored except the first. From e9d639cd57c8ec8606d0af88bb675782a4944d31 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 23:39:06 -0700 Subject: [PATCH 330/632] build/pkgs/nest_asyncio: Update to 1.5.6 --- build/pkgs/nest_asyncio/checksums.ini | 6 +++--- build/pkgs/nest_asyncio/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/nest_asyncio/checksums.ini b/build/pkgs/nest_asyncio/checksums.ini index 3ffec6bed8b..1a11eb5178b 100644 --- a/build/pkgs/nest_asyncio/checksums.ini +++ b/build/pkgs/nest_asyncio/checksums.ini @@ -1,5 +1,5 @@ tarball=nest_asyncio-VERSION.tar.gz -sha1=ca7b9c57e544a7ca1e96d37ead648588cfb9a3eb -md5=0243278ed8804811b00049a545856dcb -cksum=3944772454 +sha1=1e862862afe4c2e057065212eefe7203ccee4927 +md5=7c7108921a64e7abbb6993803343819b +cksum=982689987 upstream_url=https://pypi.io/packages/source/n/nest_asyncio/nest_asyncio-VERSION.tar.gz diff --git a/build/pkgs/nest_asyncio/package-version.txt b/build/pkgs/nest_asyncio/package-version.txt index 9075be49515..eac1e0ada6d 100644 --- a/build/pkgs/nest_asyncio/package-version.txt +++ b/build/pkgs/nest_asyncio/package-version.txt @@ -1 +1 @@ -1.5.5 +1.5.6 From 40e60b630c407bf8d8bd808105310e884320c1db Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 23:39:32 -0700 Subject: [PATCH 331/632] build/pkgs/platformdirs: Update to 2.5.2 --- build/pkgs/platformdirs/checksums.ini | 6 +++--- build/pkgs/platformdirs/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/platformdirs/checksums.ini b/build/pkgs/platformdirs/checksums.ini index e6a6c0613a8..8dc4a85a66d 100644 --- a/build/pkgs/platformdirs/checksums.ini +++ b/build/pkgs/platformdirs/checksums.ini @@ -1,5 +1,5 @@ tarball=platformdirs-VERSION.tar.gz -sha1=16e9a89587b4471041c6d1a2444d200a92292c73 -md5=83d3ce3feb4af1ccfaca24375574f44d -cksum=1234653032 +sha1=344841a3cd4eb5b1a1b8adb8a57e845e5a06b236 +md5=2301a8a29c3082a49ee293073d893887 +cksum=1100125935 upstream_url=https://pypi.io/packages/source/p/platformdirs/platformdirs-VERSION.tar.gz diff --git a/build/pkgs/platformdirs/package-version.txt b/build/pkgs/platformdirs/package-version.txt index 73462a5a134..f225a78adf0 100644 --- a/build/pkgs/platformdirs/package-version.txt +++ b/build/pkgs/platformdirs/package-version.txt @@ -1 +1 @@ -2.5.1 +2.5.2 From e7f7639aa326705f89ccd8b3377fa48b607cedb4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 23:41:50 -0700 Subject: [PATCH 332/632] build/pkgs/pytz: Update to 2022.4 --- build/pkgs/pytz/checksums.ini | 6 +++--- build/pkgs/pytz/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/pytz/checksums.ini b/build/pkgs/pytz/checksums.ini index 0faabac40af..0d031b316d0 100644 --- a/build/pkgs/pytz/checksums.ini +++ b/build/pkgs/pytz/checksums.ini @@ -1,5 +1,5 @@ tarball=pytz-VERSION.tar.gz -sha1=fa6729d40cfa607daee0f40cdab165976ab0e0a3 -md5=d7b7060bbac4970afa2050c139c9fcb6 -cksum=3161093227 +sha1=74975d8618b7c71bfc38958979ab96da4d9a3aba +md5=b1d2ed6592bbdf6002ef52b4ab8e2efe +cksum=1769835625 upstream_url=https://pypi.io/packages/source/p/pytz/pytz-VERSION.tar.gz diff --git a/build/pkgs/pytz/package-version.txt b/build/pkgs/pytz/package-version.txt index c9c8e05b0f8..a2a5548bc1c 100644 --- a/build/pkgs/pytz/package-version.txt +++ b/build/pkgs/pytz/package-version.txt @@ -1 +1 @@ -2021.3 +2022.4 From 670ae27ae2357ebc3737b72ec41f494c3114ad3d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 23:42:03 -0700 Subject: [PATCH 333/632] build/pkgs/requests: Update to 2.28.1 --- build/pkgs/requests/checksums.ini | 6 +++--- build/pkgs/requests/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/requests/checksums.ini b/build/pkgs/requests/checksums.ini index 4a2693a8d90..5aabe04f278 100644 --- a/build/pkgs/requests/checksums.ini +++ b/build/pkgs/requests/checksums.ini @@ -1,5 +1,5 @@ tarball=requests-VERSION.tar.gz -sha1=a72bdfba339f5058c051c71954b59bef94c84455 -md5=5c1f6e737e1cb6f86a91d7a1473eda95 -cksum=4021136359 +sha1=53381250a0d114109a9e712dd7ce8e40e63e61e2 +md5=796ea875cdae283529c03b9203d9c454 +cksum=4112189908 upstream_url=https://pypi.io/packages/source/r/requests/requests-VERSION.tar.gz diff --git a/build/pkgs/requests/package-version.txt b/build/pkgs/requests/package-version.txt index 90efbd4e31e..9738a24f699 100644 --- a/build/pkgs/requests/package-version.txt +++ b/build/pkgs/requests/package-version.txt @@ -1 +1 @@ -2.28.0 +2.28.1 From 73cfdaf41dcf5c50dfa334271c94559a3a214240 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 23:42:47 -0700 Subject: [PATCH 334/632] build/pkgs/tomlkit: Update to 0.11.5 --- build/pkgs/tomlkit/checksums.ini | 6 +++--- build/pkgs/tomlkit/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/tomlkit/checksums.ini b/build/pkgs/tomlkit/checksums.ini index f43c80b7196..c5c309c6b4b 100644 --- a/build/pkgs/tomlkit/checksums.ini +++ b/build/pkgs/tomlkit/checksums.ini @@ -1,5 +1,5 @@ tarball=tomlkit-VERSION.tar.gz -sha1=65f56e209410e4eee4b45d048e6b4dc0fcaad74a -md5=d0edd43143c7840deb88185685cea8dd -cksum=846256591 +sha1=3d03c83e2dc36d2b1cef1bb82902f89ce639e2fe +md5=d5702dd3ecf513935d24d673761f5296 +cksum=3072168699 upstream_url=https://pypi.io/packages/source/t/tomlkit/tomlkit-VERSION.tar.gz diff --git a/build/pkgs/tomlkit/package-version.txt b/build/pkgs/tomlkit/package-version.txt index 35ad34429be..62d5dbdf3c7 100644 --- a/build/pkgs/tomlkit/package-version.txt +++ b/build/pkgs/tomlkit/package-version.txt @@ -1 +1 @@ -0.11.4 +0.11.5 From 783704656201cf6356a7c68eaeec64479326f7ec Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 23:43:04 -0700 Subject: [PATCH 335/632] build/pkgs/tinycss2: Update to 1.2.0 --- build/pkgs/tinycss2/checksums.ini | 6 +++--- build/pkgs/tinycss2/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/tinycss2/checksums.ini b/build/pkgs/tinycss2/checksums.ini index 668d72fa9e4..cf10f3f8d3f 100644 --- a/build/pkgs/tinycss2/checksums.ini +++ b/build/pkgs/tinycss2/checksums.ini @@ -1,5 +1,5 @@ tarball=tinycss2-VERSION.tar.gz -sha1=4a05bce2f350b120791a59c6595564274ca4c6b3 -md5=60272f58f8d5834b2e09ffbc9bd5de53 -cksum=3520456675 +sha1=250a0e4d72241428b4172c0431b7a5d759ccb285 +md5=13b8548422f600032722c25a74706daa +cksum=2174916905 upstream_url=https://pypi.io/packages/source/t/tinycss2/tinycss2-VERSION.tar.gz diff --git a/build/pkgs/tinycss2/package-version.txt b/build/pkgs/tinycss2/package-version.txt index 524cb55242b..26aaba0e866 100644 --- a/build/pkgs/tinycss2/package-version.txt +++ b/build/pkgs/tinycss2/package-version.txt @@ -1 +1 @@ -1.1.1 +1.2.0 From e70c275d808218e7bb4627b85ebca5cffc98c320 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 23:43:14 -0700 Subject: [PATCH 336/632] build/pkgs/typing_extensions: Update to 4.4.0 --- build/pkgs/typing_extensions/checksums.ini | 6 +++--- build/pkgs/typing_extensions/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/typing_extensions/checksums.ini b/build/pkgs/typing_extensions/checksums.ini index 6a9672ec92f..d6cbf2607c0 100644 --- a/build/pkgs/typing_extensions/checksums.ini +++ b/build/pkgs/typing_extensions/checksums.ini @@ -1,5 +1,5 @@ tarball=typing_extensions-VERSION.tar.gz -sha1=efa40572330e9a3c38ba519028f36d7f93647a39 -md5=9b5b33ae64c94479fa0862cf8ae89d58 -cksum=1588180971 +sha1=9dbf798784009efaef80c8198a75b2a9e519eb95 +md5=5cfcb56ea6fc4972c3600c0030f4d136 +cksum=386983249 upstream_url=https://pypi.io/packages/source/t/typing_extensions/typing_extensions-VERSION.tar.gz diff --git a/build/pkgs/typing_extensions/package-version.txt b/build/pkgs/typing_extensions/package-version.txt index 37c18af77ed..fdc6698807a 100644 --- a/build/pkgs/typing_extensions/package-version.txt +++ b/build/pkgs/typing_extensions/package-version.txt @@ -1 +1 @@ -3.10.0.0 +4.4.0 From 84bf1dec0cc5742b4fcf92b09db535db5953d1a3 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 23:43:25 -0700 Subject: [PATCH 337/632] build/pkgs/tzdata: Update to 2022.5 --- build/pkgs/tzdata/checksums.ini | 6 +++--- build/pkgs/tzdata/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/tzdata/checksums.ini b/build/pkgs/tzdata/checksums.ini index 0f4b1dacf0e..bc975d6471a 100644 --- a/build/pkgs/tzdata/checksums.ini +++ b/build/pkgs/tzdata/checksums.ini @@ -1,5 +1,5 @@ tarball=tzdata-VERSION.tar.gz -sha1=b63835812a08e453376558df28e05bf30168e4c6 -md5=b458f1ceb9371d9d3051de2fd2c45bb8 -cksum=3348191929 +sha1=3acb57630d74572e9db51f91c5bdd38ef64ac100 +md5=23da690574817c8233185edeada3924c +cksum=467850857 upstream_url=https://pypi.io/packages/source/t/tzdata/tzdata-VERSION.tar.gz diff --git a/build/pkgs/tzdata/package-version.txt b/build/pkgs/tzdata/package-version.txt index 7eaecee1e5c..ef52d12ba35 100644 --- a/build/pkgs/tzdata/package-version.txt +++ b/build/pkgs/tzdata/package-version.txt @@ -1 +1 @@ -2022.1 +2022.5 From 6613a205120bae52c40eb8aab832502bf0433a65 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 23:43:37 -0700 Subject: [PATCH 338/632] build/pkgs/urllib3: Update to 1.26.12 --- build/pkgs/urllib3/checksums.ini | 6 +++--- build/pkgs/urllib3/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/urllib3/checksums.ini b/build/pkgs/urllib3/checksums.ini index 102eb61180b..bfb7c5afa48 100644 --- a/build/pkgs/urllib3/checksums.ini +++ b/build/pkgs/urllib3/checksums.ini @@ -1,5 +1,5 @@ tarball=urllib3-VERSION.tar.gz -sha1=f7fc28c04042d141272d9aa24ca9506b9e59f870 -md5=d4b58522821a33c5e421191b83e0dbac -cksum=1538549000 +sha1=ad6bd811a3f4c3e04d86c2706c9994c3e2236e53 +md5=ba308b52b9092184cf4905bc59a88fc0 +cksum=2776794349 upstream_url=https://pypi.io/packages/source/u/urllib3/urllib3-VERSION.tar.gz diff --git a/build/pkgs/urllib3/package-version.txt b/build/pkgs/urllib3/package-version.txt index 3cd33cde0f7..b74a856da82 100644 --- a/build/pkgs/urllib3/package-version.txt +++ b/build/pkgs/urllib3/package-version.txt @@ -1 +1 @@ -1.26.9 +1.26.12 From bb6351f85221e8d9b18bd48949b45e7630af598b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 23:44:02 -0700 Subject: [PATCH 339/632] build/pkgs/virtualenv: Update to 20.16.5 --- build/pkgs/virtualenv/checksums.ini | 6 +++--- build/pkgs/virtualenv/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/virtualenv/checksums.ini b/build/pkgs/virtualenv/checksums.ini index 5e1728927f4..2d9e0f511fe 100644 --- a/build/pkgs/virtualenv/checksums.ini +++ b/build/pkgs/virtualenv/checksums.ini @@ -1,5 +1,5 @@ tarball=virtualenv-VERSION.tar.gz -sha1=3c00a243652c2bdbf700828b2bca7733941e1453 -md5=1f7ddaff06a58ee4508bed1f03a5ce1c -cksum=2270354493 +sha1=24745f6095d59917577890172fd52feb8851f547 +md5=44c7d81666301c6c9d0b03a6751d9604 +cksum=1850303439 upstream_url=https://pypi.io/packages/source/v/virtualenv/virtualenv-VERSION.tar.gz diff --git a/build/pkgs/virtualenv/package-version.txt b/build/pkgs/virtualenv/package-version.txt index 418ef16ce2b..140bca73f83 100644 --- a/build/pkgs/virtualenv/package-version.txt +++ b/build/pkgs/virtualenv/package-version.txt @@ -1 +1 @@ -20.14.1 +20.16.5 From 4b389bc295bd1655a7df6b2acdee0f217b6f3465 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 23:44:13 -0700 Subject: [PATCH 340/632] build/pkgs/tox: Update to 3.26.0 --- build/pkgs/tox/checksums.ini | 6 +++--- build/pkgs/tox/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/tox/checksums.ini b/build/pkgs/tox/checksums.ini index f64c6e12b24..af645e47c4b 100644 --- a/build/pkgs/tox/checksums.ini +++ b/build/pkgs/tox/checksums.ini @@ -1,5 +1,5 @@ tarball=tox-VERSION.tar.gz -sha1=b755ebd6a4fe57bb64bd9cc460761ac38d2c1bfd -md5=b270c0b956305570fb9a638ab72fecec -cksum=3980705014 +sha1=70341b4bd57be86410f352c7b30a07cefef087c3 +md5=7e261bc4050a698adf06dfb553869b58 +cksum=3295576412 upstream_url=https://pypi.io/packages/source/t/tox/tox-VERSION.tar.gz diff --git a/build/pkgs/tox/package-version.txt b/build/pkgs/tox/package-version.txt index 693bd59e3e6..419ede3b9cb 100644 --- a/build/pkgs/tox/package-version.txt +++ b/build/pkgs/tox/package-version.txt @@ -1 +1 @@ -3.24.3 +3.26.0 From 826d419baecbd3061614b5eadcf862125c3b5cdd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 23:44:25 -0700 Subject: [PATCH 341/632] build/pkgs/zipp: Update to 3.9.0 --- build/pkgs/zipp/checksums.ini | 6 +++--- build/pkgs/zipp/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/zipp/checksums.ini b/build/pkgs/zipp/checksums.ini index 3c82ab5cc17..8a729185177 100644 --- a/build/pkgs/zipp/checksums.ini +++ b/build/pkgs/zipp/checksums.ini @@ -1,5 +1,5 @@ tarball=zipp-VERSION.tar.gz -sha1=52f43da4467b178ac97ca78a2487ecbc94b4a9b2 -md5=8864ff5ed01cd28755cc87f1443dbc67 -cksum=3776620702 +sha1=8ce743d9a8850db38b52c318087dbe4a2a9868d3 +md5=46815096f7e8cf91de9a0d6c84727608 +cksum=616631595 upstream_url=https://pypi.io/packages/source/z/zipp/zipp-VERSION.tar.gz diff --git a/build/pkgs/zipp/package-version.txt b/build/pkgs/zipp/package-version.txt index 19811903a7f..a5c4c763394 100644 --- a/build/pkgs/zipp/package-version.txt +++ b/build/pkgs/zipp/package-version.txt @@ -1 +1 @@ -3.8.0 +3.9.0 From 38ba4c8032af9f0fb7eae5b26aa10d3a141df2f8 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 23:44:54 -0700 Subject: [PATCH 342/632] build/pkgs/asttokens: Update to 2.0.8 --- build/pkgs/asttokens/checksums.ini | 6 +++--- build/pkgs/asttokens/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/asttokens/checksums.ini b/build/pkgs/asttokens/checksums.ini index 2ebd46ce567..73c747fbc73 100644 --- a/build/pkgs/asttokens/checksums.ini +++ b/build/pkgs/asttokens/checksums.ini @@ -1,5 +1,5 @@ tarball=asttokens-VERSION.tar.gz -sha1=8a9c1fc8752fedb52189441f9f874f5e1afd5866 -md5=0a2a057b9c9a220bffdb3e7512062f17 -cksum=2612374104 +sha1=a8555556ffc39df85963ac3ee82bb8af063296c9 +md5=ba3cb54e6a851636df293b8a4253f80f +cksum=3689538391 upstream_url=https://pypi.io/packages/source/a/asttokens/asttokens-VERSION.tar.gz diff --git a/build/pkgs/asttokens/package-version.txt b/build/pkgs/asttokens/package-version.txt index e01025862f7..815e68dd20e 100644 --- a/build/pkgs/asttokens/package-version.txt +++ b/build/pkgs/asttokens/package-version.txt @@ -1 +1 @@ -2.0.5 +2.0.8 From df5430bc0a84da05179832d535765674e3f00f91 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 23:45:25 -0700 Subject: [PATCH 343/632] build/pkgs/bleach: Update to 5.0.1 --- build/pkgs/bleach/checksums.ini | 6 +++--- build/pkgs/bleach/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/bleach/checksums.ini b/build/pkgs/bleach/checksums.ini index 1eb2d84effa..4d4855aab72 100644 --- a/build/pkgs/bleach/checksums.ini +++ b/build/pkgs/bleach/checksums.ini @@ -1,5 +1,5 @@ tarball=bleach-VERSION.tar.gz -sha1=8b4652eb5a4a1cd6dbf35905a25f389da512f940 -md5=97322e672e4b285e6354c40d07166fc4 -cksum=1632919602 +sha1=73c6b8fad993b318859ca65c365ac2191edd35fc +md5=03b5faa43c0d771a86a2c4cb2575d070 +cksum=4204308806 upstream_url=https://pypi.io/packages/source/b/bleach/bleach-VERSION.tar.gz diff --git a/build/pkgs/bleach/package-version.txt b/build/pkgs/bleach/package-version.txt index 0062ac97180..6b244dcd696 100644 --- a/build/pkgs/bleach/package-version.txt +++ b/build/pkgs/bleach/package-version.txt @@ -1 +1 @@ -5.0.0 +5.0.1 From b588629e7d14300b8e5d0208ee320d27b4f936f0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 23:45:35 -0700 Subject: [PATCH 344/632] build/pkgs/cffi: Update to 1.15.1 --- build/pkgs/cffi/checksums.ini | 6 +++--- build/pkgs/cffi/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/cffi/checksums.ini b/build/pkgs/cffi/checksums.ini index 9d2863a8496..5e9ebc003f4 100644 --- a/build/pkgs/cffi/checksums.ini +++ b/build/pkgs/cffi/checksums.ini @@ -1,5 +1,5 @@ tarball=cffi-VERSION.tar.gz -sha1=9c51c29e35510adf7f94542e1f8e05611930b07b -md5=f3a3f26cd3335fc597479c9475da0a0b -cksum=3482630007 +sha1=c42a46cd11f6153f299cf10e9c236e8b2a143c21 +md5=f493860a6e98cd0c4178149568a6b4f6 +cksum=585894851 upstream_url=https://pypi.io/packages/source/c/cffi/cffi-VERSION.tar.gz diff --git a/build/pkgs/cffi/package-version.txt b/build/pkgs/cffi/package-version.txt index 141f2e805be..ace44233b4a 100644 --- a/build/pkgs/cffi/package-version.txt +++ b/build/pkgs/cffi/package-version.txt @@ -1 +1 @@ -1.15.0 +1.15.1 From a099c0389c979f553782bd875e2dcc7312b7f9c0 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 23:45:52 -0700 Subject: [PATCH 345/632] build/pkgs/debugpy: Update to 1.6.3 --- build/pkgs/debugpy/checksums.ini | 6 +++--- build/pkgs/debugpy/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/debugpy/checksums.ini b/build/pkgs/debugpy/checksums.ini index 5872243e9f5..94e60c8de63 100644 --- a/build/pkgs/debugpy/checksums.ini +++ b/build/pkgs/debugpy/checksums.ini @@ -1,5 +1,5 @@ tarball=debugpy-VERSION.zip -sha1=5a0066e4641659c63ecc8d6ce35e96a2fd89b195 -md5=27a4789bfda161dc7de6a6860eeeff38 -cksum=708733483 +sha1=44ae7bfe2d355990604f83ee4c24eb81631b4433 +md5=a999f81d29db030bfacab544d5fb0976 +cksum=3944616380 upstream_url=https://pypi.io/packages/source/d/debugpy/debugpy-VERSION.zip diff --git a/build/pkgs/debugpy/package-version.txt b/build/pkgs/debugpy/package-version.txt index dc1e644a101..266146b87cb 100644 --- a/build/pkgs/debugpy/package-version.txt +++ b/build/pkgs/debugpy/package-version.txt @@ -1 +1 @@ -1.6.0 +1.6.3 From 8f2142f043a644fb384d5bb6b1c9c35bb061a5c3 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 23:46:01 -0700 Subject: [PATCH 346/632] build/pkgs/executing: Update to 1.1.1 --- build/pkgs/executing/checksums.ini | 6 +++--- build/pkgs/executing/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/executing/checksums.ini b/build/pkgs/executing/checksums.ini index 660ea8654b9..bc94cbcea54 100644 --- a/build/pkgs/executing/checksums.ini +++ b/build/pkgs/executing/checksums.ini @@ -1,5 +1,5 @@ tarball=executing-VERSION.tar.gz -sha1=9588832c2abca704a0a287d2c43690c046424697 -md5=43da806fc75eaba315e4947b329d2a90 -cksum=2140182428 +sha1=e962c1719fe8e259b8a7988421e5995ca0a4b8d3 +md5=5b4e105769218bc64eb0df434cb1cedf +cksum=3777168231 upstream_url=https://pypi.io/packages/source/e/executing/executing-VERSION.tar.gz diff --git a/build/pkgs/executing/package-version.txt b/build/pkgs/executing/package-version.txt index ee94dd834b5..524cb55242b 100644 --- a/build/pkgs/executing/package-version.txt +++ b/build/pkgs/executing/package-version.txt @@ -1 +1 @@ -0.8.3 +1.1.1 From 701db6ef42ef6b17577cdc69c751f608ba43ebed Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 17 Oct 2022 23:46:11 -0700 Subject: [PATCH 347/632] build/pkgs/fastjsonschema: Update to 2.16.2 --- build/pkgs/fastjsonschema/checksums.ini | 6 +++--- build/pkgs/fastjsonschema/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/fastjsonschema/checksums.ini b/build/pkgs/fastjsonschema/checksums.ini index 94301089b1a..9b6be281643 100644 --- a/build/pkgs/fastjsonschema/checksums.ini +++ b/build/pkgs/fastjsonschema/checksums.ini @@ -1,5 +1,5 @@ tarball=fastjsonschema-VERSION.tar.gz -sha1=3634374e5004103a3789753f0c145bb798f90874 -md5=c371e5315f66bdd18b62e14c66f89543 -cksum=2483060937 +sha1=a6c53c1eed4f0fbb9c7eaf0fc21fc2c0be85bcd8 +md5=d7d76db7518e64b53a13a7a2315a1671 +cksum=1205433737 upstream_url=https://pypi.io/packages/source/f/fastjsonschema/fastjsonschema-VERSION.tar.gz diff --git a/build/pkgs/fastjsonschema/package-version.txt b/build/pkgs/fastjsonschema/package-version.txt index 6480dd5ed87..43c85e79255 100644 --- a/build/pkgs/fastjsonschema/package-version.txt +++ b/build/pkgs/fastjsonschema/package-version.txt @@ -1 +1 @@ -2.15.3 +2.16.2 From 3077298f9b13bfbb905adb7b823e94d5001ebb14 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 18 Oct 2022 00:26:44 -0700 Subject: [PATCH 348/632] build/pkgs/setuptools_scm: Update to 6.4.2 --- build/pkgs/setuptools_scm/checksums.ini | 6 +++--- build/pkgs/setuptools_scm/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/setuptools_scm/checksums.ini b/build/pkgs/setuptools_scm/checksums.ini index a5c492c3e2f..a6f5ff1395b 100644 --- a/build/pkgs/setuptools_scm/checksums.ini +++ b/build/pkgs/setuptools_scm/checksums.ini @@ -1,5 +1,5 @@ tarball=setuptools_scm-VERSION.tar.gz -sha1=a4f02fddae697614e356cadfddb6241cc7737f38 -md5=32918d8ac566360c21411e0b3556c695 -cksum=1450556136 +sha1=0f34eba670121f9c41939ca8d805687de359f71c +md5=b4e02bf8e62ed49142ea7b42a68671d7 +cksum=1975160274 upstream_url=https://pypi.io/packages/source/s/setuptools_scm/setuptools_scm-VERSION.tar.gz diff --git a/build/pkgs/setuptools_scm/package-version.txt b/build/pkgs/setuptools_scm/package-version.txt index 91e4a9f2622..a4c853ea2ea 100644 --- a/build/pkgs/setuptools_scm/package-version.txt +++ b/build/pkgs/setuptools_scm/package-version.txt @@ -1 +1 @@ -6.3.2 +6.4.2 From 045e2282c5f3f8b1fb35a35d4195fa52cea0c480 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 18 Oct 2022 00:31:05 -0700 Subject: [PATCH 349/632] build/pkgs/platformdirs/dependencies: Update --- build/pkgs/hatch_vcs/SPKG.rst | 16 ++++++++++++++++ build/pkgs/hatch_vcs/checksums.ini | 5 +++++ build/pkgs/hatch_vcs/dependencies | 4 ++++ build/pkgs/hatch_vcs/install-requires.txt | 1 + build/pkgs/hatch_vcs/package-version.txt | 1 + build/pkgs/hatch_vcs/spkg-install.in | 2 ++ build/pkgs/hatch_vcs/type | 1 + build/pkgs/platformdirs/dependencies | 2 +- 8 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 build/pkgs/hatch_vcs/SPKG.rst create mode 100644 build/pkgs/hatch_vcs/checksums.ini create mode 100644 build/pkgs/hatch_vcs/dependencies create mode 100644 build/pkgs/hatch_vcs/install-requires.txt create mode 100644 build/pkgs/hatch_vcs/package-version.txt create mode 100644 build/pkgs/hatch_vcs/spkg-install.in create mode 100644 build/pkgs/hatch_vcs/type diff --git a/build/pkgs/hatch_vcs/SPKG.rst b/build/pkgs/hatch_vcs/SPKG.rst new file mode 100644 index 00000000000..51f4780749e --- /dev/null +++ b/build/pkgs/hatch_vcs/SPKG.rst @@ -0,0 +1,16 @@ +hatch_vcs: Hatch plugin for versioning with your preferred VCS +============================================================== + +Description +----------- + +Hatch plugin for versioning with your preferred VCS + +License +------- + +Upstream Contact +---------------- + +https://pypi.org/project/hatch-vcs/ + diff --git a/build/pkgs/hatch_vcs/checksums.ini b/build/pkgs/hatch_vcs/checksums.ini new file mode 100644 index 00000000000..47e0c350f03 --- /dev/null +++ b/build/pkgs/hatch_vcs/checksums.ini @@ -0,0 +1,5 @@ +tarball=hatch_vcs-VERSION.tar.gz +sha1=9d38f55610b156b513d3d2a79f81cbf4fdea3cb2 +md5=e56b6d0c05cfb9b59d493c67f94d6e48 +cksum=680867691 +upstream_url=https://pypi.io/packages/source/h/hatch_vcs/hatch_vcs-VERSION.tar.gz diff --git a/build/pkgs/hatch_vcs/dependencies b/build/pkgs/hatch_vcs/dependencies new file mode 100644 index 00000000000..0738c2d7777 --- /dev/null +++ b/build/pkgs/hatch_vcs/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/hatch_vcs/install-requires.txt b/build/pkgs/hatch_vcs/install-requires.txt new file mode 100644 index 00000000000..04e2069fbb3 --- /dev/null +++ b/build/pkgs/hatch_vcs/install-requires.txt @@ -0,0 +1 @@ +hatch-vcs diff --git a/build/pkgs/hatch_vcs/package-version.txt b/build/pkgs/hatch_vcs/package-version.txt new file mode 100644 index 00000000000..0ea3a944b39 --- /dev/null +++ b/build/pkgs/hatch_vcs/package-version.txt @@ -0,0 +1 @@ +0.2.0 diff --git a/build/pkgs/hatch_vcs/spkg-install.in b/build/pkgs/hatch_vcs/spkg-install.in new file mode 100644 index 00000000000..37ac1a53437 --- /dev/null +++ b/build/pkgs/hatch_vcs/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install . diff --git a/build/pkgs/hatch_vcs/type b/build/pkgs/hatch_vcs/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/hatch_vcs/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/platformdirs/dependencies b/build/pkgs/platformdirs/dependencies index 0738c2d7777..5b4aec583a4 100644 --- a/build/pkgs/platformdirs/dependencies +++ b/build/pkgs/platformdirs/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | $(PYTHON_TOOLCHAIN) +$(PYTHON) setuptools_scm | $(PYTHON_TOOLCHAIN) hatchling hatch_vcs ---------- All lines of this file are ignored except the first. From 8ecf31d16c95420094a572f6276958213170703b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 18 Oct 2022 12:36:44 -0700 Subject: [PATCH 350/632] build/pkgs/hatchling: Update to 1.11.0 --- build/pkgs/hatchling/checksums.ini | 6 +++--- build/pkgs/hatchling/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/hatchling/checksums.ini b/build/pkgs/hatchling/checksums.ini index 598cd79a2c7..cfa9e772f32 100644 --- a/build/pkgs/hatchling/checksums.ini +++ b/build/pkgs/hatchling/checksums.ini @@ -1,5 +1,5 @@ tarball=hatchling-VERSION.tar.gz -sha1=8f102796a225fb18b0571a44308341c7211d5d94 -md5=c50eff4f711cee451037ec7eb780154a -cksum=3180958969 +sha1=e5f87d37fe2a2a20aff992ad5bdc710b4233059d +md5=9bd7e55ca208af96a1fcfd192ce54eec +cksum=3049487864 upstream_url=https://pypi.io/packages/source/h/hatchling/hatchling-VERSION.tar.gz diff --git a/build/pkgs/hatchling/package-version.txt b/build/pkgs/hatchling/package-version.txt index 81c871de46b..1cac385c6cb 100644 --- a/build/pkgs/hatchling/package-version.txt +++ b/build/pkgs/hatchling/package-version.txt @@ -1 +1 @@ -1.10.0 +1.11.0 From 5fc21762242d41875be7e733dc2dd93b64a0cfc8 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 Oct 2022 14:41:13 -0700 Subject: [PATCH 351/632] build/pkgs/asttokens: Update to 2.1.0 --- build/pkgs/asttokens/checksums.ini | 6 +++--- build/pkgs/asttokens/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/asttokens/checksums.ini b/build/pkgs/asttokens/checksums.ini index 73c747fbc73..e69f1ecdfb3 100644 --- a/build/pkgs/asttokens/checksums.ini +++ b/build/pkgs/asttokens/checksums.ini @@ -1,5 +1,5 @@ tarball=asttokens-VERSION.tar.gz -sha1=a8555556ffc39df85963ac3ee82bb8af063296c9 -md5=ba3cb54e6a851636df293b8a4253f80f -cksum=3689538391 +sha1=cca6058c6c23195148be93bfa32c0a0ca9b2f873 +md5=67b269e359fcb404cd8626985f3676ae +cksum=3749309047 upstream_url=https://pypi.io/packages/source/a/asttokens/asttokens-VERSION.tar.gz diff --git a/build/pkgs/asttokens/package-version.txt b/build/pkgs/asttokens/package-version.txt index 815e68dd20e..7ec1d6db408 100644 --- a/build/pkgs/asttokens/package-version.txt +++ b/build/pkgs/asttokens/package-version.txt @@ -1 +1 @@ -2.0.8 +2.1.0 From 7d876d9228b7c816866f4953a8b83e8014cd91ee Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 Oct 2022 14:43:02 -0700 Subject: [PATCH 352/632] build/pkgs/hatchling: Update to 1.11.1 --- build/pkgs/hatchling/checksums.ini | 6 +++--- build/pkgs/hatchling/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/hatchling/checksums.ini b/build/pkgs/hatchling/checksums.ini index cfa9e772f32..2980620c8d8 100644 --- a/build/pkgs/hatchling/checksums.ini +++ b/build/pkgs/hatchling/checksums.ini @@ -1,5 +1,5 @@ tarball=hatchling-VERSION.tar.gz -sha1=e5f87d37fe2a2a20aff992ad5bdc710b4233059d -md5=9bd7e55ca208af96a1fcfd192ce54eec -cksum=3049487864 +sha1=5d2e7ac6feffa2dfdbf81e7d10661ac1b08b9608 +md5=e06cc65ac646f9b01df5406aa1f97022 +cksum=310056602 upstream_url=https://pypi.io/packages/source/h/hatchling/hatchling-VERSION.tar.gz diff --git a/build/pkgs/hatchling/package-version.txt b/build/pkgs/hatchling/package-version.txt index 1cac385c6cb..720c7384c61 100644 --- a/build/pkgs/hatchling/package-version.txt +++ b/build/pkgs/hatchling/package-version.txt @@ -1 +1 @@ -1.11.0 +1.11.1 From eb4ef6a0e9b9988377e95791ba32022ca6e1a4d5 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 Oct 2022 14:44:01 -0700 Subject: [PATCH 353/632] build/pkgs/pytz: Update to 2022.5 --- build/pkgs/pytz/checksums.ini | 6 +++--- build/pkgs/pytz/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/pytz/checksums.ini b/build/pkgs/pytz/checksums.ini index 0d031b316d0..9c3074def07 100644 --- a/build/pkgs/pytz/checksums.ini +++ b/build/pkgs/pytz/checksums.ini @@ -1,5 +1,5 @@ tarball=pytz-VERSION.tar.gz -sha1=74975d8618b7c71bfc38958979ab96da4d9a3aba -md5=b1d2ed6592bbdf6002ef52b4ab8e2efe -cksum=1769835625 +sha1=b356ab5a8b326e9857bbce3e7a1799fc56844827 +md5=91747f483e2906cddda91b0df0b01254 +cksum=635792532 upstream_url=https://pypi.io/packages/source/p/pytz/pytz-VERSION.tar.gz diff --git a/build/pkgs/pytz/package-version.txt b/build/pkgs/pytz/package-version.txt index a2a5548bc1c..ef52d12ba35 100644 --- a/build/pkgs/pytz/package-version.txt +++ b/build/pkgs/pytz/package-version.txt @@ -1 +1 @@ -2022.4 +2022.5 From 2385e729c6d5cc8fed860ccda2ef7aa6119849d7 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 Oct 2022 14:44:31 -0700 Subject: [PATCH 354/632] build/pkgs/tinycss2: Update to 1.2.1 --- build/pkgs/tinycss2/checksums.ini | 6 +++--- build/pkgs/tinycss2/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/tinycss2/checksums.ini b/build/pkgs/tinycss2/checksums.ini index cf10f3f8d3f..c905e293396 100644 --- a/build/pkgs/tinycss2/checksums.ini +++ b/build/pkgs/tinycss2/checksums.ini @@ -1,5 +1,5 @@ tarball=tinycss2-VERSION.tar.gz -sha1=250a0e4d72241428b4172c0431b7a5d759ccb285 -md5=13b8548422f600032722c25a74706daa -cksum=2174916905 +sha1=3871ffec30bde346d1a17f80a423dce488bad4f7 +md5=e8a06102e7f42ca791463f11ce7b814d +cksum=1840765267 upstream_url=https://pypi.io/packages/source/t/tinycss2/tinycss2-VERSION.tar.gz diff --git a/build/pkgs/tinycss2/package-version.txt b/build/pkgs/tinycss2/package-version.txt index 26aaba0e866..6085e946503 100644 --- a/build/pkgs/tinycss2/package-version.txt +++ b/build/pkgs/tinycss2/package-version.txt @@ -1 +1 @@ -1.2.0 +1.2.1 From 881ed1cb9738b706b0bc50560d5e8f65b4b9a28d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 Oct 2022 14:44:45 -0700 Subject: [PATCH 355/632] build/pkgs/tomlkit: Update to 0.11.6 --- build/pkgs/tomlkit/checksums.ini | 6 +++--- build/pkgs/tomlkit/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/tomlkit/checksums.ini b/build/pkgs/tomlkit/checksums.ini index c5c309c6b4b..545bb2bc02b 100644 --- a/build/pkgs/tomlkit/checksums.ini +++ b/build/pkgs/tomlkit/checksums.ini @@ -1,5 +1,5 @@ tarball=tomlkit-VERSION.tar.gz -sha1=3d03c83e2dc36d2b1cef1bb82902f89ce639e2fe -md5=d5702dd3ecf513935d24d673761f5296 -cksum=3072168699 +sha1=b097f71385b3b693a41a23ecd551959faae73e0d +md5=ac33a015aa5f3f8e8e0667081b388bb7 +cksum=785586052 upstream_url=https://pypi.io/packages/source/t/tomlkit/tomlkit-VERSION.tar.gz diff --git a/build/pkgs/tomlkit/package-version.txt b/build/pkgs/tomlkit/package-version.txt index 62d5dbdf3c7..e5cbde33e62 100644 --- a/build/pkgs/tomlkit/package-version.txt +++ b/build/pkgs/tomlkit/package-version.txt @@ -1 +1 @@ -0.11.5 +0.11.6 From a0e053d39f57bb4f069f6185997f0d5268e9989b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 Oct 2022 14:44:57 -0700 Subject: [PATCH 356/632] build/pkgs/tox: Update to 3.27.0 --- build/pkgs/tox/checksums.ini | 6 +++--- build/pkgs/tox/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/tox/checksums.ini b/build/pkgs/tox/checksums.ini index af645e47c4b..19a159a8e4a 100644 --- a/build/pkgs/tox/checksums.ini +++ b/build/pkgs/tox/checksums.ini @@ -1,5 +1,5 @@ tarball=tox-VERSION.tar.gz -sha1=70341b4bd57be86410f352c7b30a07cefef087c3 -md5=7e261bc4050a698adf06dfb553869b58 -cksum=3295576412 +sha1=4a17b94eea345a2fb1a76106fb4d01ac9aca3569 +md5=ed4a11d13cd6a206b516c84750109602 +cksum=3144404727 upstream_url=https://pypi.io/packages/source/t/tox/tox-VERSION.tar.gz diff --git a/build/pkgs/tox/package-version.txt b/build/pkgs/tox/package-version.txt index 419ede3b9cb..8c53120442c 100644 --- a/build/pkgs/tox/package-version.txt +++ b/build/pkgs/tox/package-version.txt @@ -1 +1 @@ -3.26.0 +3.27.0 From 88556b190d8b301506b74e78154e1e8520adbed9 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 Oct 2022 14:45:14 -0700 Subject: [PATCH 357/632] build/pkgs/tzdata: Update to 2022.6 --- build/pkgs/tzdata/checksums.ini | 6 +++--- build/pkgs/tzdata/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/tzdata/checksums.ini b/build/pkgs/tzdata/checksums.ini index bc975d6471a..86bf2db8de4 100644 --- a/build/pkgs/tzdata/checksums.ini +++ b/build/pkgs/tzdata/checksums.ini @@ -1,5 +1,5 @@ tarball=tzdata-VERSION.tar.gz -sha1=3acb57630d74572e9db51f91c5bdd38ef64ac100 -md5=23da690574817c8233185edeada3924c -cksum=467850857 +sha1=e244bf1bde63515d3f8a452d3bbe9f97738e1e70 +md5=2ad4652fecc6ef4f6794726d3f367363 +cksum=2894139369 upstream_url=https://pypi.io/packages/source/t/tzdata/tzdata-VERSION.tar.gz diff --git a/build/pkgs/tzdata/package-version.txt b/build/pkgs/tzdata/package-version.txt index ef52d12ba35..abd454cf321 100644 --- a/build/pkgs/tzdata/package-version.txt +++ b/build/pkgs/tzdata/package-version.txt @@ -1 +1 @@ -2022.5 +2022.6 From 495c138b2ac8f2c46dafe1bce8375999d38b2a8c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 Oct 2022 14:45:35 -0700 Subject: [PATCH 358/632] build/pkgs/virtualenv: Update to 20.16.6 --- build/pkgs/virtualenv/checksums.ini | 6 +++--- build/pkgs/virtualenv/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/virtualenv/checksums.ini b/build/pkgs/virtualenv/checksums.ini index 2d9e0f511fe..e324ed781ee 100644 --- a/build/pkgs/virtualenv/checksums.ini +++ b/build/pkgs/virtualenv/checksums.ini @@ -1,5 +1,5 @@ tarball=virtualenv-VERSION.tar.gz -sha1=24745f6095d59917577890172fd52feb8851f547 -md5=44c7d81666301c6c9d0b03a6751d9604 -cksum=1850303439 +sha1=8371dccb9866b40c3fdc5c0aa9c8f034cc0b174b +md5=b2d60f3c431f370b5fed5169b94f4798 +cksum=3124829245 upstream_url=https://pypi.io/packages/source/v/virtualenv/virtualenv-VERSION.tar.gz diff --git a/build/pkgs/virtualenv/package-version.txt b/build/pkgs/virtualenv/package-version.txt index 140bca73f83..d1df974a56b 100644 --- a/build/pkgs/virtualenv/package-version.txt +++ b/build/pkgs/virtualenv/package-version.txt @@ -1 +1 @@ -20.16.5 +20.16.6 From 99b938824441aee06465e709387e591244fda46a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 Oct 2022 14:45:44 -0700 Subject: [PATCH 359/632] build/pkgs/zipp: Update to 3.10.0 --- build/pkgs/zipp/checksums.ini | 6 +++--- build/pkgs/zipp/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/zipp/checksums.ini b/build/pkgs/zipp/checksums.ini index 8a729185177..5876b93b924 100644 --- a/build/pkgs/zipp/checksums.ini +++ b/build/pkgs/zipp/checksums.ini @@ -1,5 +1,5 @@ tarball=zipp-VERSION.tar.gz -sha1=8ce743d9a8850db38b52c318087dbe4a2a9868d3 -md5=46815096f7e8cf91de9a0d6c84727608 -cksum=616631595 +sha1=017268ef95e7da55ca11e695b63cda797d7a64be +md5=f75b65d022528a44877626641f0f95c3 +cksum=1239244009 upstream_url=https://pypi.io/packages/source/z/zipp/zipp-VERSION.tar.gz diff --git a/build/pkgs/zipp/package-version.txt b/build/pkgs/zipp/package-version.txt index a5c4c763394..30291cba223 100644 --- a/build/pkgs/zipp/package-version.txt +++ b/build/pkgs/zipp/package-version.txt @@ -1 +1 @@ -3.9.0 +3.10.0 From 2affa9db5b8ab66c1ff52c96182b3842a08b4e5a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 Oct 2022 14:46:20 -0700 Subject: [PATCH 360/632] build/pkgs/stack_data: Update to 0.6.0 --- build/pkgs/stack_data/checksums.ini | 6 +++--- build/pkgs/stack_data/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/stack_data/checksums.ini b/build/pkgs/stack_data/checksums.ini index 7058412912e..13d92e56bd8 100644 --- a/build/pkgs/stack_data/checksums.ini +++ b/build/pkgs/stack_data/checksums.ini @@ -1,5 +1,5 @@ tarball=stack_data-VERSION.tar.gz -sha1=280dc05517f29dd0d450679304a9bb6a0fa41a25 -md5=d39afb043bdb116b8d568c2a82f32227 -cksum=3446203117 +sha1=5e69d397ae31e6dcd995765d4ab51cb6b897fb11 +md5=eedee8944e6e08ddb3195f968553e861 +cksum=64651866 upstream_url=https://pypi.io/packages/source/s/stack_data/stack_data-VERSION.tar.gz diff --git a/build/pkgs/stack_data/package-version.txt b/build/pkgs/stack_data/package-version.txt index 0d91a54c7d4..a918a2aa18d 100644 --- a/build/pkgs/stack_data/package-version.txt +++ b/build/pkgs/stack_data/package-version.txt @@ -1 +1 @@ -0.3.0 +0.6.0 From 14344db952b4f0a66f46b25dbc9eca3f91b35587 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 Oct 2022 14:51:45 -0700 Subject: [PATCH 361/632] build/pkgs/jsonschema/dependencies: Add hatch_vcs --- build/pkgs/jsonschema/dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/jsonschema/dependencies b/build/pkgs/jsonschema/dependencies index e488e4e4422..51698156cf0 100644 --- a/build/pkgs/jsonschema/dependencies +++ b/build/pkgs/jsonschema/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) vcversioner attrs importlib_metadata pyrsistent | $(PYTHON_TOOLCHAIN) hatchling hatch_fancy_pypi_readme +$(PYTHON) vcversioner attrs importlib_metadata pyrsistent | $(PYTHON_TOOLCHAIN) hatchling hatch_vcs hatch_fancy_pypi_readme ---------- All lines of this file are ignored except the first. From 9604ef08f7403a1c9cfbd4d9659741dc23baa078 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 Oct 2022 15:01:11 -0700 Subject: [PATCH 362/632] build/pkgs/executing: Update to 1.2.0 --- build/pkgs/executing/checksums.ini | 6 +++--- build/pkgs/executing/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/executing/checksums.ini b/build/pkgs/executing/checksums.ini index bc94cbcea54..71e107aafbe 100644 --- a/build/pkgs/executing/checksums.ini +++ b/build/pkgs/executing/checksums.ini @@ -1,5 +1,5 @@ tarball=executing-VERSION.tar.gz -sha1=e962c1719fe8e259b8a7988421e5995ca0a4b8d3 -md5=5b4e105769218bc64eb0df434cb1cedf -cksum=3777168231 +sha1=ac9b0cbedd1166bce7a3b9f8542f8d1fafdd8c73 +md5=e6fa9a6abf00555ccc8a6b3524729238 +cksum=1761713270 upstream_url=https://pypi.io/packages/source/e/executing/executing-VERSION.tar.gz diff --git a/build/pkgs/executing/package-version.txt b/build/pkgs/executing/package-version.txt index 524cb55242b..26aaba0e866 100644 --- a/build/pkgs/executing/package-version.txt +++ b/build/pkgs/executing/package-version.txt @@ -1 +1 @@ -1.1.1 +1.2.0 From 6a281622f4fcaaeb8c76231c5d9cb0a949ed4e0a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 30 Oct 2022 15:01:43 -0700 Subject: [PATCH 363/632] build/pkgs: Add missing dependencies --- build/pkgs/hatch_fancy_pypi_readme/dependencies | 2 +- build/pkgs/hatch_vcs/dependencies | 2 +- build/pkgs/ipykernel/dependencies | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/pkgs/hatch_fancy_pypi_readme/dependencies b/build/pkgs/hatch_fancy_pypi_readme/dependencies index 0738c2d7777..8cd44d06682 100644 --- a/build/pkgs/hatch_fancy_pypi_readme/dependencies +++ b/build/pkgs/hatch_fancy_pypi_readme/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | $(PYTHON_TOOLCHAIN) +$(PYTHON) | $(PYTHON_TOOLCHAIN) hatchling ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/hatch_vcs/dependencies b/build/pkgs/hatch_vcs/dependencies index 0738c2d7777..8cd44d06682 100644 --- a/build/pkgs/hatch_vcs/dependencies +++ b/build/pkgs/hatch_vcs/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) | $(PYTHON_TOOLCHAIN) +$(PYTHON) | $(PYTHON_TOOLCHAIN) hatchling ---------- All lines of this file are ignored except the first. diff --git a/build/pkgs/ipykernel/dependencies b/build/pkgs/ipykernel/dependencies index d2677a8d27b..792c3e70634 100644 --- a/build/pkgs/ipykernel/dependencies +++ b/build/pkgs/ipykernel/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) ipython_genutils importlib_metadata matplotlib_inline ipython jupyter_client tornado appnope traitlets | $(PYTHON_TOOLCHAIN) +$(PYTHON) ipython_genutils importlib_metadata matplotlib_inline ipython jupyter_client tornado appnope traitlets executing | $(PYTHON_TOOLCHAIN) ---------- All lines of this file are ignored except the first. From 0d6e8035b30a012848579546257f2ad6d9521ac5 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 31 Oct 2022 08:46:35 +0900 Subject: [PATCH 364/632] Fixing failing test due to change. --- src/sage/algebras/fusion_rings/fusion_ring.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/sage/algebras/fusion_rings/fusion_ring.py b/src/sage/algebras/fusion_rings/fusion_ring.py index a9bc2eed23a..80532a5988c 100644 --- a/src/sage/algebras/fusion_rings/fusion_ring.py +++ b/src/sage/algebras/fusion_rings/fusion_ring.py @@ -587,8 +587,18 @@ def root_of_unity(self, r, base_coercion=True): sage: A11 = FusionRing("A1", 1) sage: A11.field() Cyclotomic Field of order 24 and degree 8 - sage: [A11.root_of_unity(2/x) for x in [1..7]] - [1, -1, zeta24^4 - 1, zeta24^6, None, zeta24^4, None] + sage: for n in [1..7]: + ....: try: + ....: print(n, A11.root_of_unity(2/n)) + ....: except ValueError as err: + ....: print(n, err) + 1 1 + 2 -1 + 3 zeta24^4 - 1 + 4 zeta24^6 + 5 not an integer root of unity + 6 zeta24^4 + 7 not an integer root of unity """ n = 2 * r * self._cyclotomic_order if n not in ZZ: From 0be29f7a065db5eabe78d5ff11ff9391279fa013 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 31 Oct 2022 08:52:01 +0900 Subject: [PATCH 365/632] Changing to a more accurate error message. --- src/sage/algebras/fusion_rings/fusion_ring.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/algebras/fusion_rings/fusion_ring.py b/src/sage/algebras/fusion_rings/fusion_ring.py index 80532a5988c..87b356662b7 100644 --- a/src/sage/algebras/fusion_rings/fusion_ring.py +++ b/src/sage/algebras/fusion_rings/fusion_ring.py @@ -596,13 +596,13 @@ def root_of_unity(self, r, base_coercion=True): 2 -1 3 zeta24^4 - 1 4 zeta24^6 - 5 not an integer root of unity + 5 not a root of unity in the field 6 zeta24^4 - 7 not an integer root of unity + 7 not a root of unity in the field """ n = 2 * r * self._cyclotomic_order if n not in ZZ: - raise ValueError("not an integer root of unity") + raise ValueError("not a root of unity in the field") ret = self.field().gen() ** n if (not base_coercion) or (self._basecoer is None): return ret From f3c6b0db8e5e621c68d94a93441d944d0c5dad0d Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Sun, 30 Oct 2022 23:11:11 -0400 Subject: [PATCH 366/632] small changes --- src/sage/modular/modform/element.py | 14 ++++++++++++-- src/sage/modular/modform/ring.py | 7 +------ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/sage/modular/modform/element.py b/src/sage/modular/modform/element.py index 855a46ad1ef..9936fbdd88e 100644 --- a/src/sage/modular/modform/element.py +++ b/src/sage/modular/modform/element.py @@ -3234,6 +3234,14 @@ def __init__(self, parent, forms_datum): Traceback (most recent call last): ... ValueError: the group and/or the base ring of at least one modular form (q - 6*q^2 + 9*q^3 + 4*q^4 + 6*q^5 + O(q^6)) is not consistant with the base space + sage: M = ModularFormsRing(Gamma0(2)) + sage: E4 = ModularForms(1, 4).0 + sage: M(E4)[4].parent() + Modular Forms space of dimension 2 for Congruence Subgroup Gamma0(2) of weight 4 over Rational Field + sage: M = ModularFormsRing(Gamma1(3), base_ring=GF(7)) + sage: E6 = ModularForms(1, 6, base_ring=GF(7)).0 + sage: M(E6)[6].parent() + Modular Forms space of dimension 3 for Congruence Subgroup Gamma1(3) of weight 6 over Finite Field of size 7 """ forms_dictionary = {} if isinstance(forms_datum, dict): @@ -3245,7 +3253,8 @@ def __init__(self, parent, forms_datum): elif is_ModularFormElement(f): if f.weight() == k: if parent.group().is_subgroup(f.group()) and parent.base_ring().has_coerce_map_from(f.base_ring()): - forms_dictionary[k] = f + M = parent.modular_forms_of_weight(f.weight()).change_ring(parent.base_ring()) + forms_dictionary[k] = M(f) else: raise ValueError('the group and/or the base ring of at least one modular form (%s) is not consistant with the base space' % (f)) else: @@ -3261,7 +3270,8 @@ def __init__(self, parent, forms_datum): if (chi is not None) and (not chi.is_trivial()): raise NotImplementedError("graded modular forms for non-trivial characters is not yet implemented") if parent.group().is_subgroup(f.group()) and parent.base_ring().has_coerce_map_from(f.base_ring()): - forms_dictionary[f.weight()] = forms_dictionary.get(f.weight(), 0) + f + M = parent.modular_forms_of_weight(f.weight()).change_ring(parent.base_ring()) + forms_dictionary[f.weight()] = M(forms_dictionary.get(f.weight(), 0) + f) else: raise ValueError('the group and/or the base ring of at least one modular form (%s) is not consistant with the base space' % (f)) else: diff --git a/src/sage/modular/modform/ring.py b/src/sage/modular/modform/ring.py index 83b70d0d11f..e122c73814e 100644 --- a/src/sage/modular/modform/ring.py +++ b/src/sage/modular/modform/ring.py @@ -511,10 +511,6 @@ def _element_constructor_(self, forms_datum): Traceback (most recent call last): ... NotImplementedError: conversion from q-expansion not yet implemented - sage: M = ModularFormsRing(Gamma0(2)) - sage: E4 = ModularForms(1, 4).0 - sage: M(E4)[4].parent() - Modular Forms space of dimension 2 for Congruence Subgroup Gamma0(2) of weight 4 over Rational Field """ if isinstance(forms_datum, (dict, list)): forms_dictionary = forms_datum @@ -522,8 +518,7 @@ def _element_constructor_(self, forms_datum): forms_dictionary = forms_datum._forms_dictionary elif is_ModularFormElement(forms_datum): if self.group().is_subgroup(forms_datum.group()) and self.base_ring().has_coerce_map_from(forms_datum.base_ring()): - M = ModularForms(self.group(), forms_datum.weight(), self.base_ring()) - forms_dictionary = {forms_datum.weight() : M(forms_datum)} + forms_dictionary = {forms_datum.weight() : forms_datum} else: raise ValueError('the group (%s) and/or the base ring (%s) of the given modular form is not consistant with the base space: %s'%(forms_datum.group(), forms_datum.base_ring(), self)) elif forms_datum in self.base_ring(): From 57825e92cd660d98ef4162a23cd20012b7a8884b Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 31 Oct 2022 15:13:24 +0900 Subject: [PATCH 367/632] Updating the documentation. --- src/sage/algebras/fusion_rings/fusion_ring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/algebras/fusion_rings/fusion_ring.py b/src/sage/algebras/fusion_rings/fusion_ring.py index 87b356662b7..d96afeaaee3 100644 --- a/src/sage/algebras/fusion_rings/fusion_ring.py +++ b/src/sage/algebras/fusion_rings/fusion_ring.py @@ -49,7 +49,7 @@ class FusionRing(WeylCharacterRing): The cyclotomic order is an integer `N` such that all computations will return elements of the cyclotomic field of `N`-th roots of unity. Normally you will never need to change this but consider changing it - if :meth:`root_of_unity` ever returns ``None``. + if :meth:`root_of_unity` raises a ``ValueError``. This algebra has a basis (sometimes called *primary fields* but here called *simple objects*) indexed by the weights of level `\leq k`. From 4d56288de56b4b90b5200c5a10f5536e87ce2c99 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 31 Oct 2022 17:13:05 +0900 Subject: [PATCH 368/632] Checking that the RHS is a scalar of 1, not the LHS. --- src/sage/algebras/clifford_algebra_element.pyx | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/sage/algebras/clifford_algebra_element.pyx b/src/sage/algebras/clifford_algebra_element.pyx index e065b6b70b9..00295967ae1 100644 --- a/src/sage/algebras/clifford_algebra_element.pyx +++ b/src/sage/algebras/clifford_algebra_element.pyx @@ -90,6 +90,15 @@ cdef class CliffordAlgebraElement(IndexedFreeModuleElement): 0 sage: 0*x 0 + + :trac:`34707`:: + + sage: Q = QuadraticForm(QQ, 2, [0,5,0]) + sage: C. = CliffordAlgebra(Q) + sage: (q * p) * q + 5*q + sage: q * (p * q) + 5*q """ Q = self._parent._quadratic_form zero = self._parent._base.zero() @@ -110,7 +119,7 @@ cdef class CliffordAlgebraElement(IndexedFreeModuleElement): if ml.isempty(): return rhs._mul_term_self(ml, cl) if len(rhs._monomial_coefficients) == 1: - mr, cr = next(iter(self._monomial_coefficients.items())) + mr, cr = next(iter(rhs._monomial_coefficients.items())) if mr.isempty(): return self._mul_self_term(mr, cr) @@ -125,7 +134,7 @@ cdef class CliffordAlgebraElement(IndexedFreeModuleElement): # the dictionary describing the element # ``e[i]`` * (the element described by the dictionary ``cur``) # (where ``e[i]`` is the ``i``-th standard basis vector). - for mr,cr in cur.items(): + for mr, cr in cur.items(): # Commute the factor as necessary until we are in order for j in mr: @@ -161,7 +170,7 @@ cdef class CliffordAlgebraElement(IndexedFreeModuleElement): cur = next_level # Add the distributed terms to the total - for index,coeff in cur.items(): + for index, coeff in cur.items(): d[index] = d.get(index, zero) + cl * coeff if d[index] == zero: del d[index] From eac534bb41b1a3987ba9d25239106092066a77b0 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Mon, 31 Oct 2022 09:31:20 -0400 Subject: [PATCH 369/632] src/sage/modular/quasimodform/ring.py: delete unused import --- src/sage/modular/quasimodform/ring.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/modular/quasimodform/ring.py b/src/sage/modular/quasimodform/ring.py index 80417c12ced..be1a14b2d96 100644 --- a/src/sage/modular/quasimodform/ring.py +++ b/src/sage/modular/quasimodform/ring.py @@ -176,7 +176,6 @@ from sage.modular.modform.ring import ModularFormsRing from sage.rings.integer import Integer -from sage.rings.integer_ring import ZZ from sage.rings.polynomial.multi_polynomial import MPolynomial from sage.rings.polynomial.polynomial_element import Polynomial from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing From 9c35f8def921539249331f6969b45dfc9365c2b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 31 Oct 2022 15:05:31 +0100 Subject: [PATCH 370/632] add test for absolute disc of number fields --- src/sage/categories/number_fields.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/sage/categories/number_fields.py b/src/sage/categories/number_fields.py index 69fe01147f4..b54a38dbaa2 100644 --- a/src/sage/categories/number_fields.py +++ b/src/sage/categories/number_fields.py @@ -209,5 +209,28 @@ def zeta_function(self, prec=53, raise ValueError('algorithm must be "gp" or "pari"') + def _test_absolute_disc(self, **options): + r""" + Run basic tests for the method :meth:`absolute_discriminant` of ``self``. + + See the documentation for :class:`TestSuite` for information on + further options. + + INPUT: + + - ``options`` -- any keyword arguments accepted by :meth:`_tester` + + EXAMPLES: + + By default, this method tests only the elements returned by + ``self.some_elements()``:: + + sage: S = NumberField(x**3-x-1, 'a') + sage: S._test_absolute_disc() + """ + from sage.rings.integer import Integer + tester = self._tester(**options) + tester.assertIsInstance(self.absolute_discriminant(), Integer) + class ElementMethods: pass From 1f2bd704f13f617447d3a2b5dfe6ae849971543b Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Mon, 31 Oct 2022 15:35:53 +0000 Subject: [PATCH 371/632] Do not check for normalized matrix in hadmard_matrix --- src/sage/combinat/matrices/hadamard_matrix.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 0344c4ade32..f4911807d9e 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -462,7 +462,7 @@ def hadamard_matrix(n,existence=False, check=True): raise ValueError("The Hadamard matrix of order %s is not yet implemented." % n) if check: - assert is_hadamard_matrix(M, normalized=True) + assert is_hadamard_matrix(M) return M From 810fc34a9a6f7097a996e12a1258bbdf6dc68161 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 31 Oct 2022 20:42:40 +0100 Subject: [PATCH 372/632] partial pep8 cleanup for number_field.py --- src/sage/rings/number_field/number_field.py | 491 ++++++++++---------- 1 file changed, 239 insertions(+), 252 deletions(-) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index a2f0e3d047c..ccafb7e20be 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -565,9 +565,9 @@ def NumberField(polynomial, name=None, check=True, names=None, embedding=None, latex_name = latex_names for key, val in kwds.items(): if key not in ['implementation', 'prec']: - raise TypeError("NumberField() got an unexpected keyword argument '%s'"%key) + raise TypeError("NumberField() got an unexpected keyword argument '%s'" % key) if not (val is None or isinstance(val, list) and all(c is None for c in val)): - raise NotImplementedError("Number field with prescribed %s is not implemented"%key) + raise NotImplementedError("Number field with prescribed %s is not implemented" % key) if isinstance(polynomial, (list,tuple)): return NumberFieldTower(polynomial, names=name, check=check, embeddings=embedding, latex_names=latex_name, assume_disc_small=assume_disc_small, maximize_at_primes=maximize_at_primes, structures=structure) @@ -843,7 +843,7 @@ def NumberFieldTower(polynomials, names, check=True, embeddings=None, latex_name except IndexError: names = normalize_names(1, names) if len(polynomials) > 1: - names = ['%s%s'%(names[0], i) for i in range(len(polynomials))] + names = ['%s%s' % (names[0], i) for i in range(len(polynomials))] if embeddings is None: embeddings = [None] * len(polynomials) @@ -869,7 +869,7 @@ def NumberFieldTower(polynomials, names, check=True, embeddings=None, latex_name var = f.variable_name() if is_Polynomial(f) else 'x' R = w[var] # polynomial ring - return w.extension(R(f), name, check=check, embedding=embeddings[0], structure=structures[0], latex_name=latex_names[0]) # currently, extension does not accept assume_disc_small, or maximize_at_primes + return w.extension(R(f), name, check=check, embedding=embeddings[0], structure=structures[0], latex_name=latex_names[0]) # currently, extension does not accept assume_disc_small, or maximize_at_primes def QuadraticField(D, name='a', check=True, embedding=True, latex_name='sqrt', **args): @@ -1042,7 +1042,7 @@ def is_AbsoluteNumberField(x): return isinstance(x, NumberField_absolute) -def is_QuadraticField(x): +def is_QuadraticField(x) -> bool: r""" Return True if x is of the quadratic *number* field type. @@ -1236,7 +1236,7 @@ def create_object(self, version, key, **extra_args): CyclotomicField = CyclotomicFieldFactory("sage.rings.number_field.number_field.CyclotomicField") -def is_CyclotomicField(x): +def is_CyclotomicField(x) -> bool: """ Return True if x is a cyclotomic field, i.e., of the special cyclotomic field class. This function does not return True for a @@ -1448,17 +1448,17 @@ def __init__(self, polynomial, name, latex_name, if category is None: category = default_category else: - assert category.is_subcategory(default_category), "%s is not a subcategory of %s"%(category, default_category) + assert category.is_subcategory(default_category), "%s is not a subcategory of %s" % (category, default_category) ParentWithGens.__init__(self, QQ, name, category=category) if not isinstance(polynomial, polynomial_element.Polynomial): - raise TypeError("polynomial (=%s) must be a polynomial"%repr(polynomial)) + raise TypeError("polynomial (=%s) must be a polynomial" % repr(polynomial)) if check: if not polynomial.parent().base_ring() == QQ: raise TypeError("polynomial must be defined over rational field") if not polynomial.is_irreducible(): - raise ValueError("defining polynomial (%s) must be irreducible"%polynomial) + raise ValueError("defining polynomial (%s) must be irreducible" % polynomial) self._assign_names(name) self._latex_names = (latex_name,) @@ -1584,12 +1584,12 @@ def _magma_init_(self, magma): """ # Get magma version of defining polynomial of this number field f = self._magma_polynomial_(magma) - s = 'NumberField(%s)'%f.name() + s = 'NumberField(%s)' % f.name() return magma._with_names(s, self.variable_names()) def construction(self): r""" - Construction of self + Construction of self. EXAMPLES:: @@ -1813,7 +1813,7 @@ def _element_constructor_(self, x, check=True): % (x, self.pari_polynomial())) beta = self._pari_absolute_structure()[2] x = x(beta).lift() - else: # constant polynomial + else: # constant polynomial x = x[0] else: raise TypeError("%s has unsupported PARI type %s" % (x, x.type())) @@ -1824,10 +1824,10 @@ def _element_constructor_(self, x, check=True): if self.variable_name() in s: return self._convert_from_str(s) return self._convert_from_str(s.replace('!', '')) - elif isinstance(x,str): + elif isinstance(x, str): return self._convert_from_str(x) elif (isinstance(x, (tuple, list)) or - isinstance(x, sage.modules.free_module_element.FreeModuleElement)): + isinstance(x, sage.modules.free_module_element.FreeModuleElement)): if len(x) != self.relative_degree(): raise ValueError("Length must be equal to the degree of this number field") base = self.base_ring() @@ -1901,9 +1901,9 @@ def _convert_non_number_field_element(self, x): return self._element_class(self, x) if isinstance(x, sage.rings.polynomial.polynomial_quotient_ring_element.PolynomialQuotientRingElement)\ - and (x in self.polynomial_quotient_ring()): + and (x in self.polynomial_quotient_ring()): y = self.polynomial_ring().gen() - return x.lift().subs({y:self.gen()}) + return x.lift().subs({y: self.gen()}) if isinstance(x, (sage.rings.qqbar.AlgebraicNumber, sage.rings.qqbar.AlgebraicReal)): return self._convert_from_qqbar(x) @@ -2371,7 +2371,7 @@ def change_generator(self, alpha, name=None, names=None): alpha = self(alpha) K, from_K = self.subfield(alpha, name=name) if K.degree() != self.degree(): - raise ValueError("alpha must generate a field of degree %s, but alpha generates a subfield of degree %s"%(self.degree(), K.degree())) + raise ValueError("alpha must generate a field of degree %s, but alpha generates a subfield of degree %s" % (self.degree(), K.degree())) # Now compute to_K, which is an isomorphism # from self to K such that from_K(to_K(x)) == x for all x, # and to_K(from_K(y)) == y. @@ -2613,7 +2613,7 @@ def quadratic_defect(self, a, p, check=True): raise TypeError(str(a) + " must be an element of " + str(self)) if not self == QQ and not p.parent() == self.ideal_monoid(): raise TypeError(str(p) + " is not a prime ideal in " - + str(self.ideal_monoid())) + + str(self.ideal_monoid())) if check and not p.is_prime(): raise ValueError(str(p) + " must be prime") if a.is_zero(): @@ -2646,7 +2646,7 @@ def quadratic_defect(self, a, p, check=True): s = self(q((a - 1) / pi**w)**(1/2)) a = a / (1 + s*(pi**(w/2)))**2 w = (a - 1).valuation(p) - if w < u and w % 2 ==1: + if w < u and w % 2: return v + w if w == u and (f + F((a-1) / 4)).is_irreducible(): return v + w @@ -2676,7 +2676,7 @@ def absolute_field(self, names): """ return NumberField(self.defining_polynomial(), names, check=False, structure=structure.NameChange(self)) - def is_isomorphic(self, other, isomorphism_maps = False): + def is_isomorphic(self, other, isomorphism_maps=False) -> bool: """ Return True if self is isomorphic as a number field to other. @@ -2850,16 +2850,14 @@ def is_CM(self): True sage: E.is_CM_extension() False - """ - - #Return cached answer if available + # Return cached answer if available try: return self.__is_CM except(AttributeError): pass - #Then, deal with simple cases + # Then, deal with simple cases if is_odd(self.absolute_degree()): self.__is_CM = False return False @@ -2885,8 +2883,8 @@ def is_CM(self): return True K = self.absolute_field('z') - #Check for index 2 subextensions that are totally real - possibilities = K.subfields(K.absolute_degree()/2) + # Check for index 2 subextensions that are totally real + possibilities = K.subfields(K.absolute_degree() / 2) for F, phi, _ in possibilities: if F.is_totally_real(): self.__is_CM = True @@ -2933,16 +2931,14 @@ def complex_conjugation(self): -a sage: cc(b) -b - """ - - #Return cached answer if available + # Return cached answer if available try: return self.__complex_conjugation except(AttributeError): pass - #Then, deal with simple cases + # Then, deal with simple cases if isinstance( self, sage.rings.number_field.number_field.NumberField_quadratic): disc = self.discriminant() @@ -2967,7 +2963,7 @@ def complex_conjugation(self): if not self.is_CM(): raise ValueError('Complex conjugation is only well-defined for fields contained in CM fields.') - #In the remaining case, self.is_CM() should have cached __max_tot_real_sub + # In the remaining case, self.is_CM() should have cached __max_tot_real_sub try: F, phi = self.__max_tot_real_sub except(AttributeError): @@ -2975,8 +2971,8 @@ def complex_conjugation(self): if self.is_absolute(): K_rel = self.relativize(phi, self.variable_name() * 2) to_abs, from_abs = K_rel.structure() - self.__complex_conjugation = K_rel.automorphisms()[1].pre_compose( \ - from_abs).post_compose(to_abs) + self.__complex_conjugation = K_rel.automorphisms()[1].pre_compose( + from_abs).post_compose(to_abs) self.__complex_conjugation = self.hom([self.__complex_conjugation(self.gen())], check=False) return self.__complex_conjugation else: @@ -3410,17 +3406,17 @@ def dirichlet_group(self): Dirichlet character modulo 11 of conductor 11 mapping 2 |--> zeta5^3 sage: X[4]^2 in X True - """ - #todo : turn this into an abelian group rather than a list. + # todo : turn this into an abelian group rather than a list. from sage.modular.dirichlet import DirichletGroup m = self.conductor() d = self.degree() - A = _splitting_classes_gens_(self,m,d) - # d could be improved to be the exponent of the Galois group rather than the degree, but I do not see how to go about that yet. + A = _splitting_classes_gens_(self, m, d) + # d could be improved to be the exponent of the Galois group + # rather than the degree, but I do not see how to go about that yet. G = DirichletGroup(m, CyclotomicField(d)) - H = [G(1)] + H = [G.one()] for chi in G: if len(H) == d: break @@ -3491,8 +3487,8 @@ def _latex_(self): \Bold{Q}[\theta_{25}]/(\theta_{25}^{25} + \theta_{25} + 1) """ latex_name = self.latex_variable_names()[0] - return "%s[%s]/(%s)"%(latex(QQ), latex_name, - self.polynomial()._latex_(latex_name)) + return "%s[%s]/(%s)" % (latex(QQ), latex_name, + self.polynomial()._latex_(latex_name)) def _ideal_class_(self, n=0): """ @@ -3549,7 +3545,6 @@ def ideal(self, *gens, **kwds): sage: K. = NumberField(x^6 - x^5 - 5*x^4 + 4*x^3 + 6*x^2 - 3*x - 1) sage: K.ideal(1,1) Fractional ideal (1) - """ try: return self.fractional_ideal(*gens, **kwds) @@ -3824,11 +3819,12 @@ def primes_above(self, x, degree=None): """ if degree is not None: degree = ZZ(degree) - facs = sorted([ (id.residue_class_degree(), id.absolute_norm(), id) for id in self.prime_factors(x) ]) + facs = sorted((id.residue_class_degree(), id.absolute_norm(), id) + for id in self.prime_factors(x)) if degree is None: - return [ id for d, n, id in facs ] + return [id for d, n, id in facs] else: - return [ id for d, n, id in facs if d == degree ] + return [id for d, n, id in facs if d == degree] def prime_above(self, x, degree=None): r""" @@ -3972,18 +3968,19 @@ def primes_of_bounded_norm(self, B): B = ZZ(B.ceil()) except (TypeError, AttributeError): raise TypeError("%s is not valid bound on prime ideals" % B) - if B<2: + if B < 2: return [] from sage.rings.fast_arith import prime_range if self is QQ: - #return arith.primes(B+1) - return prime_range(B+1, algorithm="pari_isprime") + # return arith.primes(B+1) + return prime_range(B + 1, algorithm="pari_isprime") else: - #P = [pp for p in arith.primes(B+1) for pp in self.primes_above(p)] - P = [pp for p in prime_range(B+1, algorithm="pari_isprime") for pp in self.primes_above(p)] + # P = [pp for p in arith.primes(B+1) for pp in self.primes_above(p)] + P = (pp for p in prime_range(B + 1, algorithm="pari_isprime") + for pp in self.primes_above(p)) P = [p for p in P if p.norm() <= B] - P.sort(key=lambda P: (P.norm(),P)) + P.sort(key=lambda P: (P.norm(), P)) return P def primes_of_bounded_norm_iter(self, B): @@ -4030,12 +4027,12 @@ def primes_of_bounded_norm_iter(self, B): from sage.rings.fast_arith import prime_range if self is QQ: - #for p in arith.primes(B+1): - for p in prime_range(B+1,algorithm="pari_isprime"): + # for p in arith.primes(B+1): + for p in prime_range(B + 1, algorithm="pari_isprime"): yield p else: - #for p in arith.primes(B+1): - for p in prime_range(B+1,algorithm="pari_isprime"): + # for p in arith.primes(B+1): + for p in prime_range(B + 1, algorithm="pari_isprime"): for pp in self.primes_above(p): if pp.norm() <= B: yield pp @@ -4099,7 +4096,6 @@ def primes_of_degree_one_list(self, n, num_integer_primes=10000, max_iterations= INPUT: - - ``num_integer_primes (default: 10000)`` - an integer. We try to find primes of absolute norm no greater than the num_integer_primes-th prime number. For example, if @@ -4108,8 +4104,7 @@ def primes_of_degree_one_list(self, n, num_integer_primes=10000, max_iterations= - ``max_iterations (default: 100)`` - an integer. We test max_iterations integers to find small primes before raising - StopIteration. - + ``StopIteration``. EXAMPLES:: @@ -4123,9 +4118,9 @@ def primes_of_degree_one_list(self, n, num_integer_primes=10000, max_iterations= [1, 1, 1] """ it = self.primes_of_degree_one_iter() - return [ next(it) for i in range(n) ] + return [next(it) for i in range(n)] - def completely_split_primes(self, B = 200): + def completely_split_primes(self, B=200): r""" Return a list of rational primes which split completely in the number field `K`. @@ -4150,7 +4145,7 @@ def completely_split_primes(self, B = 200): split_primes = [] for p in prime_range(B): Fp = GF(p) - FpT = PolynomialRing(Fp,'T') + FpT = PolynomialRing(Fp, 'T') g = FpT(self.defining_polynomial()) if len(factor(g)) == self.degree(): split_primes.append(p) @@ -4530,16 +4525,15 @@ def _gap_init_(self): [ tau ] sage: gap(tau)^3 !2 - """ if not self.is_absolute(): raise NotImplementedError("Currently, only simple algebraic extensions are implemented in gap") G = sage.interfaces.gap.gap q = self.polynomial() - if q.variable_name()!='E': - return 'CallFuncList(function() local %s,E; %s:=Indeterminate(%s,"%s"); E:=AlgebraicExtension(%s,%s,"%s"); return E; end,[])'%(q.variable_name(),q.variable_name(),G(self.base_ring()).name(),q.variable_name(),G(self.base_ring()).name(),repr(self.polynomial()),str(self.gen())) - else: - return 'CallFuncList(function() local %s,F; %s:=Indeterminate(%s,"%s"); F:=AlgebraicExtension(%s,%s,"%s"); return F; end,[])'%(q.variable_name(),q.variable_name(),G(self.base_ring()).name(),q.variable_name(),G(self.base_ring()).name(),repr(self.polynomial()),str(self.gen())) + if q.variable_name() != 'E': + return 'CallFuncList(function() local %s,E; %s:=Indeterminate(%s,"%s"); E:=AlgebraicExtension(%s,%s,"%s"); return E; end,[])' % (q.variable_name(), q.variable_name(), G(self.base_ring()).name(), q.variable_name(), G(self.base_ring()).name(), repr(self.polynomial()), str(self.gen())) + + return 'CallFuncList(function() local %s,F; %s:=Indeterminate(%s,"%s"); F:=AlgebraicExtension(%s,%s,"%s"); return F; end,[])' % (q.variable_name(), q.variable_name(), G(self.base_ring()).name(), q.variable_name(), G(self.base_ring()).name(), repr(self.polynomial()), str(self.gen())) def characteristic(self): """ @@ -4624,10 +4618,10 @@ def class_group(self, proof=None, names='c'): except AttributeError: self.__class_group = {} k = self.pari_bnf(proof) - cycle_structure = tuple( ZZ(c) for c in k.bnf_get_cyc() ) + cycle_structure = tuple(ZZ(c) for c in k.bnf_get_cyc()) # Gens is a list of ideals (the generators) - gens = tuple( self.ideal(hnf) for hnf in k.bnf_get_gen() ) + gens = tuple(self.ideal(hnf) for hnf in k.bnf_get_gen()) G = ClassGroup(cycle_structure, names, self, gens, proof=proof) self.__class_group[proof, names] = G @@ -5030,7 +5024,7 @@ def selmer_generators(self, S, m, proof=True, orders=False): ords.append(m) else: m1 = order.gcd(m) - if m1!= 1: + if m1 != 1: gens.append(unit) ords.append(m1) card_S = len(S) @@ -5584,8 +5578,8 @@ def composite_fields(self, other, names=None, both_maps=False, preserve_embeddin i -= 1 else: self_to_F = self.hom([a_in_F]) - other_to_F = RelativeNumberFieldHomomorphism_from_abs(other.Hom(F), other_abs_to_F*to_other_abs) - rets.append( (F, self_to_F, other_to_F, k) ) + other_to_F = RelativeNumberFieldHomomorphism_from_abs(other.Hom(F), other_abs_to_F * to_other_abs) + rets.append((F, self_to_F, other_to_F, k)) else: rets.append(F) return rets @@ -5818,7 +5812,7 @@ def extension(self, poly, name=None, names=None, latex_name=None, latex_names=No try: poly = poly.polynomial(self) except (AttributeError, TypeError): - raise TypeError("polynomial (=%s) must be a polynomial."%repr(poly)) + raise TypeError("polynomial (=%s) must be a polynomial" % repr(poly)) if poly.base_ring() is not self: poly = poly.change_ring(self) if names is not None: @@ -6139,7 +6133,6 @@ def is_abelian(self): sage: NumberField(x^6 + x^5 - 5*x^4 - 4*x^3 + 6*x^2 + 3*x - 1, 'a').is_abelian() True """ - if not self.is_galois(): return False @@ -6152,7 +6145,7 @@ def is_abelian(self): return self.galois_group().is_abelian() pari_pol = pari(self.polynomial()) - return pari_pol.galoisinit().galoisisabelian(1)==1 + return pari_pol.galoisinit().galoisisabelian(1) == 1 @cached_method def galois_group(self, type=None, algorithm='pari', names=None, gc_numbering=None): @@ -6324,7 +6317,7 @@ def power_basis(self): [1, zeta15, zeta15^2, zeta15^3, zeta15^4, zeta15^5, zeta15^6, zeta15^7] """ g = self.gen() - return [ g**i for i in range(self.relative_degree()) ] + return [g**i for i in range(self.relative_degree())] def integral_basis(self, v=None): """ @@ -6333,11 +6326,9 @@ def integral_basis(self, v=None): INPUT: - - ``v`` - None, a prime, or a list of primes. See the documentation for self.maximal_order. - EXAMPLES:: sage: K. = NumberField(x^5 + 10*x + 1) @@ -6421,7 +6412,7 @@ def _pari_integral_basis(self, v=None, important=True): trialdivlimit2 = pari(10**12) trialdivlimit3 = pari(10**18) if all(p < trialdivlimit2 or (e == 1 and p < trialdivlimit3) or p.isprime() for p, e in zip(m[0], m[1])): - B = f.nfbasis(fa = m) + B = f.nfbasis(fa=m) else: raise RuntimeError("Unable to factor discriminant with trial division") else: @@ -6578,25 +6569,25 @@ def reduced_gram_matrix(self, prec=None): if self.is_totally_real(): B = self.reduced_basis() self.__reduced_gram_matrix = matrix(ZZ, d, d, - [[(x*y).trace() for x in B] + [[(x * y).trace() for x in B] for y in B]) else: M = self.minkowski_embedding(prec=prec) - T = matrix(d, flatten([ a.vector().list() - for a in self.reduced_basis(prec=prec) ])) - A = M*(T.transpose()) - self.__reduced_gram_matrix = A.transpose()*A + T = matrix(d, flatten([a.vector().list() + for a in self.reduced_basis(prec=prec)])) + A = M * T.transpose() + self.__reduced_gram_matrix = A.transpose() * A if prec is None: - ## this is the default choice for minkowski_embedding + # this is the default choice for minkowski_embedding self.__reduced_gram_matrix_prec = 53 else: self.__reduced_gram_matrix_prec = prec return self.__reduced_gram_matrix - #****************************************************** - # Supplementary algorithm to enumerate lattice points - #****************************************************** + # ****************************************************** + # Supplementary algorithm to enumerate lattice points + # ****************************************************** def _positive_integral_elements_with_trace(self, C): r""" @@ -6632,14 +6623,14 @@ def _positive_integral_elements_with_trace(self, C): B = self.reduced_basis() T = self.reduced_gram_matrix() - P = pari(T).qfminim((C[1]**2)*(1./2), 10**6)[2] + P = pari(T).qfminim((C[1]**2) * 0.5, 10**6)[2] S = [] for p in P: - theta = sum([ p.list()[i]*B[i] for i in range(self.degree())]) + theta = sum([p.list()[i] * B[i] for i in range(self.degree())]) if theta.trace() < 0: theta *= -1 - if theta.trace() >= C[0] and theta.trace() <= C[1]: + if C[0] <= theta.trace() <= C[1]: if self(theta).is_totally_positive(): S.append(self(theta)) return S @@ -6886,7 +6877,7 @@ def residue_field(self, prime, names=None, check=True): """ from sage.rings.number_field.number_field_ideal import is_NumberFieldIdeal if is_NumberFieldIdeal(prime) and prime.number_field() is not self: - raise ValueError("%s is not an ideal of %s"%(prime,self)) + raise ValueError("%s is not an ideal of %s" % (prime, self)) # This allows principal ideals to be specified using a generator: try: prime = self.ideal(prime) @@ -6894,9 +6885,9 @@ def residue_field(self, prime, names=None, check=True): pass if not is_NumberFieldIdeal(prime) or prime.number_field() is not self: - raise ValueError("%s is not an ideal of %s"%(prime,self)) + raise ValueError("%s is not an ideal of %s" % (prime, self)) if check and not prime.is_prime(): - raise ValueError("%s is not a prime ideal"%prime) + raise ValueError("%s is not a prime ideal" % prime) from sage.rings.finite_rings.residue_field import ResidueField return ResidueField(prime, names=names, check=False) @@ -7277,14 +7268,14 @@ def S_unit_group(self, proof=None, S=None): try: S = tuple(self.ideal(S).prime_factors()) except (NameError, TypeError, ValueError): - raise ValueError("Cannot make a set of primes from %s"%(S,)) + raise ValueError(f"Cannot make a set of primes from {S}") else: try: S = tuple(self.ideal(P) for P in S) except (NameError, TypeError, ValueError): - raise ValueError("Cannot make a set of primes from %s"%(S,)) + raise ValueError(f"Cannot make a set of primes from {S}") if not all(P.is_prime() for P in S): - raise ValueError("Not all elements of %s are prime ideals"%(S,)) + raise ValueError(f"Not all elements of {S} are prime ideals") try: return self._S_unit_group_cache[S] @@ -7592,7 +7583,7 @@ def roots_of_unity(self): """ z = self.primitive_root_of_unity() n = self.zeta_order() - return [ z**k for k in range(1, n+1) ] + return [z**k for k in range(1, n + 1)] def zeta_coefficients(self, n): """ @@ -7648,11 +7639,11 @@ def solve_CRT(self, reslist, Ilist, check=True): reslist = [self(x) for x in reslist] except ValueError: raise ValueError("solve_CRT requires a list of arguments in the field") - if n==0: + if n == 0: return self.zero() - if n==1: + if n == 1: return reslist[0] - if n==2: + if n == 2: try: r = Ilist[0].element_1_mod(Ilist[1]) except TypeError: @@ -7980,7 +7971,7 @@ def __init__(self, polynomial, name, latex_name=None, check=True, embedding=None assume_disc_small=assume_disc_small, maximize_at_primes=maximize_at_primes, structure=structure) self._element_class = number_field_element.NumberFieldElement_absolute self._zero_element = self._element_class(self, 0) - self._one_element = self._element_class(self, 1) + self._one_element = self._element_class(self, 1) self._init_embedding_approx() @@ -8153,22 +8144,22 @@ def _coerce_from_other_number_field(self, x): F = LF Kgen = F(Kgen) else: - raise TypeError("No compatible natural embeddings found for %s and %s"%(KF,LF)) + raise TypeError("No compatible natural embeddings found for %s and %s" % (KF, LF)) # List of candidates for K(x) f = x.minpoly() ys = f.roots(ring=K, multiplicities=False) if not ys: - raise ValueError("Cannot convert %s to %s (regardless of embeddings)"%(x,K)) + raise ValueError("Cannot convert %s to %s (regardless of embeddings)" % (x, K)) # Define a function are_roots_equal to determine whether two # roots of f are equal. A simple a == b does not suffice for # inexact fields because of floating-point errors. if F.is_exact(): - are_roots_equal = lambda a,b: a == b + are_roots_equal = lambda a, b: a == b else: - ### Compute a lower bound on the distance between the roots of f. - ### This essentially gives the precision to work with. + # Compute a lower bound on the distance between the roots of f. + # This essentially gives the precision to work with. # A function # log2abs: F --> RR @@ -8180,7 +8171,7 @@ def _coerce_from_other_number_field(self, x): # Compute half Fujiwara's bound on the roots of f n = f.degree() log_half_root_bound = log2abs(f[0]/2)/n - for i in range(1,n): + for i in range(1, n): bd = log2abs(f[i])/(n-i) if bd > log_half_root_bound: log_half_root_bound = bd @@ -8216,7 +8207,7 @@ def _coerce_from_other_number_field(self, x): emb_y = y.polynomial()(Kgen) if are_roots_equal(emb_x, emb_y): return y - raise ValueError("Cannot convert %s to %s (using the specified embeddings)"%(x,K)) + raise ValueError("Cannot convert %s to %s (using the specified embeddings)" % (x, K)) def _coerce_map_from_(self, R): """ @@ -8315,7 +8306,7 @@ def _coerce_map_from_(self, R): if self.coerce_embedding() is not None: try: return number_field_morphisms.EmbeddedNumberFieldMorphism(R, self) - except ValueError: # no common embedding found + except ValueError: # no common embedding found return None else: # R is embedded, self isn't. So, we could only have @@ -8744,12 +8735,12 @@ def _subfields_helper(self, degree=0, name=None, both_maps=True, optimize=False) embedding = self.coerce_embedding()(a) # trac 7695 add a _ to prevent zeta70 etc. if name[-1].isdigit(): - new_name= name+ '_' + str(i) + new_name = name + '_' + str(i) else: new_name = name + str(i) K = NumberField(f, names=new_name, embedding=embedding) - from_K = K.hom([a]) # check=False here ?? would be safe unless there are bugs. + from_K = K.hom([a]) # check=False here ?? would be safe unless there are bugs. if both_maps and K.degree() == self.degree(): g = K['x'](self.polynomial()) @@ -9007,7 +8998,7 @@ def _galois_closure_and_embedding(self, names=None): L = self.__galois_closure.change_names(names) L_to_orig, orig_to_L = L.structure() # "flatten" the composition by hand - self_into_L = self.hom([ (orig_to_L * self.__galois_closure_embedding)(self.gen()) ]) + self_into_L = self.hom([(orig_to_L * self.__galois_closure_embedding)(self.gen())]) return (L, self_into_L) except AttributeError: pass @@ -9081,17 +9072,14 @@ def galois_closure(self, names=None, map=False): Defn: a |--> 1/240*cc^5 - 41/120*cc """ L, self_into_L = self._galois_closure_and_embedding(names) - if map: - return (L, self_into_L) - else: - return L + return (L, self_into_L) if map else L def automorphisms(self): r""" Compute all Galois automorphisms of self. - This uses PARI's :pari:`nfgaloisconj` and is much faster than root finding - for many fields. + This uses PARI's :pari:`nfgaloisconj` and is much faster than + root finding for many fields. EXAMPLES:: @@ -9144,12 +9132,12 @@ def embeddings(self, K): not even be a number field, e.g., it could be the complex numbers). This will return an identical result when given K as input again. - If possible, the most natural embedding of this field into K is put first - in the list. + If possible, the most natural embedding of this field into K + is put first in the list. INPUT: - - ``K`` - a field + - ``K`` -- a field EXAMPLES:: @@ -9225,7 +9213,7 @@ def embeddings(self, K): # If there is an embedding that preserves variable names # then it is most natural, so we put it first. put_natural_embedding_first(v) - return Sequence(v, cr=v!=[], immutable=True, + return Sequence(v, cr=bool(v), immutable=True, check=False, universe=self.Hom(K)) def minkowski_embedding(self, B=None, prec=None): @@ -9287,14 +9275,14 @@ def minkowski_embedding(self, B=None, prec=None): R = sage.rings.real_double.RDF else: R = sage.rings.real_mpfr.RealField(prec) - r,s = self.signature() + r, s = self.signature() places = self.places(prec=prec) if B is None: - B = [ (self.gen(0))**i for i in range(n) ] + B = [(self.gen(0))**i for i in range(n)] A = ZZ['x'] - f = A.gen(0)**2-2 + f = A.gen(0)**2 - 2 sqrt2 = f.roots(R)[1][0] d = {} @@ -9302,7 +9290,7 @@ def minkowski_embedding(self, B=None, prec=None): for col in range(n): for row in range(r): - d[(row,col)] = places[row](B[col]) + d[(row, col)] = places[row](B[col]) for i in range(s): z = places[r+i](B[col]) @@ -9468,27 +9456,28 @@ def places(self, all_complex=False, prec=None): R = sage.rings.real_mpfr.RealField(prec) C = sage.rings.complex_mpfr.ComplexField(prec) - ## first, find the intervals with roots, and see how much - ## precision we need to approximate the roots - ## - all_intervals = [ x[0] for x in self.defining_polynomial().roots(C) ] + # first, find the intervals with roots, and see how much + # precision we need to approximate the roots + # + all_intervals = [x[0] for x in self.defining_polynomial().roots(C)] - ## first, set up the real places + # first, set up the real places if all_complex: - real_intervals = [ x for x in all_intervals if x.imag().is_zero() ] + real_intervals = [x for x in all_intervals if x.imag().is_zero()] else: - real_intervals = [ x[0] for x in self.defining_polynomial().roots(R) ] + real_intervals = [x[0] for x in self.defining_polynomial().roots(R)] if prec is None: - real_places = [ self.hom([i.center()], check=False) for i in real_intervals ] + real_places = [self.hom([i.center()], check=False) + for i in real_intervals] - complex_places = [ self.hom([i.center()], check=False) for i in - all_intervals if i.imag() > 0 ] + complex_places = [self.hom([i.center()], check=False) + for i in all_intervals if i.imag() > 0] else: - real_places = [ self.hom([i], check=False) for i in real_intervals ] + real_places = [self.hom([i], check=False) for i in real_intervals] - complex_places = [ self.hom([i], check=False) for i in - all_intervals if i.imag() > 0 ] + complex_places = [self.hom([i], check=False) + for i in all_intervals if i.imag() > 0] return real_places + complex_places @@ -9768,12 +9757,12 @@ def relativize(self, alpha, names, structure=None): if alpha.codomain() != self: raise ValueError("Co-domain of morphism must be self") L = alpha.domain() - alpha = alpha(L.gen()) # relativize over phi's domain + alpha = alpha(L.gen()) # relativize over phi's domain if L is QQ: from sage.rings.polynomial.polynomial_ring import polygen f = polygen(QQ) else: - f = L.defining_polynomial() # = alpha.minpoly() + f = L.defining_polynomial() # = alpha.minpoly() names = normalize_names(len(names), names) else: # alpha must be an element coercible to self @@ -9785,13 +9774,14 @@ def relativize(self, alpha, names, structure=None): # now we do some linear algebra to find the minpoly of self.gen() over L L_into_self = L.hom([alpha]) - extdeg = self.absolute_degree() // L.absolute_degree() # [ L : self ] + extdeg = self.absolute_degree() // L.absolute_degree() # [ L : self ] a = self.gen() # we will find a linear relation between small powers of a over L - basis = [ a**i * b for i in range(extdeg) for b in map(L_into_self, L.power_basis()) ] - basis.append(a**extdeg) # this one makes the basis no longer a basis - mat = matrix([ b.vector() for b in basis ]) + basis = [a**i * b for i in range(extdeg) + for b in map(L_into_self, L.power_basis())] + basis.append(a**extdeg) # this one makes the basis no longer a basis + mat = matrix([b.vector() for b in basis]) soln_space = mat.left_kernel(mat.row_space()(0)) # the solution space is one dimensional and the last entry is non-zero # because a satisfies no smaller linear relation @@ -9801,8 +9791,8 @@ def relativize(self, alpha, names, structure=None): reln = reln * ~reln[-1] # now we need to get those coeffs in L - coeff_mat = matrix(extdeg, f.degree(), list(reln)[:-1]) # easy way to divide into the correct lengths - coeffs_in_L = [ r*vector(L.power_basis()) for r in coeff_mat.rows() ] + coeff_mat = matrix(extdeg, f.degree(), list(reln)[:-1]) # easy way to divide into the correct lengths + coeffs_in_L = [r * vector(L.power_basis()) for r in coeff_mat.rows()] # f is the minimal polynomial of a over L f = L['x'](coeffs_in_L + [1]) # sanity check... @@ -9923,7 +9913,7 @@ def relative_different(self): """ return self.different() - def hilbert_symbol(self, a, b, P = None): + def hilbert_symbol(self, a, b, P=None): r""" Return the Hilbert symbol `(a,b)_P` for a prime P of self and non-zero elements a and b of self. @@ -10134,7 +10124,7 @@ def hilbert_symbol(self, a, b, P = None): codom = P.codomain() from sage.rings.qqbar import AA, QQbar if isinstance(codom, (sage.rings.abc.ComplexField, sage.rings.abc.ComplexDoubleField, sage.rings.abc.ComplexIntervalField)) or \ - codom is QQbar: + codom is QQbar: if P(self.gen()).imag() == 0: raise ValueError("Possibly real place (=%s) given as complex embedding in hilbert_symbol. Is it real or complex?" % P) return 1 @@ -10219,7 +10209,7 @@ def hilbert_symbol_negative_at_S(self, S, b, check=True): # input checks if not type(S) is list: - raise TypeError( "first argument must be a list") + raise TypeError("first argument must be a list") if b not in self: raise TypeError("second argument must be an element of this field") b = self(b) @@ -10233,15 +10223,15 @@ def hilbert_symbol_negative_at_S(self, S, b, check=True): if not p.is_prime(): raise ValueError("not a prime ideal") if self.quadratic_defect(b, p) == infinity.Infinity: - raise ValueError("%s is a square in the completion "%b + - "with respect to %s"%p) + raise ValueError(f"{b} is a square in the completion " + f"with respect to {p}") else: if p not in self.real_places(): raise ValueError("entries of the list must be " "prime ideals or real places") if p(b) > 0: - raise ValueError("%s is a square in the completion " - "with respect to %s" % (b, p)) + raise ValueError(f"{b} is a square in the completion " + f"with respect to {p}") # L is the list of primes that we need to consider, b must have # nonzero valuation for each prime in L, this is the set S' @@ -10353,7 +10343,6 @@ def hilbert_conductor(self,a,b): AUTHOR: - Aly Deines - """ a, b = self(a), self(b) d = self.ideal(1) @@ -10625,22 +10614,22 @@ def __init__(self, n, names, embedding=None, assume_disc_small=False, maximize_a """ f = QQ['x'].cyclotomic_polynomial(n) if names[0].startswith('zeta'): - latex_name = "\\zeta_{%s}"%n + latex_name = "\\zeta_{%s}" % n else: latex_name = latex_variable_name(names[0]) self.__n = n = Integer(n) NumberField_absolute.__init__(self, f, - name= names, + name=names, latex_name=latex_name, check=False, - embedding = embedding, + embedding=embedding, assume_disc_small=assume_disc_small, maximize_at_primes=maximize_at_primes) if n % 2: self.__zeta_order = 2 * n else: self.__zeta_order = n - ## quadratic number fields require this: + # quadratic number fields require this: if f.degree() == 2: # define a boolean flag as for NumberField_quadratic to know, which # square root we choose (True means no embedding or positive @@ -10656,12 +10645,12 @@ def __init__(self, n, names, embedding=None, assume_disc_small=False, maximize_a if n == 4: self._element_class = number_field_element_quadratic.NumberFieldElement_gaussian self._D = ZZ(-1) - self._NumberField_generic__gen = self._element_class(self, (QQ(0), QQ(1))) + self._NumberField_generic__gen = self._element_class(self, (QQ(0), QQ.one())) else: - ## n is 3 or 6 + # n is 3 or 6 self._element_class = number_field_element_quadratic.NumberFieldElement_quadratic self._D = ZZ(-3) - one_half = ZZ(1)/ZZ(2) + one_half = QQ((1, 2)) if n == 3: self._NumberField_generic__gen = self._element_class(self, (one_half-1, one_half)) else: @@ -10671,8 +10660,8 @@ def __init__(self, n, names, embedding=None, assume_disc_small=False, maximize_a # _one_element to NumberFieldElement_absolute values, which is # wrong (and dangerous; such elements can actually be used to # crash Sage: see #5316). Overwrite them with correct values. - self._zero_element = self._element_class(self, (QQ(0),QQ(0))) - self._one_element = self._element_class(self, (QQ(1),QQ(0))) + self._zero_element = self._element_class(self, (QQ(0), QQ(0))) + self._one_element = self._element_class(self, (QQ.one(), QQ(0))) zeta = self.gen() zeta._set_multiplicative_order(n) @@ -10753,7 +10742,7 @@ def _magma_init_(self, magma): sage: K._magma_init_(magma) # optional - magma 'SageCreateWithNames(CyclotomicField(7),["zeta"])' """ - s = 'CyclotomicField(%s)'%self.__n + s = 'CyclotomicField(%s)' % self.__n return magma._with_names(s, self.variable_names()) def _gap_init_(self): @@ -10786,7 +10775,7 @@ def _gap_init_(self): zeta3 -zeta3 - 1 """ - return 'CyclotomicField(%s)'%self.__n + return 'CyclotomicField(%s)' % self.__n def _libgap_(self): """ @@ -10819,8 +10808,8 @@ def _repr_(self): sage: CyclotomicField(400)._repr_() 'Cyclotomic Field of order 400 and degree 160' """ - return "Cyclotomic Field of order %s and degree %s"%( - self.__n, self.degree()) + n = self._n + return f"Cyclotomic Field of order {n} and degree {self.degree()}" def _n(self): """ @@ -10871,9 +10860,8 @@ def _latex_(self): """ v = self.latex_variable_names()[0] if v.startswith('\\zeta_'): - return "%s(%s)"%(latex(QQ), v) - else: - return NumberField_generic._latex_(self) + return "%s(%s)" % (latex(QQ), v) + return NumberField_generic._latex_(self) def _coerce_map_from_(self, K): r""" @@ -11171,86 +11159,86 @@ def _element_constructor_(self, x, check=True): # breaks the doctests in eisenstein_submodule.py. # FIX THIS. -## def _will_be_better_coerce_from_other_cyclotomic_field(self, x, only_canonical=False): -## """ -## Coerce an element x of a cyclotomic field into self, if at all possible. +# def _will_be_better_coerce_from_other_cyclotomic_field(self, x, only_canonical=False): +# """ +# Coerce an element x of a cyclotomic field into self, if at all possible. -## INPUT: -## x -- number field element +# INPUT: +# x -- number field element -## only_canonical -- bool (default: False); Attempt to work, -## even in some cases when x is not in a subfield of -## the cyclotomics (as long as x is a root of unity). +# only_canonical -- bool (default: False); Attempt to work, +# even in some cases when x is not in a subfield of +# the cyclotomics (as long as x is a root of unity). -## EXAMPLES:: +# EXAMPLES:: -## sage: k5 = CyclotomicField(5) -## sage: k3 = CyclotomicField(3) -## sage: k15 = CyclotomicField(15) -## sage: k15._coerce_from_other_cyclotomic_field(k3.gen()) -## zeta15^5 -## sage: k15._coerce_from_other_cyclotomic_field(k3.gen()^2 + 17/3) -## -zeta15^5 + 14/3 -## sage: k3._coerce_from_other_cyclotomic_field(k15.gen()^5) -## zeta3 -## sage: k3._coerce_from_other_cyclotomic_field(-2/3 * k15.gen()^5 + 2/3) -## -2/3*zeta3 + 2/3 -## """ +# sage: k5 = CyclotomicField(5) +# sage: k3 = CyclotomicField(3) +# sage: k15 = CyclotomicField(15) +# sage: k15._coerce_from_other_cyclotomic_field(k3.gen()) +# zeta15^5 +# sage: k15._coerce_from_other_cyclotomic_field(k3.gen()^2 + 17/3) +# -zeta15^5 + 14/3 +# sage: k3._coerce_from_other_cyclotomic_field(k15.gen()^5) +# zeta3 +# sage: k3._coerce_from_other_cyclotomic_field(-2/3 * k15.gen()^5 + 2/3) +# -2/3*zeta3 + 2/3 +# """ -## K = x.parent() +# K = x.parent() -## if K is self: -## return x -## n = K.zeta_order() -## m = self.zeta_order() +# if K is self: +# return x +# n = K.zeta_order() +# m = self.zeta_order() -## self_gen = self.gen() +# self_gen = self.gen() -## if m % n == 0: # easy case -## # pass this off to a method in the element class -## # it can be done very quickly and easily by the cython<->NTL -## # interface there -## return x._lift_cyclotomic_element(self) +# if m % n == 0: # easy case +# # pass this off to a method in the element class +# # it can be done very quickly and easily by the cython<->NTL +# # interface there +# return x._lift_cyclotomic_element(self) -## # Whatever happens below, it has to be consistent with -## # zeta_r |---> (zeta_s)^m +# # Whatever happens below, it has to be consistent with +# # zeta_r |---> (zeta_s)^m -## if m % 2 and not n%2: -## m *= 2 -## self_gen = -self_gen +# if m % 2 and not n%2: +# m *= 2 +# self_gen = -self_gen -## if only_canonical and m % n: -## raise TypeError, "no canonical coercion" +# if only_canonical and m % n: +# raise TypeError, "no canonical coercion" -## if not is_CyclotomicField(K): -## raise TypeError, "x must be in a cyclotomic field" +# if not is_CyclotomicField(K): +# raise TypeError, "x must be in a cyclotomic field" -## v = x.list() +# v = x.list() -## # Find the smallest power r >= 1 of the generator g of K that is in self, -## # i.e., find the smallest r such that g^r has order dividing m. +# # Find the smallest power r >= 1 of the generator g of K that is in self, +# # i.e., find the smallest r such that g^r has order dividing m. -## d = sage.arith.all.gcd(m,n) -## r = n // d +# d = sage.arith.all.gcd(m,n) +# r = n // d -## # Since we use the power basis for cyclotomic fields, if every -## # v[i] with i not divisible by r is 0, then we're good. +# # Since we use the power basis for cyclotomic fields, if every +# # v[i] with i not divisible by r is 0, then we're good. -## # If h generates self and has order m, then the element g^r -## # maps to the power of self of order gcd(m,n)., i.e., h^(m/gcd(m,n)) -## # -## z = self_gen**(m // d) -## w = self(1) +# # If h generates self and has order m, then the element g^r +# # maps to the power of self of order gcd(m,n)., i.e., h^(m/gcd(m,n)) +# # +# z = self_gen**(m // d) +# w = self(1) -## a = self(0) -## for i in range(len(v)): -## if i%r: -## if v[i]: -## raise TypeError, "element does not belong to cyclotomic field" -## else: -## a += w*v[i] -## w *= z -## return a +# a = self(0) +# for i in range(len(v)): +# if i%r: +# if v[i]: +# raise TypeError, "element does not belong to cyclotomic field" +# else: +# a += w*v[i] +# w *= z +# return a def _coerce_from_other_cyclotomic_field(self, x, only_canonical=False): """ @@ -11588,9 +11576,9 @@ def signature(self): """ m = ZZ(self.degree()) if m == 1: - return (ZZ(1), ZZ(0)) + return (ZZ.one(), ZZ(0)) else: - return (ZZ(0), ZZ(m/2)) + return (ZZ(0), m // 2) def different(self): """ @@ -11654,17 +11642,17 @@ def discriminant(self, v=None): except AttributeError: n = self._n() deg = self.degree() - d = ZZ(1) # so that CyclotomicField(1).disc() has the right type + d = ZZ.one() # so that CyclotomicField(1).disc() has the right type factors = n.factor() for (p, r) in factors: - e = (r*p - r - 1) * deg // (p-1) + e = (r * p - r - 1) * deg // (p - 1) d *= p**e sign = 1 if len(factors) == 1 and (n == 4 or factors[0][0].mod(4) == 3): sign = -1 elif len(factors) == 2 and factors[0] == (2, 1) and factors[1][0].mod(4) == 3: sign = -1 - self.__disc = sign*d + self.__disc = sign * d return self.__disc else: return NumberField_generic.discriminant(self, v) @@ -11842,18 +11830,18 @@ def zeta(self, n=None, all=False): if n % 2 == 0 and m % 2 == 1: # In the n-th cyclotomic field, n odd, there are # actually 2*n-th roots of unity, so we include them. - z = -z**((m+1)//2) # -z - m = 2*m + z = -z**((m+1)//2) # -z + m = 2 * m if m % n != 0: raise ValueError("%s does not divide order of generator (%s)" % - (n, self.zeta_order())) - a = z**(m//n) + (n, self.zeta_order())) + a = z**(m // n) if not all: return a v = [a] - b = a*a - for i in range(2,n): + b = a * a + for i in range(2, n): if n.gcd(i).is_one(): v.append(b) b = b * a @@ -11870,7 +11858,7 @@ def number_of_roots_of_unity(self): 42 """ n = self._n() - if n%2: + if n % 2: n *= 2 return n @@ -11884,13 +11872,12 @@ def roots_of_unity(self): sage: K. = CyclotomicField(3) sage: zs = K.roots_of_unity(); zs [1, a, -a - 1, -1, -a, a + 1] - sage: [ z**K.number_of_roots_of_unity() for z in zs ] + sage: [z**K.number_of_roots_of_unity() for z in zs] [1, 1, 1, 1, 1, 1] """ - z = self.gen() n = self._n() - v = [z**k for k in range(n)] - if n%2: + v = self.gen().powers(n) + if n % 2: v += [-x for x in v] return v @@ -12008,7 +11995,7 @@ def _coerce_map_from_(self, K): if K is ZZ: return number_field_element_quadratic.Z_to_quadratic_field_element(self) if K is int: - return self._coerce_map_via([ZZ], int) # faster than direct + return self._coerce_map_via([ZZ], int) # faster than direct if K is QQ: return number_field_element_quadratic.Q_to_quadratic_field_element(self) return NumberField_absolute._coerce_map_from_(self, K) @@ -12029,7 +12016,7 @@ def _latex_(self): """ v = self.latex_variable_names()[0] if v.startswith('\\sqrt'): - return "%s(%s)"%(latex(QQ), v) + return "%s(%s)" % (latex(QQ), v) else: return NumberField_generic._latex_(self) @@ -12281,8 +12268,8 @@ def is_fundamental_discriminant(D): if d not in [0, 1]: return False return D != 1 and D != 0 and \ - (arith.is_squarefree(D) or \ - (d == 0 and (D//4)%4 in [2,3] and arith.is_squarefree(D//4))) + (arith.is_squarefree(D) or + (d == 0 and (D // 4) % 4 in [2, 3] and arith.is_squarefree(D // 4))) ################### From fd5f450cdf260f8d107357a0193ba5d9f0eb55e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 31 Oct 2022 20:45:41 +0100 Subject: [PATCH 373/632] fix the doc --- src/sage/categories/number_fields.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/sage/categories/number_fields.py b/src/sage/categories/number_fields.py index b54a38dbaa2..c29e4c57255 100644 --- a/src/sage/categories/number_fields.py +++ b/src/sage/categories/number_fields.py @@ -220,10 +220,7 @@ def _test_absolute_disc(self, **options): - ``options`` -- any keyword arguments accepted by :meth:`_tester` - EXAMPLES: - - By default, this method tests only the elements returned by - ``self.some_elements()``:: + EXAMPLES:: sage: S = NumberField(x**3-x-1, 'a') sage: S._test_absolute_disc() From a1b70f3da76e4e2324b6b1c7cf3765bbc4f7d737 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Mon, 31 Oct 2022 23:09:18 +0900 Subject: [PATCH 374/632] Keep objects entries in toc --- .../algebras/cubic_hecke_algebra.rst | 2 +- src/doc/en/reference/algebras/index.rst | 16 +++++----- .../en/reference/algebras/lie_algebras.rst | 2 +- .../en/reference/algebras/quantum_groups.rst | 2 +- src/doc/en/reference/arithgroup/index.rst | 2 +- .../en/reference/arithmetic_curves/index.rst | 16 +++++----- src/doc/en/reference/categories/index.rst | 16 +++++----- src/doc/en/reference/coding/index.rst | 8 ++--- src/doc/en/reference/coercion/index.rst | 2 +- src/doc/en/reference/constants/index.rst | 2 +- src/doc/en/reference/cpython/index.rst | 2 +- src/doc/en/reference/cryptography/index.rst | 2 +- src/doc/en/reference/curves/index.rst | 6 ++-- .../en/reference/data_structures/index.rst | 2 +- .../diophantine_approximation/index.rst | 2 +- src/doc/en/reference/doctest/index.rst | 2 +- .../reference/dynamics/cellular_automata.rst | 2 +- .../reference/dynamics/complex_dynamics.rst | 2 +- src/doc/en/reference/dynamics/index.rst | 4 +-- .../en/reference/euclidean_spaces/index.rst | 3 +- src/doc/en/reference/finance/index.rst | 2 +- src/doc/en/reference/finite_rings/index.rst | 14 ++++----- src/doc/en/reference/functions/index.rst | 2 +- src/doc/en/reference/game_theory/index.rst | 2 +- src/doc/en/reference/games/index.rst | 2 +- src/doc/en/reference/groups/index.rst | 14 ++++----- src/doc/en/reference/hecke/index.rst | 2 +- src/doc/en/reference/homology/index.rst | 2 +- src/doc/en/reference/interfaces/index.rst | 2 +- src/doc/en/reference/knots/index.rst | 2 +- src/doc/en/reference/lfunctions/index.rst | 2 +- src/doc/en/reference/libs/index.rst | 30 +++++++++---------- src/doc/en/reference/logic/index.rst | 2 +- src/doc/en/reference/manifolds/chart.rst | 2 +- .../en/reference/manifolds/continuous_map.rst | 2 +- .../reference/manifolds/degenerate_metric.rst | 2 +- src/doc/en/reference/manifolds/diff_form.rst | 2 +- .../en/reference/manifolds/diff_manifold.rst | 2 +- src/doc/en/reference/manifolds/diff_map.rst | 4 +-- .../reference/manifolds/diff_scalarfield.rst | 2 +- .../manifolds/diff_vector_bundle.rst | 2 +- .../reference/manifolds/euclidean_space.rst | 2 +- src/doc/en/reference/manifolds/manifold.rst | 2 +- src/doc/en/reference/manifolds/mixed_form.rst | 2 +- .../en/reference/manifolds/multivector.rst | 2 +- .../en/reference/manifolds/scalarfield.rst | 2 +- .../en/reference/manifolds/tangent_space.rst | 2 +- .../en/reference/manifolds/tensorfield.rst | 2 +- .../en/reference/manifolds/vector_bundle.rst | 2 +- .../en/reference/manifolds/vectorfield.rst | 2 +- src/doc/en/reference/matrices/index.rst | 2 +- src/doc/en/reference/modabvar/index.rst | 2 +- src/doc/en/reference/modfrm/index.rst | 4 +-- .../reference/modfrm_hecketriangle/index.rst | 2 +- src/doc/en/reference/modmisc/index.rst | 2 +- src/doc/en/reference/modsym/index.rst | 4 +-- src/doc/en/reference/modules/index.rst | 18 +++++------ src/doc/en/reference/monoids/index.rst | 2 +- .../noncommutative_polynomial_rings/index.rst | 4 +-- src/doc/en/reference/padics/index.rst | 2 +- src/doc/en/reference/parallel/index.rst | 2 +- src/doc/en/reference/plot3d/index.rst | 10 +++---- src/doc/en/reference/plotting/index.rst | 10 +++---- .../en/reference/polynomial_rings/index.rst | 6 ++-- .../polynomial_rings/invariant_theory.rst | 2 +- .../polynomial_rings_multivar.rst | 2 +- .../polynomial_rings_toy_implementations.rst | 2 +- .../polynomial_rings_univar.rst | 2 +- src/doc/en/reference/power_series/index.rst | 2 +- src/doc/en/reference/probability/index.rst | 2 +- .../en/reference/quadratic_forms/index.rst | 2 +- src/doc/en/reference/quasimodfrm/index.rst | 2 +- src/doc/en/reference/quat_algebras/index.rst | 2 +- src/doc/en/reference/quivers/index.rst | 2 +- src/doc/en/reference/repl/index.rst | 24 +++++++-------- src/doc/en/reference/resolutions/index.rst | 2 +- .../reference/riemannian_geometry/index.rst | 2 +- src/doc/en/reference/rings/index.rst | 18 +++++------ .../en/reference/rings_numerical/index.rst | 6 ++-- src/doc/en/reference/rings_standard/index.rst | 4 +-- src/doc/en/reference/semirings/index.rst | 2 +- src/doc/en/reference/stats/index.rst | 2 +- .../tensor_free_modules/alt_forms.rst | 2 +- .../reference/tensor_free_modules/index.rst | 2 +- .../tensor_free_modules/morphisms.rst | 2 +- .../reference/tensor_free_modules/tensors.rst | 2 +- src/doc/en/reference/topology/index.rst | 2 +- src/doc/en/reference/valuations/index.rst | 6 ++-- src/sage_docbuild/conf.py | 18 +++++++++++ 89 files changed, 205 insertions(+), 188 deletions(-) diff --git a/src/doc/en/reference/algebras/cubic_hecke_algebra.rst b/src/doc/en/reference/algebras/cubic_hecke_algebra.rst index 5ba74eb00f0..94ca9bb27cc 100644 --- a/src/doc/en/reference/algebras/cubic_hecke_algebra.rst +++ b/src/doc/en/reference/algebras/cubic_hecke_algebra.rst @@ -2,7 +2,7 @@ Cubic Hecke Algebras ==================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/algebras/hecke_algebras/cubic_hecke_algebra sage/algebras/hecke_algebras/cubic_hecke_base_ring diff --git a/src/doc/en/reference/algebras/index.rst b/src/doc/en/reference/algebras/index.rst index 237343faf32..157426d9800 100644 --- a/src/doc/en/reference/algebras/index.rst +++ b/src/doc/en/reference/algebras/index.rst @@ -12,7 +12,7 @@ Free associative algebras and quotients --------------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/algebras/free_algebra sage/algebras/free_algebra_element @@ -29,7 +29,7 @@ Finite dimensional algebras --------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra_element @@ -40,7 +40,7 @@ Named associative algebras -------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/algebras/affine_nil_temperley_lieb sage/algebras/askey_wilson @@ -73,7 +73,7 @@ Hecke algebras -------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/algebras/hecke_algebras/ariki_koike_algebra sage/algebras/iwahori_hecke_algebra @@ -85,7 +85,7 @@ Graded algebras --------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/algebras/finite_gca sage/algebras/commutative_dga @@ -94,7 +94,7 @@ Various associative algebras ---------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/algebras/associated_graded sage/algebras/cellular_basis @@ -106,7 +106,7 @@ Non-associative algebras ------------------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 lie_algebras lie_conformal_algebras @@ -115,6 +115,6 @@ Non-associative algebras sage/combinat/free_prelie_algebra sage/algebras/shuffle_algebra sage/algebras/free_zinbiel_algebra - + .. include:: ../footer.txt diff --git a/src/doc/en/reference/algebras/lie_algebras.rst b/src/doc/en/reference/algebras/lie_algebras.rst index bc132fe88bd..23152ac0449 100644 --- a/src/doc/en/reference/algebras/lie_algebras.rst +++ b/src/doc/en/reference/algebras/lie_algebras.rst @@ -2,7 +2,7 @@ Lie Algebras ============ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/algebras/lie_algebras/abelian sage/algebras/lie_algebras/affine_lie_algebra diff --git a/src/doc/en/reference/algebras/quantum_groups.rst b/src/doc/en/reference/algebras/quantum_groups.rst index d89afc4b906..83a8e53cae1 100644 --- a/src/doc/en/reference/algebras/quantum_groups.rst +++ b/src/doc/en/reference/algebras/quantum_groups.rst @@ -2,7 +2,7 @@ Quantum Groups ============== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/algebras/quantum_groups/ace_quantum_onsager sage/algebras/quantum_groups/fock_space diff --git a/src/doc/en/reference/arithgroup/index.rst b/src/doc/en/reference/arithgroup/index.rst index c16f34d88fd..bb7f650320d 100644 --- a/src/doc/en/reference/arithgroup/index.rst +++ b/src/doc/en/reference/arithgroup/index.rst @@ -5,7 +5,7 @@ This chapter describes the basic functionality for finite index subgroups of the modular group `\SL_2(\ZZ)`. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modular/arithgroup/arithgroup_generic sage/modular/arithgroup/arithgroup_perm diff --git a/src/doc/en/reference/arithmetic_curves/index.rst b/src/doc/en/reference/arithmetic_curves/index.rst index 73f6f602490..40073920d61 100644 --- a/src/doc/en/reference/arithmetic_curves/index.rst +++ b/src/doc/en/reference/arithmetic_curves/index.rst @@ -2,7 +2,7 @@ Elliptic curves ========================= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/schemes/elliptic_curves/constructor sage/schemes/elliptic_curves/jacobian @@ -15,7 +15,7 @@ Elliptic curves Maps between them .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/schemes/elliptic_curves/hom sage/schemes/elliptic_curves/weierstrass_morphism @@ -29,7 +29,7 @@ Elliptic curves over number fields ---------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/schemes/elliptic_curves/ell_rational_field sage/schemes/elliptic_curves/ec_database @@ -51,7 +51,7 @@ Elliptic curves over number fields The following relate to elliptic curves over local nonarchimedean fields. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/schemes/elliptic_curves/ell_local_data sage/schemes/elliptic_curves/kodaira_symbol @@ -60,7 +60,7 @@ The following relate to elliptic curves over local nonarchimedean fields. Analytic properties over `\CC`. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/schemes/elliptic_curves/ell_wp sage/schemes/elliptic_curves/period_lattice @@ -69,7 +69,7 @@ Analytic properties over `\CC`. Modularity and `L`-series over `\QQ`. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/schemes/elliptic_curves/modular_parametrization sage/schemes/elliptic_curves/ell_modular_symbols @@ -82,7 +82,7 @@ To be sorted ------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/schemes/elliptic_curves/descent_two_isogeny sage/schemes/elliptic_curves/ell_egros @@ -98,7 +98,7 @@ Hyperelliptic curves ==================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/schemes/hyperelliptic_curves/constructor sage/schemes/hyperelliptic_curves/hyperelliptic_generic diff --git a/src/doc/en/reference/categories/index.rst b/src/doc/en/reference/categories/index.rst index f45e9026068..a40cca76e0f 100644 --- a/src/doc/en/reference/categories/index.rst +++ b/src/doc/en/reference/categories/index.rst @@ -5,7 +5,7 @@ Introduction ------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/categories/all @@ -13,7 +13,7 @@ The Sage Category Framework --------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/categories/primer sage/categories/category @@ -25,7 +25,7 @@ Maps and Morphisms ------------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/categories/map sage/categories/homset @@ -36,7 +36,7 @@ Individual Categories --------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/categories/action sage/categories/additive_groups @@ -206,7 +206,7 @@ Technical Categories ~~~~~~~~~~~~~~~~~~~~ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/categories/facade_sets @@ -214,7 +214,7 @@ Functorial constructions ------------------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/categories/covariant_functorial_construction @@ -239,7 +239,7 @@ Examples of parents using categories ------------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/categories/examples/algebras_with_basis sage/categories/examples/commutative_additive_monoids @@ -276,7 +276,7 @@ Internals --------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/categories/category_types sage/categories/category_singleton diff --git a/src/doc/en/reference/coding/index.rst b/src/doc/en/reference/coding/index.rst index abba3458380..637d1a2e65f 100644 --- a/src/doc/en/reference/coding/index.rst +++ b/src/doc/en/reference/coding/index.rst @@ -95,7 +95,7 @@ is from a special code family, the derived codes inherit structural properties like decoding radius or minimum distance: .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/coding/subfield_subcode sage/coding/punctured_code @@ -127,7 +127,7 @@ Automorphism Groups of Linear Codes ----------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/coding/codecan/codecan sage/coding/codecan/autgroup_can_label @@ -136,7 +136,7 @@ Bounds for Parameters of Linear Codes ------------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/coding/code_bounds sage/coding/delsarte_bounds @@ -145,7 +145,7 @@ Databases for Coding Theory --------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/coding/databases sage/coding/two_weight_db diff --git a/src/doc/en/reference/coercion/index.rst b/src/doc/en/reference/coercion/index.rst index d8d7c2ade79..d6d117cd263 100644 --- a/src/doc/en/reference/coercion/index.rst +++ b/src/doc/en/reference/coercion/index.rst @@ -671,7 +671,7 @@ Modules ------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/structure/coerce sage/structure/coerce_actions diff --git a/src/doc/en/reference/constants/index.rst b/src/doc/en/reference/constants/index.rst index 96ae5e256ae..b64ddc46a98 100644 --- a/src/doc/en/reference/constants/index.rst +++ b/src/doc/en/reference/constants/index.rst @@ -2,7 +2,7 @@ Constants ========= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/symbolic/constants diff --git a/src/doc/en/reference/cpython/index.rst b/src/doc/en/reference/cpython/index.rst index dce1253a535..5cb77b05605 100644 --- a/src/doc/en/reference/cpython/index.rst +++ b/src/doc/en/reference/cpython/index.rst @@ -5,7 +5,7 @@ SageMath has various modules to provide access to low-level Python internals. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/cpython/atexit sage/cpython/string diff --git a/src/doc/en/reference/cryptography/index.rst b/src/doc/en/reference/cryptography/index.rst index b2d628ab796..3c7997247be 100644 --- a/src/doc/en/reference/cryptography/index.rst +++ b/src/doc/en/reference/cryptography/index.rst @@ -2,7 +2,7 @@ Cryptography ============ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/crypto/cryptosystem diff --git a/src/doc/en/reference/curves/index.rst b/src/doc/en/reference/curves/index.rst index af3aad831d4..b95ab97f839 100644 --- a/src/doc/en/reference/curves/index.rst +++ b/src/doc/en/reference/curves/index.rst @@ -17,7 +17,7 @@ Plane conics ============ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/schemes/plane_conics/constructor sage/schemes/plane_conics/con_field @@ -30,7 +30,7 @@ Plane quartics ========================= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/schemes/plane_quartics/quartic_constructor sage/schemes/plane_quartics/quartic_generic @@ -39,7 +39,7 @@ Riemann surfaces ================ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/schemes/riemann_surfaces/riemann_surface diff --git a/src/doc/en/reference/data_structures/index.rst b/src/doc/en/reference/data_structures/index.rst index 3cecb0fcfda..08c03313ad3 100644 --- a/src/doc/en/reference/data_structures/index.rst +++ b/src/doc/en/reference/data_structures/index.rst @@ -2,7 +2,7 @@ Data Structures =============== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/misc/binary_tree sage/data_structures/bitset diff --git a/src/doc/en/reference/diophantine_approximation/index.rst b/src/doc/en/reference/diophantine_approximation/index.rst index a1c49919f81..06a90c647aa 100644 --- a/src/doc/en/reference/diophantine_approximation/index.rst +++ b/src/doc/en/reference/diophantine_approximation/index.rst @@ -6,7 +6,7 @@ The diophantine approximation deals with the approximation of real numbers See the article :wikipedia:`Diophantine_approximation` for more information. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/continued_fraction diff --git a/src/doc/en/reference/doctest/index.rst b/src/doc/en/reference/doctest/index.rst index 88ec7a54e90..e224d33e4ae 100644 --- a/src/doc/en/reference/doctest/index.rst +++ b/src/doc/en/reference/doctest/index.rst @@ -2,7 +2,7 @@ Sage's Doctesting Framework =========================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/doctest/control sage/doctest/sources diff --git a/src/doc/en/reference/dynamics/cellular_automata.rst b/src/doc/en/reference/dynamics/cellular_automata.rst index 6197e127e23..846c5e91677 100644 --- a/src/doc/en/reference/dynamics/cellular_automata.rst +++ b/src/doc/en/reference/dynamics/cellular_automata.rst @@ -2,7 +2,7 @@ Cellular Automata ================= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 ../sage/dynamics/cellular_automata/catalog diff --git a/src/doc/en/reference/dynamics/complex_dynamics.rst b/src/doc/en/reference/dynamics/complex_dynamics.rst index 06496b4d8a0..4ca4066dffd 100644 --- a/src/doc/en/reference/dynamics/complex_dynamics.rst +++ b/src/doc/en/reference/dynamics/complex_dynamics.rst @@ -2,6 +2,6 @@ Plotting of Mandelbrot and Julia Sets ======================================================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 ../sage/dynamics/complex_dynamics/mandel_julia diff --git a/src/doc/en/reference/dynamics/index.rst b/src/doc/en/reference/dynamics/index.rst index badd1b5cf3a..83cb7782f3a 100644 --- a/src/doc/en/reference/dynamics/index.rst +++ b/src/doc/en/reference/dynamics/index.rst @@ -4,7 +4,7 @@ Discrete dynamics ================= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 cellular_automata complex_dynamics @@ -21,7 +21,7 @@ Arithmetic Dynamical Systems ---------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/dynamics/arithmetic_dynamics/generic_ds sage/dynamics/arithmetic_dynamics/affine_ds diff --git a/src/doc/en/reference/euclidean_spaces/index.rst b/src/doc/en/reference/euclidean_spaces/index.rst index 948458201c0..2f5ce8384d7 100644 --- a/src/doc/en/reference/euclidean_spaces/index.rst +++ b/src/doc/en/reference/euclidean_spaces/index.rst @@ -4,8 +4,7 @@ Euclidean Spaces and Vector Calculus ==================================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/differentiable/examples/euclidean - sage/manifolds/operators diff --git a/src/doc/en/reference/finance/index.rst b/src/doc/en/reference/finance/index.rst index bba570a13ae..2c0f62cbf3c 100644 --- a/src/doc/en/reference/finance/index.rst +++ b/src/doc/en/reference/finance/index.rst @@ -2,7 +2,7 @@ Quantitative Finance ====================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/finance/stock sage/finance/option diff --git a/src/doc/en/reference/finite_rings/index.rst b/src/doc/en/reference/finite_rings/index.rst index dddbd66a54f..02e7f69ed8b 100644 --- a/src/doc/en/reference/finite_rings/index.rst +++ b/src/doc/en/reference/finite_rings/index.rst @@ -5,7 +5,7 @@ Finite Rings ------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/finite_rings/integer_mod_ring sage/rings/finite_rings/integer_mod @@ -14,7 +14,7 @@ Finite Fields ------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/finite_rings/finite_field_constructor sage/rings/finite_rings/finite_field_base @@ -26,7 +26,7 @@ Prime Fields ------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/finite_rings/finite_field_prime_modn sage/rings/finite_rings/hom_prime_finite_field @@ -35,7 +35,7 @@ Finite Fields Using Pari ------------------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/finite_rings/finite_field_pari_ffelt sage/rings/finite_rings/element_pari_ffelt @@ -44,7 +44,7 @@ Finite Fields Using Givaro -------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/finite_rings/finite_field_givaro sage/rings/finite_rings/element_givaro @@ -54,7 +54,7 @@ Finite Fields of Characteristic 2 Using NTL ------------------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/finite_rings/finite_field_ntl_gf2e sage/rings/finite_rings/element_ntl_gf2e @@ -63,7 +63,7 @@ Miscellaneous ------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/finite_rings/residue_field sage/rings/algebraic_closure_finite_field diff --git a/src/doc/en/reference/functions/index.rst b/src/doc/en/reference/functions/index.rst index 54769b6a95e..3916eb0c260 100644 --- a/src/doc/en/reference/functions/index.rst +++ b/src/doc/en/reference/functions/index.rst @@ -8,7 +8,7 @@ Built-in Functions ================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/functions/log sage/functions/trig diff --git a/src/doc/en/reference/game_theory/index.rst b/src/doc/en/reference/game_theory/index.rst index 1bee7a0d46d..eb7dab7193c 100644 --- a/src/doc/en/reference/game_theory/index.rst +++ b/src/doc/en/reference/game_theory/index.rst @@ -2,7 +2,7 @@ Game Theory =========== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/game_theory/cooperative_game sage/game_theory/matching_game diff --git a/src/doc/en/reference/games/index.rst b/src/doc/en/reference/games/index.rst index 8511337bb5d..12a7647655b 100644 --- a/src/doc/en/reference/games/index.rst +++ b/src/doc/en/reference/games/index.rst @@ -6,7 +6,7 @@ Rubik's cube solver (see `Rubik's Cube Group <../groups/sage/groups/perm_gps/cubegroup.html>`_). .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/games/sudoku sage/games/quantumino diff --git a/src/doc/en/reference/groups/index.rst b/src/doc/en/reference/groups/index.rst index dd0de5ba2f6..09d05d78aff 100644 --- a/src/doc/en/reference/groups/index.rst +++ b/src/doc/en/reference/groups/index.rst @@ -2,7 +2,7 @@ Groups ====== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/groups/groups_catalog sage/groups/group @@ -31,7 +31,7 @@ Abelian Groups -------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/groups/abelian_gps/abelian_group sage/groups/abelian_gps/abelian_group_gap @@ -51,7 +51,7 @@ Permutation Groups ------------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/groups/perm_gps/permutation_groups_catalog sage/groups/perm_gps/constructor @@ -66,7 +66,7 @@ Matrix and Affine Groups ------------------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/groups/matrix_gps/catalog sage/groups/matrix_gps/matrix_group @@ -90,7 +90,7 @@ Lie Groups ------------------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/groups/lie_gps/nilpotent_lie_group @@ -98,7 +98,7 @@ Partition Refinement -------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/groups/perm_gps/partn_ref/canonical_augmentation sage/groups/perm_gps/partn_ref/data_structures @@ -110,7 +110,7 @@ Internals --------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/groups/matrix_gps/named_group diff --git a/src/doc/en/reference/hecke/index.rst b/src/doc/en/reference/hecke/index.rst index 4726b4e1cc9..24f18d9c662 100644 --- a/src/doc/en/reference/hecke/index.rst +++ b/src/doc/en/reference/hecke/index.rst @@ -8,7 +8,7 @@ Symbols <../modsym/index.html>`_ and `Modular Forms <../modfrm/index.html>`_. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modular/hecke/module sage/modular/hecke/ambient_module diff --git a/src/doc/en/reference/homology/index.rst b/src/doc/en/reference/homology/index.rst index 32424286980..8188233a52b 100644 --- a/src/doc/en/reference/homology/index.rst +++ b/src/doc/en/reference/homology/index.rst @@ -5,7 +5,7 @@ Sage includes some tools for algebraic topology, and in particular computing homology groups. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/homology/chain_complex sage/homology/chains diff --git a/src/doc/en/reference/interfaces/index.rst b/src/doc/en/reference/interfaces/index.rst index d007f6778f3..a2df80ff4b3 100644 --- a/src/doc/en/reference/interfaces/index.rst +++ b/src/doc/en/reference/interfaces/index.rst @@ -58,7 +58,7 @@ exact actual program available (especially useful for tab completion and testing to make sure nothing funny is going on). .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/interfaces/interface sage/interfaces/expect diff --git a/src/doc/en/reference/knots/index.rst b/src/doc/en/reference/knots/index.rst index 8fc794b4705..ed42964e5a5 100644 --- a/src/doc/en/reference/knots/index.rst +++ b/src/doc/en/reference/knots/index.rst @@ -2,7 +2,7 @@ Knot Theory =========== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/knots/knot sage/knots/link diff --git a/src/doc/en/reference/lfunctions/index.rst b/src/doc/en/reference/lfunctions/index.rst index 03238db6534..89487b344a9 100644 --- a/src/doc/en/reference/lfunctions/index.rst +++ b/src/doc/en/reference/lfunctions/index.rst @@ -5,7 +5,7 @@ Sage includes several standard open source packages for computing with :math:`L`-functions. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/lfunctions/lcalc sage/lfunctions/sympow diff --git a/src/doc/en/reference/libs/index.rst b/src/doc/en/reference/libs/index.rst index 43da10f063c..3b58fb289da 100644 --- a/src/doc/en/reference/libs/index.rst +++ b/src/doc/en/reference/libs/index.rst @@ -21,14 +21,14 @@ to be aware of the modules described in this chapter. ECL --- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/ecl eclib ----- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/eclib/interface sage/libs/eclib/mwrank @@ -40,7 +40,7 @@ eclib FLINT ----- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/flint/flint sage/libs/flint/fmpz_poly @@ -49,35 +49,35 @@ FLINT Giac ---- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/giac GMP-ECM ------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/libecm GSL --- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/gsl/array lcalc ----- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/lcalc/lcalc_Lfunction libSingular ----------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/singular/function sage/libs/singular/function_factory @@ -90,7 +90,7 @@ libSingular GAP --- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/gap/context_managers sage/libs/gap/gap_functions @@ -104,35 +104,35 @@ GAP LinBox ------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/linbox/linbox_flint_interface lrcalc ------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/lrcalc/lrcalc mpmath ------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/mpmath/utils NTL --- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/ntl/all PARI ---- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/pari sage/libs/pari/convert_sage @@ -141,7 +141,7 @@ PARI Symmetrica ---------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/libs/symmetrica/symmetrica diff --git a/src/doc/en/reference/logic/index.rst b/src/doc/en/reference/logic/index.rst index 8628a5e5bbd..bb914195f9b 100644 --- a/src/doc/en/reference/logic/index.rst +++ b/src/doc/en/reference/logic/index.rst @@ -2,7 +2,7 @@ Symbolic Logic ============== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/logic/propcalc sage/logic/boolformula diff --git a/src/doc/en/reference/manifolds/chart.rst b/src/doc/en/reference/manifolds/chart.rst index d9f38e2fee1..8cd42210dd7 100644 --- a/src/doc/en/reference/manifolds/chart.rst +++ b/src/doc/en/reference/manifolds/chart.rst @@ -2,7 +2,7 @@ Coordinate Charts ================= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/chart diff --git a/src/doc/en/reference/manifolds/continuous_map.rst b/src/doc/en/reference/manifolds/continuous_map.rst index bd17639a2f4..59c6180099f 100644 --- a/src/doc/en/reference/manifolds/continuous_map.rst +++ b/src/doc/en/reference/manifolds/continuous_map.rst @@ -2,7 +2,7 @@ Continuous Maps =============== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/manifold_homset diff --git a/src/doc/en/reference/manifolds/degenerate_metric.rst b/src/doc/en/reference/manifolds/degenerate_metric.rst index c4f1ca4f415..2af47655f3a 100644 --- a/src/doc/en/reference/manifolds/degenerate_metric.rst +++ b/src/doc/en/reference/manifolds/degenerate_metric.rst @@ -2,7 +2,7 @@ Degenerate Metric Manifolds =========================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/differentiable/degenerate diff --git a/src/doc/en/reference/manifolds/diff_form.rst b/src/doc/en/reference/manifolds/diff_form.rst index 4ce40bde2ab..e042770f5bd 100644 --- a/src/doc/en/reference/manifolds/diff_form.rst +++ b/src/doc/en/reference/manifolds/diff_form.rst @@ -2,7 +2,7 @@ Differential Forms ================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/differentiable/diff_form_module diff --git a/src/doc/en/reference/manifolds/diff_manifold.rst b/src/doc/en/reference/manifolds/diff_manifold.rst index 31e86015d97..2edb2aee20f 100644 --- a/src/doc/en/reference/manifolds/diff_manifold.rst +++ b/src/doc/en/reference/manifolds/diff_manifold.rst @@ -2,7 +2,7 @@ Differentiable Manifolds ======================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/differentiable/manifold diff --git a/src/doc/en/reference/manifolds/diff_map.rst b/src/doc/en/reference/manifolds/diff_map.rst index 385248c0506..11177f6004d 100644 --- a/src/doc/en/reference/manifolds/diff_map.rst +++ b/src/doc/en/reference/manifolds/diff_map.rst @@ -2,12 +2,12 @@ Differentiable Maps and Curves ============================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/differentiable/manifold_homset sage/manifolds/differentiable/diff_map sage/manifolds/differentiable/curve - + sage/manifolds/differentiable/integrated_curve diff --git a/src/doc/en/reference/manifolds/diff_scalarfield.rst b/src/doc/en/reference/manifolds/diff_scalarfield.rst index 9eb217c7547..f1d86cbd221 100644 --- a/src/doc/en/reference/manifolds/diff_scalarfield.rst +++ b/src/doc/en/reference/manifolds/diff_scalarfield.rst @@ -2,7 +2,7 @@ Scalar Fields ============= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/differentiable/scalarfield_algebra diff --git a/src/doc/en/reference/manifolds/diff_vector_bundle.rst b/src/doc/en/reference/manifolds/diff_vector_bundle.rst index fdfdf39df61..d237f8ffca6 100644 --- a/src/doc/en/reference/manifolds/diff_vector_bundle.rst +++ b/src/doc/en/reference/manifolds/diff_vector_bundle.rst @@ -2,7 +2,7 @@ Differentiable Vector Bundles ============================= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/differentiable/vector_bundle diff --git a/src/doc/en/reference/manifolds/euclidean_space.rst b/src/doc/en/reference/manifolds/euclidean_space.rst index 6ee1c2c820f..45c592d39a8 100644 --- a/src/doc/en/reference/manifolds/euclidean_space.rst +++ b/src/doc/en/reference/manifolds/euclidean_space.rst @@ -4,7 +4,7 @@ Euclidean Spaces and Vector Calculus ==================================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/differentiable/examples/euclidean diff --git a/src/doc/en/reference/manifolds/manifold.rst b/src/doc/en/reference/manifolds/manifold.rst index 1384b507bf3..055ccebca68 100644 --- a/src/doc/en/reference/manifolds/manifold.rst +++ b/src/doc/en/reference/manifolds/manifold.rst @@ -2,7 +2,7 @@ Topological Manifolds ===================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/manifold diff --git a/src/doc/en/reference/manifolds/mixed_form.rst b/src/doc/en/reference/manifolds/mixed_form.rst index 0afbec4f527..51c98b61f15 100644 --- a/src/doc/en/reference/manifolds/mixed_form.rst +++ b/src/doc/en/reference/manifolds/mixed_form.rst @@ -2,7 +2,7 @@ Mixed Differential Forms ======================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/differentiable/mixed_form_algebra diff --git a/src/doc/en/reference/manifolds/multivector.rst b/src/doc/en/reference/manifolds/multivector.rst index fe2c90815cb..1199ea1fdad 100644 --- a/src/doc/en/reference/manifolds/multivector.rst +++ b/src/doc/en/reference/manifolds/multivector.rst @@ -2,7 +2,7 @@ Alternating Multivector Fields ============================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/differentiable/multivector_module diff --git a/src/doc/en/reference/manifolds/scalarfield.rst b/src/doc/en/reference/manifolds/scalarfield.rst index 3ba7a2e1e4e..62100d7feb3 100644 --- a/src/doc/en/reference/manifolds/scalarfield.rst +++ b/src/doc/en/reference/manifolds/scalarfield.rst @@ -2,7 +2,7 @@ Scalar Fields ============= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/scalarfield_algebra diff --git a/src/doc/en/reference/manifolds/tangent_space.rst b/src/doc/en/reference/manifolds/tangent_space.rst index 6c9e17899b5..8e55de2dbf6 100644 --- a/src/doc/en/reference/manifolds/tangent_space.rst +++ b/src/doc/en/reference/manifolds/tangent_space.rst @@ -2,7 +2,7 @@ Tangent Spaces ============== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/differentiable/tangent_space diff --git a/src/doc/en/reference/manifolds/tensorfield.rst b/src/doc/en/reference/manifolds/tensorfield.rst index 6710757d5df..cf70207f185 100644 --- a/src/doc/en/reference/manifolds/tensorfield.rst +++ b/src/doc/en/reference/manifolds/tensorfield.rst @@ -2,7 +2,7 @@ Tensor Fields ============= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/differentiable/tensorfield_module diff --git a/src/doc/en/reference/manifolds/vector_bundle.rst b/src/doc/en/reference/manifolds/vector_bundle.rst index 85905cbec94..f52b203609e 100644 --- a/src/doc/en/reference/manifolds/vector_bundle.rst +++ b/src/doc/en/reference/manifolds/vector_bundle.rst @@ -2,7 +2,7 @@ Topological Vector Bundles ========================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/vector_bundle diff --git a/src/doc/en/reference/manifolds/vectorfield.rst b/src/doc/en/reference/manifolds/vectorfield.rst index df9817539df..d489df1669a 100644 --- a/src/doc/en/reference/manifolds/vectorfield.rst +++ b/src/doc/en/reference/manifolds/vectorfield.rst @@ -2,7 +2,7 @@ Vector Fields ============= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/manifolds/differentiable/vectorfield_module diff --git a/src/doc/en/reference/matrices/index.rst b/src/doc/en/reference/matrices/index.rst index 6dc2a83a821..25aed6c2cb3 100644 --- a/src/doc/en/reference/matrices/index.rst +++ b/src/doc/en/reference/matrices/index.rst @@ -43,7 +43,7 @@ Finally, this module contains some data-structures for matrix-like objects like operation tables (e.g. the multiplication table of a group). .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/matrix/matrix_space diff --git a/src/doc/en/reference/modabvar/index.rst b/src/doc/en/reference/modabvar/index.rst index 768ede8e1f4..efad7a2c386 100644 --- a/src/doc/en/reference/modabvar/index.rst +++ b/src/doc/en/reference/modabvar/index.rst @@ -2,7 +2,7 @@ Modular Abelian Varieties ========================= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modular/abvar/constructor sage/modular/abvar/abvar diff --git a/src/doc/en/reference/modfrm/index.rst b/src/doc/en/reference/modfrm/index.rst index c4b3cfa9c09..51b6c2cbb59 100644 --- a/src/doc/en/reference/modfrm/index.rst +++ b/src/doc/en/reference/modfrm/index.rst @@ -5,7 +5,7 @@ Module List ----------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modular/modform/constructor sage/modular/modform/space @@ -32,7 +32,7 @@ Design Notes ------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modular/modform/notes diff --git a/src/doc/en/reference/modfrm_hecketriangle/index.rst b/src/doc/en/reference/modfrm_hecketriangle/index.rst index 32368bb5aaa..2f783b529e1 100644 --- a/src/doc/en/reference/modfrm_hecketriangle/index.rst +++ b/src/doc/en/reference/modfrm_hecketriangle/index.rst @@ -3,7 +3,7 @@ Modular Forms for Hecke Triangle Groups .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modular/modform_hecketriangle/readme diff --git a/src/doc/en/reference/modmisc/index.rst b/src/doc/en/reference/modmisc/index.rst index a465c78c3ad..191fe5fbefc 100644 --- a/src/doc/en/reference/modmisc/index.rst +++ b/src/doc/en/reference/modmisc/index.rst @@ -2,7 +2,7 @@ Miscellaneous Modular-Form-Related Modules ========================================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modular/dirichlet sage/modular/cusps diff --git a/src/doc/en/reference/modsym/index.rst b/src/doc/en/reference/modsym/index.rst index d23bf9b0224..e5b54be0644 100644 --- a/src/doc/en/reference/modsym/index.rst +++ b/src/doc/en/reference/modsym/index.rst @@ -2,7 +2,7 @@ Modular Symbols =============== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modular/modsym/modsym @@ -36,7 +36,7 @@ Overconvergent modular symbols ---------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modular/pollack_stevens/space sage/modular/pollack_stevens/distributions diff --git a/src/doc/en/reference/modules/index.rst b/src/doc/en/reference/modules/index.rst index 643a669a6b0..c6109643c35 100644 --- a/src/doc/en/reference/modules/index.rst +++ b/src/doc/en/reference/modules/index.rst @@ -4,7 +4,7 @@ Modules Sage provides modules of various kinds over various base rings. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modules/tutorial_free_modules @@ -13,7 +13,7 @@ Free modules, submodules, and quotients --------------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modules/module sage/modules/free_module @@ -27,7 +27,7 @@ Modules with basis ------------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modules/with_basis/__init__ sage/modules/with_basis/cell_module @@ -41,7 +41,7 @@ Finitely generated modules over a PID ------------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modules/fg_pid/fgp_module sage/modules/fg_pid/fgp_element @@ -51,7 +51,7 @@ Finitely presented graded modules --------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modules/fp_graded/free_module sage/modules/fp_graded/free_element @@ -69,7 +69,7 @@ Special modules --------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modules/free_module_integer sage/modules/free_quadratic_module @@ -82,7 +82,7 @@ Morphisms --------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modules/vector_space_homspace sage/modules/vector_space_morphism @@ -96,7 +96,7 @@ Vectors ------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modules/vector_integer_dense sage/modules/vector_mod2_dense @@ -114,7 +114,7 @@ Misc ---- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modules/diamond_cutting sage/modules/tensor_operations diff --git a/src/doc/en/reference/monoids/index.rst b/src/doc/en/reference/monoids/index.rst index 0e7279db6d3..fa394698b28 100644 --- a/src/doc/en/reference/monoids/index.rst +++ b/src/doc/en/reference/monoids/index.rst @@ -6,7 +6,7 @@ finite number of indeterminates, as well as free partially commutative monoids (trace monoids). .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/monoids/monoid sage/monoids/free_monoid diff --git a/src/doc/en/reference/noncommutative_polynomial_rings/index.rst b/src/doc/en/reference/noncommutative_polynomial_rings/index.rst index 9848988d126..dca4c3fe931 100644 --- a/src/doc/en/reference/noncommutative_polynomial_rings/index.rst +++ b/src/doc/en/reference/noncommutative_polynomial_rings/index.rst @@ -5,7 +5,7 @@ Univariate Ore polynomial rings ------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/polynomial/ore_polynomial_ring sage/rings/polynomial/ore_polynomial_element @@ -18,7 +18,7 @@ Fraction field of Ore polynomial rings -------------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/polynomial/ore_function_field sage/rings/polynomial/ore_function_element diff --git a/src/doc/en/reference/padics/index.rst b/src/doc/en/reference/padics/index.rst index 68b68bd3922..6efcee751a6 100644 --- a/src/doc/en/reference/padics/index.rst +++ b/src/doc/en/reference/padics/index.rst @@ -2,7 +2,7 @@ ======================= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/padics/tutorial diff --git a/src/doc/en/reference/parallel/index.rst b/src/doc/en/reference/parallel/index.rst index 6900e061a94..13b16a23a53 100644 --- a/src/doc/en/reference/parallel/index.rst +++ b/src/doc/en/reference/parallel/index.rst @@ -2,7 +2,7 @@ Parallel Computing ================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/parallel/decorate sage/parallel/reference diff --git a/src/doc/en/reference/plot3d/index.rst b/src/doc/en/reference/plot3d/index.rst index 2cbabe1b75c..cf8ab828276 100644 --- a/src/doc/en/reference/plot3d/index.rst +++ b/src/doc/en/reference/plot3d/index.rst @@ -2,7 +2,7 @@ =========== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/plot/plot3d/introduction @@ -10,7 +10,7 @@ Function and Data Plots ----------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/plot/plot3d/plot3d sage/plot/plot3d/parametric_plot3d @@ -24,7 +24,7 @@ Basic Shapes and Primitives --------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/plot/plot3d/base sage/plot/plot3d/shapes @@ -37,7 +37,7 @@ Infrastructure -------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/plot/plot3d/texture sage/plot/plot3d/index_face_set @@ -48,7 +48,7 @@ Backends -------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/plot/plot3d/tachyon threejs diff --git a/src/doc/en/reference/plotting/index.rst b/src/doc/en/reference/plotting/index.rst index 662c5678481..1c1c4a03b17 100644 --- a/src/doc/en/reference/plotting/index.rst +++ b/src/doc/en/reference/plotting/index.rst @@ -5,7 +5,7 @@ General ------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/plot/plot sage/plot/text @@ -16,7 +16,7 @@ Function and Data Plots ----------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/plot/complex_plot sage/plot/contour_plot @@ -32,7 +32,7 @@ Plots of Other Mathematical Objects ----------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/graphs/graph_plot sage/plot/matrix_plot @@ -41,7 +41,7 @@ Basic Shapes ------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/plot/arc sage/plot/arrow @@ -60,7 +60,7 @@ Infrastructure and Low-Level Functions -------------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/plot/graphics sage/plot/multigraphics diff --git a/src/doc/en/reference/polynomial_rings/index.rst b/src/doc/en/reference/polynomial_rings/index.rst index bbdf14685ca..8a8ef70563c 100644 --- a/src/doc/en/reference/polynomial_rings/index.rst +++ b/src/doc/en/reference/polynomial_rings/index.rst @@ -5,7 +5,7 @@ Polynomial Rings ---------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/polynomial/polynomial_ring_constructor @@ -13,7 +13,7 @@ Univariate Polynomials ---------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 polynomial_rings_univar sage/rings/polynomial/convolution @@ -23,7 +23,7 @@ Multivariate Polynomials ------------------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 polynomial_rings_multivar invariant_theory diff --git a/src/doc/en/reference/polynomial_rings/invariant_theory.rst b/src/doc/en/reference/polynomial_rings/invariant_theory.rst index e57e1226eed..48cca8f9cd0 100644 --- a/src/doc/en/reference/polynomial_rings/invariant_theory.rst +++ b/src/doc/en/reference/polynomial_rings/invariant_theory.rst @@ -3,7 +3,7 @@ Classical Invariant Theory ========================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/invariants/invariant_theory sage/rings/invariants/reconstruction diff --git a/src/doc/en/reference/polynomial_rings/polynomial_rings_multivar.rst b/src/doc/en/reference/polynomial_rings/polynomial_rings_multivar.rst index 6147929ccf2..9a2ebf494ae 100644 --- a/src/doc/en/reference/polynomial_rings/polynomial_rings_multivar.rst +++ b/src/doc/en/reference/polynomial_rings/polynomial_rings_multivar.rst @@ -20,7 +20,7 @@ are implemented using the PolyBoRi library (cf. :mod:`sage.rings.polynomial.pbor .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/polynomial/term_order diff --git a/src/doc/en/reference/polynomial_rings/polynomial_rings_toy_implementations.rst b/src/doc/en/reference/polynomial_rings/polynomial_rings_toy_implementations.rst index 1a27c451f23..baa14664c08 100644 --- a/src/doc/en/reference/polynomial_rings/polynomial_rings_toy_implementations.rst +++ b/src/doc/en/reference/polynomial_rings/polynomial_rings_toy_implementations.rst @@ -3,7 +3,7 @@ Educational Versions of Groebner Basis Related Algorithms ========================================================= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/polynomial/toy_buchberger sage/rings/polynomial/toy_variety diff --git a/src/doc/en/reference/polynomial_rings/polynomial_rings_univar.rst b/src/doc/en/reference/polynomial_rings/polynomial_rings_univar.rst index bd55ed8b014..414c04bb611 100644 --- a/src/doc/en/reference/polynomial_rings/polynomial_rings_univar.rst +++ b/src/doc/en/reference/polynomial_rings/polynomial_rings_univar.rst @@ -12,7 +12,7 @@ than pure Python classes and thus can only inherit from a single base class, whereas others have multiple bases. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/polynomial/polynomial_ring sage/rings/polynomial/polynomial_ring_homomorphism diff --git a/src/doc/en/reference/power_series/index.rst b/src/doc/en/reference/power_series/index.rst index dba377477f1..6ab1c2004e3 100644 --- a/src/doc/en/reference/power_series/index.rst +++ b/src/doc/en/reference/power_series/index.rst @@ -2,7 +2,7 @@ Power Series Rings and Laurent Series Rings =========================================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/power_series_ring sage/rings/power_series_ring_element diff --git a/src/doc/en/reference/probability/index.rst b/src/doc/en/reference/probability/index.rst index 633219bddbf..da9318b3398 100644 --- a/src/doc/en/reference/probability/index.rst +++ b/src/doc/en/reference/probability/index.rst @@ -2,7 +2,7 @@ Probability =========== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/probability/probability_distribution sage/probability/random_variable diff --git a/src/doc/en/reference/quadratic_forms/index.rst b/src/doc/en/reference/quadratic_forms/index.rst index 65fb2563115..7169e7ac503 100644 --- a/src/doc/en/reference/quadratic_forms/index.rst +++ b/src/doc/en/reference/quadratic_forms/index.rst @@ -2,7 +2,7 @@ Quadratic Forms =============== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/quadratic_forms/quadratic_form sage/quadratic_forms/binary_qf diff --git a/src/doc/en/reference/quasimodfrm/index.rst b/src/doc/en/reference/quasimodfrm/index.rst index 4fb81ca671d..0d450ed9dc2 100644 --- a/src/doc/en/reference/quasimodfrm/index.rst +++ b/src/doc/en/reference/quasimodfrm/index.rst @@ -5,7 +5,7 @@ Module List ----------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/modular/quasimodform/ring sage/modular/quasimodform/element diff --git a/src/doc/en/reference/quat_algebras/index.rst b/src/doc/en/reference/quat_algebras/index.rst index 110d732342b..1cdef1b7cea 100644 --- a/src/doc/en/reference/quat_algebras/index.rst +++ b/src/doc/en/reference/quat_algebras/index.rst @@ -2,7 +2,7 @@ Quaternion Algebras =================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/algebras/quatalg/quaternion_algebra sage/algebras/quatalg/quaternion_algebra_element diff --git a/src/doc/en/reference/quivers/index.rst b/src/doc/en/reference/quivers/index.rst index b1e3aa48f87..98bf561c4dc 100644 --- a/src/doc/en/reference/quivers/index.rst +++ b/src/doc/en/reference/quivers/index.rst @@ -2,7 +2,7 @@ Quivers ======= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/quivers/algebra sage/quivers/algebra_elements diff --git a/src/doc/en/reference/repl/index.rst b/src/doc/en/reference/repl/index.rst index 21a2f285f1f..d12d8866dda 100644 --- a/src/doc/en/reference/repl/index.rst +++ b/src/doc/en/reference/repl/index.rst @@ -3,7 +3,7 @@ The Sage Command Line The Sage Read-Eval-Print-Loop (REPL) is based on IPython. In this document, you'll find how the IPython integration works. You should -also be familiar with the documentation for IPython. +also be familiar with the documentation for IPython. For more details about using the Sage command line, see the Sage tutorial. @@ -12,7 +12,7 @@ Running Sage ------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 options startup @@ -26,8 +26,8 @@ Sage commands are "preparsed" to valid Python syntax. This allows for example to support the ``R. = QQ[]`` syntax. .. toctree:: - :maxdepth: 2 - + :maxdepth: 1 + sage/repl/preparse @@ -39,8 +39,8 @@ in a Sage session. Attaching is similar, except that the attached file is reloaded whenever it is changed. .. toctree:: - :maxdepth: 2 - + :maxdepth: 1 + sage/repl/load sage/repl/attach @@ -53,18 +53,18 @@ printed. This again builds on how IPython formats output. Technically, this works using a modified displayhook in Python. .. toctree:: - :maxdepth: 2 - + :maxdepth: 1 + sage/repl/display/formatter sage/repl/display/pretty_print sage/repl/display/fancy_repr sage/repl/display/util - + Display Backend Infrastructure ------------------------------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/repl/rich_output/display_manager sage/repl/rich_output/preferences @@ -75,7 +75,7 @@ Display Backend Infrastructure sage/repl/rich_output/output_graphics3d sage/repl/rich_output/output_video sage/repl/rich_output/output_catalog - + sage/repl/rich_output/backend_base sage/repl/rich_output/test_backend sage/repl/rich_output/backend_doctest @@ -85,7 +85,7 @@ Miscellaneous ------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/repl/interpreter sage/repl/ipython_extension diff --git a/src/doc/en/reference/resolutions/index.rst b/src/doc/en/reference/resolutions/index.rst index 82f73cb94c8..861023c8aec 100644 --- a/src/doc/en/reference/resolutions/index.rst +++ b/src/doc/en/reference/resolutions/index.rst @@ -5,7 +5,7 @@ Free and graded resolutions are tools for commutative algebra and algebraic geometry. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/homology/free_resolution sage/homology/graded_resolution diff --git a/src/doc/en/reference/riemannian_geometry/index.rst b/src/doc/en/reference/riemannian_geometry/index.rst index caa6ea15685..5f16761f845 100644 --- a/src/doc/en/reference/riemannian_geometry/index.rst +++ b/src/doc/en/reference/riemannian_geometry/index.rst @@ -2,7 +2,7 @@ Differential Geometry of Curves and Surfaces ============================================ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/geometry/riemannian_manifolds/parametrized_surface3d sage/geometry/riemannian_manifolds/surface3d_generators diff --git a/src/doc/en/reference/rings/index.rst b/src/doc/en/reference/rings/index.rst index 58451740464..31e1b5285fc 100644 --- a/src/doc/en/reference/rings/index.rst +++ b/src/doc/en/reference/rings/index.rst @@ -5,7 +5,7 @@ Base Classes for Rings, Algebras and Fields ------------------------------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/ring @@ -13,7 +13,7 @@ Ideals ------ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/ideal sage/rings/ideal_monoid @@ -23,7 +23,7 @@ Ring Morphisms -------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/morphism sage/rings/homset @@ -32,7 +32,7 @@ Quotient Rings -------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/quotient_ring sage/rings/quotient_ring_element @@ -41,7 +41,7 @@ Fraction Fields --------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/fraction_field sage/rings/fraction_field_element @@ -50,7 +50,7 @@ Localization --------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/localization @@ -58,7 +58,7 @@ Ring Extensions --------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/ring_extension sage/rings/ring_extension_element @@ -69,7 +69,7 @@ Utilities --------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/big_oh sage/rings/infinity @@ -79,7 +79,7 @@ Derivation ---------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/derivation diff --git a/src/doc/en/reference/rings_numerical/index.rst b/src/doc/en/reference/rings_numerical/index.rst index 955c61beee6..a20066c2ec7 100644 --- a/src/doc/en/reference/rings_numerical/index.rst +++ b/src/doc/en/reference/rings_numerical/index.rst @@ -16,7 +16,7 @@ which builds on GMP. In many cases the PARI C-library is used to compute special functions when implementations aren't otherwise available. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/real_mpfr sage/rings/complex_mpfr @@ -32,7 +32,7 @@ Sage implements real and complex interval arithmetic using MPFI ComplexBallField). .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/real_mpfi sage/rings/real_interval_absolute @@ -46,7 +46,7 @@ Exact Real Arithmetic --------------------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/real_lazy diff --git a/src/doc/en/reference/rings_standard/index.rst b/src/doc/en/reference/rings_standard/index.rst index 922de453f59..4bb5338609f 100644 --- a/src/doc/en/reference/rings_standard/index.rst +++ b/src/doc/en/reference/rings_standard/index.rst @@ -5,7 +5,7 @@ Integers -------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/integer_ring sage/rings/integer @@ -30,7 +30,7 @@ Rationals --------- .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/rational_field sage/rings/rational diff --git a/src/doc/en/reference/semirings/index.rst b/src/doc/en/reference/semirings/index.rst index 6cab3c1421a..b40e71c54e1 100644 --- a/src/doc/en/reference/semirings/index.rst +++ b/src/doc/en/reference/semirings/index.rst @@ -2,7 +2,7 @@ Standard Semirings ================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/semirings/non_negative_integer_semiring sage/rings/semirings/tropical_semiring diff --git a/src/doc/en/reference/stats/index.rst b/src/doc/en/reference/stats/index.rst index e55454305c6..0020b113d66 100644 --- a/src/doc/en/reference/stats/index.rst +++ b/src/doc/en/reference/stats/index.rst @@ -2,7 +2,7 @@ Statistics ========== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/stats/basic_stats sage/stats/intlist diff --git a/src/doc/en/reference/tensor_free_modules/alt_forms.rst b/src/doc/en/reference/tensor_free_modules/alt_forms.rst index a6a2b87818d..c614c8fd531 100644 --- a/src/doc/en/reference/tensor_free_modules/alt_forms.rst +++ b/src/doc/en/reference/tensor_free_modules/alt_forms.rst @@ -2,7 +2,7 @@ Alternating tensors =================== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/tensor/modules/ext_pow_free_module diff --git a/src/doc/en/reference/tensor_free_modules/index.rst b/src/doc/en/reference/tensor_free_modules/index.rst index 2d2f7db3613..5e67a0e0389 100644 --- a/src/doc/en/reference/tensor_free_modules/index.rst +++ b/src/doc/en/reference/tensor_free_modules/index.rst @@ -9,7 +9,7 @@ not depend upon other SageManifolds classes. In other words, it constitutes a self-consistent subset that can be used independently of SageManifolds. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/tensor/modules/finite_rank_free_module diff --git a/src/doc/en/reference/tensor_free_modules/morphisms.rst b/src/doc/en/reference/tensor_free_modules/morphisms.rst index fa661c8978d..e05a1670861 100644 --- a/src/doc/en/reference/tensor_free_modules/morphisms.rst +++ b/src/doc/en/reference/tensor_free_modules/morphisms.rst @@ -2,7 +2,7 @@ Morphisms ========= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/tensor/modules/free_module_homset diff --git a/src/doc/en/reference/tensor_free_modules/tensors.rst b/src/doc/en/reference/tensor_free_modules/tensors.rst index 434ea734191..f7d6e61e34f 100644 --- a/src/doc/en/reference/tensor_free_modules/tensors.rst +++ b/src/doc/en/reference/tensor_free_modules/tensors.rst @@ -2,7 +2,7 @@ Tensors ======= .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/tensor/modules/tensor_free_module diff --git a/src/doc/en/reference/topology/index.rst b/src/doc/en/reference/topology/index.rst index 8550c2b0c42..1442b327ed2 100644 --- a/src/doc/en/reference/topology/index.rst +++ b/src/doc/en/reference/topology/index.rst @@ -8,7 +8,7 @@ also available, mainly for developers who want to use it as a base for other types of cell complexes. .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/topology/simplicial_complex sage/topology/simplicial_complex_morphism diff --git a/src/doc/en/reference/valuations/index.rst b/src/doc/en/reference/valuations/index.rst index 67daac25902..90a8c3c97f7 100644 --- a/src/doc/en/reference/valuations/index.rst +++ b/src/doc/en/reference/valuations/index.rst @@ -46,7 +46,7 @@ Similarly, valuations can be defined on function fields:: sage: v = K.valuation(x) sage: v(1/x) -1 - + sage: v = K.valuation(1/x) sage: v(1/x) 1 @@ -88,7 +88,7 @@ polynomial the minimum of the coefficient valuations:: sage: R. = QQ[] sage: v = GaussValuation(R, valuations.TrivialValuation(QQ)) - + The Gauss valuation can be augmented by specifying that `x - 4` has valuation 1:: sage: v = v.augmentation(x - 4, 1); v @@ -184,7 +184,7 @@ More Details ============ .. toctree:: - :maxdepth: 2 + :maxdepth: 1 sage/rings/valuation/value_group sage/rings/valuation/valuation diff --git a/src/sage_docbuild/conf.py b/src/sage_docbuild/conf.py index 4c880cdca63..4c00a1d338b 100644 --- a/src/sage_docbuild/conf.py +++ b/src/sage_docbuild/conf.py @@ -187,6 +187,24 @@ def sphinx_plot(graphics, **kwds): highlighting.lexers['ipython'] = IPyLexer() highlight_language = 'ipycon' +# Create table of contents entries for domain objects (e.g. functions, classes, +# attributes, etc.). Default is True. +toc_object_entries = True + +# A string that determines how domain objects (e.g. functions, classes, +# attributes, etc.) are displayed in their table of contents entry. +# +# Use "domain" to allow the domain to determine the appropriate number of parents +# to show. For example, the Python domain would show Class.method() and +# function(), leaving out the module. level of parents. This is the default +# setting. +# +# Use "hide" to only show the name of the element without any parents (i.e. method()). +# +# Use "all" to show the fully-qualified name for the object (i.e. module.Class.method()), +# displaying all parents. +toc_object_entries_show_parents = 'hide' + # Extension configuration # ----------------------- From aa2c315ba7ca755992c0e6b1278e560a70e75408 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 1 Nov 2022 00:00:09 -0700 Subject: [PATCH 375/632] build/pkgs/ipython: Update to 8.6.0 --- build/pkgs/ipython/checksums.ini | 6 +++--- build/pkgs/ipython/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/ipython/checksums.ini b/build/pkgs/ipython/checksums.ini index ab40792a0a7..a539eb830b4 100644 --- a/build/pkgs/ipython/checksums.ini +++ b/build/pkgs/ipython/checksums.ini @@ -1,5 +1,5 @@ tarball=ipython-VERSION.tar.gz -sha1=98270c68edcfbcd02b3d76d9c1ab6030bbb92171 -md5=dc5d78f3d622b027a0f32f9a545b44ff -cksum=3907293880 +sha1=e0dd247f29befed1159d9bdca987d90c2ee0d34a +md5=8c98f6def0622ea32975cb779247c3d7 +cksum=2860792697 upstream_url=https://pypi.io/packages/source/i/ipython/ipython-VERSION.tar.gz diff --git a/build/pkgs/ipython/package-version.txt b/build/pkgs/ipython/package-version.txt index a2f28f43be3..acd405b1d62 100644 --- a/build/pkgs/ipython/package-version.txt +++ b/build/pkgs/ipython/package-version.txt @@ -1 +1 @@ -8.4.0 +8.6.0 From eb2173ce26a4a9d45699cbee92e995bd1bca2d27 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 1 Nov 2022 00:06:31 -0700 Subject: [PATCH 376/632] build/pkgs/traitlets: Update to 5.5.0 --- build/pkgs/traitlets/checksums.ini | 6 +++--- build/pkgs/traitlets/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/traitlets/checksums.ini b/build/pkgs/traitlets/checksums.ini index fd4f1d895ed..4d90280ec63 100644 --- a/build/pkgs/traitlets/checksums.ini +++ b/build/pkgs/traitlets/checksums.ini @@ -1,5 +1,5 @@ tarball=traitlets-VERSION.tar.gz -sha1=80e1e820c169295ca66428e1414cf33c36c61386 -md5=ea9b920bd09fa8fcc44325c4d6a6800d -cksum=1563220888 +sha1=cb5c3545ddad62ee6700ea0f279b3d168ac58ed9 +md5=d5f87bbea8acf897ac3e435c7b71acdc +cksum=238091586 upstream_url=https://pypi.io/packages/source/t/traitlets/traitlets-VERSION.tar.gz diff --git a/build/pkgs/traitlets/package-version.txt b/build/pkgs/traitlets/package-version.txt index 03f488b076a..d50359de185 100644 --- a/build/pkgs/traitlets/package-version.txt +++ b/build/pkgs/traitlets/package-version.txt @@ -1 +1 @@ -5.3.0 +5.5.0 From c517afb637b3f9f60d8403886c82d712f6fd9026 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 1 Nov 2022 00:07:19 -0700 Subject: [PATCH 377/632] build/pkgs/pygments: Update to 2.13.0 --- build/pkgs/pygments/checksums.ini | 6 +++--- build/pkgs/pygments/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/pygments/checksums.ini b/build/pkgs/pygments/checksums.ini index f7b5fdbde3a..14fc1c04cdb 100644 --- a/build/pkgs/pygments/checksums.ini +++ b/build/pkgs/pygments/checksums.ini @@ -1,5 +1,5 @@ tarball=Pygments-VERSION.tar.gz -sha1=92e83541b557dad3f692d0d9d02733c2c842c836 -md5=2137c19d9ac0cc556badc89e746c0e62 -cksum=3384039979 +sha1=adaf31bf13a7bcc210568537138e0984ecdea626 +md5=6ccae578d28d18968b30a4711652fd9a +cksum=613387624 upstream_url=https://pypi.io/packages/source/p/pygments/Pygments-VERSION.tar.gz diff --git a/build/pkgs/pygments/package-version.txt b/build/pkgs/pygments/package-version.txt index d8b698973a4..fb2c0766b7c 100644 --- a/build/pkgs/pygments/package-version.txt +++ b/build/pkgs/pygments/package-version.txt @@ -1 +1 @@ -2.12.0 +2.13.0 From 07a9a33642e1e93c23acd5491f16a520767b159e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 1 Nov 2022 00:12:05 -0700 Subject: [PATCH 378/632] build/pkgs/nbformat: Update to 5.7.0 --- build/pkgs/nbformat/checksums.ini | 6 +++--- build/pkgs/nbformat/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/nbformat/checksums.ini b/build/pkgs/nbformat/checksums.ini index 39cbb5ac27b..af04e4f35aa 100644 --- a/build/pkgs/nbformat/checksums.ini +++ b/build/pkgs/nbformat/checksums.ini @@ -1,5 +1,5 @@ tarball=nbformat-VERSION.tar.gz -sha1=77f321311289719867b958f40f58a570f3825c11 -md5=a11ccf44c2d984d1b8325a3463a9ae20 -cksum=3746246082 +sha1=ecad83c07bdc475f6fd88d28485cf8fe31fbba41 +md5=5e11cc3240d4b1410610786309cc6076 +cksum=767940068 upstream_url=https://pypi.io/packages/source/n/nbformat/nbformat-VERSION.tar.gz diff --git a/build/pkgs/nbformat/package-version.txt b/build/pkgs/nbformat/package-version.txt index 8a30e8f94a3..42cdd0b540f 100644 --- a/build/pkgs/nbformat/package-version.txt +++ b/build/pkgs/nbformat/package-version.txt @@ -1 +1 @@ -5.4.0 +5.7.0 From 5da03da6517633e1e07715c50b1339ca45b09f5a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 1 Nov 2022 00:18:25 -0700 Subject: [PATCH 379/632] build/pkgs/hatch_nodejs_version: New (nbformat build dep) --- build/pkgs/hatch_nodejs_version/SPKG.rst | 18 ++++++++++++++++++ build/pkgs/hatch_nodejs_version/checksums.ini | 5 +++++ build/pkgs/hatch_nodejs_version/dependencies | 4 ++++ .../hatch_nodejs_version/install-requires.txt | 1 + .../hatch_nodejs_version/package-version.txt | 1 + .../pkgs/hatch_nodejs_version/spkg-install.in | 2 ++ build/pkgs/hatch_nodejs_version/type | 1 + build/pkgs/nbformat/dependencies | 2 +- 8 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 build/pkgs/hatch_nodejs_version/SPKG.rst create mode 100644 build/pkgs/hatch_nodejs_version/checksums.ini create mode 100644 build/pkgs/hatch_nodejs_version/dependencies create mode 100644 build/pkgs/hatch_nodejs_version/install-requires.txt create mode 100644 build/pkgs/hatch_nodejs_version/package-version.txt create mode 100644 build/pkgs/hatch_nodejs_version/spkg-install.in create mode 100644 build/pkgs/hatch_nodejs_version/type diff --git a/build/pkgs/hatch_nodejs_version/SPKG.rst b/build/pkgs/hatch_nodejs_version/SPKG.rst new file mode 100644 index 00000000000..6a1cd5f991c --- /dev/null +++ b/build/pkgs/hatch_nodejs_version/SPKG.rst @@ -0,0 +1,18 @@ +hatch_nodejs_version: Hatch plugin for versioning from a package.json file +========================================================================== + +Description +----------- + +Hatch plugin for versioning from a package.json file + +License +------- + +MIT + +Upstream Contact +---------------- + +https://pypi.org/project/hatch-nodejs-version/ + diff --git a/build/pkgs/hatch_nodejs_version/checksums.ini b/build/pkgs/hatch_nodejs_version/checksums.ini new file mode 100644 index 00000000000..d5210b7eab0 --- /dev/null +++ b/build/pkgs/hatch_nodejs_version/checksums.ini @@ -0,0 +1,5 @@ +tarball=hatch_nodejs_version-VERSION.tar.gz +sha1=27420a3bae3f787640b2c8ad522d75fa32526294 +md5=644c2aea7558de91bbd66217fec6a1b9 +cksum=4058013444 +upstream_url=https://pypi.io/packages/source/h/hatch_nodejs_version/hatch_nodejs_version-VERSION.tar.gz diff --git a/build/pkgs/hatch_nodejs_version/dependencies b/build/pkgs/hatch_nodejs_version/dependencies new file mode 100644 index 00000000000..e9293f104ca --- /dev/null +++ b/build/pkgs/hatch_nodejs_version/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) hatchling | $(PYTHON_TOOLCHAIN) + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/hatch_nodejs_version/install-requires.txt b/build/pkgs/hatch_nodejs_version/install-requires.txt new file mode 100644 index 00000000000..5c606fe80f5 --- /dev/null +++ b/build/pkgs/hatch_nodejs_version/install-requires.txt @@ -0,0 +1 @@ +hatch-nodejs-version diff --git a/build/pkgs/hatch_nodejs_version/package-version.txt b/build/pkgs/hatch_nodejs_version/package-version.txt new file mode 100644 index 00000000000..0d91a54c7d4 --- /dev/null +++ b/build/pkgs/hatch_nodejs_version/package-version.txt @@ -0,0 +1 @@ +0.3.0 diff --git a/build/pkgs/hatch_nodejs_version/spkg-install.in b/build/pkgs/hatch_nodejs_version/spkg-install.in new file mode 100644 index 00000000000..37ac1a53437 --- /dev/null +++ b/build/pkgs/hatch_nodejs_version/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install . diff --git a/build/pkgs/hatch_nodejs_version/type b/build/pkgs/hatch_nodejs_version/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/hatch_nodejs_version/type @@ -0,0 +1 @@ +standard diff --git a/build/pkgs/nbformat/dependencies b/build/pkgs/nbformat/dependencies index 258e07a9163..6c8921f1382 100644 --- a/build/pkgs/nbformat/dependencies +++ b/build/pkgs/nbformat/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) jsonschema fastjsonschema jupyter_core traitlets | $(PYTHON_TOOLCHAIN) +$(PYTHON) jsonschema fastjsonschema jupyter_core traitlets | $(PYTHON_TOOLCHAIN) hatchling hatch_nodejs_version ---------- All lines of this file are ignored except the first. From bfe5a2619cfb3f8bc2a93880f35f3ac446c7e008 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 1 Nov 2022 00:29:21 -0700 Subject: [PATCH 380/632] build/pkgs/nbclient: Update to 0.7.0 --- build/pkgs/nbclient/checksums.ini | 6 +++--- build/pkgs/nbclient/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/nbclient/checksums.ini b/build/pkgs/nbclient/checksums.ini index de286fc5e1b..459341ff20e 100644 --- a/build/pkgs/nbclient/checksums.ini +++ b/build/pkgs/nbclient/checksums.ini @@ -1,5 +1,5 @@ tarball=nbclient-VERSION.tar.gz -sha1=e20fb7fc1fe8c421f3f7e8f749ee8fdc38c1811a -md5=ad28a33484bbd348ef04f142d32669f5 -cksum=2210542646 +sha1=8f6309bc37fb2c5b49fcdfe835b6ef9762fa583c +md5=6a59800791be74079cf2ade421526289 +cksum=1870577652 upstream_url=https://pypi.io/packages/source/n/nbclient/nbclient-VERSION.tar.gz diff --git a/build/pkgs/nbclient/package-version.txt b/build/pkgs/nbclient/package-version.txt index d2b13eb644d..faef31a4357 100644 --- a/build/pkgs/nbclient/package-version.txt +++ b/build/pkgs/nbclient/package-version.txt @@ -1 +1 @@ -0.6.4 +0.7.0 From de39745601c2e627f5c844310a7099fa055347de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 1 Nov 2022 08:32:34 +0100 Subject: [PATCH 381/632] fix wrong change --- src/sage/rings/number_field/number_field.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index ccafb7e20be..b670965e0c9 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -568,7 +568,7 @@ def NumberField(polynomial, name=None, check=True, names=None, embedding=None, raise TypeError("NumberField() got an unexpected keyword argument '%s'" % key) if not (val is None or isinstance(val, list) and all(c is None for c in val)): raise NotImplementedError("Number field with prescribed %s is not implemented" % key) - if isinstance(polynomial, (list,tuple)): + if isinstance(polynomial, (list, tuple)): return NumberFieldTower(polynomial, names=name, check=check, embeddings=embedding, latex_names=latex_name, assume_disc_small=assume_disc_small, maximize_at_primes=maximize_at_primes, structures=structure) return NumberField_version2(polynomial=polynomial, name=name, check=check, embedding=embedding, latex_name=latex_name, assume_disc_small=assume_disc_small, maximize_at_primes=maximize_at_primes, structure=structure) @@ -655,7 +655,7 @@ def create_key_and_extra_args(self, polynomial, name, check, embedding, latex_na polynomial = polynomial.change_ring(polynomial.base_ring().fraction_field()) # normalize embedding - if isinstance(embedding, (list,tuple)): + if isinstance(embedding, (list, tuple)): if len(embedding) != 1: raise TypeError("embedding must be a list of length 1") embedding = embedding[0] @@ -681,7 +681,7 @@ def create_key_and_extra_args(self, polynomial, name, check, embedding, latex_na raise TypeError("structure must be a list of length 1") structure = structure[0] - return (polynomial.base_ring(), polynomial, name, embedding, latex_name, maximize_at_primes, assume_disc_small, structure), {"check":check} + return (polynomial.base_ring(), polynomial, name, embedding, latex_name, maximize_at_primes, assume_disc_small, structure), {"check": check} def create_object(self, version, key, check): r""" @@ -10808,7 +10808,7 @@ def _repr_(self): sage: CyclotomicField(400)._repr_() 'Cyclotomic Field of order 400 and degree 160' """ - n = self._n + n = self.__n return f"Cyclotomic Field of order {n} and degree {self.degree()}" def _n(self): @@ -11952,7 +11952,7 @@ def __init__(self, polynomial, name=None, latex_name=None, check=True, embedding # we must set the flag _standard_embedding *before* any element creation # Note that in the following code, no element is built. if self.coerce_embedding() is not None and CDF.has_coerce_map_from(self): - rootD = CDF(number_field_element_quadratic.NumberFieldElement_quadratic(self, (QQ(0),QQ(1)))) + rootD = CDF(number_field_element_quadratic.NumberFieldElement_quadratic(self, (QQ(0), QQ(1)))) if D > 0: self._standard_embedding = rootD.real() > 0 else: @@ -12548,7 +12548,7 @@ def is_real_place(v): return False -def _splitting_classes_gens_(K,m,d): +def _splitting_classes_gens_(K, m, d): r""" Given a number field `K` of conductor `m` and degree `d`, this returns a set of multiplicative generators of the @@ -12597,7 +12597,7 @@ def map_Zmstar_to_Zm(h): Hgens = [] H = Zmstar.subgroup([]) p = 0 - Horder = arith.euler_phi(m)/d + Horder = arith.euler_phi(m) / d for g in Zmstar: if H.order() == Horder: break From 5f2315fa0bb94cb19f0c53268f6676b29ee6e602 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 1 Nov 2022 00:32:43 -0700 Subject: [PATCH 382/632] build/pkgs/tornado: Update to 6.2 --- build/pkgs/tornado/checksums.ini | 6 +++--- build/pkgs/tornado/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/tornado/checksums.ini b/build/pkgs/tornado/checksums.ini index e0e818876c0..9f69d85d5dc 100644 --- a/build/pkgs/tornado/checksums.ini +++ b/build/pkgs/tornado/checksums.ini @@ -1,5 +1,5 @@ tarball=tornado-VERSION.tar.gz -sha1=5fc7bd2ccb94efb8fdc7c9438eca454f2f5bab9b -md5=f324f5e7607798552359d6ab054c4321 -cksum=3079653030 +sha1=2fa6cbd83ebafad83f49e89fbd5bbd20c42bbdc9 +md5=32fbad606b439c3e1bf4e79d4e872741 +cksum=3183867326 upstream_url=https://pypi.io/packages/source/t/tornado/tornado-VERSION.tar.gz diff --git a/build/pkgs/tornado/package-version.txt b/build/pkgs/tornado/package-version.txt index a435f5a56fa..0cda48ac61e 100644 --- a/build/pkgs/tornado/package-version.txt +++ b/build/pkgs/tornado/package-version.txt @@ -1 +1 @@ -6.1 +6.2 From 31014d934889b91e1d7ec64efdf579ab2e347227 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 1 Nov 2022 00:35:47 -0700 Subject: [PATCH 383/632] build/pkgs/terminado: Update to 0.17.0 --- build/pkgs/terminado/checksums.ini | 6 +++--- build/pkgs/terminado/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/terminado/checksums.ini b/build/pkgs/terminado/checksums.ini index 794b2cda764..27333946973 100644 --- a/build/pkgs/terminado/checksums.ini +++ b/build/pkgs/terminado/checksums.ini @@ -1,5 +1,5 @@ tarball=terminado-VERSION.tar.gz -sha1=b0ff75a4f024dc07c9a819c1a63d75908624a5d3 -md5=e3fe92b48b3885ffa19b9890ed41578f -cksum=1261401246 +sha1=e2e37fe16c03d5bb6ab035c78e99a89bc742384c +md5=cf5f5f7dd1ece772f16013ad355b75e1 +cksum=2626848318 upstream_url=https://pypi.io/packages/source/t/terminado/terminado-VERSION.tar.gz diff --git a/build/pkgs/terminado/package-version.txt b/build/pkgs/terminado/package-version.txt index a5510516948..c5523bd09b1 100644 --- a/build/pkgs/terminado/package-version.txt +++ b/build/pkgs/terminado/package-version.txt @@ -1 +1 @@ -0.15.0 +0.17.0 From 8c1f91d8ac5ea3294abded103f6ece927c3526ba Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 1 Nov 2022 00:39:01 -0700 Subject: [PATCH 384/632] build/pkgs/jupyter_client: Update to 7.4.4 --- build/pkgs/jupyter_client/checksums.ini | 6 +++--- build/pkgs/jupyter_client/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/jupyter_client/checksums.ini b/build/pkgs/jupyter_client/checksums.ini index 6f89053ffab..7e31fb35fc6 100644 --- a/build/pkgs/jupyter_client/checksums.ini +++ b/build/pkgs/jupyter_client/checksums.ini @@ -1,5 +1,5 @@ tarball=jupyter_client-VERSION.tar.gz -sha1=99e1ce34f9022acbd3cc301d501ff83099f559ff -md5=3863b317e78ba701f5b844c2221a101f -cksum=1126052456 +sha1=0a9446eda476e3614d4509db0646ae5a89f6b492 +md5=481db492a8a0d16022c49481438e6285 +cksum=3316985535 upstream_url=https://pypi.io/packages/source/j/jupyter_client/jupyter_client-VERSION.tar.gz diff --git a/build/pkgs/jupyter_client/package-version.txt b/build/pkgs/jupyter_client/package-version.txt index c968a5762bf..4e61aeef901 100644 --- a/build/pkgs/jupyter_client/package-version.txt +++ b/build/pkgs/jupyter_client/package-version.txt @@ -1 +1 @@ -7.3.4 +7.4.4 From 1721d58ac35e654f2acd6df72c3cb557b98245d4 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 1 Nov 2022 00:42:31 -0700 Subject: [PATCH 385/632] build/pkgs/jupyter_core: Update to 4.11.2 --- build/pkgs/jupyter_core/checksums.ini | 6 +++--- build/pkgs/jupyter_core/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/jupyter_core/checksums.ini b/build/pkgs/jupyter_core/checksums.ini index 88fbd83a188..5a49b040b98 100644 --- a/build/pkgs/jupyter_core/checksums.ini +++ b/build/pkgs/jupyter_core/checksums.ini @@ -1,5 +1,5 @@ tarball=jupyter_core-VERSION.tar.gz -sha1=f5db3f86f2cf40c9371bfa16a783a79a3502cac4 -md5=812d7410ffb4b671b23a68ef4bf40c12 -cksum=2575874942 +sha1=6e48f90477c11ad41b9404732a2bdcb485f4e630 +md5=84d207d4c48513a2b87ff2ed508beb98 +cksum=2072829465 upstream_url=https://pypi.io/packages/source/j/jupyter_core/jupyter_core-VERSION.tar.gz diff --git a/build/pkgs/jupyter_core/package-version.txt b/build/pkgs/jupyter_core/package-version.txt index 2da4316236a..4f89fb96069 100644 --- a/build/pkgs/jupyter_core/package-version.txt +++ b/build/pkgs/jupyter_core/package-version.txt @@ -1 +1 @@ -4.10.0 +4.11.2 From 9d723b509ff96e20a815feed535b98dc10575308 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 1 Nov 2022 00:45:39 -0700 Subject: [PATCH 386/632] build/pkgs/nbconvert: Update to 6.5.3 --- build/pkgs/nbconvert/checksums.ini | 6 +++--- build/pkgs/nbconvert/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/nbconvert/checksums.ini b/build/pkgs/nbconvert/checksums.ini index e10e8b224e8..b3512a1d552 100644 --- a/build/pkgs/nbconvert/checksums.ini +++ b/build/pkgs/nbconvert/checksums.ini @@ -1,5 +1,5 @@ tarball=nbconvert-VERSION.tar.gz -sha1=061c9f18039aa8b6c5f83fc2281cbf8f6c4f3198 -md5=486a48c4dc3986e8801058273964d189 -cksum=183380977 +sha1=2bbadb417b371c8ae46163007e5c4c1c27204f0e +md5=6bb8f731af28bbdc7502f6d524d7d347 +cksum=2406762264 upstream_url=https://pypi.io/packages/source/n/nbconvert/nbconvert-VERSION.tar.gz diff --git a/build/pkgs/nbconvert/package-version.txt b/build/pkgs/nbconvert/package-version.txt index f22d756da39..db0785f2737 100644 --- a/build/pkgs/nbconvert/package-version.txt +++ b/build/pkgs/nbconvert/package-version.txt @@ -1 +1 @@ -6.5.0 +6.5.3 From 5b0d5f9c46036f052d9cb76524781ab9f7b04c3f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 1 Nov 2022 00:50:48 -0700 Subject: [PATCH 387/632] build/pkgs/nbconvert: Update to 7.2.3, change to wheel package --- build/pkgs/nbconvert/SPKG.rst | 11 +++++++++++ build/pkgs/nbconvert/checksums.ini | 10 +++++----- build/pkgs/nbconvert/package-version.txt | 2 +- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/build/pkgs/nbconvert/SPKG.rst b/build/pkgs/nbconvert/SPKG.rst index 98ee0393814..be08606f92a 100644 --- a/build/pkgs/nbconvert/SPKG.rst +++ b/build/pkgs/nbconvert/SPKG.rst @@ -6,3 +6,14 @@ Description jupyter nbconvert converts notebooks to various other formats via Jinja templates. + +License +------- + +BSD + +Upstream Contact +---------------- + +https://pypi.org/project/nbconvert/ + diff --git a/build/pkgs/nbconvert/checksums.ini b/build/pkgs/nbconvert/checksums.ini index b3512a1d552..1c3926c6ce3 100644 --- a/build/pkgs/nbconvert/checksums.ini +++ b/build/pkgs/nbconvert/checksums.ini @@ -1,5 +1,5 @@ -tarball=nbconvert-VERSION.tar.gz -sha1=2bbadb417b371c8ae46163007e5c4c1c27204f0e -md5=6bb8f731af28bbdc7502f6d524d7d347 -cksum=2406762264 -upstream_url=https://pypi.io/packages/source/n/nbconvert/nbconvert-VERSION.tar.gz +tarball=nbconvert-VERSION-py3-none-any.whl +sha1=de3dd5e475d84c2d143c03dfc22bfc490c03092f +md5=942dd716bd6976c58fdbcfec97bfbe20 +cksum=610202196 +upstream_url=https://pypi.io/packages/py3/n/nbconvert/nbconvert-VERSION-py3-none-any.whl diff --git a/build/pkgs/nbconvert/package-version.txt b/build/pkgs/nbconvert/package-version.txt index db0785f2737..429dc57af3a 100644 --- a/build/pkgs/nbconvert/package-version.txt +++ b/build/pkgs/nbconvert/package-version.txt @@ -1 +1 @@ -6.5.3 +7.2.3 From d91e7c085bd64c0933b517de96106b65fa5c12a7 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 1 Nov 2022 00:53:17 -0700 Subject: [PATCH 388/632] build/pkgs/mistune: Update to 2.0.4 --- build/pkgs/mistune/checksums.ini | 6 +++--- build/pkgs/mistune/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/mistune/checksums.ini b/build/pkgs/mistune/checksums.ini index fe33dfb5b7d..b8b60db1d7b 100644 --- a/build/pkgs/mistune/checksums.ini +++ b/build/pkgs/mistune/checksums.ini @@ -1,5 +1,5 @@ tarball=mistune-VERSION.tar.gz -sha1=fd210c038fa7b0f2dffad6847b17dc139e7dd9fe -md5=fb6ab174ece938dea09f8b2adad771e4 -cksum=4120783541 +sha1=c15d02c98d04a3e615c3c1932d1b9a3b1759067a +md5=a4437edb22cf6519a7c61730fecb1a3f +cksum=2925260381 upstream_url=https://pypi.io/packages/source/m/mistune/mistune-VERSION.tar.gz diff --git a/build/pkgs/mistune/package-version.txt b/build/pkgs/mistune/package-version.txt index b60d71966ae..2165f8f9b6a 100644 --- a/build/pkgs/mistune/package-version.txt +++ b/build/pkgs/mistune/package-version.txt @@ -1 +1 @@ -0.8.4 +2.0.4 From 4fc91e11292ce137885d4fc84135cd351d52a6fb Mon Sep 17 00:00:00 2001 From: Marc Mezzarobba Date: Wed, 2 Nov 2022 11:38:10 +0100 Subject: [PATCH 389/632] #34714 fast path for factoring monomials over number fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trivial instances of univariate polynomial factorization over complicated number fields are extremely slow because Sage starts by trying to compute an integral basis. We introduce a fast path that handles monomials — most importantly, the identity polynomial. Ideally, we should do something similar over arbitrary rings, but this is currently difficult to implement in a generic because the factor() method of constants is under-specified. In particular, calling factor() on a rational number factors the numerator and denominator, which we certainly do not want when factoring the leading coefficient of an element of ℚ[x]. In the number field case, a possible generalization would be to detect constant multiples of polynomials that factor over ℚ. Not sure if this is a good idea. --- src/sage/rings/number_field/number_field.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index a2f0e3d047c..62f5af5b6a4 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -10509,11 +10509,21 @@ def _factor_univariate_polynomial(self, poly, **kwargs): sage: x = polygen(K,'x') sage: factor(x*x+4) # indirect doctest (x - 2*i) * (x + 2*i) + + TESTS:: + + sage: with seed(0): + ....: P. = NumberField(QQ['a'].random_element(30,10^100), 'a')[] + sage: (3*y^4/4).factor() + (3/4) * y^4 """ if self.degree() == 1: factors = poly.change_ring(QQ).factor() return Factorization([(p.change_ring(self), e) for p, e in factors], self(factors.unit())) + elif poly.is_term(): + return Factorization([(poly.parent().gen(), poly.degree())], + poly.leading_coefficient()) # Convert the polynomial we want to factor to PARI f = poly._pari_with_name() From b694ec2e30b611701f18b553e74c1dc6fef420ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 2 Nov 2022 19:25:26 +0100 Subject: [PATCH 390/632] remove old commented code, fix more pep8 --- src/sage/rings/number_field/number_field.py | 140 ++++---------------- 1 file changed, 26 insertions(+), 114 deletions(-) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index b670965e0c9..95e468f16cd 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -2077,7 +2077,7 @@ def structure(self): """ if self._structure is None: f = self.hom(self) - return f,f + return f, f else: return self._structure.create_structure(self) @@ -2635,7 +2635,7 @@ def quadratic_defect(self, a, p, check=True): return Infinity return v # The dyadic case - s = self(F.lift((1/F(a)).sqrt())) + s = self(F.lift((1 / F(a)).sqrt())) a = self(s**2) * a u = self(4).valuation(p) w = (a - 1).valuation(p) @@ -3551,7 +3551,7 @@ def ideal(self, *gens, **kwds): except ValueError: return sage.rings.ring.Ring.ideal(self, gens, **kwds) - def idealchinese(self,ideals,residues): + def idealchinese(self, ideals, residues): r""" Return a solution of the Chinese Remainder Theorem problem for ideals in a number field. @@ -3599,16 +3599,15 @@ def idealchinese(self,ideals,residues): ....: for P,k in I.factor() ....: ) True - """ factorizations = [I.factor() for I in ideals] - y = [a for a,f in zip(residues,factorizations) for _ in f] + y = [a for a, f in zip(residues, factorizations) for _ in f] x = pari.Mat([ - pari.Col([p.pari_prime(),k]) + pari.Col([p.pari_prime(), k]) for f in factorizations - for p,k in f + for p, k in f ]).mattranspose() - r = self.pari_nf().idealchinese(x,y) + r = self.pari_nf().idealchinese(x, y) return self(r) def fractional_ideal(self, *gens, **kwds): @@ -6921,10 +6920,10 @@ def trace_pairing(self, v): import sage.matrix.matrix_space A = sage.matrix.matrix_space.MatrixSpace(self.base_ring(), len(v))(0) for i in range(len(v)): - for j in range(i,len(v)): + for j in range(i, len(v)): t = (self(v[i]*v[j])).trace() - A[i,j] = t - A[j,i] = t + A[i, j] = t + A[j, i] = t return A def uniformizer(self, P, others="positive"): @@ -7166,7 +7165,7 @@ def unit_group(self, proof=None): except AttributeError: pass - U = UnitGroup(self,proof) + U = UnitGroup(self, proof) if proof: self._unit_group = U else: @@ -7292,7 +7291,7 @@ def S_unit_group(self, proof=None, S=None): except KeyError: pass - U = UnitGroup(self,proof,S=S) + U = UnitGroup(self, proof, S=S) if proof: self._S_unit_group_cache[S] = U else: @@ -7650,8 +7649,8 @@ def solve_CRT(self, reslist, Ilist, check=True): raise ArithmeticError("ideals in solve_CRT() must be pairwise coprime") x = ((1-r)*reslist[0]+r*reslist[1]).mod(prod(Ilist)) else: # n>2;, use induction / recursion - x = self.solve_CRT([reslist[0],self.solve_CRT(reslist[1:],Ilist[1:])], - [Ilist[0],prod(Ilist[1:])], check=check) + x = self.solve_CRT([reslist[0], self.solve_CRT(reslist[1:], Ilist[1:])], + [Ilist[0], prod(Ilist[1:])], check=check) if check and not all(x - xi in Ii for xi, Ii in zip(reslist, Ilist)): raise RuntimeError("Error in number field solve_CRT()") return self(x) @@ -8188,7 +8187,7 @@ def _coerce_from_other_number_field(self, x): # they are equal. The factor 128 is arbitrary, it is an extra # safety margin. eps = (log_root_diff - 7.0).exp2() - are_roots_equal = lambda a,b: (a-b).abs() <= eps + are_roots_equal = lambda a, b: (a-b).abs() <= eps if F is CC: # Adjust the precision of F, sufficient to represent all # the temporaries in the computation with a precision @@ -8558,8 +8557,8 @@ def optimized_subfields(self, degree=0, name=None, both_maps=True): Defn: a |--> -1/2*a6^3 + a6^2 - 1/2*a6) ] """ - return self._subfields_helper(degree=degree,name=name, - both_maps=both_maps,optimize=True) + return self._subfields_helper(degree=degree, name=name, + both_maps=both_maps, optimize=True) def change_names(self, names): r""" @@ -9294,8 +9293,8 @@ def minkowski_embedding(self, B=None, prec=None): for i in range(s): z = places[r+i](B[col]) - d[(r+2*i,col)] = z.real()*sqrt2 - d[(r+2*i+1,col)] = z.imag()*sqrt2 + d[(r+2*i, col)] = z.real()*sqrt2 + d[(r+2*i+1, col)] = z.imag()*sqrt2 return sage.matrix.all.matrix(d) @@ -10315,7 +10314,7 @@ def phi(x): assert phi(a) == v, "oops" return a - def hilbert_conductor(self,a,b): + def hilbert_conductor(self, a, b): """ This is the product of all (finite) primes where the Hilbert symbol is -1. What is the same, this is the (reduced) discriminant of the quaternion @@ -10347,7 +10346,7 @@ def hilbert_conductor(self,a,b): a, b = self(a), self(b) d = self.ideal(1) for p in set(self.ideal(2).prime_factors()).union(self.ideal(a).prime_factors()).union(self.ideal(b).prime_factors()): - if self.hilbert_symbol(a,b,p) == -1: + if self.hilbert_symbol(a, b, p) == -1: d *= p return d @@ -11143,103 +11142,16 @@ def _element_constructor_(self, x, check=True): elif (sage.interfaces.gap.is_GapElement(x) or isinstance(x, sage.libs.gap.element.GapElement)): return self._coerce_from_gap(x) - elif isinstance(x,str): + elif isinstance(x, str): return self._convert_from_str(x) # late import because of speed from sage.rings.universal_cyclotomic_field import UniversalCyclotomicFieldElement - if isinstance(x,UniversalCyclotomicFieldElement): + if isinstance(x, UniversalCyclotomicFieldElement): return x.to_cyclotomic_field(self) else: return self._convert_non_number_field_element(x) - # TODO: - # The following is very nice and much more flexible / powerful. - # However, it is simply not *consistent*, since it totally - # breaks the doctests in eisenstein_submodule.py. - # FIX THIS. - -# def _will_be_better_coerce_from_other_cyclotomic_field(self, x, only_canonical=False): -# """ -# Coerce an element x of a cyclotomic field into self, if at all possible. - -# INPUT: -# x -- number field element - -# only_canonical -- bool (default: False); Attempt to work, -# even in some cases when x is not in a subfield of -# the cyclotomics (as long as x is a root of unity). - -# EXAMPLES:: - -# sage: k5 = CyclotomicField(5) -# sage: k3 = CyclotomicField(3) -# sage: k15 = CyclotomicField(15) -# sage: k15._coerce_from_other_cyclotomic_field(k3.gen()) -# zeta15^5 -# sage: k15._coerce_from_other_cyclotomic_field(k3.gen()^2 + 17/3) -# -zeta15^5 + 14/3 -# sage: k3._coerce_from_other_cyclotomic_field(k15.gen()^5) -# zeta3 -# sage: k3._coerce_from_other_cyclotomic_field(-2/3 * k15.gen()^5 + 2/3) -# -2/3*zeta3 + 2/3 -# """ - -# K = x.parent() - -# if K is self: -# return x -# n = K.zeta_order() -# m = self.zeta_order() - -# self_gen = self.gen() - -# if m % n == 0: # easy case -# # pass this off to a method in the element class -# # it can be done very quickly and easily by the cython<->NTL -# # interface there -# return x._lift_cyclotomic_element(self) - -# # Whatever happens below, it has to be consistent with -# # zeta_r |---> (zeta_s)^m - -# if m % 2 and not n%2: -# m *= 2 -# self_gen = -self_gen - -# if only_canonical and m % n: -# raise TypeError, "no canonical coercion" - -# if not is_CyclotomicField(K): -# raise TypeError, "x must be in a cyclotomic field" - -# v = x.list() - -# # Find the smallest power r >= 1 of the generator g of K that is in self, -# # i.e., find the smallest r such that g^r has order dividing m. - -# d = sage.arith.all.gcd(m,n) -# r = n // d - -# # Since we use the power basis for cyclotomic fields, if every -# # v[i] with i not divisible by r is 0, then we're good. - -# # If h generates self and has order m, then the element g^r -# # maps to the power of self of order gcd(m,n)., i.e., h^(m/gcd(m,n)) -# # -# z = self_gen**(m // d) -# w = self(1) - -# a = self(0) -# for i in range(len(v)): -# if i%r: -# if v[i]: -# raise TypeError, "element does not belong to cyclotomic field" -# else: -# a += w*v[i] -# w *= z -# return a - def _coerce_from_other_cyclotomic_field(self, x, only_canonical=False): """ Coerce an element x of a cyclotomic field into self, if at all @@ -11345,7 +11257,7 @@ def _coerce_from_gap(self, x): return self(QQ(x)) coeffs = x.CoeffsCyc(self.__n) zeta = self.gen() - return sum(QQ(c)*zeta**i for i,c in enumerate(coeffs)) + return sum(QQ(c) * zeta**i for i, c in enumerate(coeffs)) def _Hom_(self, codomain, cat=None): """ @@ -11496,7 +11408,7 @@ def embeddings(self, K): # zeta not defined return super().embeddings(K) else: - X = [m for m in range(n) if arith.gcd(m,n) == 1] + X = (m for m in range(n) if arith.gcd(m, n) == 1) v = [self.hom([z**i], check=False) for i in X] else: v = [] @@ -11744,7 +11656,7 @@ def _multiplicative_order_table(self): zeta = self.zeta(n) # todo: this desperately needs to be optimized!!! for i in range(n): - t[x.polynomial()] = n//arith.GCD(m,n) # multiplicative_order of (zeta_n)**m + t[x.polynomial()] = n // arith.GCD(m, n) # multiplicative_order of (zeta_n)**m x *= zeta m += 1 self.__multiplicative_order_table = t From 93dc05ccd9a6bf3358e57902cf84da87b0d91339 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Thu, 3 Nov 2022 14:21:10 +0800 Subject: [PATCH 391/632] prevent assertion failure in _discrete_log_pgroup when group is trivial --- .../additive_abelian_wrapper.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/sage/groups/additive_abelian/additive_abelian_wrapper.py b/src/sage/groups/additive_abelian/additive_abelian_wrapper.py index 7c0aec05ff8..1894ac24bc1 100644 --- a/src/sage/groups/additive_abelian/additive_abelian_wrapper.py +++ b/src/sage/groups/additive_abelian/additive_abelian_wrapper.py @@ -371,6 +371,9 @@ def discrete_log(self, x, gens=None): y = cofactor * x pvals = [o.valuation(p) for o in ords] + if not any(pvals): + continue + plog = _discrete_log_pgroup(p, pvals, pgens, y) for i, (r, v) in enumerate(zip(plog, pvals)): @@ -472,7 +475,7 @@ def _element_constructor_(self, x, check=False): def _discrete_log_pgroup(p, vals, aa, b): r""" Attempt to express an element of p-power order in terms of - generators of a p-subgroup of this group. + generators of a nontrivial p-subgroup of this group. Used as a subroutine in :meth:`discrete_log`. @@ -491,6 +494,18 @@ def _discrete_log_pgroup(p, vals, aa, b): sage: from sage.groups.additive_abelian.additive_abelian_wrapper import _discrete_log_pgroup sage: _discrete_log_pgroup(5, [1,2,4,4], gs, a + 17*b + 123*c + 456*d) (1, 17, 123, 456) + + TESTS: + + Check for :trac:`34716`:: + + sage: E = EllipticCurve(GF(487^2), [311,205]) + sage: G = E.abelian_group().torsion_subgroup(42) + sage: G.invariants() + (6, 42) + sage: P, Q = G.torsion_subgroup(6).gens() + sage: G.discrete_log(2*P + 3*Q, [P, Q]) # indirect doctest + (2, 3) """ from itertools import product as iproduct From a155a98e4ba797bc0286dd7e29d852e8c7653055 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Mon, 4 Jul 2022 12:17:25 +0800 Subject: [PATCH 392/632] move carmichael_lambda to sage.arith.misc --- src/sage/arith/misc.py | 154 ++++++++++++++++++++++++++++++++++++++++ src/sage/crypto/util.py | 152 --------------------------------------- 2 files changed, 154 insertions(+), 152 deletions(-) diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index 1bd4d4c6a1f..071d99c3bf2 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -3134,6 +3134,160 @@ def plot(self, xmin=1, xmax=50, pointsize=30, rgbcolor=(0, 0, 1), euler_phi = Euler_Phi() +def carmichael_lambda(n): + r""" + Return the Carmichael function of a positive integer ``n``. + + The Carmichael function of `n`, denoted `\lambda(n)`, is the smallest + positive integer `k` such that `a^k \equiv 1 \pmod{n}` for all + `a \in \ZZ/n\ZZ` satisfying `\gcd(a, n) = 1`. Thus, `\lambda(n) = k` + is the exponent of the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. + + INPUT: + + - ``n`` -- a positive integer. + + OUTPUT: + + - The Carmichael function of ``n``. + + ALGORITHM: + + If `n = 2, 4` then `\lambda(n) = \varphi(n)`. Let `p \geq 3` be an odd + prime and let `k` be a positive integer. Then + `\lambda(p^k) = p^{k - 1}(p - 1) = \varphi(p^k)`. If `k \geq 3`, then + `\lambda(2^k) = 2^{k - 2}`. Now consider the case where `n > 3` is + composite and let `n = p_1^{k_1} p_2^{k_2} \cdots p_t^{k_t}` be the + prime factorization of `n`. Then + + .. MATH:: + + \lambda(n) + = \lambda(p_1^{k_1} p_2^{k_2} \cdots p_t^{k_t}) + = \text{lcm}(\lambda(p_1^{k_1}), \lambda(p_2^{k_2}), \dots, \lambda(p_t^{k_t})) + + EXAMPLES: + + The Carmichael function of all positive integers up to and including 10:: + + sage: from sage.arith.misc import carmichael_lambda + sage: list(map(carmichael_lambda, [1..10])) + [1, 1, 2, 2, 4, 2, 6, 2, 6, 4] + + The Carmichael function of the first ten primes:: + + sage: list(map(carmichael_lambda, primes_first_n(10))) + [1, 2, 4, 6, 10, 12, 16, 18, 22, 28] + + Cases where the Carmichael function is equivalent to the Euler phi + function:: + + sage: carmichael_lambda(2) == euler_phi(2) + True + sage: carmichael_lambda(4) == euler_phi(4) + True + sage: p = random_prime(1000, lbound=3, proof=True) + sage: k = randint(1, 1000) + sage: carmichael_lambda(p^k) == euler_phi(p^k) + True + + A case where `\lambda(n) \neq \varphi(n)`:: + + sage: k = randint(3, 1000) + sage: carmichael_lambda(2^k) == 2^(k - 2) + True + sage: carmichael_lambda(2^k) == 2^(k - 2) == euler_phi(2^k) + False + + Verifying the current implementation of the Carmichael function using + another implementation. The other implementation that we use for + verification is an exhaustive search for the exponent of the + multiplicative group `(\ZZ/n\ZZ)^{\ast}`. :: + + sage: from sage.arith.misc import carmichael_lambda + sage: n = randint(1, 500) + sage: c = carmichael_lambda(n) + sage: def coprime(n): + ....: return [i for i in range(n) if gcd(i, n) == 1] + sage: def znpower(n, k): + ....: L = coprime(n) + ....: return list(map(power_mod, L, [k]*len(L), [n]*len(L))) + sage: def my_carmichael(n): + ....: if n == 1: + ....: return 1 + ....: for k in range(1, n): + ....: L = znpower(n, k) + ....: ones = [1] * len(L) + ....: T = [L[i] == ones[i] for i in range(len(L))] + ....: if all(T): + ....: return k + sage: c == my_carmichael(n) + True + + Carmichael's theorem states that `a^{\lambda(n)} \equiv 1 \pmod{n}` + for all elements `a` of the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. + Here, we verify Carmichael's theorem. :: + + sage: from sage.arith.misc import carmichael_lambda + sage: n = randint(2, 1000) + sage: c = carmichael_lambda(n) + sage: ZnZ = IntegerModRing(n) + sage: M = ZnZ.list_of_elements_of_multiplicative_group() + sage: ones = [1] * len(M) + sage: P = [power_mod(a, c, n) for a in M] + sage: P == ones + True + + TESTS: + + The input ``n`` must be a positive integer:: + + sage: from sage.arith.misc import carmichael_lambda + sage: carmichael_lambda(0) + Traceback (most recent call last): + ... + ValueError: Input n must be a positive integer. + sage: carmichael_lambda(randint(-10, 0)) + Traceback (most recent call last): + ... + ValueError: Input n must be a positive integer. + + Bug reported in :trac:`8283`:: + + sage: from sage.arith.misc import carmichael_lambda + sage: type(carmichael_lambda(16)) + + + REFERENCES: + + - :wikipedia:`Carmichael_function` + """ + n = Integer(n) + # sanity check + if n < 1: + raise ValueError("Input n must be a positive integer.") + + L = n.factor() + t = [] + + # first get rid of the prime factor 2 + if n & 1 == 0: + e = L[0][1] + L = L[1:] # now, n = 2**e * L.value() + if e < 3: # for 1 <= k < 3, lambda(2**k) = 2**(k - 1) + e = e - 1 + else: # for k >= 3, lambda(2**k) = 2**(k - 2) + e = e - 2 + t.append(1 << e) # 2**e + + # then other prime factors + t += [p**(k - 1) * (p - 1) for p, k in L] + + # finish the job + from .functions import lcm + return lcm(t) + + def crt(a, b, m=None, n=None): r""" Return a solution to a Chinese Remainder Theorem problem. diff --git a/src/sage/crypto/util.py b/src/sage/crypto/util.py index 42b262c9858..cc149941777 100644 --- a/src/sage/crypto/util.py +++ b/src/sage/crypto/util.py @@ -255,158 +255,6 @@ def bin_to_ascii(B): A.append(chr(ascii_integer(b[8*i: 8*(i+1)]))) return "".join(A) -def carmichael_lambda(n): - r""" - Return the Carmichael function of a positive integer ``n``. - - The Carmichael function of `n`, denoted `\lambda(n)`, is the smallest - positive integer `k` such that `a^k \equiv 1 \pmod{n}` for all - `a \in \ZZ/n\ZZ` satisfying `\gcd(a, n) = 1`. Thus, `\lambda(n) = k` - is the exponent of the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. - - INPUT: - - - ``n`` -- a positive integer. - - OUTPUT: - - - The Carmichael function of ``n``. - - ALGORITHM: - - If `n = 2, 4` then `\lambda(n) = \varphi(n)`. Let `p \geq 3` be an odd - prime and let `k` be a positive integer. Then - `\lambda(p^k) = p^{k - 1}(p - 1) = \varphi(p^k)`. If `k \geq 3`, then - `\lambda(2^k) = 2^{k - 2}`. Now consider the case where `n > 3` is - composite and let `n = p_1^{k_1} p_2^{k_2} \cdots p_t^{k_t}` be the - prime factorization of `n`. Then - - .. MATH:: - - \lambda(n) - = \lambda(p_1^{k_1} p_2^{k_2} \cdots p_t^{k_t}) - = \text{lcm}(\lambda(p_1^{k_1}), \lambda(p_2^{k_2}), \dots, \lambda(p_t^{k_t})) - - EXAMPLES: - - The Carmichael function of all positive integers up to and including 10:: - - sage: from sage.crypto.util import carmichael_lambda - sage: list(map(carmichael_lambda, [1..10])) - [1, 1, 2, 2, 4, 2, 6, 2, 6, 4] - - The Carmichael function of the first ten primes:: - - sage: list(map(carmichael_lambda, primes_first_n(10))) - [1, 2, 4, 6, 10, 12, 16, 18, 22, 28] - - Cases where the Carmichael function is equivalent to the Euler phi - function:: - - sage: carmichael_lambda(2) == euler_phi(2) - True - sage: carmichael_lambda(4) == euler_phi(4) - True - sage: p = random_prime(1000, lbound=3, proof=True) - sage: k = randint(1, 1000) - sage: carmichael_lambda(p^k) == euler_phi(p^k) - True - - A case where `\lambda(n) \neq \varphi(n)`:: - - sage: k = randint(3, 1000) - sage: carmichael_lambda(2^k) == 2^(k - 2) - True - sage: carmichael_lambda(2^k) == 2^(k - 2) == euler_phi(2^k) - False - - Verifying the current implementation of the Carmichael function using - another implementation. The other implementation that we use for - verification is an exhaustive search for the exponent of the - multiplicative group `(\ZZ/n\ZZ)^{\ast}`. :: - - sage: from sage.crypto.util import carmichael_lambda - sage: n = randint(1, 500) - sage: c = carmichael_lambda(n) - sage: def coprime(n): - ....: return [i for i in range(n) if gcd(i, n) == 1] - sage: def znpower(n, k): - ....: L = coprime(n) - ....: return list(map(power_mod, L, [k]*len(L), [n]*len(L))) - sage: def my_carmichael(n): - ....: if n == 1: - ....: return 1 - ....: for k in range(1, n): - ....: L = znpower(n, k) - ....: ones = [1] * len(L) - ....: T = [L[i] == ones[i] for i in range(len(L))] - ....: if all(T): - ....: return k - sage: c == my_carmichael(n) - True - - Carmichael's theorem states that `a^{\lambda(n)} \equiv 1 \pmod{n}` - for all elements `a` of the multiplicative group `(\ZZ/n\ZZ)^{\ast}`. - Here, we verify Carmichael's theorem. :: - - sage: from sage.crypto.util import carmichael_lambda - sage: n = randint(2, 1000) - sage: c = carmichael_lambda(n) - sage: ZnZ = IntegerModRing(n) - sage: M = ZnZ.list_of_elements_of_multiplicative_group() - sage: ones = [1] * len(M) - sage: P = [power_mod(a, c, n) for a in M] - sage: P == ones - True - - TESTS: - - The input ``n`` must be a positive integer:: - - sage: from sage.crypto.util import carmichael_lambda - sage: carmichael_lambda(0) - Traceback (most recent call last): - ... - ValueError: Input n must be a positive integer. - sage: carmichael_lambda(randint(-10, 0)) - Traceback (most recent call last): - ... - ValueError: Input n must be a positive integer. - - Bug reported in :trac:`8283`:: - - sage: from sage.crypto.util import carmichael_lambda - sage: type(carmichael_lambda(16)) - - - REFERENCES: - - - :wikipedia:`Carmichael_function` - """ - n = Integer(n) - # sanity check - if n < 1: - raise ValueError("Input n must be a positive integer.") - - L = n.factor() - t = [] - - # first get rid of the prime factor 2 - if n & 1 == 0: - e = L[0][1] - L = L[1:] # now, n = 2**e * L.value() - if e < 3: # for 1 <= k < 3, lambda(2**k) = 2**(k - 1) - e = e - 1 - else: # for k >= 3, lambda(2**k) = 2**(k - 2) - e = e - 2 - t.append(1 << e) # 2**e - - # then other prime factors - t += [p**(k - 1) * (p - 1) for p, k in L] - - # finish the job - return lcm(t) - def has_blum_prime(lbound, ubound): r""" From 846d280319df122e9649153a0d38e70294181585 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Mon, 4 Jul 2022 12:18:22 +0800 Subject: [PATCH 393/632] make carmichael_lambda() available by default --- src/sage/arith/all.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/arith/all.py b/src/sage/arith/all.py index d89f401f211..c93e9f05bc9 100644 --- a/src/sage/arith/all.py +++ b/src/sage/arith/all.py @@ -9,8 +9,8 @@ inverse_mod, get_gcd, get_inverse_mod, power_mod, rational_reconstruction, mqrr_rational_reconstruction, trial_division, factor, prime_divisors, odd_part, prime_to_m_part, - is_square, is_squarefree, euler_phi, crt, CRT, CRT_list, CRT_basis, - CRT_vectors, multinomial, multinomial_coefficients, + is_square, is_squarefree, euler_phi, carmichael_lambda, crt, CRT, + CRT_list, CRT_basis, CRT_vectors, multinomial, multinomial_coefficients, binomial, factorial, kronecker_symbol, kronecker, legendre_symbol, primitive_root, nth_prime, quadratic_residues, moebius, continuant, number_of_divisors, hilbert_symbol, hilbert_conductor, From ebf7e415eb73d2eb496d3debd7668e425333293c Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Mon, 4 Jul 2022 12:39:11 +0800 Subject: [PATCH 394/632] simplify power-of-2 handling in carmichael_lambda() --- src/sage/arith/misc.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index 071d99c3bf2..9caf22c1f06 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -33,6 +33,7 @@ from sage.rings.abc import RealField, ComplexField from sage.rings.fast_arith import arith_int, arith_llong, prime_range +from sage.arith.functions import LCM_list ################################################################## @@ -3272,20 +3273,16 @@ def carmichael_lambda(n): # first get rid of the prime factor 2 if n & 1 == 0: - e = L[0][1] - L = L[1:] # now, n = 2**e * L.value() - if e < 3: # for 1 <= k < 3, lambda(2**k) = 2**(k - 1) - e = e - 1 - else: # for k >= 3, lambda(2**k) = 2**(k - 2) - e = e - 2 - t.append(1 << e) # 2**e + two,e = L.pop(0) + assert two == 2 + k = e - 2 if e >= 3 else e - 1 + t.append(1 << k) # then other prime factors t += [p**(k - 1) * (p - 1) for p, k in L] # finish the job - from .functions import lcm - return lcm(t) + return LCM_list(t) def crt(a, b, m=None, n=None): From 37ec15490ada135df0d2e4377f7092603435f3d4 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Mon, 4 Jul 2022 12:41:05 +0800 Subject: [PATCH 395/632] deprecation for sage.crypto.carmichael_lambda --- src/sage/crypto/util.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sage/crypto/util.py b/src/sage/crypto/util.py index cc149941777..aa013f9aca4 100644 --- a/src/sage/crypto/util.py +++ b/src/sage/crypto/util.py @@ -25,6 +25,9 @@ from sage.rings.integer import Integer from sage.rings.finite_rings.integer_mod import Mod as mod +from sage.misc.lazy_import import lazy_import +lazy_import('sage.arith.misc', ('carmichael_lambda'), deprecation=34719) + def ascii_integer(B): r""" Return the ASCII integer corresponding to the binary string ``B``. From cf49d570110a48ff5238e5f894795e4739b45641 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Fri, 4 Nov 2022 15:30:18 +0800 Subject: [PATCH 396/632] some code cleanup around WeierstrassIsomorphism --- .../schemes/elliptic_curves/ell_generic.py | 54 +++--- .../elliptic_curves/weierstrass_morphism.py | 181 +++++++++--------- 2 files changed, 116 insertions(+), 119 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 154a581bc2a..948b5006912 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -2425,21 +2425,21 @@ def automorphisms(self, field=None): OUTPUT: - (list) A list of ``WeierstrassIsomorphism`` objects + (list) A list of :class:`~wm.WeierstrassIsomorphism` objects consisting of all the isomorphisms from the curve ``self`` to itself defined over ``field``. EXAMPLES:: - sage: E = EllipticCurve_from_j(QQ(0)) # a curve with j=0 over QQ - sage: E.automorphisms(); + sage: E = EllipticCurve_from_j(QQ(0)) # a curve with j=0 over QQ + sage: E.automorphisms() [Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Rational Field Via: (u,r,s,t) = (-1, 0, 0, -1), Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Rational Field Via: (u,r,s,t) = (1, 0, 0, 0)] We can also find automorphisms defined over extension fields:: - sage: K. = NumberField(x^2+3) # adjoin roots of unity + sage: K. = NumberField(x^2+3) # adjoin roots of unity sage: E.automorphisms(K) [Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Number Field in a with defining polynomial x^2 + 3 Via: (u,r,s,t) = (-1, 0, 0, -1), @@ -2449,15 +2449,12 @@ def automorphisms(self, field=None): :: - sage: [ len(EllipticCurve_from_j(GF(q,'a')(0)).automorphisms()) for q in [2,4,3,9,5,25,7,49]] + sage: [len(EllipticCurve_from_j(GF(q,'a')(0)).automorphisms()) for q in [2,4,3,9,5,25,7,49]] [2, 24, 2, 12, 2, 6, 6, 6] """ - if field is None: - return [wm.WeierstrassIsomorphism(self, urst, self) - for urst in wm.isomorphisms(self, self)] - E = self.change_ring(field) - return [wm.WeierstrassIsomorphism(E, urst, E) - for urst in wm.isomorphisms(E, E)] + if field is not None: + self = self.change_ring(field) + return self.isomorphisms(self) def isomorphisms(self, other, field=None): """ @@ -2473,7 +2470,7 @@ def isomorphisms(self, other, field=None): OUTPUT: - (list) A list of ``WeierstrassIsomorphism`` objects consisting of all + (list) A list of :class:`~wm.WeierstrassIsomorphism` objects consisting of all the isomorphisms from the curve ``self`` to the curve ``other`` defined over ``field``. @@ -2481,7 +2478,7 @@ def isomorphisms(self, other, field=None): sage: E = EllipticCurve_from_j(QQ(0)) # a curve with j=0 over QQ sage: F = EllipticCurve('27a3') # should be the same one - sage: E.isomorphisms(F); + sage: E.isomorphisms(F) [Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Rational Field Via: (u,r,s,t) = (-1, 0, 0, -1), Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Rational Field @@ -2502,13 +2499,11 @@ def isomorphisms(self, other, field=None): To: Elliptic Curve defined by y^2 = x^3 + x + 6 over Finite Field in a of size 7^2 Via: (u,r,s,t) = (6*a + 4, 0, 0, 0)] """ - if field is None: - return [wm.WeierstrassIsomorphism(self, urst, other) - for urst in wm.isomorphisms(self, other)] - E = self.change_ring(field) - F = other.change_ring(field) - return [wm.WeierstrassIsomorphism(E, urst, F) - for urst in wm.isomorphisms(E, F)] + if field is not None: + self = self.change_ring(field) + other = other.change_ring(field) + return sorted(wm.WeierstrassIsomorphism(self, urst, other) + for urst in wm.isomorphisms(self, other)) def is_isomorphic(self, other, field=None): """ @@ -2542,17 +2537,16 @@ def is_isomorphic(self, other, field=None): if field is None: if self.base_ring() != other.base_ring(): return False - elif self.j_invariant() != other.j_invariant(): # easy check - return False - else: - return wm.isomorphisms(self, other, True) is not None else: - E = self.base_extend(field) - F = other.base_extend(field) - if E.j_invariant() != F.j_invariant(): # easy check - return False - else: - return wm.isomorphisms(E, other, F) is not None + self = self.base_extend(field) + other = other.base_extend(field) + if self.j_invariant() != other.j_invariant(): # easy check + return False + try: + next(wm.isomorphisms(self, other)) + except StopIteration: + return False + return True def change_weierstrass_model(self, *urst): r""" diff --git a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py index a9ad45f6fb2..543ac23758e 100644 --- a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py +++ b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py @@ -39,15 +39,14 @@ class baseWI(): This class implements the basic arithmetic of isomorphisms between Weierstrass models of elliptic curves. - These are specified by lists of the form `[u,r,s,t]` (with - `u\not=0`) which specifies a transformation `(x,y) \mapsto (x',y')` - where + These are specified by lists of the form `[u,r,s,t]` (with `u \neq 0`) + which specifies a transformation `(x,y) \mapsto (x',y')` where `(x,y) = (u^2x'+r , u^3y' + su^2x' + t).` INPUT: - - ``u,r,s,t`` (default (1,0,0,0)) -- standard parameters of an + - ``u,r,s,t`` (default `(1,0,0,0)`) -- standard parameters of an isomorphism between Weierstrass models. EXAMPLES:: @@ -67,7 +66,7 @@ def __init__(self, u=1, r=0, s=0, t=0): INPUT: - - ``u,r,s,t`` (default (1,0,0,0)) -- standard parameters of an + - ``u,r,s,t`` (default `(1,0,0,0)`) -- standard parameters of an isomorphism between Weierstrass models. EXAMPLES:: @@ -81,7 +80,7 @@ def __init__(self, u=1, r=0, s=0, t=0): sage: baseWI(u,r,s,t) (u, r, s, t) """ - if u == 0: + if not u: raise ValueError("u!=0 required for baseWI") self.u = u self.r = r @@ -117,7 +116,7 @@ def __richcmp__(self, other, op): False """ if not isinstance(other, baseWI): - return (op == op_NE) + return op == op_NE return richcmp(self.tuple(), other.tuple(), op) def tuple(self): @@ -174,7 +173,7 @@ def __invert__(self): (1, 0, 0, 0) """ u, r, s, t = self.tuple() - return baseWI(1/u, -r/(u**2), -s/u, (r*s-t)/(u**3)) + return baseWI(1/u, -r/u**2, -s/u, (r*s-t)/u**3) def __repr__(self): r""" @@ -255,38 +254,35 @@ def __call__(self, EorP): raise ValueError("baseWI(a) only for a=(x,y), (x:y:z) or (a1,a2,a3,a4,a6)") -def isomorphisms(E, F, JustOne=False): +def isomorphisms(E, F): r""" - Return one or all isomorphisms between two elliptic curves. + Enumerate all isomorphisms between two elliptic curves, + as a generator object. INPUT: - ``E``, ``F`` (EllipticCurve) -- Two elliptic curves. - - ``JustOne`` (bool) If ``True``, returns one isomorphism, or ``None`` if - the curves are not isomorphic. If ``False``, returns a (possibly - empty) list of isomorphisms. - OUTPUT: - Either ``None``, or a 4-tuple `(u,r,s,t)` representing an isomorphism, - or a list of these. + A generator object producing 4-tuples `(u,r,s,t)` representing an isomorphism. .. NOTE:: - This function is not intended for users, who should use the - interface provided by ``ell_generic``. + This function is not intended for users, who should use the methods + :meth:`~sage.schemes.elliptic_curves.ell_generic.isomorphisms` + and + :meth:`~sage.schemes.elliptic_curves.ell_generic.isomorphism_to` + and + :meth:`~sage.schemes.elliptic_curves.ell_generic.automorphisms`. EXAMPLES:: sage: from sage.schemes.elliptic_curves.weierstrass_morphism import * - sage: isomorphisms(EllipticCurve_from_j(0),EllipticCurve('27a3')) - [(-1, 0, 0, -1), (1, 0, 0, 0)] - sage: isomorphisms(EllipticCurve_from_j(0),EllipticCurve('27a3'),JustOne=True) - (1, 0, 0, 0) - sage: isomorphisms(EllipticCurve_from_j(0),EllipticCurve('27a1')) + sage: list(isomorphisms(EllipticCurve_from_j(0), EllipticCurve('27a3'))) + [(1, 0, 0, 0), (-1, 0, 0, -1)] + sage: list(isomorphisms(EllipticCurve_from_j(0), EllipticCurve('27a1'))) [] - sage: isomorphisms(EllipticCurve_from_j(0),EllipticCurve('27a1'),JustOne=True) TESTS: @@ -294,23 +290,61 @@ def isomorphisms(E, F, JustOne=False): sage: z8 = GF(2^8).gen() sage: E1 = EllipticCurve([z8, z8, z8, z8, z8]) - sage: isomorphisms(E1, E1) + sage: list(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) + sage: list(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)] + + Random testing:: + + sage: p = random_prime(100) + sage: F = GF(p).algebraic_closure() + sage: while True: + ....: try: + ....: E = EllipticCurve(list((F^5).random_element())) + ....: except ArithmeticError: + ....: continue + ....: break + sage: Aut = E.automorphisms() + sage: len(set(Aut)) == len(Aut) + True + sage: all(-a in Aut for a in Aut) + True + sage: len(Aut) in (2, 4, 6, 12, 24) + True + sage: j = E.j_invariant() + sage: { + ....: 2: j not in (0, 1728), + ....: 4: p >= 5 and j == 1728, + ....: 6: p >= 5 and j == 0, + ....: 12: p == 3 and j in (0, 1728), + ....: 24: p == 2 and j in (0, 1728), + ....: }[len(Aut)] + True + sage: u,r,s,t = (F^4).random_element() + sage: u = u or 1 + sage: F = E.change_weierstrass_model(u,r,s,t) + sage: Iso = E.isomorphisms(F) + sage: len(set(Iso)) == len(Iso) + True + sage: all(-f in Iso for f in Iso) + True + sage: len(Iso) == len(Aut) + True + sage: all({iso2*iso1 for iso1 in Iso} == set(Aut) for iso2 in F.isomorphisms(E)) + True """ from .ell_generic import is_EllipticCurve if not is_EllipticCurve(E) or not is_EllipticCurve(F): raise ValueError("arguments are not elliptic curves") - K = E.base_ring() j = E.j_invariant() if j != F.j_invariant(): - if JustOne: - return None - return [] + return + + K = E.base_ring() from sage.rings.polynomial.polynomial_ring import polygen x = polygen(K, 'x') @@ -322,71 +356,46 @@ def isomorphisms(E, F, JustOne=False): if char == 2: if j == 0: - ulist = (x**3-(a3E/a3F)).roots(multiplicities=False) - ans = [] + ulist = (x**3 - a3E/a3F).roots(multiplicities=False) for u in ulist: - slist = (x**4+a3E*x+(a2F**2+a4F)*u**4+a2E**2+a4E).roots(multiplicities=False) + slist = (x**4 + a3E*x + (a2F**2 + a4F)*u**4 + a2E**2 + a4E).roots(multiplicities=False) for s in slist: - r = s**2+a2E+a2F*u**2 + r = s**2 + a2E + a2F*u**2 tlist = (x**2 + a3E*x + r**3 + a2E*r**2 + a4E*r + a6E + a6F*u**6).roots(multiplicities=False) for t in tlist: - if JustOne: - return (u, r, s, t) - ans.append((u, r, s, t)) - if JustOne: - return None - ans.sort() - return ans + yield (u, r, s, t) else: - ans = [] u = a1E/a1F - r = (a3E+a3F*u**3)/a1E - slist = [s[0] for s in (x**2+a1E*x+(r+a2E+a2F*u**2)).roots()] + r = (a3E + a3F*u**3)/a1E + slist = (x**2 + a1E*x + r + a2E + a2F*u**2).roots(multiplicities=False) for s in slist: - 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)) - if JustOne: - return None - ans.sort() - return ans + t = (a4E + a4F*u**4 + s*a3E + r*s*a1E + r**2) / a1E + yield (u, r, s, t) + return b2E, b4E, b6E, b8E = E.b_invariants() b2F, b4F, b6F, b8F = F.b_invariants() if char == 3: if j == 0: - ulist = (x**4-(b4E/b4F)).roots(multiplicities=False) - ans = [] + ulist = (x**4 - b4E/b4F).roots(multiplicities=False) for u in ulist: - s = a1E-a1F*u - t = a3E-a3F*u**3 - rlist = (x**3-b4E*x+(b6E-b6F*u**6)).roots(multiplicities=False) + s = a1E - a1F*u + t = a3E - a3F*u**3 + rlist = (x**3 - b4E*x + b6E - b6F*u**6).roots(multiplicities=False) for r in rlist: - if JustOne: - return (u, r, s, t+r*a1E) - ans.append((u, r, s, t+r*a1E)) - if JustOne: - return None - ans.sort() - return ans + yield (u, r, s, t + r*a1E) else: - ulist = (x**2 - b2E / b2F).roots(multiplicities=False) - ans = [] + ulist = (x**2 - b2E/b2F).roots(multiplicities=False) for u in ulist: r = (b4F * u**4 - b4E) / b2E - s = (a1E - a1F * u) - t = (a3E - a3F * u**3 + a1E * r) - if JustOne: - return (u, r, s, t) - ans.append((u, r, s, t)) - if JustOne: - return None - ans.sort() - return ans - -# now char!=2,3: + s = a1E - a1F * u + t = a3E - a3F * u**3 + a1E * r + yield (u, r, s, t) + return + + # now char != 2,3: + c4E, c6E = E.c_invariants() c4F, c6F = F.c_invariants() @@ -396,19 +405,12 @@ def isomorphisms(E, F, JustOne=False): m, um = 4, c4E/c4F else: m, um = 2, (c6E*c4F)/(c6F*c4E) - ulist = (x**m-um).roots(multiplicities=False) - ans = [] + ulist = (x**m - um).roots(multiplicities=False) for u in ulist: s = (a1F*u - a1E)/2 r = (a2F*u**2 + a1E*s + s**2 - a2E)/3 t = (a3F*u**3 - a1E*r - a3E)/2 - if JustOne: - return (u, r, s, t) - ans.append((u, r, s, t)) - if JustOne: - return None - ans.sort() - return ans + yield (u, r, s, t) class WeierstrassIsomorphism(EllipticCurveHom, baseWI): @@ -516,8 +518,9 @@ def __init__(self, E=None, urst=None, F=None): E = EllipticCurve(baseWI.__call__(inv_urst, list(F.a_invariants()))) elif urst is None: # try to construct the morphism - urst = isomorphisms(E, F, True) - if urst is None: + try: + urst = next(isomorphisms(E, F)) + except StopIteration: raise ValueError("elliptic curves not isomorphic") baseWI.__init__(self, *urst) @@ -549,7 +552,7 @@ def _comparison_impl(left, right, op): sage: w1 = E.isomorphism_to(F) sage: w1 == w1 True - sage: w2 = F.automorphisms()[0] *w1 + sage: w2 = F.automorphisms()[0] * w1 sage: w1 == w2 False From 99a6e3b014ad198fd6e25eb2d40debcf0714120f Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Fri, 4 Nov 2022 15:33:41 +0800 Subject: [PATCH 397/632] hide internal function --- .../elliptic_curves/ell_curve_isogeny.py | 6 ++--- .../schemes/elliptic_curves/ell_generic.py | 4 ++-- .../elliptic_curves/weierstrass_morphism.py | 23 ++++++------------- 3 files changed, 12 insertions(+), 21 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index d03346deacc..468f8cc857b 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -92,7 +92,7 @@ from sage.schemes.elliptic_curves.ell_generic import is_EllipticCurve from sage.schemes.elliptic_curves.weierstrass_morphism \ - import WeierstrassIsomorphism, isomorphisms, baseWI + import WeierstrassIsomorphism, _isomorphisms, baseWI # # Private function for parsing input to determine the type of @@ -3359,9 +3359,9 @@ def compute_intermediate_curves(E1, E2): # We cannot even just use pre_iso = E1.isomorphism_to(E1w) since # it may have u=-1; similarly for E2 - urst = [w for w in isomorphisms(E1, E1w) if w[0] == 1][0] + urst = [w for w in _isomorphisms(E1, E1w) if w[0] == 1][0] pre_iso = WeierstrassIsomorphism(E1, urst, E1w) - urst = [w for w in isomorphisms(E2w, E2) if w[0] == 1][0] + urst = [w for w in _isomorphisms(E2w, E2) if w[0] == 1][0] post_iso = WeierstrassIsomorphism(E2w, urst, E2) return E1w, E2w, pre_iso, post_iso diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 948b5006912..c1dcc975424 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -2503,7 +2503,7 @@ def isomorphisms(self, other, field=None): self = self.change_ring(field) other = other.change_ring(field) return sorted(wm.WeierstrassIsomorphism(self, urst, other) - for urst in wm.isomorphisms(self, other)) + for urst in wm._isomorphisms(self, other)) def is_isomorphic(self, other, field=None): """ @@ -2543,7 +2543,7 @@ def is_isomorphic(self, other, field=None): if self.j_invariant() != other.j_invariant(): # easy check return False try: - next(wm.isomorphisms(self, other)) + next(wm._isomorphisms(self, other)) except StopIteration: return False return True diff --git a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py index 543ac23758e..c8f8abb258b 100644 --- a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py +++ b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py @@ -254,7 +254,7 @@ def __call__(self, EorP): raise ValueError("baseWI(a) only for a=(x,y), (x:y:z) or (a1,a2,a3,a4,a6)") -def isomorphisms(E, F): +def _isomorphisms(E, F): r""" Enumerate all isomorphisms between two elliptic curves, as a generator object. @@ -267,21 +267,12 @@ def isomorphisms(E, F): A generator object producing 4-tuples `(u,r,s,t)` representing an isomorphism. - .. NOTE:: - - This function is not intended for users, who should use the methods - :meth:`~sage.schemes.elliptic_curves.ell_generic.isomorphisms` - and - :meth:`~sage.schemes.elliptic_curves.ell_generic.isomorphism_to` - and - :meth:`~sage.schemes.elliptic_curves.ell_generic.automorphisms`. - EXAMPLES:: - sage: from sage.schemes.elliptic_curves.weierstrass_morphism import * - sage: list(isomorphisms(EllipticCurve_from_j(0), EllipticCurve('27a3'))) + sage: from sage.schemes.elliptic_curves.weierstrass_morphism import _isomorphisms + sage: list(_isomorphisms(EllipticCurve_from_j(0), EllipticCurve('27a3'))) [(1, 0, 0, 0), (-1, 0, 0, -1)] - sage: list(isomorphisms(EllipticCurve_from_j(0), EllipticCurve('27a1'))) + sage: list(_isomorphisms(EllipticCurve_from_j(0), EllipticCurve('27a1'))) [] TESTS: @@ -290,10 +281,10 @@ def isomorphisms(E, F): sage: z8 = GF(2^8).gen() sage: E1 = EllipticCurve([z8, z8, z8, z8, z8]) - sage: list(isomorphisms(E1, E1)) + sage: list(_isomorphisms(E1, E1)) [(1, 0, 0, 0), (1, 0, z8, z8)] sage: E2 = EllipticCurve([z8^2, 0, 0, 0, z8^7 + z8^4]) - sage: list(isomorphisms(E1, E2)) + sage: list(_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)] @@ -519,7 +510,7 @@ def __init__(self, E=None, urst=None, F=None): elif urst is None: # try to construct the morphism try: - urst = next(isomorphisms(E, F)) + urst = next(_isomorphisms(E, F)) except StopIteration: raise ValueError("elliptic curves not isomorphic") baseWI.__init__(self, *urst) From 7e9aefca872ab761410a5283c2a1c0701aadff4a Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Fri, 4 Nov 2022 16:06:39 +0800 Subject: [PATCH 398/632] fix doctest failures --- src/sage/arith/misc.py | 2 +- src/sage/crypto/stream.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index 9caf22c1f06..e03c4b95b4d 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -3268,7 +3268,7 @@ def carmichael_lambda(n): if n < 1: raise ValueError("Input n must be a positive integer.") - L = n.factor() + L = list(n.factor()) t = [] # first get rid of the prime factor 2 diff --git a/src/sage/crypto/stream.py b/src/sage/crypto/stream.py index f0554d7a6cc..728e73ca59b 100644 --- a/src/sage/crypto/stream.py +++ b/src/sage/crypto/stream.py @@ -288,7 +288,7 @@ def blum_blum_shub(length, seed=None, p=None, q=None, is the period. :: sage: from sage.crypto.stream import blum_blum_shub - sage: from sage.crypto.util import carmichael_lambda + sage: from sage.arith.misc import carmichael_lambda sage: carmichael_lambda(carmichael_lambda(7*11)) 4 sage: s = [GF(2)(int(str(x))) for x in blum_blum_shub(60, p=7, q=11, seed=13)] From 7eea8892b6d584d74f80c0554ae31a67e58cdff8 Mon Sep 17 00:00:00 2001 From: Johann Birnick Date: Fri, 4 Nov 2022 19:55:05 +0000 Subject: [PATCH 399/632] changed function name --- .../polynomial/multi_polynomial_ring_base.pyx | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx index 6e66b079a1e..befcf7c4172 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_ring_base.pyx @@ -347,7 +347,7 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): """ return self.remove_var(x)[str(x)] - def multivariate_interpolation(self, bound, *args): + def interpolation(self, bound, *args): """ Create a polynomial with specified evaluations. @@ -355,9 +355,9 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): This function can be called in two ways: - 1. multivariate_interpolation(bound, points, values) + 1. interpolation(bound, points, values) - 2. multivariate_interpolation(bound, function) + 2. interpolation(bound, function) INPUT: @@ -386,7 +386,7 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): ....: return a^3*b + b + c^2 + 25 ....: sage: R. = PolynomialRing(QQ) - sage: R.multivariate_interpolation(4, F) + sage: R.interpolation(4, F) x^3*y + z^2 + y + 25 @@ -394,7 +394,7 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): ....: return a^3*b + b + c^2 + 25 ....: sage: R. = PolynomialRing(QQ) - sage: R.multivariate_interpolation([3,1,2], F) + sage: R.interpolation([3,1,2], F) x^3*y + z^2 + y + 25 @@ -406,7 +406,7 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): ....: (2,7,0),(1,10,13),(0,0,1),(-1,1,0),(2,5,3),(1,1,1),(7,4,11), ....: (12,1,9),(1,1,3),(4,-1,2),(0,1,5),(5,1,3),(3,1,-2),(2,11,3), ....: (4,12,19),(3,1,1),(5,2,-3),(12,1,1),(2,3,4)] - sage: R.multivariate_interpolation([3,1,2], points, [F(*x) for x in points]) + sage: R.interpolation([3,1,2], points, [F(*x) for x in points]) x^3*y + z^2 + y + 25 ALGORITHM: @@ -424,6 +424,9 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): Also, if the solution is not unique, it spits out one solution, without any notice that there are more. + Lastly, the interpolation function for univariate polynomial rings + is called ``lagrange_polynomial()``. + .. WARNING:: If you don't provide point/value pairs but just a function, it @@ -437,7 +440,7 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing): ....: return a^3*b + b + c^2 + 25 ....: sage: R. = PolynomialRing(QQ) - sage: R.multivariate_interpolation(3,F) + sage: R.interpolation(3,F) 1/2*x^3 + x*y + z^2 - 1/2*x + y + 25 .. SEEALSO:: From a6e99a73a0bb120cfe91b2bf487e3b6580205ab2 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Mon, 7 Nov 2022 14:23:10 +0800 Subject: [PATCH 400/632] use abs(m) to compute m-division polynomial --- src/sage/schemes/elliptic_curves/ell_generic.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 154a581bc2a..d66d868fc80 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -2350,10 +2350,23 @@ def multiplication_by_m_isogeny(self, m): sage: E.multiplication_by_m_isogeny(2).rational_maps() ((1/4*x^4 + 33/4*x^2 - 121/2*x + 363/4)/(x^3 - 3/4*x^2 - 33/2*x + 121/4), (-1/256*x^7 + 1/128*x^6*y - 7/256*x^6 - 3/256*x^5*y - 105/256*x^5 - 165/256*x^4*y + 1255/256*x^4 + 605/128*x^3*y - 473/64*x^3 - 1815/128*x^2*y - 10527/256*x^2 + 2541/128*x*y + 4477/32*x - 1331/128*y - 30613/256)/(1/16*x^6 - 3/32*x^5 - 519/256*x^4 + 341/64*x^3 + 1815/128*x^2 - 3993/64*x + 14641/256)) + + Test for :trac:`34727`:: + + sage: E = EllipticCurve([5,5]) + sage: E.multiplication_by_m_isogeny(-1) + Isogeny of degree 1 from Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field to Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field + sage: E.multiplication_by_m_isogeny(-2) + Isogeny of degree 4 from Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field to Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field + sage: E.multiplication_by_m_isogeny(-3) + Isogeny of degree 9 from Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field to Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Rational Field + sage: mu = E.multiplication_by_m_isogeny + sage: all(mu(-m) == -mu(m) for m in (1,2,3,5,7)) + True """ mx, my = self.multiplication_by_m(m) - torsion_poly = self.torsion_polynomial(m).monic() + torsion_poly = self.torsion_polynomial(abs(m)).monic() phi = self.isogeny(torsion_poly, codomain=self) phi._EllipticCurveIsogeny__initialize_rational_maps(precomputed_maps=(mx, my)) From 6eaaf7e51cccaf7ea03890a2874f7f00eb7ab92c Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Mon, 7 Nov 2022 13:45:55 +0800 Subject: [PATCH 401/632] change sorting order for WeierstrassIsomorphisms --- .../schemes/elliptic_curves/ell_generic.py | 48 +++++++++++++++---- .../elliptic_curves/weierstrass_morphism.py | 39 +++++++++++---- 2 files changed, 71 insertions(+), 16 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 99cd02515d9..445f2ee4343 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -2430,6 +2430,9 @@ def automorphisms(self, field=None): """ Return the set of isomorphisms from self to itself (as a list). + The identity and negation morphisms are guaranteed to appear + as the first and second entry of the returned list. + INPUT: - ``field`` (default ``None``) -- a field into which the @@ -2447,23 +2450,52 @@ def automorphisms(self, field=None): sage: E = EllipticCurve_from_j(QQ(0)) # a curve with j=0 over QQ sage: E.automorphisms() [Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Rational Field - Via: (u,r,s,t) = (-1, 0, 0, -1), Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Rational Field - Via: (u,r,s,t) = (1, 0, 0, 0)] + Via: (u,r,s,t) = (1, 0, 0, 0), + Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Rational Field + Via: (u,r,s,t) = (-1, 0, 0, -1)] We can also find automorphisms defined over extension fields:: sage: K. = NumberField(x^2+3) # adjoin roots of unity sage: E.automorphisms(K) [Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Number Field in a with defining polynomial x^2 + 3 - Via: (u,r,s,t) = (-1, 0, 0, -1), - ... - Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Number Field in a with defining polynomial x^2 + 3 - Via: (u,r,s,t) = (1, 0, 0, 0)] + Via: (u,r,s,t) = (1, 0, 0, 0), + Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Number Field in a with defining polynomial x^2 + 3 + Via: (u,r,s,t) = (-1, 0, 0, -1), + Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Number Field in a with defining polynomial x^2 + 3 + Via: (u,r,s,t) = (-1/2*a - 1/2, 0, 0, 0), + Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Number Field in a with defining polynomial x^2 + 3 + Via: (u,r,s,t) = (1/2*a + 1/2, 0, 0, -1), + Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Number Field in a with defining polynomial x^2 + 3 + Via: (u,r,s,t) = (1/2*a - 1/2, 0, 0, 0), + Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Number Field in a with defining polynomial x^2 + 3 + Via: (u,r,s,t) = (-1/2*a + 1/2, 0, 0, -1)] :: sage: [len(EllipticCurve_from_j(GF(q,'a')(0)).automorphisms()) for q in [2,4,3,9,5,25,7,49]] [2, 24, 2, 12, 2, 6, 6, 6] + + TESTS: + + Random testing:: + + sage: p = random_prime(100) + sage: k = randrange(1,30) + sage: F. = GF((p,k)) + sage: while True: + ....: try: + ....: E = EllipticCurve(list((F^5).random_element())) + ....: except ArithmeticError: + ....: continue + ....: break + sage: Aut = E.automorphisms() + sage: Aut[0] == E.multiplication_by_m_isogeny(1) + True + sage: Aut[1] == E.multiplication_by_m_isogeny(-1) + True + sage: sorted(Aut) == Aut + True """ if field is not None: self = self.change_ring(field) @@ -2493,9 +2525,9 @@ def isomorphisms(self, other, field=None): sage: F = EllipticCurve('27a3') # should be the same one sage: E.isomorphisms(F) [Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Rational Field - Via: (u,r,s,t) = (-1, 0, 0, -1), + Via: (u,r,s,t) = (1, 0, 0, 0), Elliptic-curve endomorphism of Elliptic Curve defined by y^2 + y = x^3 over Rational Field - Via: (u,r,s,t) = (1, 0, 0, 0)] + Via: (u,r,s,t) = (-1, 0, 0, -1)] We can also find isomorphisms defined over extension fields:: diff --git a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py index c8f8abb258b..150fafcf97a 100644 --- a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py +++ b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py @@ -32,6 +32,32 @@ from sage.structure.sequence import Sequence from sage.rings.all import Integer, PolynomialRing +def _urst_sorting_key(tup): + r""" + Return a sorting key for `(u,r,s,t)` tuples representing + elliptic-curve isomorphisms. The key is chosen in such a + way that an isomorphism and its negative appear next to + one another in a sorted list, and such that normalized + isomorphisms come first. One particular consequence of + this is that the identity and negation morphisms are the + first and second entries of the list returned by + :meth:`~sage.schemes.elliptic_curves.ell_generic.EllipticCurve_generic.automorphisms`. + + TESTS:: + + sage: from sage.schemes.elliptic_curves.weierstrass_morphism import _urst_sorting_key + sage: _urst_sorting_key((1,0,0,0)) < _urst_sorting_key((-1,0,0,0)) + True + sage: _urst_sorting_key((-1,0,0,0)) < _urst_sorting_key((2,0,0,0)) + True + sage: _urst_sorting_key((1,2,3,4)) < _urst_sorting_key((-1,0,0,0)) + True + """ + v = tup[0] + h = 0 if v == 1 else 1 if v == -1 else 2 + if -v < v: + v = -v + return (h, v) + tup @richcmp_method class baseWI(): @@ -91,12 +117,7 @@ def __richcmp__(self, other, op): """ Standard comparison function. - The ordering is just lexicographic on the tuple `(u,r,s,t)`. - - .. NOTE:: - - In a list of automorphisms, there is no guarantee that the - identity will be first! + The ordering is done according to :func:`_urst_sorting_key`. EXAMPLES:: @@ -117,7 +138,9 @@ def __richcmp__(self, other, op): """ if not isinstance(other, baseWI): return op == op_NE - return richcmp(self.tuple(), other.tuple(), op) + key1 = _urst_sorting_key(self.tuple()) + key2 = _urst_sorting_key(other.tuple()) + return richcmp(key1, key2, op) def tuple(self): r""" @@ -543,7 +566,7 @@ def _comparison_impl(left, right, op): sage: w1 = E.isomorphism_to(F) sage: w1 == w1 True - sage: w2 = F.automorphisms()[0] * w1 + sage: w2 = F.automorphisms()[1] * w1 sage: w1 == w2 False From 4c4ce932ca309af16087c265ae7ccce052ee110b Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Mon, 7 Nov 2022 23:53:25 +0800 Subject: [PATCH 402/632] 0 == 1728 in characteristics 2 and 3 --- src/sage/schemes/elliptic_curves/weierstrass_morphism.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py index 150fafcf97a..7095fde32dd 100644 --- a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py +++ b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py @@ -333,8 +333,8 @@ def _isomorphisms(E, F): ....: 2: j not in (0, 1728), ....: 4: p >= 5 and j == 1728, ....: 6: p >= 5 and j == 0, - ....: 12: p == 3 and j in (0, 1728), - ....: 24: p == 2 and j in (0, 1728), + ....: 12: p == 3 and j == 0, # note 1728 == 0 + ....: 24: p == 2 and j == 0, # note 1728 == 0 ....: }[len(Aut)] True sage: u,r,s,t = (F^4).random_element() From c9e964632bdfd95c1f89557a5c7e1ef1c78a794a Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Tue, 8 Nov 2022 13:11:29 +0800 Subject: [PATCH 403/632] compare entire (u,r,s,t) tuple In characteristics 2 and 3, there can be multiple (u,r,s,t) between two curves with the same u-coefficient. --- .../elliptic_curves/weierstrass_morphism.py | 76 ++++--------------- 1 file changed, 16 insertions(+), 60 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py index 7095fde32dd..3be4b6ee106 100644 --- a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py +++ b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py @@ -27,39 +27,10 @@ from .constructor import EllipticCurve from sage.schemes.elliptic_curves.hom import EllipticCurveHom -from sage.structure.richcmp import (richcmp_method, richcmp, richcmp_not_equal, - op_NE) +from sage.structure.richcmp import (richcmp, richcmp_not_equal, op_EQ, op_NE) from sage.structure.sequence import Sequence from sage.rings.all import Integer, PolynomialRing -def _urst_sorting_key(tup): - r""" - Return a sorting key for `(u,r,s,t)` tuples representing - elliptic-curve isomorphisms. The key is chosen in such a - way that an isomorphism and its negative appear next to - one another in a sorted list, and such that normalized - isomorphisms come first. One particular consequence of - this is that the identity and negation morphisms are the - first and second entries of the list returned by - :meth:`~sage.schemes.elliptic_curves.ell_generic.EllipticCurve_generic.automorphisms`. - - TESTS:: - - sage: from sage.schemes.elliptic_curves.weierstrass_morphism import _urst_sorting_key - sage: _urst_sorting_key((1,0,0,0)) < _urst_sorting_key((-1,0,0,0)) - True - sage: _urst_sorting_key((-1,0,0,0)) < _urst_sorting_key((2,0,0,0)) - True - sage: _urst_sorting_key((1,2,3,4)) < _urst_sorting_key((-1,0,0,0)) - True - """ - v = tup[0] - h = 0 if v == 1 else 1 if v == -1 else 2 - if -v < v: - v = -v - return (h, v) + tup - -@richcmp_method class baseWI(): r""" This class implements the basic arithmetic of isomorphisms between @@ -113,35 +84,6 @@ def __init__(self, u=1, r=0, s=0, t=0): self.s = s self.t = t - def __richcmp__(self, other, op): - """ - Standard comparison function. - - The ordering is done according to :func:`_urst_sorting_key`. - - EXAMPLES:: - - sage: from sage.schemes.elliptic_curves.weierstrass_morphism import baseWI - sage: baseWI(1,2,3,4) == baseWI(1,2,3,4) - True - sage: baseWI(1,2,3,4) != baseWI(1,2,3,4) - False - sage: baseWI(1,2,3,4) < baseWI(1,2,3,5) - True - sage: baseWI(1,2,3,4) > baseWI(1,2,3,4) - False - - It will never return equality if ``other`` is of another type:: - - sage: baseWI() == 1 - False - """ - if not isinstance(other, baseWI): - return op == op_NE - key1 = _urst_sorting_key(self.tuple()) - key2 = _urst_sorting_key(other.tuple()) - return richcmp(key1, key2, op) - def tuple(self): r""" Return the parameters `u,r,s,t` as a tuple. @@ -595,7 +537,21 @@ def _comparison_impl(left, right, op): if lx != rx: return richcmp_not_equal(lx, rx, op) - return baseWI.__richcmp__(left, right, op) + if op in (op_EQ, op_NE): + return richcmp(left.tuple(), right.tuple(), op) + + # This makes sure that the identity and negation morphisms + # come first in a sorted list of WeierstrassIsomorphisms. + # More generally, we're making sure that a morphism and its + # negative appear next to each other, and that those pairs + # of isomorphisms satisfying u=+-1 come first. + def _sorting_key(iso): + v, w = iso.tuple(), (-iso).tuple() + i = 0 if (1,0,0,0) in (v,w) else 1 + j = 0 if v[0] == 1 else 1 if w[0] == 1 else 2 + return (i,) + min(v,w) + (j,) + v + + return richcmp(_sorting_key(left), _sorting_key(right), op) def _eval(self, P): r""" From 94a9718bb25511f52ee7c1a8890c7a9a257822ee Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Tue, 8 Nov 2022 14:37:10 +0900 Subject: [PATCH 404/632] Rebase sage autodoc --- src/doc/en/prep/Programming.rst | 2 +- .../en/prep/Symbolics-and-Basic-Plotting.rst | 2 +- src/sage/arith/misc.py | 8 +- src/sage/combinat/root_system/cartan_type.py | 2 +- src/sage/combinat/sf/classical.py | 2 +- src/sage/finance/option.pyx | 2 +- src/sage/graphs/strongly_regular_db.pyx | 2 +- src/sage/interfaces/sage0.py | 2 +- src/sage/libs/singular/ring.pyx | 2 +- src/sage/manifolds/manifold.py | 2 +- src/sage/misc/c3_controlled.pyx | 2 +- src/sage/misc/sagedoc_conf.py | 4 +- src/sage/misc/sageinspect.py | 2 +- .../rings/finite_rings/hom_finite_field.pyx | 4 +- .../rings/function_field/function_field.py | 2 +- .../rings/polynomial/laurent_polynomial.pyx | 6 +- src/sage/sets/disjoint_set.pyx | 8 +- src/sage/structure/category_object.pyx | 6 +- src/sage/structure/factory.pyx | 2 +- src/sage/structure/global_options.py | 2 +- src/sage/structure/list_clone.pyx | 4 +- src/sage/symbolic/expression.pyx | 1 - src/sage/symbolic/function.pyx | 17 +- src/sage/symbolic/getitem_impl.pxi | 2 +- src/sage/symbolic/ring.pyx | 7 +- src/sage_docbuild/conf.py | 6 +- src/sage_docbuild/ext/sage_autodoc.py | 2997 ++++++++++++----- src/sage_setup/cython_options.py | 5 + 28 files changed, 2211 insertions(+), 892 deletions(-) diff --git a/src/doc/en/prep/Programming.rst b/src/doc/en/prep/Programming.rst index 3ae980667df..056326927b6 100644 --- a/src/doc/en/prep/Programming.rst +++ b/src/doc/en/prep/Programming.rst @@ -109,7 +109,7 @@ It is very important to keep in the parentheses. :: sage: A.det # Won't work - + This is so useful because we can use the 'tab' key, remember! diff --git a/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst b/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst index 6e0ab69765a..bf11ef481b1 100644 --- a/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst +++ b/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst @@ -178,7 +178,7 @@ This is a good place for a few reminders of basic help. :: sage: z.simplify - + Finally, recall that you can get nicely typeset versions of the output in several ways. diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index 1bd4d4c6a1f..0eca5554dc9 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -2163,9 +2163,9 @@ def get_gcd(order): EXAMPLES:: sage: sage.arith.misc.get_gcd(4000) - + sage: sage.arith.misc.get_gcd(400000) - + sage: sage.arith.misc.get_gcd(4000000000) """ @@ -2185,9 +2185,9 @@ def get_inverse_mod(order): EXAMPLES:: sage: sage.arith.misc.get_inverse_mod(6000) - + sage: sage.arith.misc.get_inverse_mod(600000) - + sage: sage.arith.misc.get_inverse_mod(6000000000) """ diff --git a/src/sage/combinat/root_system/cartan_type.py b/src/sage/combinat/root_system/cartan_type.py index dafe2f62366..d9857727ab1 100644 --- a/src/sage/combinat/root_system/cartan_type.py +++ b/src/sage/combinat/root_system/cartan_type.py @@ -2025,7 +2025,7 @@ def classical(self): We check that :meth:`classical`, :meth:`sage.combinat.root_system.cartan_type.CartanType_crystallographic.dynkin_diagram`, - and :meth:`.special_node` are consistent:: + and :meth:`special_node` are consistent:: sage: for ct in CartanType.samples(affine = True): ....: g1 = ct.classical().dynkin_diagram() diff --git a/src/sage/combinat/sf/classical.py b/src/sage/combinat/sf/classical.py index 0477629f3d1..3de5db72457 100644 --- a/src/sage/combinat/sf/classical.py +++ b/src/sage/combinat/sf/classical.py @@ -46,7 +46,7 @@ def init(): sage: sage.combinat.sf.classical.conversion_functions = {} sage: init() sage: sage.combinat.sf.classical.conversion_functions[('Schur', 'powersum')] - + The following checks if the bug described in :trac:`15312` is fixed. :: diff --git a/src/sage/finance/option.pyx b/src/sage/finance/option.pyx index 31b290e0ce9..79c514c4677 100644 --- a/src/sage/finance/option.pyx +++ b/src/sage/finance/option.pyx @@ -36,7 +36,7 @@ def black_scholes(double spot_price, double strike_price, double time_to_maturit sage: finance.black_scholes doctest:warning... DeprecationWarning: the package sage.finance is deprecated... - + sage: finance.black_scholes(42, 40, 0.5, 0.1, 0.2, 'call') # abs tol 1e-10 4.759422392871532 diff --git a/src/sage/graphs/strongly_regular_db.pyx b/src/sage/graphs/strongly_regular_db.pyx index 1e01738662d..58ae72ae4e2 100644 --- a/src/sage/graphs/strongly_regular_db.pyx +++ b/src/sage/graphs/strongly_regular_db.pyx @@ -1154,7 +1154,7 @@ def is_RSHCD(int v, int k, int l, int mu): sage: from sage.graphs.strongly_regular_db import is_RSHCD sage: t = is_RSHCD(64,27,10,12); t - [, 64, 27, 10, 12] + [, 64, 27, 10, 12] sage: g = t[0](*t[1:]); g Graph on 64 vertices sage: g.is_strongly_regular(parameters=True) diff --git a/src/sage/interfaces/sage0.py b/src/sage/interfaces/sage0.py index a43a059e0ec..665a00f6525 100644 --- a/src/sage/interfaces/sage0.py +++ b/src/sage/interfaces/sage0.py @@ -554,7 +554,7 @@ def _repr_(self): EXAMPLES:: sage: sage0(4).gcd - + """ return str(self._obj.parent().eval('%s.%s' % (self._obj._name, self._name))) diff --git a/src/sage/libs/singular/ring.pyx b/src/sage/libs/singular/ring.pyx index 04bd16e8784..828e782d887 100644 --- a/src/sage/libs/singular/ring.pyx +++ b/src/sage/libs/singular/ring.pyx @@ -772,7 +772,7 @@ cpdef poison_currRing(frame, event, arg): sage: from sage.libs.singular.ring import poison_currRing sage: sys.settrace(poison_currRing) sage: sys.gettrace() - + sage: sys.settrace(previous_trace_func) # switch it off again """ global currRing diff --git a/src/sage/manifolds/manifold.py b/src/sage/manifolds/manifold.py index 09b040fb3fc..bb3f6cc42f5 100644 --- a/src/sage/manifolds/manifold.py +++ b/src/sage/manifolds/manifold.py @@ -2206,7 +2206,7 @@ class options(GlobalOptions): sage: M.options._reset() """ NAME = 'manifolds' - module = 'sage.manifolds' + module = 'sage.manifolds.manifold' option_class = 'TopologicalManifold' textbook_output = dict(default=True, description='textbook-like output instead of the Pynac output for derivatives', diff --git a/src/sage/misc/c3_controlled.pyx b/src/sage/misc/c3_controlled.pyx index ebadb070d1d..03561005657 100644 --- a/src/sage/misc/c3_controlled.pyx +++ b/src/sage/misc/c3_controlled.pyx @@ -1091,7 +1091,7 @@ class HierarchyElement(object, metaclass=ClasscallMetaclass): sage: x._bases [5, 2] sage: x._key - + sage: x._key(10) 10 diff --git a/src/sage/misc/sagedoc_conf.py b/src/sage/misc/sagedoc_conf.py index 83df405a3a1..2d2a950c23e 100644 --- a/src/sage/misc/sagedoc_conf.py +++ b/src/sage/misc/sagedoc_conf.py @@ -23,6 +23,7 @@ def process_docstring_aliases(app, what, name, obj, options, docstringlines): """ Change the docstrings for aliases to point to the original object. """ + basename = name.rpartition('.')[2] if hasattr(obj, '__name__') and obj.__name__ != basename: docstringlines[:] = ['See :obj:`%s`.' % name] @@ -146,9 +147,6 @@ def apply(self): node.rawsource = source node[:] = [nodes.Text(source)] -from sage.misc.sageinspect import sage_getargspec -autodoc_builtin_argspec = sage_getargspec - # This is only used by sage.misc.sphinxify def setup(app): app.connect('autodoc-process-docstring', process_docstring_cython) diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index fbca2defc20..3eb25382884 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -152,7 +152,7 @@ def is_function_or_cython_function(obj): sage: is_function_or_cython_function(_mul_parent) True sage: is_function_or_cython_function(Integer.digits) # unbound method - False + True sage: is_function_or_cython_function(Integer(1).digits) # bound method False diff --git a/src/sage/rings/finite_rings/hom_finite_field.pyx b/src/sage/rings/finite_rings/hom_finite_field.pyx index 81c7620d42d..75c91514aa2 100644 --- a/src/sage/rings/finite_rings/hom_finite_field.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field.pyx @@ -435,7 +435,7 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): sage: Frob = k.frobenius_endomorphism() sage: embed = Frob.fixed_field()[1] sage: embed.__reduce__() # indirect doctest - (, + (, (, Set of field embeddings from Finite Field of size 5 to Finite Field in t of size 5^3, {}, @@ -835,7 +835,7 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism(2) sage: Frob.__reduce__() # indirect doctest - (, + (, (, Automorphism group of Finite Field in t of size 5^3, {}, diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index e8cf51beda6..f1e130e3b0e 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -128,7 +128,7 @@ sage: TestSuite(S).run() # long time (4s) Global function fields ----------------------- +====================== A global function field in Sage is an extension field of a rational function field over a *finite* constant field by an irreducible separable polynomial over the diff --git a/src/sage/rings/polynomial/laurent_polynomial.pyx b/src/sage/rings/polynomial/laurent_polynomial.pyx index 5e4188ed80f..f75d26a27b3 100644 --- a/src/sage/rings/polynomial/laurent_polynomial.pyx +++ b/src/sage/rings/polynomial/laurent_polynomial.pyx @@ -3308,12 +3308,12 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): to variables supplied in args. Multiple variables and iteration counts may be supplied; see - documentation for the global derivative() function for more + documentation for the global :func:`derivative` function for more details. .. SEEALSO:: - :meth:`_derivative` + :meth:`_derivative` EXAMPLES:: @@ -3336,7 +3336,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): respect to the given variable. If var is among the generators of this ring, the derivative - is with respect to the generator. Otherwise, _derivative(var) is called + is with respect to the generator. Otherwise, ``_derivative(var)`` is called recursively for each coefficient of this polynomial. .. SEEALSO:: :meth:`derivative` diff --git a/src/sage/sets/disjoint_set.pyx b/src/sage/sets/disjoint_set.pyx index aaa12438f15..dac6e75779b 100644 --- a/src/sage/sets/disjoint_set.pyx +++ b/src/sage/sets/disjoint_set.pyx @@ -358,14 +358,14 @@ cdef class DisjointSet_of_integers(DisjointSet_class): sage: d = DisjointSet(5) sage: d.__reduce__() - (, (5,), [0, 1, 2, 3, 4]) + (, (5,), [0, 1, 2, 3, 4]) :: sage: d.union(2,4) sage: d.union(1,3) sage: d.__reduce__() - (, (5,), [0, 1, 2, 1, 2]) + (, (5,), [0, 1, 2, 1, 2]) """ return DisjointSet, (self._nodes.degree,), self.__getstate__() @@ -674,7 +674,7 @@ cdef class DisjointSet_of_hashables(DisjointSet_class): {{0}, {1}, {2}, {3}, {4}} sage: d = _ sage: d.__reduce__() - (, + (, ([0, 1, 2, 3, 4],), [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]) @@ -683,7 +683,7 @@ cdef class DisjointSet_of_hashables(DisjointSet_class): sage: d.union(2,4) sage: d.union(1,3) sage: d.__reduce__() - (, + (, ([0, 1, 2, 3, 4],), [(0, 0), (1, 1), (2, 2), (3, 1), (4, 2)]) """ diff --git a/src/sage/structure/category_object.pyx b/src/sage/structure/category_object.pyx index 52abd7d918b..99679d2e3ca 100644 --- a/src/sage/structure/category_object.pyx +++ b/src/sage/structure/category_object.pyx @@ -571,7 +571,7 @@ cdef class CategoryObject(SageObject): sage: F.base_ring() Integer Ring sage: F.__class__.base_ring - + Note that the coordinates of the elements of a module can lie in a bigger ring, the ``coordinate_ring``:: @@ -591,7 +591,7 @@ cdef class CategoryObject(SageObject): sage: F.base_ring() Rational Field sage: F.__class__.base_ring - + sage: E = CombinatorialFreeModule(ZZ, [1,2,3]) sage: F = CombinatorialFreeModule(ZZ, [2,3,4]) @@ -599,7 +599,7 @@ cdef class CategoryObject(SageObject): sage: H.base_ring() Integer Ring sage: H.__class__.base_ring - + .. TODO:: diff --git a/src/sage/structure/factory.pyx b/src/sage/structure/factory.pyx index a4a13186973..4e656be479f 100644 --- a/src/sage/structure/factory.pyx +++ b/src/sage/structure/factory.pyx @@ -550,7 +550,7 @@ cdef class UniqueFactory(SageObject): sage: a = test_factory(1, 2) Making object (1, 2) sage: test_factory.reduce_data(a) - (, + (, (, (...), (1, 2), diff --git a/src/sage/structure/global_options.py b/src/sage/structure/global_options.py index 8a3411bdc8f..f4d983a0658 100644 --- a/src/sage/structure/global_options.py +++ b/src/sage/structure/global_options.py @@ -1390,7 +1390,7 @@ def __eq__(self, other): sage: Partitions.options == Tableaux.options False """ - return self.__getstate__() == other.__getstate__() + return isinstance(other, GlobalOptions) and self.__getstate__() == other.__getstate__() def _add_option(self, option, specifications): r""" diff --git a/src/sage/structure/list_clone.pyx b/src/sage/structure/list_clone.pyx index 125f32762ef..50e57b34ca9 100644 --- a/src/sage/structure/list_clone.pyx +++ b/src/sage/structure/list_clone.pyx @@ -934,7 +934,7 @@ cdef class ClonableArray(ClonableElement): sage: loads(dumps(el)) [1, 2, 4] sage: t = el.__reduce__(); t - (, + (, (, , [1, 2, 4], @@ -1720,7 +1720,7 @@ cdef class ClonableIntArray(ClonableElement): sage: loads(dumps(el)) [1, 2, 4] sage: t = el.__reduce__(); t - (, + (, (, , [1, 2, 4], diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 903a8541460..2c9c1ba894c 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -6363,7 +6363,6 @@ cdef class Expression(Expression_abc): sage: type(u._unpack_operands()[0]) <... 'tuple'> """ - from sage.symbolic.expression import unpack_operands return unpack_operands(self) def operands(self): diff --git a/src/sage/symbolic/function.pyx b/src/sage/symbolic/function.pyx index 0baedce69c0..24cb7adc6c1 100644 --- a/src/sage/symbolic/function.pyx +++ b/src/sage/symbolic/function.pyx @@ -141,11 +141,6 @@ is attempted, and after that ``sin()`` which succeeds:: from sage.structure.sage_object cimport SageObject from sage.structure.element cimport Element, parent, Expression from sage.misc.lazy_attribute import lazy_attribute -from .expression import ( - call_registered_function, find_registered_function, register_or_update_function, - get_sfunction_from_hash -) -from .expression import get_sfunction_from_serial as get_sfunction_from_serial from sage.structure.coerce cimport (coercion_model, py_scalar_to_element, is_numpy_type, is_mpmath_type) @@ -263,6 +258,8 @@ cdef class Function(SageObject): f(x) """ + from .expression import register_or_update_function + self._serial = register_or_update_function(self, self._name, self._latex_name, self._nargs, self._evalf_params_first, False) @@ -544,6 +541,8 @@ cdef class Function(SageObject): if not isinstance(a, Expression): raise TypeError("arguments must be symbolic expressions") + from .expression import call_registered_function + return call_registered_function(self._serial, self._nargs, args, hold, not symbolic_input, SR) @@ -838,6 +837,8 @@ cdef class GinacFunction(BuiltinFunction): preserved_arg=preserved_arg, alt_name=alt_name) cdef _is_registered(self): + from .expression import find_registered_function, get_sfunction_from_serial + # Since this is function is defined in C++, it is already in # ginac's function registry fname = self._ginac_name if self._ginac_name is not None else self._name @@ -845,6 +846,8 @@ cdef class GinacFunction(BuiltinFunction): return bool(get_sfunction_from_serial(self._serial)) cdef _register_function(self): + from .expression import register_or_update_function + # We don't need to add anything to GiNaC's function registry # However, if any custom methods were provided in the python class, # we should set the properties of the function_options object @@ -1094,6 +1097,8 @@ cdef class BuiltinFunction(Function): sage: loads(dumps(cot)) == cot # trac #15138 True """ + from .expression import find_registered_function, get_sfunction_from_serial + # check if already defined cdef unsigned int serial @@ -1191,6 +1196,8 @@ cdef class SymbolicFunction(Function): evalf_params_first) cdef _is_registered(SymbolicFunction self): + from .expression import get_sfunction_from_hash + # see if there is already a SymbolicFunction with the same state cdef long myhash = self._hash_() cdef SymbolicFunction sfunc = get_sfunction_from_hash(myhash) diff --git a/src/sage/symbolic/getitem_impl.pxi b/src/sage/symbolic/getitem_impl.pxi index 72bf9d5f0e5..ff8a9d94138 100644 --- a/src/sage/symbolic/getitem_impl.pxi +++ b/src/sage/symbolic/getitem_impl.pxi @@ -182,7 +182,7 @@ cdef class OperandsWrapper(SageObject): TESTS:: sage: (x^2).op.__reduce__() - (, (x^2,)) + (, (x^2,)) sage: loads(dumps((x^2).op)) Operands of x^2 """ diff --git a/src/sage/symbolic/ring.pyx b/src/sage/symbolic/ring.pyx index 2036a7331d4..77002bef25c 100644 --- a/src/sage/symbolic/ring.pyx +++ b/src/sage/symbolic/ring.pyx @@ -52,8 +52,11 @@ from sage.structure.coerce cimport is_numpy_type import sage.rings.abc from sage.rings.integer_ring import ZZ -# is_SymbolicVariable used to be defined here; re-export it -from sage.symbolic.expression import _is_SymbolicVariable as is_SymbolicVariable +# is_SymbolicVariable used to be defined here; re-export it here lazily +cpdef bint is_SymbolicVariable(x): + from sage.symbolic.expression import _is_SymbolicVariable + + return _is_SymbolicVariable(x) import keyword import operator diff --git a/src/sage_docbuild/conf.py b/src/sage_docbuild/conf.py index 4c880cdca63..a369978cf1d 100644 --- a/src/sage_docbuild/conf.py +++ b/src/sage_docbuild/conf.py @@ -40,12 +40,12 @@ # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ - 'sage_docbuild.ext.inventory_builder', - 'sage_docbuild.ext.multidocs', - 'sage_docbuild.ext.sage_autodoc', 'sphinx.ext.todo', 'sphinx.ext.extlinks', 'sphinx.ext.mathjax', + 'sage_docbuild.ext.sage_autodoc', + 'sage_docbuild.ext.inventory_builder', + 'sage_docbuild.ext.multidocs', 'IPython.sphinxext.ipython_directive', 'matplotlib.sphinxext.plot_directive', 'jupyter_sphinx', diff --git a/src/sage_docbuild/ext/sage_autodoc.py b/src/sage_docbuild/ext/sage_autodoc.py index 0d15eec91a2..418cf67dbd6 100644 --- a/src/sage_docbuild/ext/sage_autodoc.py +++ b/src/sage_docbuild/ext/sage_autodoc.py @@ -1,20 +1,21 @@ -# -*- coding: utf-8 -*- """ Sage autodoc extension -This is based on :mod:`sphinx.ext.autodoc` from Sphinx. +This is :mod:`sphinx.ext.autodoc` extension modified for Sage objects. -From :mod:`sphinx.ext.autodoc`: +The original headline of :mod:`sphinx.ext.autodoc`: + + Extension to create automatic documentation from code docstrings. Automatically insert docstrings for functions, classes or whole modules into the doctree, thus avoiding duplication between docstrings and documentation for those who like elaborate docstrings. - :copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS. - :license: BSD, see LICENSE for details. +Presently this module is based on :mod:`sphinx.ext.autodoc` from Sphinx version 5.3.0. +The upstream original source file is `sphinx/ext/autodoc/__init__.py `_. -The upstream original can be found at -https://github.com/sphinx-doc/sphinx/blob/master/sphinx/ext/autodoc/__init__.py +In the source file of this module, major modifications are delimited by a pair +of comment dividers. To lessen maintenance burdens, we aim at reducing those modifications. AUTHORS: @@ -25,38 +26,49 @@ - Simon King (2011-04): use sageinspect; include public cython attributes only in the documentation if they have a docstring -- Kwankyu Lee (2018-12-26): rebased on the latest sphinx.ext.autodoc +- Kwankyu Lee (2018-12-26, 2022-11-08): rebased on the latest sphinx.ext.autodoc """ -import inspect import re -import sys +import warnings +from inspect import Parameter, Signature +from types import ModuleType +from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterator, List, Optional, Sequence, + Set, Tuple, Type, TypeVar, Union) -from docutils.statemachine import ViewList +from docutils.statemachine import StringList import sphinx -from sphinx.ext.autodoc import mock, ObjectMember -from sphinx.ext.autodoc.importer import import_object, get_object_members +from sphinx.application import Sphinx +from sphinx.config import ENUM, Config +from sphinx.deprecation import RemovedInSphinx60Warning +from sphinx.environment import BuildEnvironment +from sphinx.ext.autodoc.importer import (get_class_members, get_object_members, import_module, + import_object) +from sphinx.ext.autodoc.mock import ismock, mock, undecorate from sphinx.locale import _, __ -from sphinx.pycode import ModuleAnalyzer -from sphinx.errors import PycodeError -from sphinx.util import logging -from sphinx.util.docstrings import prepare_docstring -from sphinx.util.inspect import isdescriptor, \ - safe_getattr, object_description, is_builtin_class_method, \ - isenumattribute, isclassmethod, isstaticmethod, getdoc - +from sphinx.pycode import ModuleAnalyzer, PycodeError +from sphinx.util import inspect, logging +from sphinx.util.docstrings import prepare_docstring, separate_metadata +from sphinx.util.inspect import (evaluate_signature, getdoc, object_description, safe_getattr, + stringify_signature) +from sphinx.util.typing import OptionSpec, get_type_hints, restify +from sphinx.util.typing import stringify as stringify_typehint + +# ------------------------------------------------------------------ from sage.misc.sageinspect import (sage_getdoc_original, sage_getargspec, isclassinstance, sage_formatargspec, is_function_or_cython_function) -from sage.misc.lazy_import import LazyImport -# This is used to filter objects of classes that inherit from -# ClasscallMetaclass. See the class AttributeDocumenter below. -from sage.misc.classcall_metaclass import ClasscallMetaclass +_getdoc = getdoc +def getdoc(obj, *args, **kwargs): + return sage_getdoc_original(obj) +# ------------------------------------------------------------------ +if TYPE_CHECKING: + from sphinx.ext.autodoc.directive import DocumenterBridge logger = logging.getLogger(__name__) @@ -66,7 +78,7 @@ MethodDescriptorType = type(type.__subclasses__) -# extended signature RE: with explicit module name separated by ``::`` +#: extended signature RE: with explicit module name separated by ``::``. py_ext_sig_re = re.compile( r'''^ ([\w.]+::)? # explicit module name ([\w.]+\.)? # module and/or class name(s) @@ -75,116 +87,119 @@ (?:\s* -> \s* (.*))? # return annotation )? $ # and nothing more ''', re.VERBOSE) +special_member_re = re.compile(r'^__\S+__$') -def identity(x): - # type: (Any) -> Any +def identity(x: Any) -> Any: return x -ALL = object() +class _All: + """A special value for :*-members: that matches to any member.""" + + def __contains__(self, item: Any) -> bool: + return True + + def append(self, item: Any) -> None: + pass # nothing + + +class _Empty: + """A special value for :exclude-members: that never matches to any member.""" + + def __contains__(self, item: Any) -> bool: + return False + + +ALL = _All() +EMPTY = _Empty() +UNINITIALIZED_ATTR = object() INSTANCEATTR = object() +SLOTSATTR = object() -def members_option(arg): - # type: (Any) -> Union[object, List[unicode]] +def members_option(arg: Any) -> Union[object, List[str]]: """Used to convert the :members: option to auto directives.""" - if arg is None: + if arg in (None, True): return ALL - return [x.strip() for x in arg.split(',')] + elif arg is False: + return None + else: + return [x.strip() for x in arg.split(',') if x.strip()] -def members_set_option(arg): - # type: (Any) -> Union[object, Set[unicode]] - """Used to convert the :members: option to auto directives.""" - if arg is None: - return ALL - return set(x.strip() for x in arg.split(',')) +def exclude_members_option(arg: Any) -> Union[object, Set[str]]: + """Used to convert the :exclude-members: option.""" + if arg in (None, True): + return EMPTY + return {x.strip() for x in arg.split(',') if x.strip()} + + +def inherited_members_option(arg: Any) -> Set[str]: + """Used to convert the :inherited-members: option to auto directives.""" + if arg in (None, True): + return {'object'} + elif arg: + return {x.strip() for x in arg.split(',')} + else: + return set() + + +def member_order_option(arg: Any) -> Optional[str]: + """Used to convert the :member-order: option to auto directives.""" + if arg in (None, True): + return None + elif arg in ('alphabetical', 'bysource', 'groupwise'): + return arg + else: + raise ValueError(__('invalid value for member-order option: %s') % arg) + + +def class_doc_from_option(arg: Any) -> Optional[str]: + """Used to convert the :class-doc-from: option to autoclass directives.""" + if arg in ('both', 'class', 'init'): + return arg + else: + raise ValueError(__('invalid value for class-doc-from option: %s') % arg) SUPPRESS = object() -def annotation_option(arg): - # type: (Any) -> Any - if arg is None: +def annotation_option(arg: Any) -> Any: + if arg in (None, True): # suppress showing the representation of the object return SUPPRESS else: return arg -def bool_option(arg): - # type: (Any) -> bool +def bool_option(arg: Any) -> bool: """Used to convert flag options to auto directives. (Instead of directives.flag(), which returns None). """ return True -def formatargspec(function, args, varargs=None, varkw=None, defaults=None, - kwonlyargs=(), kwonlydefaults={}, annotations={}): - """ - Sphinx's version of formatargspec is deprecated, so use Sage's instead. - """ - return sage_formatargspec(args, varargs=varargs, varkw=varkw, defaults=defaults, - kwonlyargs=kwonlyargs, kwonlydefaults=kwonlydefaults, - annotations=annotations) - - -class AutodocReporter(): - """ - A reporter replacement that assigns the correct source name - and line number to a system message, as recorded in a ViewList. +def merge_members_option(options: Dict) -> None: + """Merge :private-members: and :special-members: options to the + :members: option. """ - def __init__(self, viewlist, reporter): - # type: (ViewList, Reporter) -> None - self.viewlist = viewlist - self.reporter = reporter - - def __getattr__(self, name): - # type: (unicode) -> Any - return getattr(self.reporter, name) - - def system_message(self, level, message, *children, **kwargs): - # type: (int, unicode, Any, Any) -> nodes.system_message - if 'line' in kwargs and 'source' not in kwargs: - try: - source, line = self.viewlist.items[kwargs['line']] - except IndexError: - pass - else: - kwargs['source'] = source - kwargs['line'] = line - return self.reporter.system_message(level, message, - *children, **kwargs) - - def debug(self, *args, **kwargs): - # type: (Any, Any) -> nodes.system_message - if self.reporter.debug_flag: - return self.system_message(0, *args, **kwargs) + if options.get('members') is ALL: + # merging is not needed when members: ALL + return - def info(self, *args, **kwargs): - # type: (Any, Any) -> nodes.system_message - return self.system_message(1, *args, **kwargs) - - def warning(self, *args, **kwargs): - # type: (Any, Any) -> nodes.system_message - return self.system_message(2, *args, **kwargs) - - def error(self, *args, **kwargs): - # type: (Any, Any) -> nodes.system_message - return self.system_message(3, *args, **kwargs) - - def severe(self, *args, **kwargs): - # type: (Any, Any) -> nodes.system_message - return self.system_message(4, *args, **kwargs) + members = options.setdefault('members', []) + for key in {'private-members', 'special-members'}: + if key in options and options[key] not in (ALL, None): + for member in options[key]: + if member not in members: + members.append(member) # Some useful event listener factories for autodoc-process-docstring. -def cut_lines(pre, post=0, what=None): - # type: (int, int, unicode) -> Callable +def cut_lines(pre: int, post: int = 0, what: str = None) -> Callable: """Return a listener that removes the first *pre* and last *post* lines of every docstring. If *what* is a sequence of strings, only docstrings of a type in *what* will be processed. @@ -194,11 +209,10 @@ def cut_lines(pre, post=0, what=None): from sphinx.ext.autodoc import cut_lines app.connect('autodoc-process-docstring', cut_lines(4, what=['module'])) - This can (and should) be used in place of ``automodule_skip_lines`` (config - value defined in Sphinx autodoc). + This can (and should) be used in place of ``automodule_skip_lines``. """ - def process(app, what_, name, obj, options, lines): - # type: (Sphinx, unicode, unicode, Any, Any, List[unicode]) -> None + def process(app: Sphinx, what_: str, name: str, obj: Any, options: Any, lines: List[str] + ) -> None: if what and what_ not in what: return del lines[:pre] @@ -213,8 +227,8 @@ def process(app, what_, name, obj, options, lines): return process -def between(marker, what=None, keepempty=False, exclude=False): - # type: (unicode, Sequence[unicode], bool, bool) -> Callable +def between(marker: str, what: Sequence[str] = None, keepempty: bool = False, + exclude: bool = False) -> Callable: """Return a listener that either keeps, or if *exclude* is True excludes, lines between lines that match the *marker* regular expression. If no line matches, the resulting docstring would be empty, so no change will be made @@ -225,8 +239,8 @@ def between(marker, what=None, keepempty=False, exclude=False): """ marker_re = re.compile(marker) - def process(app, what_, name, obj, options, lines): - # type: (Sphinx, unicode, unicode, Any, Any, List[unicode]) -> None + def process(app: Sphinx, what_: str, name: str, obj: Any, options: Any, lines: List[str] + ) -> None: if what and what_ not in what: return deleted = 0 @@ -249,7 +263,50 @@ def process(app, what_, name, obj, options, lines): return process -class Documenter(): +# This class is used only in ``sphinx.ext.autodoc.directive``, +# But we define this class here to keep compatibility (see #4538) +class Options(dict): + """A dict/attribute hybrid that returns None on nonexisting keys.""" + def copy(self) -> "Options": + return Options(super().copy()) + + def __getattr__(self, name: str) -> Any: + try: + return self[name.replace('_', '-')] + except KeyError: + return None + + +class ObjectMember(tuple): + """A member of object. + + This is used for the result of ``Documenter.get_object_members()`` to + represent each member of the object. + + .. Note:: + + An instance of this class behaves as a tuple of (name, object) + for compatibility to old Sphinx. The behavior will be dropped + in the future. Therefore extensions should not use the tuple + interface. + """ + + def __new__(cls, name: str, obj: Any, **kwargs: Any) -> Any: + return super().__new__(cls, (name, obj)) # type: ignore + + def __init__(self, name: str, obj: Any, docstring: Optional[str] = None, + class_: Any = None, skipped: bool = False) -> None: + self.__name__ = name + self.object = obj + self.docstring = docstring + self.skipped = skipped + self.class_ = class_ + + +ObjectMembers = Union[List[ObjectMember], List[Tuple[str, Any]]] + + +class Documenter: """ A Documenter knows how to autodocument a single object type. When registered with the AutoDirective, it will be used to document objects @@ -262,13 +319,13 @@ class Documenter(): A Documenter has an *option_spec* that works like a docutils directive's; in fact, it will be used to parse an auto directive's options that matches - the documenter. + the Documenter. """ #: name by which the directive is called (auto...) and the default #: generated directive name objtype = 'object' #: indentation by which to indent the directive content - content_indent = u' ' + content_indent = ' ' #: priority if multiple documenters return True from can_document_member priority = 0 #: order if autodoc_member_order is set to 'groupwise' @@ -276,57 +333,59 @@ class Documenter(): #: true if the generated content may contain titles titles_allowed = False - option_spec = {'noindex': bool_option} # type: Dict[unicode, Callable] + option_spec: OptionSpec = { + 'noindex': bool_option + } - def get_attr(self, obj, name, *defargs): - # type: (Any, unicode, Any) -> Any + def get_attr(self, obj: Any, name: str, *defargs: Any) -> Any: """getattr() override for types such as Zope interfaces.""" return autodoc_attrgetter(self.env.app, obj, name, *defargs) @classmethod - def can_document_member(cls, member, membername, isattr, parent): - # type: (Any, unicode, bool, Any) -> bool - """Called to see if a member can be documented by this documenter.""" + def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any + ) -> bool: + """Called to see if a member can be documented by this Documenter.""" raise NotImplementedError('must be implemented in subclasses') - def __init__(self, directive, name, indent=u''): - # type: (DocumenterBridge, unicode, unicode) -> None + def __init__(self, directive: "DocumenterBridge", name: str, indent: str = '') -> None: self.directive = directive - self.env = directive.env # type: BuildEnvironment + self.config: Config = directive.env.config + self.env: BuildEnvironment = directive.env self.options = directive.genopt self.name = name self.indent = indent # the module and object path within the module, and the fully # qualified name (all set after resolve_name succeeds) - self.modname = None # type: str - self.module = None # type: ModuleType - self.objpath = None # type: List[unicode] - self.fullname = None # type: unicode + self.modname: str = None + self.module: ModuleType = None + self.objpath: List[str] = None + self.fullname: str = None # extra signature items (arguments and return annotation, # also set after resolve_name succeeds) - self.args = None # type: unicode - self.retann = None # type: unicode + self.args: str = None + self.retann: str = None # the object to document (set after import_object succeeds) - self.object = None # type: Any - self.object_name = None # type: unicode + self.object: Any = None + self.object_name: str = None # the parent/owner of the object to document - self.parent = None # type: Any + self.parent: Any = None # the module analyzer to get at attribute docs, or None - self.analyzer = None # type: Any + self.analyzer: ModuleAnalyzer = None @property - def documenters(self): - # type: () -> Dict[unicode, Type[Documenter]] + def documenters(self) -> Dict[str, Type["Documenter"]]: """Returns registered Documenter classes""" - return get_documenters(self.env.app) + return self.env.app.registry.documenters - def add_line(self, line, source, *lineno): - # type: (unicode, unicode, int) -> None + def add_line(self, line: str, source: str, *lineno: int) -> None: """Append one line of generated reST to the output.""" - self.directive.result.append(self.indent + line, source, *lineno) + if line.strip(): # not a blank line + self.directive.result.append(self.indent + line, source, *lineno) + else: + self.directive.result.append('', source, *lineno) - def resolve_name(self, modname, parents, path, base): - # type: (str, Any, str, Any) -> Tuple[str, List[unicode]] + def resolve_name(self, modname: str, parents: Any, path: str, base: Any + ) -> Tuple[str, List[str]]: """Resolve the module and name of the object to document given by the arguments and the current module/class. @@ -336,8 +395,7 @@ def resolve_name(self, modname, parents, path, base): """ raise NotImplementedError('must be implemented in subclasses') - def parse_name(self): - # type: () -> bool + def parse_name(self) -> bool: """Determine what module to import and what attribute to document. Returns True and sets *self.modname*, *self.objpath*, *self.fullname*, @@ -347,52 +405,57 @@ def parse_name(self): # functions can contain a signature which is then used instead of # an autogenerated one try: - explicit_modname, path, base, args, retann = \ - py_ext_sig_re.match(self.name).groups() # type: ignore + matched = py_ext_sig_re.match(self.name) + explicit_modname, path, base, args, retann = matched.groups() except AttributeError: - logger.warning('invalid signature for auto%s (%r)' % (self.objtype, self.name)) + logger.warning(__('invalid signature for auto%s (%r)') % (self.objtype, self.name), + type='autodoc') return False # support explicit module and class name separation via :: if explicit_modname is not None: modname = explicit_modname[:-2] - parents = path and path.rstrip('.').split('.') or [] + parents = path.rstrip('.').split('.') if path else [] else: modname = None parents = [] - self.modname, self.objpath = self.resolve_name(modname, parents, path, base) + with mock(self.config.autodoc_mock_imports): + self.modname, self.objpath = self.resolve_name(modname, parents, path, base) if not self.modname: return False self.args = args self.retann = retann - self.fullname = (self.modname or '') + \ - (self.objpath and '.' + '.'.join(self.objpath) or '') + self.fullname = ((self.modname or '') + + ('.' + '.'.join(self.objpath) if self.objpath else '')) return True - def import_object(self): - # type: () -> bool + def import_object(self, raiseerror: bool = False) -> bool: """Import the object given by *self.modname* and *self.objpath* and set it as *self.object*. Returns True if successful, False if an error occurred. """ - with mock(self.env.config.autodoc_mock_imports): + with mock(self.config.autodoc_mock_imports): try: ret = import_object(self.modname, self.objpath, self.objtype, attrgetter=self.get_attr, - warningiserror=self.env.config.autodoc_warningiserror) + warningiserror=self.config.autodoc_warningiserror) self.module, self.parent, self.object_name, self.object = ret + if ismock(self.object): + self.object = undecorate(self.object) return True except ImportError as exc: - logger.warning(exc.args[0], type='autodoc', subtype='import_object') - self.env.note_reread() - return False + if raiseerror: + raise + else: + logger.warning(exc.args[0], type='autodoc', subtype='import_object') + self.env.note_reread() + return False - def get_real_modname(self): - # type: () -> str + def get_real_modname(self) -> str: """Get the real module name of an object to document. It can differ from the name of the module through which the object was @@ -400,29 +463,27 @@ def get_real_modname(self): """ return self.get_attr(self.object, '__module__', None) or self.modname - def check_module(self): - # type: () -> bool + def check_module(self) -> bool: """Check if *self.object* is really defined in the module given by *self.modname*. """ if self.options.imported_members: return True - modname = self.get_attr(self.object, '__module__', None) + subject = inspect.unpartial(self.object) + modname = self.get_attr(subject, '__module__', None) if modname and modname != self.modname: return False return True - def format_args(self): - # type: () -> unicode + def format_args(self, **kwargs: Any) -> str: """Format the argument signature of *self.object*. Should return None if the object does not have a signature. """ return None - def format_name(self): - # type: () -> unicode + def format_name(self) -> str: """Format the name of *self.object*. This normally should be something that can be parsed by the generated @@ -433,68 +494,90 @@ def format_name(self): # directives of course) return '.'.join(self.objpath) or self.modname - def format_signature(self): - # type: () -> unicode + def _call_format_args(self, **kwargs: Any) -> str: + if kwargs: + try: + return self.format_args(**kwargs) + except TypeError: + # avoid chaining exceptions, by putting nothing here + pass + + # retry without arguments for old documenters + return self.format_args() + + def format_signature(self, **kwargs: Any) -> str: """Format the signature (arguments and return annotation) of the object. Let the user process it via the ``autodoc-process-signature`` event. """ + #if 'Expression.numerical_approx' in self.name: + # from celery.contrib import rdb; rdb.set_trace() if self.args is not None: # signature given explicitly - args = "(%s)" % self.args # type: unicode + args = "(%s)" % self.args + retann = self.retann else: # try to introspect the signature try: - args = self.format_args() - except Exception as err: - logger.warning('error while formatting arguments for %s: %s' % - (self.fullname, err)) + retann = None + args = self._call_format_args(**kwargs) + if args: + matched = re.match(r'^(\(.*\))\s+->\s+(.*)$', args) + if matched: + args = matched.group(1) + retann = matched.group(2) + except Exception as exc: + logger.warning(__('error while formatting arguments for %s: %s'), + self.fullname, exc, type='autodoc') args = None - retann = self.retann - - result = self.env.app.emit_firstresult( - 'autodoc-process-signature', self.objtype, self.fullname, - self.object, self.options, args, retann) + result = self.env.events.emit_firstresult('autodoc-process-signature', + self.objtype, self.fullname, + self.object, self.options, args, retann) if result: args, retann = result if args is not None: - return args + (retann and (' -> %s' % retann) or '') + return args + ((' -> %s' % retann) if retann else '') else: return '' - def add_directive_header(self, sig): - # type: (unicode) -> None + def add_directive_header(self, sig: str) -> None: """Add the directive header and options to the generated content.""" domain = getattr(self, 'domain', 'py') directive = getattr(self, 'directivetype', self.objtype) name = self.format_name() sourcename = self.get_sourcename() - self.add_line(u'.. %s:%s:: %s%s' % (domain, directive, name, sig), - sourcename) + + # one signature per line, indented by column + prefix = '.. %s:%s:: ' % (domain, directive) + for i, sig_line in enumerate(sig.split("\n")): + self.add_line('%s%s%s' % (prefix, name, sig_line), + sourcename) + if i == 0: + prefix = " " * len(prefix) + if self.options.noindex: - self.add_line(u' :noindex:', sourcename) + self.add_line(' :noindex:', sourcename) if self.objpath: # Be explicit about the module, this is necessary since .. class:: # etc. don't support a prepended module name - self.add_line(u' :module: %s' % self.modname, sourcename) - - def get_doc(self, encoding=None, ignore=1): - # type: (unicode, int) -> List[List[unicode]] - """Decode and return lines of the docstring(s) for the object.""" - docstring = sage_getdoc_original(self.object) - if docstring is None and self.env.config.autodoc_inherit_docstrings: - docstring = getdoc(self.object) - # make sure we have Unicode docstrings, then sanitize and split - # into lines - if isinstance(docstring, str): - return [prepare_docstring(docstring)] - # ... else it is something strange, let's ignore it + self.add_line(' :module: %s' % self.modname, sourcename) + + def get_doc(self) -> Optional[List[List[str]]]: + """Decode and return lines of the docstring(s) for the object. + + When it returns None, autodoc-process-docstring will not be called for this + object. + """ + docstring = getdoc(self.object, self.get_attr, self.config.autodoc_inherit_docstrings, + self.parent, self.object_name) + if docstring: + tab_width = self.directive.state.document.settings.tab_width + return [prepare_docstring(docstring, tab_width)] return [] - def process_doc(self, docstrings): - # type: (List[List[unicode]]) -> Iterator[unicode] + def process_doc(self, docstrings: List[List[str]]) -> Iterator[str]: """Let the user process the docstrings before adding them.""" for docstringlines in docstrings: if self.env.app: @@ -502,24 +585,31 @@ def process_doc(self, docstrings): self.env.app.emit('autodoc-process-docstring', self.objtype, self.fullname, self.object, self.options, docstringlines) - for line in docstringlines: - yield line - def get_sourcename(self): - # type: () -> unicode + if docstringlines and docstringlines[-1] != '': + # append a blank line to the end of the docstring + docstringlines.append('') + + yield from docstringlines + + def get_sourcename(self) -> str: + if (inspect.safe_getattr(self.object, '__module__', None) and + inspect.safe_getattr(self.object, '__qualname__', None)): + # Get the correct location of docstring from self.object + # to support inherited methods + fullname = '%s.%s' % (self.object.__module__, self.object.__qualname__) + else: + fullname = self.fullname + if self.analyzer: - # prevent encoding errors when the file name is non-ASCII - if not isinstance(self.analyzer.srcname, str): - filename = str(self.analyzer.srcname, - sys.getfilesystemencoding(), 'replace') - else: - filename = self.analyzer.srcname - return u'%s:docstring of %s' % (filename, self.fullname) - return u'docstring of %s' % self.fullname + return '%s:docstring of %s' % (self.analyzer.srcname, fullname) + else: + return 'docstring of %s' % fullname - def add_content(self, more_content, no_docstring=False): - # type: (Any, bool) -> None + def add_content(self, more_content: Optional[StringList]) -> None: """Add content from docstrings, attribute documentation and user.""" + docstring = True + # set sourcename and add content from attribute documentation sourcename = self.get_sourcename() if self.analyzer: @@ -527,57 +617,64 @@ def add_content(self, more_content, no_docstring=False): if self.objpath: key = ('.'.join(self.objpath[:-1]), self.objpath[-1]) if key in attr_docs: - no_docstring = True - docstrings = [attr_docs[key]] + docstring = False + # make a copy of docstring for attributes to avoid cache + # the change of autodoc-process-docstring event. + docstrings = [list(attr_docs[key])] + for i, line in enumerate(self.process_doc(docstrings)): self.add_line(line, sourcename, i) # add content from docstrings - if not no_docstring: - encoding = self.analyzer - docstrings = self.get_doc(encoding) - if not docstrings: - # append at least a dummy docstring, so that the event - # autodoc-process-docstring is fired and can add some - # content if desired - docstrings.append([]) - for i, line in enumerate(self.process_doc(docstrings)): - self.add_line(line, sourcename, i) + if docstring: + docstrings = self.get_doc() + if docstrings is None: + # Do not call autodoc-process-docstring on get_doc() returns None. + pass + else: + if not docstrings: + # append at least a dummy docstring, so that the event + # autodoc-process-docstring is fired and can add some + # content if desired + docstrings.append([]) + for i, line in enumerate(self.process_doc(docstrings)): + self.add_line(line, sourcename, i) # add additional content (e.g. from document), if present if more_content: for line, src in zip(more_content.data, more_content.items): self.add_line(line, src[0], src[1]) - def get_object_members(self, want_all): - # type: (bool) -> Tuple[bool, List[Tuple[unicode, Any]]] - """Return `(members_check_module, members)` where `members` is a - list of `(membername, member)` pairs of the members of *self.object*. + def get_object_members(self, want_all: bool) -> Tuple[bool, ObjectMembers]: + """Return ``(members_check_module, members)`` where ``members`` is a + list of ``(membername, member)`` pairs of the members of *self.object*. If *want_all* is True, return all members. Else, only return those - members given by *self.options.members* (which may also be none). + members given by *self.options.members* (which may also be None). """ + warnings.warn('The implementation of Documenter.get_object_members() will be ' + 'removed from Sphinx-6.0.', RemovedInSphinx60Warning) members = get_object_members(self.object, self.objpath, self.get_attr, self.analyzer) if not want_all: if not self.options.members: - return False, [] + return False, [] # type: ignore # specific members given selected = [] for name in self.options.members: if name in members: selected.append((name, members[name].value)) else: - logger.warning('missing attribute %s in object %s' % - (name, self.fullname)) - return False, sorted(selected) + logger.warning(__('missing attribute %s in object %s') % + (name, self.fullname), type='autodoc') + return False, selected elif self.options.inherited_members: - return False, sorted((m.name, m.value) for m in members.values()) + return False, [(m.name, m.value) for m in members.values()] else: - return False, sorted((m.name, m.value) for m in members.values() - if m.directly_defined) + return False, [(m.name, m.value) for m in members.values() + if m.directly_defined] - def filter_members(self, members, want_all): - # type: (List[Tuple[unicode, Any]], bool) -> List[Tuple[unicode, Any, bool]] + def filter_members(self, members: ObjectMembers, want_all: bool + ) -> List[Tuple[str, Any, bool]]: """Filter the given member list. Members are skipped if @@ -591,6 +688,23 @@ def filter_members(self, members, want_all): The user can override the skipping decision by connecting to the ``autodoc-skip-member`` event. """ + def is_filtered_inherited_member(name: str, obj: Any) -> bool: + inherited_members = self.options.inherited_members or set() + + if inspect.isclass(self.object): + for cls in self.object.__mro__: + if cls.__name__ in inherited_members and cls != self.object: + # given member is a member of specified *super class* + return True + elif name in cls.__dict__: + return False + elif name in self.get_attr(cls, '__annotations__', {}): + return False + elif isinstance(obj, ObjectMember) and obj.class_ is cls: + return False + + return False + ret = [] # search for members in source code too @@ -602,81 +716,129 @@ def filter_members(self, members, want_all): attr_docs = {} # process members and determine which to skip - for (membername, member) in members: - # Immediately skip lazy imports to avoid deprecation - # messages (#17455). - if isinstance(member, LazyImport): - continue + for obj in members: + try: + membername, member = obj + # --------------------------------------------------- + # Trac #17455: Immediately skip lazy imports to avoid + # deprecation messages. + from sage.misc.lazy_import import LazyImport + if isinstance(member, LazyImport): + continue + # --------------------------------------------------- + # if isattr is True, the member is documented as an attribute + if member is INSTANCEATTR: + isattr = True + elif (namespace, membername) in attr_docs: + isattr = True + else: + isattr = False - # if isattr is True, the member is documented as an attribute - isattr = False + doc = getdoc(member, self.get_attr, self.config.autodoc_inherit_docstrings, + self.object, membername) + if not isinstance(doc, str): + # Ignore non-string __doc__ + doc = None - doc = self.get_attr(member, '__doc__', None) - if doc is None and self.env.config.autodoc_inherit_docstrings: - doc = getdoc(member) + # if the member __doc__ is the same as self's __doc__, it's just + # inherited and therefore not the member's doc + cls = self.get_attr(member, '__class__', None) + if cls: + cls_doc = self.get_attr(cls, '__doc__', None) + if cls_doc == doc: + doc = None + + if isinstance(obj, ObjectMember) and obj.docstring: + # hack for ClassDocumenter to inject docstring via ObjectMember + doc = obj.docstring + + doc, metadata = separate_metadata(doc) + has_doc = bool(doc) + + if 'private' in metadata: + # consider a member private if docstring has "private" metadata + isprivate = True + elif 'public' in metadata: + # consider a member public if docstring has "public" metadata + isprivate = False + else: + isprivate = membername.startswith('_') - # if the member __doc__ is the same as self's __doc__, it's just - # inherited and therefore not the member's doc - cls = self.get_attr(member, '__class__', None) - if cls: - cls_doc = self.get_attr(cls, '__doc__', None) - if cls_doc == doc: - doc = None - has_doc = bool(doc) - - keep = False - if want_all and membername.startswith('__') and \ - membername.endswith('__') and len(membername) > 4: - # special __methods__ - if self.options.special_members is ALL and \ - membername != '__doc__': - keep = has_doc or self.options.undoc_members - elif self.options.special_members and \ - self.options.special_members is not ALL and \ - membername in self.options.special_members: - keep = has_doc or self.options.undoc_members - elif (namespace, membername) in attr_docs: - if want_all and membername.startswith('_'): - # ignore members whose name starts with _ by default - keep = self.options.private_members + keep = False + if ismock(member) and (namespace, membername) not in attr_docs: + # mocked module or object + pass + elif (self.options.exclude_members and + membername in self.options.exclude_members): + # remove members given by exclude-members + keep = False + elif want_all and special_member_re.match(membername): + # special __methods__ + if (self.options.special_members and + membername in self.options.special_members): + if membername == '__doc__': + keep = False + elif is_filtered_inherited_member(membername, obj): + keep = False + else: + keep = has_doc or self.options.undoc_members + else: + keep = False + elif (namespace, membername) in attr_docs: + if want_all and isprivate: + if self.options.private_members is None: + keep = False + else: + keep = membername in self.options.private_members + else: + # keep documented attributes + keep = True + elif want_all and isprivate: + if has_doc or self.options.undoc_members: + if self.options.private_members is None: + keep = False + elif is_filtered_inherited_member(membername, obj): + keep = False + else: + keep = membername in self.options.private_members + else: + keep = False else: - # keep documented attributes - keep = True - isattr = True - elif want_all and membername.startswith('_'): - # ignore members whose name starts with _ by default - keep = self.options.private_members and \ - (has_doc or self.options.undoc_members) - else: - # ignore undocumented members if :undoc-members: is not given - keep = has_doc or self.options.undoc_members + if (self.options.members is ALL and + is_filtered_inherited_member(membername, obj)): + keep = False + else: + # ignore undocumented members if :undoc-members: is not given + keep = has_doc or self.options.undoc_members + + if isinstance(obj, ObjectMember) and obj.skipped: + # forcedly skipped member (ex. a module attribute not defined in __all__) + keep = False - # give the user a chance to decide whether this member - # should be skipped - if self.env.app: - # let extensions preprocess docstrings - try: + # give the user a chance to decide whether this member + # should be skipped + if self.env.app: + # let extensions preprocess docstrings skip_user = self.env.app.emit_firstresult( 'autodoc-skip-member', self.objtype, membername, member, not keep, self.options) if skip_user is not None: keep = not skip_user - except Exception as exc: - logger.warning(__('autodoc: failed to determine %r to be documented.' - 'the following exception was raised:\n%s'), - member, exc) - keep = False + except Exception as exc: + logger.warning(__('autodoc: failed to determine %s.%s (%r) to be documented, ' + 'the following exception was raised:\n%s'), + self.name, membername, member, exc, type='autodoc') + keep = False if keep: ret.append((membername, member, isattr)) return ret - def document_members(self, all_members=False): - # type: (bool) -> None + def document_members(self, all_members: bool = False) -> None: """Generate reST for member documentation. - If *all_members* is True, do all members, else those given by + If *all_members* is True, document all members, else those given by *self.options.members*. """ # set current namespace for finding members @@ -684,18 +846,14 @@ def document_members(self, all_members=False): if self.objpath: self.env.temp_data['autodoc:class'] = self.objpath[0] - want_all = all_members or self.options.inherited_members or \ - self.options.members is ALL + want_all = (all_members or + self.options.inherited_members or + self.options.members is ALL) # find out which members are documentable members_check_module, members = self.get_object_members(want_all) - # remove members given by exclude-members - if self.options.exclude_members: - members = [(membername, member) for (membername, member) in members - if membername not in self.options.exclude_members] - # document non-skipped members - memberdocumenters = [] # type: List[Tuple[Documenter, bool]] + memberdocumenters: List[Tuple[Documenter, bool]] = [] for (mname, member, isattr) in self.filter_members(members, want_all): classes = [cls for cls in self.documenters.values() if cls.can_document_member(member, mname, isattr, self)] @@ -706,25 +864,12 @@ def document_members(self, all_members=False): classes.sort(key=lambda cls: cls.priority) # give explicitly separated module name, so that members # of inner classes can be documented - full_mname = self.modname + '::' + \ - '.'.join(self.objpath + [mname]) + full_mname = self.modname + '::' + '.'.join(self.objpath + [mname]) documenter = classes[-1](self.directive, full_mname, self.indent) memberdocumenters.append((documenter, isattr)) - member_order = self.options.member_order or \ - self.env.config.autodoc_member_order - if member_order == 'groupwise': - # sort by group; relies on stable sort to keep items in the - # same group sorted alphabetically - memberdocumenters.sort(key=lambda e: e[0].member_order) - elif member_order == 'bysource' and self.analyzer: - # sort by source order, by virtue of the module analyzer - tagorder = self.analyzer.tagorder - - def keyfunc(entry): - # type: (Tuple[Documenter, bool]) -> int - fullname = entry[0].name.split('::')[1] - return tagorder.get(fullname, len(tagorder)) - memberdocumenters.sort(key=keyfunc) + + member_order = self.options.member_order or self.config.autodoc_member_order + memberdocumenters = self.sort_members(memberdocumenters, member_order) for documenter, isattr in memberdocumenters: documenter.generate( @@ -735,9 +880,33 @@ def keyfunc(entry): self.env.temp_data['autodoc:module'] = None self.env.temp_data['autodoc:class'] = None - def generate(self, more_content=None, real_modname=None, - check_module=False, all_members=False): - # type: (Any, str, bool, bool) -> None + def sort_members(self, documenters: List[Tuple["Documenter", bool]], + order: str) -> List[Tuple["Documenter", bool]]: + """Sort the given member list.""" + if order == 'groupwise': + # sort by group; alphabetically within groups + documenters.sort(key=lambda e: (e[0].member_order, e[0].name)) + elif order == 'bysource': + if self.analyzer: + # sort by source order, by virtue of the module analyzer + tagorder = self.analyzer.tagorder + + def keyfunc(entry: Tuple[Documenter, bool]) -> int: + fullname = entry[0].name.split('::')[1] + return tagorder.get(fullname, len(tagorder)) + documenters.sort(key=keyfunc) + else: + # Assume that member discovery order matches source order. + # This is a reasonable assumption in Python 3.6 and up, where + # module.__dict__ is insertion-ordered. + pass + else: # alphabetical + documenters.sort(key=lambda e: e[0].name) + + return documenters + + def generate(self, more_content: Optional[StringList] = None, real_modname: str = None, + check_module: bool = False, all_members: bool = False) -> None: """Generate reST for the object given by *self.name*, and possibly for its members. @@ -749,10 +918,10 @@ def generate(self, more_content=None, real_modname=None, if not self.parse_name(): # need a module to import logger.warning( - 'don\'t know which module to import for autodocumenting ' - '%r (try placing a "module" or "currentmodule" directive ' - 'in the document, or giving an explicit module name)' % - self.name) + __('don\'t know which module to import for autodocumenting ' + '%r (try placing a "module" or "currentmodule" directive ' + 'in the document, or giving an explicit module name)') % + self.name, type='autodoc') return # now, import the module and get object to document @@ -764,7 +933,8 @@ def generate(self, more_content=None, real_modname=None, # where the attribute documentation would actually be found in. # This is used for situations where you have a module that collects the # functions and classes of internal submodules. - self.real_modname = real_modname or self.get_real_modname() # type: str + guess_modname = self.get_real_modname() + self.real_modname: str = real_modname or guess_modname # try to also get a source code analyzer for attribute docs try: @@ -772,25 +942,29 @@ def generate(self, more_content=None, real_modname=None, # parse right now, to get PycodeErrors on parsing (results will # be cached anyway) self.analyzer.find_attr_docs() - except PycodeError as err: - logger.debug('[autodoc] module analyzer failed: %s', err) + except PycodeError as exc: + logger.debug('[autodoc] module analyzer failed: %s', exc) # no source file -- e.g. for builtin and C modules self.analyzer = None - # at least add the module as a dependency - name = self.module.__name__ if hasattr(self.module, '__name__') else None - fname = self.module.__file__ if hasattr(self.module, '__file__') else None - if name != self.real_modname: - # !!! SageMath Specific for make fast-rebuild-clean !!! - # try not to record a dependency to a .pyc file but to the corresponding .py files instead. - try: - fname = ModuleAnalyzer.for_module(name).srcname - except PycodeError: - pass - if fname is not None: - self.directive.record_dependencies.add(fname) + # at least add the module.__file__ as a dependency + if hasattr(self.module, '__file__') and self.module.__file__: + self.directive.record_dependencies.add(self.module.__file__) else: self.directive.record_dependencies.add(self.analyzer.srcname) + if self.real_modname != guess_modname: + # Add module to dependency list if target object is defined in other module. + try: + analyzer = ModuleAnalyzer.for_module(guess_modname) + self.directive.record_dependencies.add(analyzer.srcname) + except PycodeError: + pass + + docstrings: List[str] = sum(self.get_doc() or [], []) + if ismock(self.object) and not docstrings: + logger.warning(__('A mocked object is detected: %r'), + self.name, type='autodoc') + # check __module__ of object (for members not given explicitly) if check_module: if not self.check_module(): @@ -804,7 +978,12 @@ def generate(self, more_content=None, real_modname=None, self.add_line('', sourcename) # format the object's signature, if any - sig = self.format_signature() + try: + sig = self.format_signature() + except Exception as exc: + logger.warning(__('error while formatting signature for %s: %s'), + self.fullname, exc, type='autodoc') + return # generate the directive header and options, if applicable self.add_directive_header(sig) @@ -825,114 +1004,154 @@ class ModuleDocumenter(Documenter): Specialized Documenter subclass for modules. """ objtype = 'module' - content_indent = u'' + content_indent = '' titles_allowed = True + _extra_indent = ' ' - option_spec = { + option_spec: OptionSpec = { 'members': members_option, 'undoc-members': bool_option, - 'noindex': bool_option, 'inherited-members': bool_option, + 'noindex': bool_option, 'inherited-members': inherited_members_option, 'show-inheritance': bool_option, 'synopsis': identity, 'platform': identity, 'deprecated': bool_option, - 'member-order': identity, 'exclude-members': members_set_option, - 'private-members': bool_option, 'special-members': members_option, - 'imported-members': bool_option, - } # type: Dict[unicode, Callable] + 'member-order': member_order_option, 'exclude-members': exclude_members_option, + 'private-members': members_option, 'special-members': members_option, + 'imported-members': bool_option, 'ignore-module-all': bool_option, + 'no-value': bool_option, + } + + def __init__(self, *args: Any) -> None: + super().__init__(*args) + merge_members_option(self.options) + self.__all__: Optional[Sequence[str]] = None + + def add_content(self, more_content: Optional[StringList]) -> None: + old_indent = self.indent + self.indent += self._extra_indent + super().add_content(None) + self.indent = old_indent + if more_content: + for line, src in zip(more_content.data, more_content.items): + self.add_line(line, src[0], src[1]) @classmethod - def can_document_member(cls, member, membername, isattr, parent): - # type: (Any, unicode, bool, Any) -> bool + def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any + ) -> bool: # don't document submodules automatically return False - def resolve_name(self, modname, parents, path, base): - # type: (str, Any, str, Any) -> Tuple[str, List[unicode]] + def resolve_name(self, modname: str, parents: Any, path: str, base: Any + ) -> Tuple[str, List[str]]: if modname is not None: - logger.warning('"::" in automodule name doesn\'t make sense') + logger.warning(__('"::" in automodule name doesn\'t make sense'), + type='autodoc') return (path or '') + base, [] - def parse_name(self): - # type: () -> bool - ret = Documenter.parse_name(self) + def parse_name(self) -> bool: + ret = super().parse_name() if self.args or self.retann: - logger.warning('signature arguments or return annotation ' - 'given for automodule %s' % self.fullname) + logger.warning(__('signature arguments or return annotation ' + 'given for automodule %s') % self.fullname, + type='autodoc') + return ret + + def import_object(self, raiseerror: bool = False) -> bool: + ret = super().import_object(raiseerror) + + try: + if not self.options.ignore_module_all: + self.__all__ = inspect.getall(self.object) + except ValueError as exc: + # invalid __all__ found. + logger.warning(__('__all__ should be a list of strings, not %r ' + '(in module %s) -- ignoring __all__') % + (exc.args[0], self.fullname), type='autodoc') + return ret - def add_directive_header(self, sig): - # type: (unicode) -> None + def add_directive_header(self, sig: str) -> None: Documenter.add_directive_header(self, sig) sourcename = self.get_sourcename() # add some module-specific options if self.options.synopsis: - self.add_line( - u' :synopsis: ' + self.options.synopsis, sourcename) + self.add_line(' :synopsis: ' + self.options.synopsis, sourcename) if self.options.platform: - self.add_line( - u' :platform: ' + self.options.platform, sourcename) + self.add_line(' :platform: ' + self.options.platform, sourcename) if self.options.deprecated: - self.add_line(u' :deprecated:', sourcename) + self.add_line(' :deprecated:', sourcename) - def get_module_members(self): + def get_module_members(self) -> Dict[str, ObjectMember]: """Get members of target module.""" if self.analyzer: attr_docs = self.analyzer.attr_docs else: attr_docs = {} - members = {} # type: Dict[str, ObjectMember] + members: Dict[str, ObjectMember] = {} for name in dir(self.object): try: value = safe_getattr(self.object, name, None) + if ismock(value): + value = undecorate(value) docstring = attr_docs.get(('', name), []) members[name] = ObjectMember(name, value, docstring="\n".join(docstring)) except AttributeError: continue # annotation only member (ex. attr: int) - try: - for name in inspect.getannotations(self.object): - if name not in members: - docstring = attr_docs.get(('', name), []) - members[name] = ObjectMember(name, INSTANCEATTR, - docstring="\n".join(docstring)) - except AttributeError: - pass + for name in inspect.getannotations(self.object): + if name not in members: + docstring = attr_docs.get(('', name), []) + members[name] = ObjectMember(name, INSTANCEATTR, + docstring="\n".join(docstring)) return members - def get_object_members(self, want_all): - # type: (bool) -> Tuple[bool, List[Tuple[unicode, object]]] + def get_object_members(self, want_all: bool) -> Tuple[bool, ObjectMembers]: members = self.get_module_members() if want_all: - if not hasattr(self.object, '__all__'): + if self.__all__ is None: # for implicit module members, check __module__ to avoid # documenting imported objects return True, list(members.values()) else: - memberlist = self.object.__all__ - # Sometimes __all__ is broken... - if not isinstance(memberlist, (list, tuple)) or not \ - all(isinstance(entry, str) for entry in memberlist): - logger.warning( - '__all__ should be a list of strings, not %r ' - '(in module %s) -- ignoring __all__' % - (memberlist, self.fullname)) - # fall back to all members - return True, list(members.values()) + for member in members.values(): + if member.__name__ not in self.__all__: + member.skipped = True + + return False, list(members.values()) else: memberlist = self.options.members or [] - ret = [] - for mname in memberlist: - if mname in members: - ret.append(members[mname]) - else: - logger.warning( - 'missing attribute mentioned in :members: or __all__: ' - 'module %s, attribute %s' % - (safe_getattr(self.object, '__name__', '???'), mname)) - return False, ret + ret = [] + for name in memberlist: + if name in members: + ret.append(members[name]) + else: + logger.warning(__('missing attribute mentioned in :members: option: ' + 'module %s, attribute %s') % + (safe_getattr(self.object, '__name__', '???'), name), + type='autodoc') + return False, ret + + def sort_members(self, documenters: List[Tuple["Documenter", bool]], + order: str) -> List[Tuple["Documenter", bool]]: + if order == 'bysource' and self.__all__: + # Sort alphabetically first (for members not listed on the __all__) + documenters.sort(key=lambda e: e[0].name) + + # Sort by __all__ + def keyfunc(entry: Tuple[Documenter, bool]) -> int: + name = entry[0].name.split('::')[1] + if self.__all__ and name in self.__all__: + return self.__all__.index(name) + else: + return len(self.__all__) + documenters.sort(key=keyfunc) + + return documenters + else: + return super().sort_members(documenters, order) class ModuleLevelDocumenter(Documenter): @@ -940,8 +1159,8 @@ class ModuleLevelDocumenter(Documenter): Specialized Documenter subclass for objects on module level (functions, classes, data/constants). """ - def resolve_name(self, modname, parents, path, base): - # type: (str, Any, str, Any) -> Tuple[str, List[unicode]] + def resolve_name(self, modname: str, parents: Any, path: str, base: Any + ) -> Tuple[str, List[str]]: if modname is None: if path: modname = path.rstrip('.') @@ -961,8 +1180,8 @@ class ClassLevelDocumenter(Documenter): Specialized Documenter subclass for objects on class level (methods, attributes). """ - def resolve_name(self, modname, parents, path, base): - # type: (str, Any, str, Any) -> Tuple[str, List[unicode]] + def resolve_name(self, modname: str, parents: Any, path: str, base: Any + ) -> Tuple[str, List[str]]: if modname is None: if path: mod_cls = path.rstrip('.') @@ -978,7 +1197,7 @@ def resolve_name(self, modname, parents, path, base): # ... if still None, there's no way to know if mod_cls is None: return None, [] - modname, _, cls = mod_cls.rpartition('.') # type: ignore + modname, sep, cls = mod_cls.rpartition('.') parents = [cls] # if the module name is still missing, get it like above if not modname: @@ -989,57 +1208,82 @@ def resolve_name(self, modname, parents, path, base): return modname, parents + [base] -class DocstringSignatureMixin(): +class DocstringSignatureMixin: """ Mixin for FunctionDocumenter and MethodDocumenter to provide the feature of reading the signature from the docstring. """ - - def _find_signature(self, encoding=None): - # type: (unicode) -> Tuple[str, str] - docstrings = self.get_doc(encoding) + _new_docstrings: List[List[str]] = None + _signatures: List[str] = None + + def _find_signature(self) -> Tuple[str, str]: + # candidates of the object name + valid_names = [self.objpath[-1]] # type: ignore + if isinstance(self, ClassDocumenter): + valid_names.append('__init__') + if hasattr(self.object, '__mro__'): + valid_names.extend(cls.__name__ for cls in self.object.__mro__) + + docstrings = self.get_doc() + if docstrings is None: + return None, None self._new_docstrings = docstrings[:] + self._signatures = [] result = None for i, doclines in enumerate(docstrings): - # no lines in docstring, no match - if not doclines: - continue - # match first line of docstring against signature RE - match = py_ext_sig_re.match(doclines[0]) # type: ignore - if not match: - continue - exmod, path, base, args, retann = match.groups() - # the base name must match ours - valid_names = [self.objpath[-1]] # type: ignore - if isinstance(self, ClassDocumenter): - valid_names.append('__init__') - if hasattr(self.object, '__mro__'): - valid_names.extend(cls.__name__ for cls in self.object.__mro__) - if base not in valid_names: - continue - # re-prepare docstring to ignore more leading indentation - self._new_docstrings[i] = prepare_docstring('\n'.join(doclines[1:])) - result = args, retann - # don't look any further - break + for j, line in enumerate(doclines): + if not line: + # no lines in docstring, no match + break + + if line.endswith('\\'): + line = line.rstrip('\\').rstrip() + + # match first line of docstring against signature RE + match = py_ext_sig_re.match(line) + if not match: + break + exmod, path, base, args, retann = match.groups() + + # the base name must match ours + if base not in valid_names: + break + + # re-prepare docstring to ignore more leading indentation + tab_width = self.directive.state.document.settings.tab_width # type: ignore + self._new_docstrings[i] = prepare_docstring('\n'.join(doclines[j + 1:]), + tab_width) + + if result is None: + # first signature + result = args, retann + else: + # subsequent signatures + self._signatures.append("(%s) -> %s" % (args, retann)) + + if result: + # finish the loop when signature found + break + return result - def get_doc(self, encoding=None, ignore=1): - # type: (unicode, int) -> List[List[unicode]] - lines = getattr(self, '_new_docstrings', None) - if lines is not None: - return lines - return Documenter.get_doc(self, encoding, ignore) # type: ignore + def get_doc(self) -> List[List[str]]: + if self._new_docstrings is not None: + return self._new_docstrings + return super().get_doc() # type: ignore - def format_signature(self): - # type: () -> unicode - if self.args is None and self.env.config.autodoc_docstring_signature: # type: ignore + def format_signature(self, **kwargs: Any) -> str: + if self.args is None and self.config.autodoc_docstring_signature: # type: ignore # only act if a signature is not explicitly given already, and if # the feature is enabled result = self._find_signature() if result is not None: self.args, self.retann = result - return Documenter.format_signature(self) + sig = super().format_signature(**kwargs) # type: ignore + if self._signatures: + return "\n".join([sig] + self._signatures) + else: + return sig class DocstringStripSignatureMixin(DocstringSignatureMixin): @@ -1047,9 +1291,8 @@ class DocstringStripSignatureMixin(DocstringSignatureMixin): Mixin for AttributeDocumenter to provide the feature of stripping any function signature from the docstring. """ - def format_signature(self): - # type: () -> unicode - if self.args is None and self.env.config.autodoc_docstring_signature: + def format_signature(self, **kwargs: Any) -> str: + if self.args is None and self.config.autodoc_docstring_signature: # type: ignore # only act if a signature is not explicitly given already, and if # the feature is enabled result = self._find_signature() @@ -1058,10 +1301,10 @@ def format_signature(self): # DocstringSignatureMixin.format_signature. # Documenter.format_signature use self.args value to format. _args, self.retann = result - return Documenter.format_signature(self) + return super().format_signature(**kwargs) -class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): +class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: ignore """ Specialized Documenter subclass for functions. """ @@ -1069,136 +1312,289 @@ class FunctionDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): member_order = 30 @classmethod - def can_document_member(cls, member, membername, isattr, parent): - # type: (Any, unicode, bool, Any) -> bool - # It can be documented if it is a genuine function. + def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any + ) -> bool: + # -------------------------------------------------------------------- + # supports functions, builtins but, unlike Sphinx' autodoc, + # does not support bound methods exported at the module level + if is_function_or_cython_function(member) or inspect.isbuiltin(member): + return True + # Trac #9976: It can be documented if it is a genuine function. # Often, a class instance has the same documentation as its class, - # and then we typically want to document the class and not the instance. - # However, there is an exception: CachedFunction(f) returns a class instance, - # whose doc string coincides with that of f and is thus different from - # that of the class CachedFunction. In that situation, we want that f is documented. - # This is part of trac #9976. - return (is_function_or_cython_function(member) or inspect.isbuiltin(member) - or (isclassinstance(member) - and sage_getdoc_original(member) != sage_getdoc_original(member.__class__))) - - # Trac #9976: This function has been rewritten to support the - # _sage_argspec_ attribute which makes it possible to get argument - # specification of decorated callables in documentation correct. - # See e.g. sage.misc.decorators.sage_wraps - def args_on_obj(self, obj): - if hasattr(obj, "_sage_argspec_"): - return obj._sage_argspec_() - if inspect.isbuiltin(obj) or \ - inspect.ismethoddescriptor(obj): - # cannot introspect arguments of a C function or method - # unless a function to do so is supplied - if self.env.config.autodoc_builtin_argspec: - argspec = self.env.config.autodoc_builtin_argspec(obj) - return argspec - else: + # and then we typically want to document the class and not the + # instance. However, there is an exception: CachedFunction(f) returns + # a class instance, whose doc string coincides with that of f and is + # thus different from that of the class CachedFunction. In that + # situation, we want that f is documented. + return (isclassinstance(member) and + sage_getdoc_original(member) != sage_getdoc_original(member.__class__)) + # -------------------------------------------------------------------- + + def format_args(self, **kwargs: Any) -> str: + if self.config.autodoc_typehints in ('none', 'description'): + kwargs.setdefault('show_annotation', False) + if self.config.autodoc_typehints_format == "short": + kwargs.setdefault('unqualified_typehints', True) + + try: + self.env.app.emit('autodoc-before-process-signature', self.object, False) + # -------------------------------------------- + # Trac #9976: Support the _sage_argspec_ attribute which makes it + # possible to get argument specification of decorated callables in + # documentation correct. See e.g. sage.misc.decorators.sage_wraps + obj = self.object + + if hasattr(obj, "_sage_argspec_"): + argspec = obj._sage_argspec_() + if inspect.isbuiltin(obj) or \ + inspect.ismethoddescriptor(obj): + # cannot introspect arguments of a C function or method + # unless a function to do so is supplied + argspec = sage_getargspec(obj) + argspec = sage_getargspec(obj) + if isclassinstance(obj) or inspect.isclass(obj): + # if a class should be documented as function, we try + # to use the constructor signature as function + # signature without the first argument. + if argspec is not None and argspec[0]: + del argspec[0][0] + + if argspec is None: return None - argspec = sage_getargspec(obj) - if isclassinstance(obj) or inspect.isclass(obj): - # if a class should be documented as function, we try - # to use the constructor signature as function - # signature without the first argument. - if argspec is not None and argspec[0]: - del argspec[0][0] - return argspec - - def format_args(self): - # type: () -> unicode - argspec = self.args_on_obj(self.object) - if argspec is None: + args = sage_formatargspec(*argspec) + # -------------------------------------------- + except TypeError as exc: + logger.warning(__("Failed to get a function signature for %s: %s"), + self.fullname, exc) return None - args = formatargspec(self.object, *argspec) - # escape backslashes for reST - args = args.replace('\\', '\\\\') + except ValueError: + args = '' + + if self.config.strip_signature_backslash: + # escape backslashes for reST + args = args.replace('\\', '\\\\') return args - def document_members(self, all_members=False): - # type: (bool) -> None + def document_members(self, all_members: bool = False) -> None: pass + def add_directive_header(self, sig: str) -> None: + sourcename = self.get_sourcename() + super().add_directive_header(sig) + + if inspect.iscoroutinefunction(self.object) or inspect.isasyncgenfunction(self.object): + self.add_line(' :async:', sourcename) + + def format_signature(self, **kwargs: Any) -> str: + if self.config.autodoc_typehints_format == "short": + kwargs.setdefault('unqualified_typehints', True) -class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): + sigs = [] + if (self.analyzer and + '.'.join(self.objpath) in self.analyzer.overloads and + self.config.autodoc_typehints != 'none'): + # Use signatures for overloaded functions instead of the implementation function. + overloaded = True + else: + overloaded = False + sig = super().format_signature(**kwargs) + sigs.append(sig) + + if inspect.is_singledispatch_function(self.object): + # append signature of singledispatch'ed functions + for typ, func in self.object.registry.items(): + if typ is object: + pass # default implementation. skipped. + else: + dispatchfunc = self.annotate_to_first_argument(func, typ) + if dispatchfunc: + documenter = FunctionDocumenter(self.directive, '') + documenter.object = dispatchfunc + documenter.objpath = [None] + sigs.append(documenter.format_signature()) + if overloaded: + actual = inspect.signature(self.object, + type_aliases=self.config.autodoc_type_aliases) + __globals__ = safe_getattr(self.object, '__globals__', {}) + for overload in self.analyzer.overloads.get('.'.join(self.objpath)): + overload = self.merge_default_value(actual, overload) + overload = evaluate_signature(overload, __globals__, + self.config.autodoc_type_aliases) + + sig = stringify_signature(overload, **kwargs) + sigs.append(sig) + + return "\n".join(sigs) + + def merge_default_value(self, actual: Signature, overload: Signature) -> Signature: + """Merge default values of actual implementation to the overload variants.""" + parameters = list(overload.parameters.values()) + for i, param in enumerate(parameters): + actual_param = actual.parameters.get(param.name) + if actual_param and param.default == '...': + parameters[i] = param.replace(default=actual_param.default) + + return overload.replace(parameters=parameters) + + def annotate_to_first_argument(self, func: Callable, typ: Type) -> Optional[Callable]: + """Annotate type hint to the first argument of function if needed.""" + try: + sig = inspect.signature(func, type_aliases=self.config.autodoc_type_aliases) + except TypeError as exc: + logger.warning(__("Failed to get a function signature for %s: %s"), + self.fullname, exc) + return None + except ValueError: + return None + + if len(sig.parameters) == 0: + return None + + def dummy(): + pass + + params = list(sig.parameters.values()) + if params[0].annotation is Parameter.empty: + params[0] = params[0].replace(annotation=typ) + try: + dummy.__signature__ = sig.replace(parameters=params) # type: ignore + return dummy + except (AttributeError, TypeError): + # failed to update signature (ex. built-in or extension types) + return None + + return func + + +class DecoratorDocumenter(FunctionDocumenter): + """ + Specialized Documenter subclass for decorator functions. + """ + objtype = 'decorator' + + # must be lower than FunctionDocumenter + priority = -1 + + def format_args(self, **kwargs: Any) -> Any: + args = super().format_args(**kwargs) + if ',' in args: + return args + else: + return None + + +# Types which have confusing metaclass signatures it would be best not to show. +# These are listed by name, rather than storing the objects themselves, to avoid +# needing to import the modules. +_METACLASS_CALL_BLACKLIST = [ + 'enum.EnumMeta.__call__', +] + + +# Types whose __new__ signature is a pass-through. +_CLASS_NEW_BLACKLIST = [ + 'typing.Generic.__new__', +] + + +class ClassDocumenter(DocstringSignatureMixin, ModuleLevelDocumenter): # type: ignore """ Specialized Documenter subclass for classes. """ objtype = 'class' member_order = 20 - option_spec = { + option_spec: OptionSpec = { 'members': members_option, 'undoc-members': bool_option, - 'noindex': bool_option, 'inherited-members': bool_option, - 'show-inheritance': bool_option, 'member-order': identity, - 'exclude-members': members_set_option, - 'private-members': bool_option, 'special-members': members_option, + 'noindex': bool_option, 'inherited-members': inherited_members_option, + 'show-inheritance': bool_option, 'member-order': member_order_option, + 'exclude-members': exclude_members_option, + 'private-members': members_option, 'special-members': members_option, + 'class-doc-from': class_doc_from_option, } + _signature_class: Any = None + _signature_method_name: str = None + + def __init__(self, *args: Any) -> None: + super().__init__(*args) + + if self.config.autodoc_class_signature == 'separated': + self.options = self.options.copy() + + # show __init__() method + if self.options.special_members is None: + self.options['special-members'] = ['__new__', '__init__'] + else: + self.options.special_members.append('__new__') + self.options.special_members.append('__init__') + + merge_members_option(self.options) + @classmethod - def can_document_member(cls, member, membername, isattr, parent): - # type: (Any, unicode, bool, Any) -> bool + def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any + ) -> bool: return isinstance(member, type) - def import_object(self): - # type: () -> Any - ret = ModuleLevelDocumenter.import_object(self) - # Objective: if the class is documented under another name, document it + def import_object(self, raiseerror: bool = False) -> bool: + ret = super().import_object(raiseerror) + # if the class is documented under another name, document it # as data/attribute - # - # Notes from trac #27692: - # - # For Python 3, we make use of self.object.__qualname__. - # - # Notes from trac #7448: - # - # The original goal of this was that if some class is aliased, the - # alias is generated as a link rather than duplicated. For example in - # - # class A: - # pass - # B = A - # - # Then B is an alias of A, and should be generated as such. - # - # The way it was solved is to compare the name under which the - # current class is found and the actual name of the class (stored in - # the attribute __name__): - # - # if hasattr(self.object, '__name__'): - # self.doc_as_attr = (self.objpath[-1] != self.object.__name__) - # else: - # self.doc_as_attr = True - # - # Now, to work around a pickling bug of nested class in Python, - # by using the metaclass NestedMetaclass, we change the attribute - # __name__ of the nested class. For example, in - # - # class A(metaclass=NestedMetaclass): - # class B(): - # pass - # - # the class B get its name changed to 'A.B'. Such dots '.' in names - # are not supposed to occur in normal python name. I use it to check - # if the class is a nested one and to compare its __name__ with its - # path. - # - # The original implementation as well as the new one here doesn't work - # if a class is aliased from a different place under the same name. For - # example, in the following, - # - # class A: - # pass - # class Container: - # A = A - # - # The nested copy Container.A is also documented. Actually, it seems - # that there is no way to solve this by introspection. I'll submbit - # this problem on sphinx trac. - # - # References: trac #5986, file sage/misc/nested_class.py if ret: + if hasattr(self.object, '__name__'): + self.doc_as_attr = (self.objpath[-1] != self.object.__name__) + # ------------------------------------------------------------------ + # Trac #27692: + # + # For Python 3, we make use of self.object.__qualname__. + # + # Notes from trac #7448: + # + # The original goal of this was that if some class is aliased, the + # alias is generated as a link rather than duplicated. For example in + # + # class A: + # pass + # B = A + # + # Then B is an alias of A, and should be generated as such. + # + # The way it was solved is to compare the name under which the + # current class is found and the actual name of the class (stored in + # the attribute __name__): + # + # if hasattr(self.object, '__name__'): + # self.doc_as_attr = (self.objpath[-1] != self.object.__name__) + # else: + # self.doc_as_attr = True + # + # Now, to work around a pickling bug of nested class in Python, + # by using the metaclass NestedMetaclass, we change the attribute + # __name__ of the nested class. For example, in + # + # class A(metaclass=NestedMetaclass): + # class B(): + # pass + # + # the class B get its name changed to 'A.B'. Such dots '.' in names + # are not supposed to occur in normal python name. I use it to check + # if the class is a nested one and to compare its __name__ with its + # path. + # + # The original implementation as well as the new one here doesn't work + # if a class is aliased from a different place under the same name. For + # example, in the following, + # + # class A: + # pass + # class Container: + # A = A + # + # The nested copy Container.A is also documented. Actually, it seems + # that there is no way to solve this by introspection. I'll submbit + # this problem on sphinx trac. + # + # References: trac #5986, file sage/misc/nested_class.py + import sys module = getattr(self.object, '__module__', False) name = getattr(self.object, '__name__', False) qualname = getattr(self.object, '__qualname__', name) @@ -1212,135 +1608,344 @@ def import_object(self): cls = getattr(cls, part, None) self.doc_as_attr = (self.objpath != qualname_parts and self.object is cls) + # ------------------------------------------------------------------ else: self.doc_as_attr = True return ret - def format_args(self): - # type: () -> unicode - # for classes, the relevant signature is the __init__ method's - initmeth = self.get_attr(self.object, '__init__', None) - # classes without __init__ method, default __init__ or - # __init__ written in C? - if initmeth is None or \ - is_builtin_class_method(self.object, '__init__') or \ - not(inspect.ismethod(initmeth) or is_function_or_cython_function(initmeth)): - return None - try: - argspec = sage_getargspec(initmeth) - if argspec[0] and argspec[0][0] in ('cls', 'self'): - del argspec[0][0] - return formatargspec(initmeth, *argspec) - except TypeError: - # still not possible: happens e.g. for old-style classes - # with __init__ in C - return None + def _get_signature(self) -> Tuple[Optional[Any], Optional[str], Optional[Signature]]: + def get_user_defined_function_or_method(obj: Any, attr: str) -> Any: + """ Get the `attr` function or method from `obj`, if it is user-defined. """ + if inspect.is_builtin_class_method(obj, attr): + return None + attr = self.get_attr(obj, attr, None) + if not (inspect.ismethod(attr) or inspect.isfunction(attr)): + return None + return attr - def format_signature(self): - # type: () -> unicode - if self.doc_as_attr: - return '' + # This sequence is copied from inspect._signature_from_callable. + # ValueError means that no signature could be found, so we keep going. - return DocstringSignatureMixin.format_signature(self) + # First, we check the obj has a __signature__ attribute + if (hasattr(self.object, '__signature__') and + isinstance(self.object.__signature__, Signature)): + return None, None, self.object.__signature__ - def add_directive_header(self, sig): - # type: (unicode) -> None - if self.doc_as_attr: - self.directivetype = 'attribute' - Documenter.add_directive_header(self, sig) + # Next, let's see if it has an overloaded __call__ defined + # in its metaclass + call = get_user_defined_function_or_method(type(self.object), '__call__') - # add inheritance info, if wanted - if not self.doc_as_attr and self.options.show_inheritance: - sourcename = self.get_sourcename() - self.add_line(u'', sourcename) - if hasattr(self.object, '__bases__') and len(self.object.__bases__): - bases = [b.__module__ in ('__builtin__', 'builtins') and - u':class:`%s`' % b.__name__ or - u':class:`%s.%s`' % (b.__module__, b.__name__) - for b in self.object.__bases__] - self.add_line(u' ' + _(u'Bases: %s') % ', '.join(bases), - sourcename) - - def get_doc(self, encoding=None, ignore=1): - # type: (unicode, int) -> List[List[unicode]] - lines = getattr(self, '_new_docstrings', None) - if lines is not None: - return lines + if call is not None: + if "{0.__module__}.{0.__qualname__}".format(call) in _METACLASS_CALL_BLACKLIST: + call = None - content = self.env.config.autoclass_content + if call is not None: + self.env.app.emit('autodoc-before-process-signature', call, True) + try: + sig = inspect.signature(call, bound_method=True, + type_aliases=self.config.autodoc_type_aliases) + return type(self.object), '__call__', sig + except ValueError: + pass - docstrings = [] - attrdocstring = sage_getdoc_original(self.object) - if attrdocstring: - docstrings.append(attrdocstring) + # Now we check if the 'obj' class has a '__new__' method + new = get_user_defined_function_or_method(self.object, '__new__') - # for classes, what the "docstring" is can be controlled via a - # config value; the default is only the class docstring - if content in ('both', 'init'): - initdocstring = sage_getdoc_original( - self.get_attr(self.object, '__init__', None)) - # for new-style classes, no __init__ means default __init__ - if (initdocstring is not None and - (initdocstring == object.__init__.__doc__ or # for pypy - initdocstring.strip() == object.__init__.__doc__)): # for !pypy - initdocstring = None - if not initdocstring: - # try __new__ - initdocstring = self.get_attr( - self.get_attr(self.object, '__new__', None), '__doc__') - # for new-style classes, no __new__ means default __new__ - if (initdocstring is not None and - (initdocstring == object.__new__.__doc__ or # for pypy - initdocstring.strip() == object.__new__.__doc__)): # for !pypy - initdocstring = None - if initdocstring: - if content == 'init': - docstrings = [initdocstring] - else: - docstrings.append(initdocstring) - doc = [] - for docstring in docstrings: - if isinstance(docstring, str): - doc.append(prepare_docstring(docstring)) - return doc - - def add_content(self, more_content, no_docstring=False): - # type: (Any, bool) -> None - if self.doc_as_attr: - # We cannot rely on __qualname__ yet for Python 2, because of a - # Cython bug: https://github.com/cython/cython/issues/2772. See - # trac #27002. - classname = safe_getattr(self.object, '__qualname__', None) - if not classname: - classname = safe_getattr(self.object, '__name__', None) - if classname: - module = safe_getattr(self.object, '__module__', None) - parentmodule = safe_getattr(self.parent, '__module__', None) - if module and module != parentmodule: - classname = str(module) + u'.' + str(classname) - content = ViewList( - [_('alias of :class:`%s`') % classname], source='') - ModuleLevelDocumenter.add_content(self, content, - no_docstring=True) - else: - ModuleLevelDocumenter.add_content(self, more_content) + if new is not None: + if "{0.__module__}.{0.__qualname__}".format(new) in _CLASS_NEW_BLACKLIST: + new = None - def document_members(self, all_members=False): - # type: (bool) -> None - if self.doc_as_attr: - return - ModuleLevelDocumenter.document_members(self, all_members) + if new is not None: + self.env.app.emit('autodoc-before-process-signature', new, True) + try: + sig = inspect.signature(new, bound_method=True, + type_aliases=self.config.autodoc_type_aliases) + return self.object, '__new__', sig + except ValueError: + pass - def generate(self, more_content=None, real_modname=None, - check_module=False, all_members=False): - # Do not pass real_modname and use the name from the __module__ + # Finally, we should have at least __init__ implemented + init = get_user_defined_function_or_method(self.object, '__init__') + if init is not None: + self.env.app.emit('autodoc-before-process-signature', init, True) + try: + sig = inspect.signature(init, bound_method=True, + type_aliases=self.config.autodoc_type_aliases) + return self.object, '__init__', sig + except ValueError: + pass + + # None of the attributes are user-defined, so fall back to let inspect + # handle it. + # We don't know the exact method that inspect.signature will read + # the signature from, so just pass the object itself to our hook. + self.env.app.emit('autodoc-before-process-signature', self.object, False) + try: + sig = inspect.signature(self.object, bound_method=False, + type_aliases=self.config.autodoc_type_aliases) + return None, None, sig + except ValueError: + pass + + # Still no signature: happens e.g. for old-style classes + # with __init__ in C and no `__text_signature__`. + return None, None, None + + def format_args(self, **kwargs: Any) -> str: + if self.config.autodoc_typehints in ('none', 'description'): + kwargs.setdefault('show_annotation', False) + if self.config.autodoc_typehints_format == "short": + kwargs.setdefault('unqualified_typehints', True) + + try: + self._signature_class, self._signature_method_name, sig = self._get_signature() + except TypeError as exc: + # __signature__ attribute contained junk + logger.warning(__("Failed to get a constructor signature for %s: %s"), + self.fullname, exc) + return None + + if sig is None: + return None + + return stringify_signature(sig, show_return_annotation=False, **kwargs) + + def _find_signature(self) -> Tuple[str, str]: + result = super()._find_signature() + if result is not None: + # Strip a return value from signature of constructor in docstring (first entry) + result = (result[0], None) + + for i, sig in enumerate(self._signatures): + if sig.endswith(' -> None'): + # Strip a return value from signatures of constructor in docstring (subsequent + # entries) + self._signatures[i] = sig[:-8] + + return result + + def format_signature(self, **kwargs: Any) -> str: + if self.doc_as_attr: + return '' + if self.config.autodoc_class_signature == 'separated': + # do not show signatures + return '' + + if self.config.autodoc_typehints_format == "short": + kwargs.setdefault('unqualified_typehints', True) + + sig = super().format_signature() + sigs = [] + + overloads = self.get_overloaded_signatures() + if overloads and self.config.autodoc_typehints != 'none': + # Use signatures for overloaded methods instead of the implementation method. + method = safe_getattr(self._signature_class, self._signature_method_name, None) + __globals__ = safe_getattr(method, '__globals__', {}) + for overload in overloads: + overload = evaluate_signature(overload, __globals__, + self.config.autodoc_type_aliases) + + parameters = list(overload.parameters.values()) + overload = overload.replace(parameters=parameters[1:], + return_annotation=Parameter.empty) + sig = stringify_signature(overload, **kwargs) + sigs.append(sig) + else: + sigs.append(sig) + + return "\n".join(sigs) + + def get_overloaded_signatures(self) -> List[Signature]: + if self._signature_class and self._signature_method_name: + for cls in self._signature_class.__mro__: + try: + analyzer = ModuleAnalyzer.for_module(cls.__module__) + analyzer.analyze() + qualname = '.'.join([cls.__qualname__, self._signature_method_name]) + if qualname in analyzer.overloads: + return analyzer.overloads.get(qualname) + elif qualname in analyzer.tagorder: + # the constructor is defined in the class, but not overridden. + return [] + except PycodeError: + pass + + return [] + + def get_canonical_fullname(self) -> Optional[str]: + __modname__ = safe_getattr(self.object, '__module__', self.modname) + __qualname__ = safe_getattr(self.object, '__qualname__', None) + if __qualname__ is None: + __qualname__ = safe_getattr(self.object, '__name__', None) + if __qualname__ and '' in __qualname__: + # No valid qualname found if the object is defined as locals + __qualname__ = None + + if __modname__ and __qualname__: + return '.'.join([__modname__, __qualname__]) + else: + return None + + def add_directive_header(self, sig: str) -> None: + sourcename = self.get_sourcename() + + if self.doc_as_attr: + self.directivetype = 'attribute' + super().add_directive_header(sig) + + if self.analyzer and '.'.join(self.objpath) in self.analyzer.finals: + self.add_line(' :final:', sourcename) + + canonical_fullname = self.get_canonical_fullname() + if not self.doc_as_attr and canonical_fullname and self.fullname != canonical_fullname: + self.add_line(' :canonical: %s' % canonical_fullname, sourcename) + + # add inheritance info, if wanted + if not self.doc_as_attr and self.options.show_inheritance: + if inspect.getorigbases(self.object): + # A subclass of generic types + # refs: PEP-560 + bases = list(self.object.__orig_bases__) + elif hasattr(self.object, '__bases__') and len(self.object.__bases__): + # A normal class + bases = list(self.object.__bases__) + else: + bases = [] + + self.env.events.emit('autodoc-process-bases', + self.fullname, self.object, self.options, bases) + + if self.config.autodoc_typehints_format == "short": + base_classes = [restify(cls, "smart") for cls in bases] + else: + base_classes = [restify(cls) for cls in bases] + + sourcename = self.get_sourcename() + self.add_line('', sourcename) + self.add_line(' ' + _('Bases: %s') % ', '.join(base_classes), sourcename) + + def get_object_members(self, want_all: bool) -> Tuple[bool, ObjectMembers]: + members = get_class_members(self.object, self.objpath, self.get_attr, + self.config.autodoc_inherit_docstrings) + if not want_all: + if not self.options.members: + return False, [] # type: ignore + # specific members given + selected = [] + for name in self.options.members: + if name in members: + selected.append(members[name]) + else: + logger.warning(__('missing attribute %s in object %s') % + (name, self.fullname), type='autodoc') + return False, selected + elif self.options.inherited_members: + return False, list(members.values()) + else: + return False, [m for m in members.values() if m.class_ == self.object] + + def get_doc(self) -> Optional[List[List[str]]]: + if self.doc_as_attr: + # Don't show the docstring of the class when it is an alias. + comment = self.get_variable_comment() + if comment: + return [] + else: + return None + + lines = getattr(self, '_new_docstrings', None) + if lines is not None: + return lines + + classdoc_from = self.options.get('class-doc-from', self.config.autoclass_content) + + docstrings = [] + attrdocstring = getdoc(self.object, self.get_attr) + if attrdocstring: + docstrings.append(attrdocstring) + + # for classes, what the "docstring" is can be controlled via a + # config value; the default is only the class docstring + if classdoc_from in ('both', 'init'): + __init__ = self.get_attr(self.object, '__init__', None) + initdocstring = getdoc(__init__, self.get_attr, + self.config.autodoc_inherit_docstrings, + self.object, '__init__') + # for new-style classes, no __init__ means default __init__ + if (initdocstring is not None and + (initdocstring == object.__init__.__doc__ or # for pypy + initdocstring.strip() == object.__init__.__doc__)): # for !pypy + initdocstring = None + if not initdocstring: + # try __new__ + __new__ = self.get_attr(self.object, '__new__', None) + initdocstring = getdoc(__new__, self.get_attr, + self.config.autodoc_inherit_docstrings, + self.object, '__new__') + # for new-style classes, no __new__ means default __new__ + if (initdocstring is not None and + (initdocstring == object.__new__.__doc__ or # for pypy + initdocstring.strip() == object.__new__.__doc__)): # for !pypy + initdocstring = None + if initdocstring: + if classdoc_from == 'init': + docstrings = [initdocstring] + else: + docstrings.append(initdocstring) + + tab_width = self.directive.state.document.settings.tab_width + return [prepare_docstring(docstring, tab_width) for docstring in docstrings] + + def get_variable_comment(self) -> Optional[List[str]]: + try: + key = ('', '.'.join(self.objpath)) + if self.doc_as_attr: + analyzer = ModuleAnalyzer.for_module(self.modname) + else: + analyzer = ModuleAnalyzer.for_module(self.get_real_modname()) + analyzer.analyze() + return list(analyzer.attr_docs.get(key, [])) + except PycodeError: + return None + + def add_content(self, more_content: Optional[StringList]) -> None: + if self.doc_as_attr and self.modname != self.get_real_modname(): + try: + # override analyzer to obtain doccomment around its definition. + self.analyzer = ModuleAnalyzer.for_module(self.modname) + self.analyzer.analyze() + except PycodeError: + pass + + if self.doc_as_attr and not self.get_variable_comment(): + try: + if self.config.autodoc_typehints_format == "short": + alias = restify(self.object, "smart") + else: + alias = restify(self.object) + more_content = StringList([_('alias of %s') % alias], source='') + except AttributeError: + pass # Invalid class object is passed. + + super().add_content(more_content) + + def document_members(self, all_members: bool = False) -> None: + if self.doc_as_attr: + return + super().document_members(all_members) + + def generate(self, more_content: Optional[StringList] = None, real_modname: str = None, + check_module: bool = False, all_members: bool = False) -> None: + # Do not pass real_modname and use the name from the __module__ # attribute of the class. # If a class gets imported into the module real_modname # the analyzer won't find the source of the class, if # it looks in real_modname. - return super(ClassDocumenter, self).generate(more_content=more_content, - check_module=check_module, - all_members=all_members) + return super().generate(more_content=more_content, + check_module=check_module, + all_members=all_members) + class ExceptionDocumenter(ClassDocumenter): """ @@ -1353,65 +1958,317 @@ class ExceptionDocumenter(ClassDocumenter): priority = 10 @classmethod - def can_document_member(cls, member, membername, isattr, parent): - # type: (Any, unicode, bool, Any) -> bool + def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any + ) -> bool: return isinstance(member, type) and issubclass(member, BaseException) -class DataDocumenter(ModuleLevelDocumenter): +class DataDocumenterMixinBase: + # define types of instance variables + config: Config = None + env: BuildEnvironment = None + modname: str = None + parent: Any = None + object: Any = None + objpath: List[str] = None + + def should_suppress_directive_header(self) -> bool: + """Check directive header should be suppressed.""" + return False + + def should_suppress_value_header(self) -> bool: + """Check :value: header should be suppressed.""" + return False + + def update_content(self, more_content: StringList) -> None: + """Update docstring for the NewType object.""" + pass + + +class GenericAliasMixin(DataDocumenterMixinBase): + """ + Mixin for DataDocumenter and AttributeDocumenter to provide the feature for + supporting GenericAliases. + """ + + def should_suppress_directive_header(self) -> bool: + return (inspect.isgenericalias(self.object) or + super().should_suppress_directive_header()) + + def update_content(self, more_content: StringList) -> None: + if inspect.isgenericalias(self.object): + if self.config.autodoc_typehints_format == "short": + alias = restify(self.object, "smart") + else: + alias = restify(self.object) + + more_content.append(_('alias of %s') % alias, '') + more_content.append('', '') + + super().update_content(more_content) + + +class NewTypeMixin(DataDocumenterMixinBase): + """ + Mixin for DataDocumenter and AttributeDocumenter to provide the feature for + supporting NewTypes. + """ + + def should_suppress_directive_header(self) -> bool: + return (inspect.isNewType(self.object) or + super().should_suppress_directive_header()) + + def update_content(self, more_content: StringList) -> None: + if inspect.isNewType(self.object): + if self.config.autodoc_typehints_format == "short": + supertype = restify(self.object.__supertype__, "smart") + else: + supertype = restify(self.object.__supertype__) + + more_content.append(_('alias of %s') % supertype, '') + more_content.append('', '') + + super().update_content(more_content) + + +class TypeVarMixin(DataDocumenterMixinBase): + """ + Mixin for DataDocumenter and AttributeDocumenter to provide the feature for + supporting TypeVars. + """ + + def should_suppress_directive_header(self) -> bool: + return (isinstance(self.object, TypeVar) or + super().should_suppress_directive_header()) + + def get_doc(self) -> Optional[List[List[str]]]: + if isinstance(self.object, TypeVar): + if self.object.__doc__ != TypeVar.__doc__: + return super().get_doc() # type: ignore + else: + return [] + else: + return super().get_doc() # type: ignore + + def update_content(self, more_content: StringList) -> None: + if isinstance(self.object, TypeVar): + attrs = [repr(self.object.__name__)] + for constraint in self.object.__constraints__: + if self.config.autodoc_typehints_format == "short": + attrs.append(stringify_typehint(constraint, "smart")) + else: + attrs.append(stringify_typehint(constraint)) + if self.object.__bound__: + if self.config.autodoc_typehints_format == "short": + bound = restify(self.object.__bound__, "smart") + else: + bound = restify(self.object.__bound__) + attrs.append(r"bound=\ " + bound) + if self.object.__covariant__: + attrs.append("covariant=True") + if self.object.__contravariant__: + attrs.append("contravariant=True") + + more_content.append(_('alias of TypeVar(%s)') % ", ".join(attrs), '') + more_content.append('', '') + + super().update_content(more_content) + + +class UninitializedGlobalVariableMixin(DataDocumenterMixinBase): + """ + Mixin for DataDocumenter to provide the feature for supporting uninitialized + (type annotation only) global variables. + """ + + def import_object(self, raiseerror: bool = False) -> bool: + try: + return super().import_object(raiseerror=True) # type: ignore + except ImportError as exc: + # annotation only instance variable (PEP-526) + try: + with mock(self.config.autodoc_mock_imports): + parent = import_module(self.modname, self.config.autodoc_warningiserror) + annotations = get_type_hints(parent, None, + self.config.autodoc_type_aliases) + if self.objpath[-1] in annotations: + self.object = UNINITIALIZED_ATTR + self.parent = parent + return True + except ImportError: + pass + + if raiseerror: + raise + else: + logger.warning(exc.args[0], type='autodoc', subtype='import_object') + self.env.note_reread() + return False + + def should_suppress_value_header(self) -> bool: + return (self.object is UNINITIALIZED_ATTR or + super().should_suppress_value_header()) + + def get_doc(self) -> Optional[List[List[str]]]: + if self.object is UNINITIALIZED_ATTR: + return [] + else: + return super().get_doc() # type: ignore + + +class DataDocumenter(GenericAliasMixin, NewTypeMixin, TypeVarMixin, + UninitializedGlobalVariableMixin, ModuleLevelDocumenter): """ Specialized Documenter subclass for data items. """ objtype = 'data' member_order = 40 priority = -10 - option_spec = dict(ModuleLevelDocumenter.option_spec) + option_spec: OptionSpec = dict(ModuleLevelDocumenter.option_spec) option_spec["annotation"] = annotation_option + option_spec["no-value"] = bool_option @classmethod - def can_document_member(cls, member, membername, isattr, parent): - # type: (Any, unicode, bool, Any) -> bool + def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any + ) -> bool: return isinstance(parent, ModuleDocumenter) and isattr - def add_directive_header(self, sig): - # type: (unicode) -> None - ModuleLevelDocumenter.add_directive_header(self, sig) + def update_annotations(self, parent: Any) -> None: + """Update __annotations__ to support type_comment and so on.""" + annotations = dict(inspect.getannotations(parent)) + parent.__annotations__ = annotations + + try: + analyzer = ModuleAnalyzer.for_module(self.modname) + analyzer.analyze() + for (classname, attrname), annotation in analyzer.annotations.items(): + if classname == '' and attrname not in annotations: + annotations[attrname] = annotation + except PycodeError: + pass + + def import_object(self, raiseerror: bool = False) -> bool: + ret = super().import_object(raiseerror) + if self.parent: + self.update_annotations(self.parent) + + return ret + + def should_suppress_value_header(self) -> bool: + if super().should_suppress_value_header(): + return True + else: + doc = self.get_doc() + docstring, metadata = separate_metadata('\n'.join(sum(doc, []))) + if 'hide-value' in metadata: + return True + + return False + + def add_directive_header(self, sig: str) -> None: + super().add_directive_header(sig) sourcename = self.get_sourcename() - if not self.options.annotation: + if self.options.annotation is SUPPRESS or self.should_suppress_directive_header(): + pass + elif self.options.annotation: + self.add_line(' :annotation: %s' % self.options.annotation, + sourcename) + else: + if self.config.autodoc_typehints != 'none': + # obtain annotation for this data + annotations = get_type_hints(self.parent, None, + self.config.autodoc_type_aliases) + if self.objpath[-1] in annotations: + if self.config.autodoc_typehints_format == "short": + objrepr = stringify_typehint(annotations.get(self.objpath[-1]), + "smart") + else: + objrepr = stringify_typehint(annotations.get(self.objpath[-1])) + self.add_line(' :type: ' + objrepr, sourcename) + try: - objrepr = object_description(self.object) + if (self.options.no_value or self.should_suppress_value_header() or + ismock(self.object)): + pass + else: + objrepr = object_description(self.object) + self.add_line(' :value: ' + objrepr, sourcename) except ValueError: pass - else: - self.add_line(u' :annotation: = ' + objrepr, sourcename) - elif self.options.annotation is SUPPRESS: + + def document_members(self, all_members: bool = False) -> None: + pass + + def get_real_modname(self) -> str: + real_modname = self.get_attr(self.parent or self.object, '__module__', None) + return real_modname or self.modname + + def get_module_comment(self, attrname: str) -> Optional[List[str]]: + try: + analyzer = ModuleAnalyzer.for_module(self.modname) + analyzer.analyze() + key = ('', attrname) + if key in analyzer.attr_docs: + return list(analyzer.attr_docs[key]) + except PycodeError: pass + + return None + + def get_doc(self) -> Optional[List[List[str]]]: + # Check the variable has a docstring-comment + comment = self.get_module_comment(self.objpath[-1]) + if comment: + return [comment] else: - self.add_line(u' :annotation: %s' % self.options.annotation, - sourcename) + return super().get_doc() - def document_members(self, all_members=False): - # type: (bool) -> None - pass + def add_content(self, more_content: Optional[StringList]) -> None: + # Disable analyzing variable comment on Documenter.add_content() to control it on + # DataDocumenter.add_content() + self.analyzer = None + if not more_content: + more_content = StringList() -class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): + self.update_content(more_content) + super().add_content(more_content) + + +class NewTypeDataDocumenter(DataDocumenter): + """ + Specialized Documenter subclass for NewTypes. + + Note: This must be invoked before FunctionDocumenter because NewType is a kind of + function object. + """ + + objtype = 'newtypedata' + directivetype = 'data' + priority = FunctionDocumenter.priority + 1 + + @classmethod + def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any + ) -> bool: + return inspect.isNewType(member) and isattr + + +class MethodDocumenter(DocstringSignatureMixin, ClassLevelDocumenter): # type: ignore """ Specialized Documenter subclass for methods (normal, static and class). """ objtype = 'method' + directivetype = 'method' member_order = 50 priority = 1 # must be more than FunctionDocumenter @classmethod - def can_document_member(cls, member, membername, isattr, parent): - # type: (Any, unicode, bool, Any) -> bool - return inspect.isroutine(member) and not \ - isinstance(parent, ModuleDocumenter) - - def import_object(self): - # type: () -> Any - ret = ClassLevelDocumenter.import_object(self) + def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any + ) -> bool: + return inspect.isroutine(member) and not isinstance(parent, ModuleDocumenter) + + def import_object(self, raiseerror: bool = False) -> bool: + ret = super().import_object(raiseerror) if not ret: return ret @@ -1420,195 +2277,643 @@ def import_object(self): if obj is None: obj = self.object - if isclassmethod(obj): - self.directivetype = 'classmethod' - # document class and static members before ordinary ones - self.member_order = self.member_order - 1 - elif isstaticmethod(obj, cls=self.parent, name=self.object_name): - self.directivetype = 'staticmethod' + if (inspect.isclassmethod(obj) or + inspect.isstaticmethod(obj, cls=self.parent, name=self.object_name)): # document class and static members before ordinary ones self.member_order = self.member_order - 1 - else: - self.directivetype = 'method' + return ret - # Trac #9976: This function has been rewritten to support the - # _sage_argspec_ attribute which makes it possible to get argument - # specification of decorated callables in documentation correct. - # See e.g. sage.misc.decorators.sage_wraps. - # - # Note, however, that sage.misc.sageinspect.sage_getargspec already - # uses a method _sage_argspec_, that only works on objects, not on classes, though. - def args_on_obj(self, obj): + def format_args(self, **kwargs: Any) -> str: + if self.config.autodoc_typehints in ('none', 'description'): + kwargs.setdefault('show_annotation', False) + if self.config.autodoc_typehints_format == "short": + kwargs.setdefault('unqualified_typehints', True) + + # ----------------------------------------------------------------- + # Trac #9976: This function has been rewritten to support the + # _sage_argspec_ attribute which makes it possible to get argument + # specification of decorated callables in documentation correct. + # See e.g. sage.misc.decorators.sage_wraps. + # + # Note, however, that sage.misc.sageinspect.sage_getargspec already + # uses a method _sage_argspec_, that only works on objects, not on classes, though. + obj = self.object if hasattr(obj, "_sage_argspec_"): argspec = obj._sage_argspec_() elif inspect.isbuiltin(obj) or inspect.ismethoddescriptor(obj): # can never get arguments of a C function or method unless # a function to do so is supplied - if self.env.config.autodoc_builtin_argspec: - argspec = self.env.config.autodoc_builtin_argspec(obj) - else: - return None + argspec = sage_getargspec(obj) else: # The check above misses ordinary Python methods in Cython # files. argspec = sage_getargspec(obj) + if argspec is not None and argspec[0] and argspec[0][0] in ('cls', 'self'): del argspec[0][0] - return argspec - - def format_args(self): - # type: () -> unicode - argspec = self.args_on_obj(self.object) if argspec is None: return None - args = formatargspec(self.object, *argspec) - # escape backslashes for reST - args = args.replace('\\', '\\\\') + args = sage_formatargspec(*argspec) + # ----------------------------------------------------------------- + + if self.config.strip_signature_backslash: + # escape backslashes for reST + args = args.replace('\\', '\\\\') return args - def document_members(self, all_members=False): - # type: (bool) -> None + def add_directive_header(self, sig: str) -> None: + super().add_directive_header(sig) + + sourcename = self.get_sourcename() + obj = self.parent.__dict__.get(self.object_name, self.object) + if inspect.isabstractmethod(obj): + self.add_line(' :abstractmethod:', sourcename) + if inspect.iscoroutinefunction(obj) or inspect.isasyncgenfunction(obj): + self.add_line(' :async:', sourcename) + if inspect.isclassmethod(obj): + self.add_line(' :classmethod:', sourcename) + if inspect.isstaticmethod(obj, cls=self.parent, name=self.object_name): + self.add_line(' :staticmethod:', sourcename) + if self.analyzer and '.'.join(self.objpath) in self.analyzer.finals: + self.add_line(' :final:', sourcename) + + def document_members(self, all_members: bool = False) -> None: pass -class AttributeDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): # type: ignore + def merge_default_value(self, actual: Signature, overload: Signature) -> Signature: + """Merge default values of actual implementation to the overload variants.""" + parameters = list(overload.parameters.values()) + for i, param in enumerate(parameters): + actual_param = actual.parameters.get(param.name) + if actual_param and param.default == '...': + parameters[i] = param.replace(default=actual_param.default) + + return overload.replace(parameters=parameters) + + def annotate_to_first_argument(self, func: Callable, typ: Type) -> Optional[Callable]: + """Annotate type hint to the first argument of function if needed.""" + try: + sig = inspect.signature(func, type_aliases=self.config.autodoc_type_aliases) + except TypeError as exc: + logger.warning(__("Failed to get a method signature for %s: %s"), + self.fullname, exc) + return None + except ValueError: + return None + + if len(sig.parameters) == 1: + return None + + def dummy(): + pass + + params = list(sig.parameters.values()) + if params[1].annotation is Parameter.empty: + params[1] = params[1].replace(annotation=typ) + try: + dummy.__signature__ = sig.replace(parameters=params) # type: ignore + return dummy + except (AttributeError, TypeError): + # failed to update signature (ex. built-in or extension types) + return None + + return func + + def get_doc(self) -> Optional[List[List[str]]]: + if self._new_docstrings is not None: + # docstring already returned previously, then modified by + # `DocstringSignatureMixin`. Just return the previously-computed + # result, so that we don't lose the processing done by + # `DocstringSignatureMixin`. + return self._new_docstrings + if self.objpath[-1] == '__init__': + docstring = getdoc(self.object, self.get_attr, + self.config.autodoc_inherit_docstrings, + self.parent, self.object_name) + if (docstring is not None and + (docstring == object.__init__.__doc__ or # for pypy + docstring.strip() == object.__init__.__doc__)): # for !pypy + docstring = None + if docstring: + tab_width = self.directive.state.document.settings.tab_width + return [prepare_docstring(docstring, tabsize=tab_width)] + else: + return [] + elif self.objpath[-1] == '__new__': + docstring = getdoc(self.object, self.get_attr, + self.config.autodoc_inherit_docstrings, + self.parent, self.object_name) + if (docstring is not None and + (docstring == object.__new__.__doc__ or # for pypy + docstring.strip() == object.__new__.__doc__)): # for !pypy + docstring = None + if docstring: + tab_width = self.directive.state.document.settings.tab_width + return [prepare_docstring(docstring, tabsize=tab_width)] + else: + return [] + else: + return super().get_doc() + + +class NonDataDescriptorMixin(DataDocumenterMixinBase): + """ + Mixin for AttributeDocumenter to provide the feature for supporting non + data-descriptors. + + .. note:: This mix-in must be inherited after other mix-ins. Otherwise, docstring + and :value: header will be suppressed unexpectedly. + """ + + def import_object(self, raiseerror: bool = False) -> bool: + ret = super().import_object(raiseerror) # type: ignore + if ret and not inspect.isattributedescriptor(self.object): + self.non_data_descriptor = True + else: + self.non_data_descriptor = False + + return ret + + def should_suppress_value_header(self) -> bool: + return (not getattr(self, 'non_data_descriptor', False) or + super().should_suppress_directive_header()) + + def get_doc(self) -> Optional[List[List[str]]]: + if getattr(self, 'non_data_descriptor', False): + # the docstring of non datadescriptor is very probably the wrong thing + # to display + return None + else: + return super().get_doc() # type: ignore + + +class SlotsMixin(DataDocumenterMixinBase): + """ + Mixin for AttributeDocumenter to provide the feature for supporting __slots__. + """ + + def isslotsattribute(self) -> bool: + """Check the subject is an attribute in __slots__.""" + try: + __slots__ = inspect.getslots(self.parent) + if __slots__ and self.objpath[-1] in __slots__: + return True + else: + return False + except (ValueError, TypeError): + return False + + def import_object(self, raiseerror: bool = False) -> bool: + ret = super().import_object(raiseerror) # type: ignore + if self.isslotsattribute(): + self.object = SLOTSATTR + + return ret + + def should_suppress_value_header(self) -> bool: + if self.object is SLOTSATTR: + return True + else: + return super().should_suppress_value_header() + + def get_doc(self) -> Optional[List[List[str]]]: + if self.object is SLOTSATTR: + try: + __slots__ = inspect.getslots(self.parent) + if __slots__ and __slots__.get(self.objpath[-1]): + docstring = prepare_docstring(__slots__[self.objpath[-1]]) + return [docstring] + else: + return [] + except ValueError as exc: + logger.warning(__('Invalid __slots__ found on %s. Ignored.'), + (self.parent.__qualname__, exc), type='autodoc') + return [] + else: + return super().get_doc() # type: ignore + + @property + def _datadescriptor(self) -> bool: + warnings.warn('AttributeDocumenter._datadescriptor() is deprecated.', + RemovedInSphinx60Warning) + if self.object is SLOTSATTR: + return True + else: + return False + + +class RuntimeInstanceAttributeMixin(DataDocumenterMixinBase): + """ + Mixin for AttributeDocumenter to provide the feature for supporting runtime + instance attributes (that are defined in __init__() methods with doc-comments). + + Example: + + class Foo: + def __init__(self): + self.attr = None #: This is a target of this mix-in. + """ + + RUNTIME_INSTANCE_ATTRIBUTE = object() + + def is_runtime_instance_attribute(self, parent: Any) -> bool: + """Check the subject is an attribute defined in __init__().""" + # An instance variable defined in __init__(). + if self.get_attribute_comment(parent, self.objpath[-1]): # type: ignore + return True + elif self.is_runtime_instance_attribute_not_commented(parent): + return True + else: + return False + + def is_runtime_instance_attribute_not_commented(self, parent: Any) -> bool: + """Check the subject is an attribute defined in __init__() without comment.""" + for cls in inspect.getmro(parent): + try: + module = safe_getattr(cls, '__module__') + qualname = safe_getattr(cls, '__qualname__') + + analyzer = ModuleAnalyzer.for_module(module) + analyzer.analyze() + if qualname and self.objpath: + key = '.'.join([qualname, self.objpath[-1]]) + if key in analyzer.tagorder: + return True + except (AttributeError, PycodeError): + pass + + return None + + def import_object(self, raiseerror: bool = False) -> bool: + """Check the existence of runtime instance attribute after failing to import the + attribute.""" + try: + return super().import_object(raiseerror=True) # type: ignore + except ImportError as exc: + try: + with mock(self.config.autodoc_mock_imports): + ret = import_object(self.modname, self.objpath[:-1], 'class', + attrgetter=self.get_attr, # type: ignore + warningiserror=self.config.autodoc_warningiserror) + parent = ret[3] + if self.is_runtime_instance_attribute(parent): + self.object = self.RUNTIME_INSTANCE_ATTRIBUTE + self.parent = parent + return True + except ImportError: + pass + + if raiseerror: + raise + else: + logger.warning(exc.args[0], type='autodoc', subtype='import_object') + self.env.note_reread() + return False + + def should_suppress_value_header(self) -> bool: + return (self.object is self.RUNTIME_INSTANCE_ATTRIBUTE or + super().should_suppress_value_header()) + + def get_doc(self) -> Optional[List[List[str]]]: + if (self.object is self.RUNTIME_INSTANCE_ATTRIBUTE and + self.is_runtime_instance_attribute_not_commented(self.parent)): + return None + else: + return super().get_doc() # type: ignore + + +class UninitializedInstanceAttributeMixin(DataDocumenterMixinBase): + """ + Mixin for AttributeDocumenter to provide the feature for supporting uninitialized + instance attributes (PEP-526 styled, annotation only attributes). + + Example: + + class Foo: + attr: int #: This is a target of this mix-in. + """ + + def is_uninitialized_instance_attribute(self, parent: Any) -> bool: + """Check the subject is an annotation only attribute.""" + annotations = get_type_hints(parent, None, self.config.autodoc_type_aliases) + if self.objpath[-1] in annotations: + return True + else: + return False + + def import_object(self, raiseerror: bool = False) -> bool: + """Check the exisitence of uninitialized instance attribute when failed to import + the attribute.""" + try: + return super().import_object(raiseerror=True) # type: ignore + except ImportError as exc: + try: + ret = import_object(self.modname, self.objpath[:-1], 'class', + attrgetter=self.get_attr, # type: ignore + warningiserror=self.config.autodoc_warningiserror) + parent = ret[3] + if self.is_uninitialized_instance_attribute(parent): + self.object = UNINITIALIZED_ATTR + self.parent = parent + return True + except ImportError: + pass + + if raiseerror: + raise + else: + logger.warning(exc.args[0], type='autodoc', subtype='import_object') + self.env.note_reread() + return False + + def should_suppress_value_header(self) -> bool: + return (self.object is UNINITIALIZED_ATTR or + super().should_suppress_value_header()) + + def get_doc(self) -> Optional[List[List[str]]]: + if self.object is UNINITIALIZED_ATTR: + return None + else: + return super().get_doc() # type: ignore + + +class AttributeDocumenter(GenericAliasMixin, NewTypeMixin, SlotsMixin, # type: ignore + TypeVarMixin, RuntimeInstanceAttributeMixin, + UninitializedInstanceAttributeMixin, NonDataDescriptorMixin, + DocstringStripSignatureMixin, ClassLevelDocumenter): """ Specialized Documenter subclass for attributes. """ objtype = 'attribute' member_order = 60 - option_spec = dict(ModuleLevelDocumenter.option_spec) + option_spec: OptionSpec = dict(ModuleLevelDocumenter.option_spec) option_spec["annotation"] = annotation_option + option_spec["no-value"] = bool_option # must be higher than the MethodDocumenter, else it will recognize # some non-data descriptors as methods priority = 10 @staticmethod - def is_function_or_method(obj): - return is_function_or_cython_function(obj) or inspect.isbuiltin(obj) or inspect.ismethod(obj) + def is_function_or_method(obj: Any) -> bool: + return inspect.isfunction(obj) or inspect.isbuiltin(obj) or inspect.ismethod(obj) @classmethod - def can_document_member(cls, member, membername, isattr, parent): - # type: (Any, unicode, bool, Any) -> bool - non_attr_types = (type, MethodDescriptorType) - isdatadesc = isdescriptor(member) and not \ - inspect.isroutine(member) and not \ - isinstance(member, non_attr_types) and not \ - type(member).__name__ == "instancemethod" - - isattribute = isdatadesc or (not isinstance(parent, ModuleDocumenter) and isattr) - - # Trac #26522: This condition is here just to pass objects of classes - # that inherit ClasscallMetaclass as attributes rather than method - # descriptors. - isattribute = isattribute or isinstance(type(member), ClasscallMetaclass) - - return isattribute - - # We ignore the obscure case supported in the following return - # statement. The additional check opens a door for attributes without - # docstrings to appear in the Sage documentation, and more seriously - # effectively prevents certain attributes to get properly documented. - # See trac #28698. - - # That last condition addresses an obscure case of C-defined - # methods using a deprecated type in Python 3, that is not otherwise - # exported anywhere by Python - return isattribute or (not isinstance(parent, ModuleDocumenter) and - not inspect.isroutine(member) and - not isinstance(member, type)) - - def document_members(self, all_members=False): - # type: (bool) -> None + def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any + ) -> bool: + if isinstance(parent, ModuleDocumenter): + return False + # ------------------------------------------------------------------- + # Trac: Do not pass objects of class CachedMethodCallerNoArgs as attributes. + # + # sage: from sphinx.util import inspect + # sage: A = AlgebrasWithBasis(QQ).example() + # sage: member = A.one_basis + # sage: type(member) + # + # sage: inspect.isattributedescriptor(member) + # True + # sage: inspect.isroutine(member) + # True + # + from sage.misc.classcall_metaclass import ClasscallMetaclass + if inspect.isattributedescriptor(member) and not inspect.isroutine(member): + return True + # Trac #26522: Pass objects of classes that inherit ClasscallMetaclass + # as attributes rather than method descriptors. + if isinstance(type(member), ClasscallMetaclass): + return True + # ------------------------------------------------------------------- + elif not inspect.isroutine(member) and not isinstance(member, type): + return True + else: + return False + + def document_members(self, all_members: bool = False) -> None: pass - def import_object(self): - # type: () -> Any - ret = ClassLevelDocumenter.import_object(self) - if isenumattribute(self.object): + def update_annotations(self, parent: Any) -> None: + """Update __annotations__ to support type_comment and so on.""" + try: + annotations = dict(inspect.getannotations(parent)) + parent.__annotations__ = annotations + + for cls in inspect.getmro(parent): + try: + module = safe_getattr(cls, '__module__') + qualname = safe_getattr(cls, '__qualname__') + + analyzer = ModuleAnalyzer.for_module(module) + analyzer.analyze() + for (classname, attrname), annotation in analyzer.annotations.items(): + if classname == qualname and attrname not in annotations: + annotations[attrname] = annotation + except (AttributeError, PycodeError): + pass + except (AttributeError, TypeError): + # Failed to set __annotations__ (built-in, extensions, etc.) + pass + + def import_object(self, raiseerror: bool = False) -> bool: + ret = super().import_object(raiseerror) + if inspect.isenumattribute(self.object): self.object = self.object.value - if isdescriptor(self.object) and \ - not self.is_function_or_method(self.object): - self._datadescriptor = True - else: - # if it's not a data descriptor - self._datadescriptor = False + if self.parent: + self.update_annotations(self.parent) + return ret - def get_real_modname(self): - # type: () -> str - return self.get_attr(self.parent or self.object, '__module__', None) \ - or self.modname + def get_real_modname(self) -> str: + real_modname = self.get_attr(self.parent or self.object, '__module__', None) + return real_modname or self.modname + + def should_suppress_value_header(self) -> bool: + if super().should_suppress_value_header(): + return True + else: + doc = self.get_doc() + if doc: + docstring, metadata = separate_metadata('\n'.join(sum(doc, []))) + if 'hide-value' in metadata: + return True + + return False - def add_directive_header(self, sig): - # type: (unicode) -> None - ClassLevelDocumenter.add_directive_header(self, sig) + def add_directive_header(self, sig: str) -> None: + super().add_directive_header(sig) sourcename = self.get_sourcename() - if not self.options.annotation: - if not self._datadescriptor: - try: - objrepr = object_description(self.object) - except ValueError: - pass - else: - self.add_line(u' :annotation: = ' + objrepr, sourcename) - elif self.options.annotation is SUPPRESS: + if self.options.annotation is SUPPRESS or self.should_suppress_directive_header(): pass + elif self.options.annotation: + self.add_line(' :annotation: %s' % self.options.annotation, sourcename) else: - self.add_line(u' :annotation: %s' % self.options.annotation, - sourcename) + if self.config.autodoc_typehints != 'none': + # obtain type annotation for this attribute + annotations = get_type_hints(self.parent, None, + self.config.autodoc_type_aliases) + if self.objpath[-1] in annotations: + if self.config.autodoc_typehints_format == "short": + objrepr = stringify_typehint(annotations.get(self.objpath[-1]), + "smart") + else: + objrepr = stringify_typehint(annotations.get(self.objpath[-1])) + self.add_line(' :type: ' + objrepr, sourcename) - def add_content(self, more_content, no_docstring=False): - # type: (Any, bool) -> None - if not self._datadescriptor: - # if it's not a data descriptor, its docstring is very probably the - # wrong thing to display - no_docstring = True - ClassLevelDocumenter.add_content(self, more_content, no_docstring) + try: + if (self.options.no_value or self.should_suppress_value_header() or + ismock(self.object)): + pass + else: + objrepr = object_description(self.object) + self.add_line(' :value: ' + objrepr, sourcename) + except ValueError: + pass + + def get_attribute_comment(self, parent: Any, attrname: str) -> Optional[List[str]]: + for cls in inspect.getmro(parent): + try: + module = safe_getattr(cls, '__module__') + qualname = safe_getattr(cls, '__qualname__') + + analyzer = ModuleAnalyzer.for_module(module) + analyzer.analyze() + if qualname and self.objpath: + key = (qualname, attrname) + if key in analyzer.attr_docs: + return list(analyzer.attr_docs[key]) + except (AttributeError, PycodeError): + pass + return None -class InstanceAttributeDocumenter(AttributeDocumenter): + def get_doc(self) -> Optional[List[List[str]]]: + # Check the attribute has a docstring-comment + comment = self.get_attribute_comment(self.parent, self.objpath[-1]) + if comment: + return [comment] + + try: + # Disable `autodoc_inherit_docstring` temporarily to avoid to obtain + # a docstring from the value which descriptor returns unexpectedly. + # ref: https://github.com/sphinx-doc/sphinx/issues/7805 + orig = self.config.autodoc_inherit_docstrings + self.config.autodoc_inherit_docstrings = False # type: ignore + return super().get_doc() + finally: + self.config.autodoc_inherit_docstrings = orig # type: ignore + + def add_content(self, more_content: Optional[StringList]) -> None: + # Disable analyzing attribute comment on Documenter.add_content() to control it on + # AttributeDocumenter.add_content() + self.analyzer = None + + if more_content is None: + more_content = StringList() + self.update_content(more_content) + super().add_content(more_content) + + +class PropertyDocumenter(DocstringStripSignatureMixin, ClassLevelDocumenter): # type: ignore """ - Specialized Documenter subclass for attributes that cannot be imported - because they are instance attributes (e.g. assigned in __init__). + Specialized Documenter subclass for properties. """ - objtype = 'instanceattribute' - directivetype = 'attribute' + objtype = 'property' member_order = 60 - # must be higher than AttributeDocumenter - priority = 11 + # before AttributeDocumenter + priority = AttributeDocumenter.priority + 1 @classmethod - def can_document_member(cls, member, membername, isattr, parent): - # type: (Any, unicode, bool, Any) -> bool - """This documents only INSTANCEATTR members.""" - return isattr and (member is INSTANCEATTR) - - def import_object(self): - # type: () -> bool - """Never import anything.""" - # disguise as an attribute - self.objtype = 'attribute' - self._datadescriptor = False - return True + def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any + ) -> bool: + if isinstance(parent, ClassDocumenter): + if inspect.isproperty(member): + return True + else: + __dict__ = safe_getattr(parent.object, '__dict__', {}) + obj = __dict__.get(membername) + return isinstance(obj, classmethod) and inspect.isproperty(obj.__func__) + else: + return False + + def import_object(self, raiseerror: bool = False) -> bool: + """Check the exisitence of uninitialized instance attribute when failed to import + the attribute.""" + ret = super().import_object(raiseerror) + if ret and not inspect.isproperty(self.object): + __dict__ = safe_getattr(self.parent, '__dict__', {}) + obj = __dict__.get(self.objpath[-1]) + if isinstance(obj, classmethod) and inspect.isproperty(obj.__func__): + self.object = obj.__func__ + self.isclassmethod = True + return True + else: + return False - def add_content(self, more_content, no_docstring=False): - # type: (Any, bool) -> None - """Never try to get a docstring from the object.""" - AttributeDocumenter.add_content(self, more_content, no_docstring=True) + self.isclassmethod = False + return ret + def document_members(self, all_members: bool = False) -> None: + pass -def get_documenters(app): - # type: (Sphinx) -> Dict[unicode, Type[Documenter]] - """Returns registered Documenter classes""" - return app.registry.documenters + def get_real_modname(self) -> str: + real_modname = self.get_attr(self.parent or self.object, '__module__', None) + return real_modname or self.modname + def add_directive_header(self, sig: str) -> None: + super().add_directive_header(sig) + sourcename = self.get_sourcename() + if inspect.isabstractmethod(self.object): + self.add_line(' :abstractmethod:', sourcename) + if self.isclassmethod: + self.add_line(' :classmethod:', sourcename) + + if safe_getattr(self.object, 'fget', None): # property + func = self.object.fget + elif safe_getattr(self.object, 'func', None): # cached_property + func = self.object.func + else: + func = None -def autodoc_attrgetter(app, obj, name, *defargs): - # type: (Sphinx, Any, unicode, Any) -> Any + if func and self.config.autodoc_typehints != 'none': + try: + signature = inspect.signature(func, + type_aliases=self.config.autodoc_type_aliases) + if signature.return_annotation is not Parameter.empty: + if self.config.autodoc_typehints_format == "short": + objrepr = stringify_typehint(signature.return_annotation, "smart") + else: + objrepr = stringify_typehint(signature.return_annotation) + self.add_line(' :type: ' + objrepr, sourcename) + except TypeError as exc: + logger.warning(__("Failed to get a function signature for %s: %s"), + self.fullname, exc) + return None + except ValueError: + return None + + +class NewTypeAttributeDocumenter(AttributeDocumenter): + """ + Specialized Documenter subclass for NewTypes. + + Note: This must be invoked before MethodDocumenter because NewType is a kind of + function object. + """ + + objtype = 'newvarattribute' + directivetype = 'attribute' + priority = MethodDocumenter.priority + 1 + + @classmethod + def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: Any + ) -> bool: + return not isinstance(parent, ModuleDocumenter) and inspect.isNewType(member) + + +def autodoc_attrgetter(app: Sphinx, obj: Any, name: str, *defargs: Any) -> Any: """Alternative getattr() for types""" for typ, func in app.registry.autodoc_attrgettrs.items(): if isinstance(obj, typ): @@ -1617,41 +2922,43 @@ def autodoc_attrgetter(app, obj, name, *defargs): return safe_getattr(obj, name, *defargs) -def setup(app): - # type: (Sphinx) -> Dict[unicode, Any] +def setup(app: Sphinx) -> Dict[str, Any]: app.add_autodocumenter(ModuleDocumenter) app.add_autodocumenter(ClassDocumenter) app.add_autodocumenter(ExceptionDocumenter) app.add_autodocumenter(DataDocumenter) + app.add_autodocumenter(NewTypeDataDocumenter) app.add_autodocumenter(FunctionDocumenter) + app.add_autodocumenter(DecoratorDocumenter) app.add_autodocumenter(MethodDocumenter) app.add_autodocumenter(AttributeDocumenter) - app.add_autodocumenter(InstanceAttributeDocumenter) + app.add_autodocumenter(PropertyDocumenter) + app.add_autodocumenter(NewTypeAttributeDocumenter) - app.add_config_value('autoclass_content', 'class', True) - app.add_config_value('autodoc_member_order', 'alphabetic', True) - app.add_config_value('autodoc_default_flags', [], True) # deprecated since Sphinx 1.8 + app.add_config_value('autoclass_content', 'class', True, ENUM('both', 'class', 'init')) + app.add_config_value('autodoc_member_order', 'alphabetical', True, + ENUM('alphabetical', 'bysource', 'groupwise')) + app.add_config_value('autodoc_class_signature', 'mixed', True, ENUM('mixed', 'separated')) app.add_config_value('autodoc_default_options', {}, True) - app.add_config_value('autodoc_docstring_signature', False, True) + app.add_config_value('autodoc_docstring_signature', True, True) app.add_config_value('autodoc_mock_imports', [], True) + app.add_config_value('autodoc_typehints', "signature", True, + ENUM("signature", "description", "none", "both")) + app.add_config_value('autodoc_typehints_description_target', 'all', True, + ENUM('all', 'documented', 'documented_params')) + app.add_config_value('autodoc_type_aliases', {}, True) + app.add_config_value('autodoc_typehints_format', "short", 'env', + ENUM("fully-qualified", "short")) app.add_config_value('autodoc_warningiserror', True, True) app.add_config_value('autodoc_inherit_docstrings', True, True) - app.add_config_value('autodoc_builtin_argspec', None, True) - + app.add_event('autodoc-before-process-signature') app.add_event('autodoc-process-docstring') app.add_event('autodoc-process-signature') app.add_event('autodoc-skip-member') + app.add_event('autodoc-process-bases') - return {'version': sphinx.__display_version__, 'parallel_read_safe': True} - + app.setup_extension('sphinx.ext.autodoc.preserve_defaults') + app.setup_extension('sphinx.ext.autodoc.type_comment') + app.setup_extension('sphinx.ext.autodoc.typehints') -class testcls: - """test doc string""" - - def __getattr__(self, x): - # type: (Any) -> Any - return x - - def __setattr__(self, x, y): - # type: (Any, Any) -> None - """Attr setter.""" + return {'version': sphinx.__display_version__, 'parallel_read_safe': True} diff --git a/src/sage_setup/cython_options.py b/src/sage_setup/cython_options.py index 086aa070ca9..a878fb754e4 100644 --- a/src/sage_setup/cython_options.py +++ b/src/sage_setup/cython_options.py @@ -10,6 +10,11 @@ def compiler_directives(profile: bool): auto_pickle=False, # Do not create __test__ dictionary automatically from docstrings autotestdict=False, + # When enabled, functions will bind to an instance when looked up as a + # class attribute (hence the name) and will emulate the attributes of + # Python functions, including introspections like argument names and + # annotations + binding=True, # Do not check for division by 0 (this is about 35% quicker than with check) cdivision=True, # Embed a textual copy of the call signature in the docstring (to support tools like IPython) From f5fc398a2fcbae655942e8b075a65319e82c49ee Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Mon, 7 Nov 2022 21:17:29 +0800 Subject: [PATCH 405/632] copy orders between isogenous curves for all EllipticCurveHom instances --- .../elliptic_curves/ell_curve_isogeny.py | 6 --- src/sage/schemes/elliptic_curves/ell_field.py | 47 ------------------- .../elliptic_curves/ell_finite_field.py | 40 ++++++++++++++++ src/sage/schemes/elliptic_curves/hom.py | 33 +++++++++++++ 4 files changed, 73 insertions(+), 53 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index d03346deacc..141ef7ef1b1 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -1018,12 +1018,6 @@ def __init__(self, E, kernel, codomain=None, degree=None, model=None, check=True # Inheritance house keeping self.__perform_inheritance_housekeeping() - # over finite fields, isogenous curves have the same number - # of rational points, hence we copy over cached curve orders - if self.__base_field.is_finite(): - self._codomain._fetch_cached_order(self._domain) - self._domain._fetch_cached_order(self._codomain) - def _eval(self, P): r""" Less strict evaluation method for internal use. diff --git a/src/sage/schemes/elliptic_curves/ell_field.py b/src/sage/schemes/elliptic_curves/ell_field.py index 6b64df4d075..2b38144b046 100644 --- a/src/sage/schemes/elliptic_curves/ell_field.py +++ b/src/sage/schemes/elliptic_curves/ell_field.py @@ -1024,53 +1024,6 @@ def division_field(self, l, names='t', map=False, **kwds): else: return L - def _fetch_cached_order(self, other): - r""" - This method copies the ``_order`` member from ``other`` - to ``self`` if their base field is the same and finite. - - This is used in :class:`EllipticCurveIsogeny` to keep track of - an already computed curve order: According to Tate's theorem - [Tate1966b]_, isogenous elliptic curves over a finite field - have the same number of rational points. - - EXAMPLES:: - - sage: E1 = EllipticCurve(GF(2^127-1), [1,2,3,4,5]) - sage: E1.set_order(170141183460469231746191640949390434666) - sage: E2 = EllipticCurve(GF(2^127-1), [115649500210559831225094148253060920818, 36348294106991415644658737184600079491]) - sage: E2._fetch_cached_order(E1) - sage: E2._order - 170141183460469231746191640949390434666 - - TESTS:: - - sage: E3 = EllipticCurve(GF(17), [1,2,3,4,5]) - sage: hasattr(E3, '_order') - False - sage: E3._fetch_cached_order(E1) - Traceback (most recent call last): - ... - ValueError: curves have distinct base fields - - :: - - sage: E4 = EllipticCurve([1,2,3,4,5]) - sage: E4._fetch_cached_order(E1.change_ring(QQ)) - sage: hasattr(E4, '_order') - False - """ - if hasattr(self, '_order') or not hasattr(other, '_order'): - return - F = self.base_field() - if F != other.base_field(): - raise ValueError('curves have distinct base fields') - if not F.is_finite(): - raise ValueError('base field must be finite') - n = getattr(other, '_order', None) - if n is not None: - self._order = n - def isogeny(self, kernel, codomain=None, degree=None, model=None, check=True, algorithm=None): r""" Return an elliptic-curve isogeny from this elliptic curve. diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 31e193a64b6..a8235585b17 100644 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -1289,6 +1289,46 @@ def set_order(self, value, num_checks=8): self._order = value + def _fetch_cached_order(self, other): + r""" + This method copies the ``_order`` member from ``other`` to + ``self``. Both curves must have the same finite base field. + + This is used in + :class:`~sage.schemes.elliptic_curves.hom.EllipticCurveHom` + to keep track of an already computed curve order: According + to Tate's theorem [Tate1966b]_, isogenous elliptic curves + over a finite field have the same number of rational points. + + EXAMPLES:: + + sage: E1 = EllipticCurve(GF(2^127-1), [1,2,3,4,5]) + sage: E1.set_order(170141183460469231746191640949390434666) + sage: E2 = EllipticCurve(GF(2^127-1), [115649500210559831225094148253060920818, 36348294106991415644658737184600079491]) + sage: E2._fetch_cached_order(E1) + sage: E2._order + 170141183460469231746191640949390434666 + + TESTS:: + + sage: E3 = EllipticCurve(GF(17), [1,2,3,4,5]) + sage: hasattr(E3, '_order') + False + sage: E3._fetch_cached_order(E1) + Traceback (most recent call last): + ... + ValueError: curves have distinct base fields + """ + if hasattr(self, '_order') or not hasattr(other, '_order'): + return + F = self.base_field() + if F != other.base_field(): + raise ValueError('curves have distinct base fields') + n = getattr(other, '_order', None) + if n is not None: + self._order = n + + # dict to hold precomputed coefficient vectors of supersingular j values (excluding 0, 1728): supersingular_j_polynomials = {} diff --git a/src/sage/schemes/elliptic_curves/hom.py b/src/sage/schemes/elliptic_curves/hom.py index bdf2c969079..e6806b7868e 100644 --- a/src/sage/schemes/elliptic_curves/hom.py +++ b/src/sage/schemes/elliptic_curves/hom.py @@ -35,6 +35,39 @@ class EllipticCurveHom(Morphism): """ Base class for elliptic-curve morphisms. """ + def __init__(self, *args, **kwds): + r""" + Constructor for elliptic-curve morphisms. + + EXAMPLES:: + + sage: E = EllipticCurve(GF(257^2), [5,5]) + sage: P = E.lift_x(1) + sage: E.isogeny(P) # indirect doctest + Isogeny of degree 127 from Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Finite Field in z2 of size 257^2 to Elliptic Curve defined by y^2 = x^3 + 151*x + 22 over Finite Field in z2 of size 257^2 + sage: E.isogeny(P, algorithm='factored') # indirect doctest + Composite morphism of degree 127 = 127: + From: Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Finite Field in z2 of size 257^2 + To: Elliptic Curve defined by y^2 = x^3 + 151*x + 22 over Finite Field in z2 of size 257^2 + sage: E.isogeny(P, algorithm='velusqrt') # indirect doctest + Elliptic-curve isogeny (using √élu) of degree 127: + From: Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Finite Field in z2 of size 257^2 + To: Elliptic Curve defined by y^2 = x^3 + 119*x + 231 over Finite Field in z2 of size 257^2 + sage: E.montgomery_model(morphism=True) # indirect doctest + (Elliptic Curve defined by y^2 = x^3 + (199*z2+73)*x^2 + x over Finite Field in z2 of size 257^2, + Elliptic-curve morphism: + From: Elliptic Curve defined by y^2 = x^3 + 5*x + 5 over Finite Field in z2 of size 257^2 + To: Elliptic Curve defined by y^2 = x^3 + (199*z2+73)*x^2 + x over Finite Field in z2 of size 257^2 + Via: (u,r,s,t) = (88*z2 + 253, 208*z2 + 90, 0, 0)) + """ + super().__init__(*args, **kwds) + + # Over finite fields, isogenous curves have the same number of + # rational points, hence we copy over the cached curve orders. + if isinstance(self.base_ring(), finite_field_base.FiniteField) and self.degree(): + self._codomain._fetch_cached_order(self._domain) + self._domain._fetch_cached_order(self._codomain) + def _repr_type(self): """ Return a textual representation of what kind of morphism From 5b880a67eb036df92e435fdc12edc119bd090878 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Mon, 7 Nov 2022 23:00:29 +0800 Subject: [PATCH 406/632] copy point order through isomorphisms --- src/sage/schemes/elliptic_curves/weierstrass_morphism.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py index a9ad45f6fb2..77957adbd63 100644 --- a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py +++ b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py @@ -637,9 +637,11 @@ def __call__(self, P): """ if P[2] == 0: return self._codomain(0) - return self._codomain.point(baseWI.__call__(self, - tuple(P._coords)), - check=False) + res = baseWI.__call__(self, tuple(P._coords)) + Q = self._codomain.point(res, check=False) + if hasattr(P, '_order'): + Q._order = P._order + return Q def __invert__(self): r""" From 3c297c6e30171f071e7fb7187ac1a0587a0d803a Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Tue, 8 Nov 2022 14:20:50 +0800 Subject: [PATCH 407/632] copy point order through coprime-degree isogenies --- src/sage/schemes/elliptic_curves/ell_curve_isogeny.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 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 141ef7ef1b1..c5f1f29919b 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -1181,7 +1181,16 @@ def _call_(self, P): yP = self.__posti_ratl_maps[1](xP, yP) xP = self.__posti_ratl_maps[0](xP) - return self._codomain(xP, yP) + Q = self._codomain(xP, yP) + if hasattr(P, '_order') and P._order.gcd(self._degree).is_one(): + # TODO: For non-coprime degree, the order of the point + # gets reduced by a divisor of the degree when passing + # through the isogeny. We could run something along the + # lines of order_from_multiple() to determine the new + # order, but this probably shouldn't happen by default + # as it'll be detrimental to performance in some cases. + Q._order = P._order + return Q def __getitem__(self, i): r""" From 177c0f6f2d3bb17a20c7c6e7661e9f94e0c8b872 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Tue, 8 Nov 2022 14:06:08 +0800 Subject: [PATCH 408/632] return original point when converting to same curve --- src/sage/schemes/elliptic_curves/ell_generic.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 154a581bc2a..4cd98d31e00 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -559,6 +559,8 @@ def __call__(self, *args, **kwds): (ell_point.EllipticCurvePoint_field, ell_point.EllipticCurvePoint_number_field, ell_point.EllipticCurvePoint)): + if P.curve() is self: + return P # check if denominator of the point contains a factor of the # characteristic of the base ring. if so, coerce the point to # infinity. From ed3735769c4744334c3ae84513dc684cc97ed008 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Tue, 8 Nov 2022 14:33:31 +0800 Subject: [PATCH 409/632] add tests for copying of point orders --- .../schemes/elliptic_curves/ell_curve_isogeny.py | 10 ++++++++++ src/sage/schemes/elliptic_curves/hom_composite.py | 10 ++++++++++ .../elliptic_curves/weierstrass_morphism.py | 14 ++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py index c5f1f29919b..1d8837356c1 100644 --- a/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py +++ b/src/sage/schemes/elliptic_curves/ell_curve_isogeny.py @@ -1151,6 +1151,16 @@ def _call_(self, P): Traceback (most recent call last): ... TypeError: (20 : 90 : 1) fails to convert into the map's domain Elliptic Curve defined by y^2 = x^3 + 7*x over Number Field in th with defining polynomial x^2 + 3, but a `pushforward` method is not properly implemented + + Check that copying the order over works:: + + sage: E = EllipticCurve(GF(431), [1,0]) + sage: P, = E.gens() + sage: Q = 2^99*P; Q.order() + 27 + sage: phi = E.isogeny(3^99*P) + sage: phi(Q)._order + 27 """ if P.is_zero(): return self._codomain(0) diff --git a/src/sage/schemes/elliptic_curves/hom_composite.py b/src/sage/schemes/elliptic_curves/hom_composite.py index b2096eda5b8..7ca458dc43d 100644 --- a/src/sage/schemes/elliptic_curves/hom_composite.py +++ b/src/sage/schemes/elliptic_curves/hom_composite.py @@ -398,6 +398,16 @@ def _call_(self, P): sage: R = E.lift_x(15/4 * (a+3)) sage: psi(R) # indirect doctest (1033648757/303450 : 58397496786187/1083316500*a - 62088706165177/2166633000 : 1) + + Check that copying the order over works:: + + sage: E = EllipticCurve(GF(431), [1,0]) + sage: P, = E.gens() + sage: Q = 2^99*P; Q.order() + 27 + sage: phi = E.isogeny(3^99*P, algorithm='factored') + sage: phi(Q)._order + 27 """ return _eval_factored_isogeny(self._phis, P) diff --git a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py index 77957adbd63..85c673101d5 100644 --- a/src/sage/schemes/elliptic_curves/weierstrass_morphism.py +++ b/src/sage/schemes/elliptic_curves/weierstrass_morphism.py @@ -634,6 +634,20 @@ def __call__(self, P): (-3/4 : 3/4 : 1) sage: w(P).curve() == E.change_weierstrass_model((2,3,4,5)) True + + TESTS: + + Check that copying the order over works:: + + sage: E = EllipticCurve(GF(431^2), [1,0]) + sage: i = next(a for a in E.automorphisms() if a^2 == -a^24) + sage: P,_ = E.gens() + sage: P._order + 432 + sage: i(P)._order + 432 + sage: E(i(P))._order + 432 """ if P[2] == 0: return self._codomain(0) From 79629444afe7ecdaccae023876ead5ec5dcff415 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Tue, 8 Nov 2022 14:10:17 +0800 Subject: [PATCH 410/632] =?UTF-8?q?make=20use=20of=20new=20and=20improved?= =?UTF-8?q?=20caching=20in=20=E2=88=9A=C3=A9lu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../schemes/elliptic_curves/hom_velusqrt.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/hom_velusqrt.py b/src/sage/schemes/elliptic_curves/hom_velusqrt.py index 144b5400970..8f0befbc5ca 100644 --- a/src/sage/schemes/elliptic_curves/hom_velusqrt.py +++ b/src/sage/schemes/elliptic_curves/hom_velusqrt.py @@ -896,24 +896,23 @@ def __init__(self, E, P, *, codomain=None, model=None, Q=None): if codomain is not None and model is not None: raise ValueError('cannot specify a codomain curve and model name simultaneously') - try: - self._raw_domain = E.short_weierstrass_model() - except ValueError: - raise NotImplementedError('only implemented for curves having a short Weierstrass model') - self._pre_iso = E.isomorphism_to(self._raw_domain) - try: P = E(P) except TypeError: raise ValueError('given kernel point P does not lie on E') - self._P = self._pre_iso(P) - - self._degree = self._P.order() + self._degree = P.order() if self._degree % 2 != 1 or self._degree < 9: raise NotImplementedError('only implemented for odd degrees >= 9') + try: + self._raw_domain = E.short_weierstrass_model() + except ValueError: + raise NotImplementedError('only implemented for curves having a short Weierstrass model') + self._pre_iso = E.isomorphism_to(self._raw_domain) + self._P = self._pre_iso(P) + if Q is not None: - self._Q = E(Q) + self._Q = self._pre_iso(E(Q)) EE = E else: self._Q = _point_outside_subgroup(self._P) # may extend base field From 8cb464d6bd4d314d33a0373ab62e7ea4c32391e9 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Tue, 8 Nov 2022 08:22:22 +0100 Subject: [PATCH 411/632] add missing dot --- src/sage/rings/invariants/invariant_theory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/invariants/invariant_theory.py b/src/sage/rings/invariants/invariant_theory.py index bb249b60560..0c0aa1c9f69 100644 --- a/src/sage/rings/invariants/invariant_theory.py +++ b/src/sage/rings/invariants/invariant_theory.py @@ -7,7 +7,7 @@ degree `d` in `n` variables. The special linear group `SL(n,\CC)` acts on the variables `(x_1,\dots, x_n)` linearly, -. MATH:: +.. MATH:: (x_1,\dots, x_n)^t \to A (x_1,\dots, x_n)^t ,\qquad From 2402ce0ea87028ef58a59e87a3855fa588316bb9 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Tue, 8 Nov 2022 17:36:17 +0800 Subject: [PATCH 412/632] fix random doctest failure --- src/sage/schemes/elliptic_curves/hom_velusqrt.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/hom_velusqrt.py b/src/sage/schemes/elliptic_curves/hom_velusqrt.py index 144b5400970..1a8809fc6aa 100644 --- a/src/sage/schemes/elliptic_curves/hom_velusqrt.py +++ b/src/sage/schemes/elliptic_curves/hom_velusqrt.py @@ -1390,7 +1390,7 @@ def _random_example_for_testing(): sage: 5 <= K.order() True """ - from sage.all import prime_range, choice, randrange, GF, gcd + from sage.all import prime_range, choice, randrange, GF, lcm, Mod while True: p = choice(prime_range(2, 100)) e = randrange(1,5) @@ -1412,9 +1412,10 @@ def _random_example_for_testing(): deg = choice(ds) break G = A.torsion_subgroup(deg) + os = G.generator_orders() while True: - v = [randrange(deg) for _ in range(G.ngens())] - if gcd([deg] + v) == 1: + v = [randrange(o) for o in os] + if lcm(Mod(c,o).additive_order() for c,o in zip(v,os)) == deg: break K = G(v).element() assert K.order() == deg From d6a834a1f1d3cd377b722bcbec97bfc1b468e252 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Tue, 8 Nov 2022 09:33:33 +0000 Subject: [PATCH 413/632] use an appropriate alogorithm to echelonize inexact matrices --- src/sage/matrix/matrix2.pyx | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index ad0f7e58e15..6c437e42c17 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -63,6 +63,8 @@ AUTHORS: Pfaffian - Moritz Firsching(2020-10-05): added ``quantum_determinant`` + +- Dima Pasechnik (2022-11-08): fixed ``echelonize`` for inexact matrices """ # **************************************************************************** @@ -7599,6 +7601,15 @@ cdef class Matrix(Matrix1): sage: transformation_matrix = m.echelonize(transformation=True) sage: m == transformation_matrix * m_original True + + TESTS:: + + Check that :trac:`34724` is fixed (indirect doctest):: + + sage: a=RR(6.12323399573677e-17) + sage: m=matrix(RR,[[-a, -1.72508242466029], [ 0.579682446302195, a]]) + sage: (~m*m).norm() + 1.0 """ self.check_mutability() @@ -7617,7 +7628,11 @@ cdef class Matrix(Matrix1): except (AttributeError, TypeError): algorithm = 'scaled_partial_pivoting_valuation' else: - algorithm = 'classical' + try: + self.base_ring(1/2).abs() + algorithm = 'scaled_partial_pivoting' + except (AttributeError, ArithmeticError, TypeError): + algorithm = 'classical' try: if self.base_ring() in _Fields: if algorithm in ['classical', 'partial_pivoting', 'scaled_partial_pivoting', 'scaled_partial_pivoting_valuation']: From 3b1adb8d65dfe2c4e6d3091081a1b12b77209c6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 8 Nov 2022 11:42:20 +0100 Subject: [PATCH 414/632] fix cython warning in blas_dict --- src/sage/data_structures/blas_dict.pxd | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/data_structures/blas_dict.pxd b/src/sage/data_structures/blas_dict.pxd index f55b77975b4..7464c0daba8 100644 --- a/src/sage/data_structures/blas_dict.pxd +++ b/src/sage/data_structures/blas_dict.pxd @@ -7,6 +7,5 @@ cpdef dict sum(dict_iter) cpdef dict linear_combination(dict_factor_iter, bint factor_on_left=*) cpdef dict sum_of_monomials(monomials, scalar) cpdef dict sum_of_terms(index_coeff_pairs) -cdef inline dict remove_zeros(dict D) +cdef dict remove_zeros(dict D) cpdef dict convert_remove_zeroes(dict D, R) - From 3e917a48683f558c90819d93e0ed0cbecc6551aa Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Tue, 8 Nov 2022 21:01:19 +0900 Subject: [PATCH 415/632] Remove spurious changes --- src/doc/en/prep/Programming.rst | 2 +- .../en/prep/Symbolics-and-Basic-Plotting.rst | 2 +- src/sage/arith/misc.py | 8 ++++---- src/sage/combinat/sf/classical.py | 2 +- src/sage/finance/option.pyx | 2 +- src/sage/graphs/strongly_regular_db.pyx | 2 +- src/sage/interfaces/sage0.py | 2 +- src/sage/libs/singular/ring.pyx | 2 +- src/sage/misc/c3_controlled.pyx | 2 +- src/sage/misc/sagedoc_conf.py | 1 - src/sage/misc/sageinspect.py | 2 +- .../rings/finite_rings/hom_finite_field.pyx | 4 ++-- src/sage/rings/function_field/function_field.py | 2 +- src/sage/sets/disjoint_set.pyx | 8 ++++---- src/sage/structure/category_object.pyx | 6 +++--- src/sage/structure/factory.pyx | 2 +- src/sage/structure/list_clone.pyx | 4 ++-- src/sage/symbolic/expression.pyx | 1 + src/sage/symbolic/function.pyx | 17 +++++------------ src/sage/symbolic/getitem_impl.pxi | 2 +- src/sage/symbolic/ring.pyx | 7 ++----- src/sage_docbuild/conf.py | 6 +++--- src/sage_setup/cython_options.py | 5 ----- 23 files changed, 38 insertions(+), 53 deletions(-) diff --git a/src/doc/en/prep/Programming.rst b/src/doc/en/prep/Programming.rst index 056326927b6..3ae980667df 100644 --- a/src/doc/en/prep/Programming.rst +++ b/src/doc/en/prep/Programming.rst @@ -109,7 +109,7 @@ It is very important to keep in the parentheses. :: sage: A.det # Won't work - + This is so useful because we can use the 'tab' key, remember! diff --git a/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst b/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst index bf11ef481b1..6e0ab69765a 100644 --- a/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst +++ b/src/doc/en/prep/Symbolics-and-Basic-Plotting.rst @@ -178,7 +178,7 @@ This is a good place for a few reminders of basic help. :: sage: z.simplify - + Finally, recall that you can get nicely typeset versions of the output in several ways. diff --git a/src/sage/arith/misc.py b/src/sage/arith/misc.py index 0eca5554dc9..1bd4d4c6a1f 100644 --- a/src/sage/arith/misc.py +++ b/src/sage/arith/misc.py @@ -2163,9 +2163,9 @@ def get_gcd(order): EXAMPLES:: sage: sage.arith.misc.get_gcd(4000) - + sage: sage.arith.misc.get_gcd(400000) - + sage: sage.arith.misc.get_gcd(4000000000) """ @@ -2185,9 +2185,9 @@ def get_inverse_mod(order): EXAMPLES:: sage: sage.arith.misc.get_inverse_mod(6000) - + sage: sage.arith.misc.get_inverse_mod(600000) - + sage: sage.arith.misc.get_inverse_mod(6000000000) """ diff --git a/src/sage/combinat/sf/classical.py b/src/sage/combinat/sf/classical.py index 3de5db72457..0477629f3d1 100644 --- a/src/sage/combinat/sf/classical.py +++ b/src/sage/combinat/sf/classical.py @@ -46,7 +46,7 @@ def init(): sage: sage.combinat.sf.classical.conversion_functions = {} sage: init() sage: sage.combinat.sf.classical.conversion_functions[('Schur', 'powersum')] - + The following checks if the bug described in :trac:`15312` is fixed. :: diff --git a/src/sage/finance/option.pyx b/src/sage/finance/option.pyx index 79c514c4677..31b290e0ce9 100644 --- a/src/sage/finance/option.pyx +++ b/src/sage/finance/option.pyx @@ -36,7 +36,7 @@ def black_scholes(double spot_price, double strike_price, double time_to_maturit sage: finance.black_scholes doctest:warning... DeprecationWarning: the package sage.finance is deprecated... - + sage: finance.black_scholes(42, 40, 0.5, 0.1, 0.2, 'call') # abs tol 1e-10 4.759422392871532 diff --git a/src/sage/graphs/strongly_regular_db.pyx b/src/sage/graphs/strongly_regular_db.pyx index 58ae72ae4e2..1e01738662d 100644 --- a/src/sage/graphs/strongly_regular_db.pyx +++ b/src/sage/graphs/strongly_regular_db.pyx @@ -1154,7 +1154,7 @@ def is_RSHCD(int v, int k, int l, int mu): sage: from sage.graphs.strongly_regular_db import is_RSHCD sage: t = is_RSHCD(64,27,10,12); t - [, 64, 27, 10, 12] + [, 64, 27, 10, 12] sage: g = t[0](*t[1:]); g Graph on 64 vertices sage: g.is_strongly_regular(parameters=True) diff --git a/src/sage/interfaces/sage0.py b/src/sage/interfaces/sage0.py index 665a00f6525..a43a059e0ec 100644 --- a/src/sage/interfaces/sage0.py +++ b/src/sage/interfaces/sage0.py @@ -554,7 +554,7 @@ def _repr_(self): EXAMPLES:: sage: sage0(4).gcd - + """ return str(self._obj.parent().eval('%s.%s' % (self._obj._name, self._name))) diff --git a/src/sage/libs/singular/ring.pyx b/src/sage/libs/singular/ring.pyx index 828e782d887..04bd16e8784 100644 --- a/src/sage/libs/singular/ring.pyx +++ b/src/sage/libs/singular/ring.pyx @@ -772,7 +772,7 @@ cpdef poison_currRing(frame, event, arg): sage: from sage.libs.singular.ring import poison_currRing sage: sys.settrace(poison_currRing) sage: sys.gettrace() - + sage: sys.settrace(previous_trace_func) # switch it off again """ global currRing diff --git a/src/sage/misc/c3_controlled.pyx b/src/sage/misc/c3_controlled.pyx index 03561005657..ebadb070d1d 100644 --- a/src/sage/misc/c3_controlled.pyx +++ b/src/sage/misc/c3_controlled.pyx @@ -1091,7 +1091,7 @@ class HierarchyElement(object, metaclass=ClasscallMetaclass): sage: x._bases [5, 2] sage: x._key - + sage: x._key(10) 10 diff --git a/src/sage/misc/sagedoc_conf.py b/src/sage/misc/sagedoc_conf.py index 2d2a950c23e..6a29adb599d 100644 --- a/src/sage/misc/sagedoc_conf.py +++ b/src/sage/misc/sagedoc_conf.py @@ -23,7 +23,6 @@ def process_docstring_aliases(app, what, name, obj, options, docstringlines): """ Change the docstrings for aliases to point to the original object. """ - basename = name.rpartition('.')[2] if hasattr(obj, '__name__') and obj.__name__ != basename: docstringlines[:] = ['See :obj:`%s`.' % name] diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index 3eb25382884..fbca2defc20 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -152,7 +152,7 @@ def is_function_or_cython_function(obj): sage: is_function_or_cython_function(_mul_parent) True sage: is_function_or_cython_function(Integer.digits) # unbound method - True + False sage: is_function_or_cython_function(Integer(1).digits) # bound method False diff --git a/src/sage/rings/finite_rings/hom_finite_field.pyx b/src/sage/rings/finite_rings/hom_finite_field.pyx index 75c91514aa2..81c7620d42d 100644 --- a/src/sage/rings/finite_rings/hom_finite_field.pyx +++ b/src/sage/rings/finite_rings/hom_finite_field.pyx @@ -435,7 +435,7 @@ cdef class FiniteFieldHomomorphism_generic(RingHomomorphism_im_gens): sage: Frob = k.frobenius_endomorphism() sage: embed = Frob.fixed_field()[1] sage: embed.__reduce__() # indirect doctest - (, + (, (, Set of field embeddings from Finite Field of size 5 to Finite Field in t of size 5^3, {}, @@ -835,7 +835,7 @@ cdef class FrobeniusEndomorphism_finite_field(FrobeniusEndomorphism_generic): sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism(2) sage: Frob.__reduce__() # indirect doctest - (, + (, (, Automorphism group of Finite Field in t of size 5^3, {}, diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index f1e130e3b0e..e8cf51beda6 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -128,7 +128,7 @@ sage: TestSuite(S).run() # long time (4s) Global function fields -====================== +---------------------- A global function field in Sage is an extension field of a rational function field over a *finite* constant field by an irreducible separable polynomial over the diff --git a/src/sage/sets/disjoint_set.pyx b/src/sage/sets/disjoint_set.pyx index dac6e75779b..aaa12438f15 100644 --- a/src/sage/sets/disjoint_set.pyx +++ b/src/sage/sets/disjoint_set.pyx @@ -358,14 +358,14 @@ cdef class DisjointSet_of_integers(DisjointSet_class): sage: d = DisjointSet(5) sage: d.__reduce__() - (, (5,), [0, 1, 2, 3, 4]) + (, (5,), [0, 1, 2, 3, 4]) :: sage: d.union(2,4) sage: d.union(1,3) sage: d.__reduce__() - (, (5,), [0, 1, 2, 1, 2]) + (, (5,), [0, 1, 2, 1, 2]) """ return DisjointSet, (self._nodes.degree,), self.__getstate__() @@ -674,7 +674,7 @@ cdef class DisjointSet_of_hashables(DisjointSet_class): {{0}, {1}, {2}, {3}, {4}} sage: d = _ sage: d.__reduce__() - (, + (, ([0, 1, 2, 3, 4],), [(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]) @@ -683,7 +683,7 @@ cdef class DisjointSet_of_hashables(DisjointSet_class): sage: d.union(2,4) sage: d.union(1,3) sage: d.__reduce__() - (, + (, ([0, 1, 2, 3, 4],), [(0, 0), (1, 1), (2, 2), (3, 1), (4, 2)]) """ diff --git a/src/sage/structure/category_object.pyx b/src/sage/structure/category_object.pyx index 99679d2e3ca..52abd7d918b 100644 --- a/src/sage/structure/category_object.pyx +++ b/src/sage/structure/category_object.pyx @@ -571,7 +571,7 @@ cdef class CategoryObject(SageObject): sage: F.base_ring() Integer Ring sage: F.__class__.base_ring - + Note that the coordinates of the elements of a module can lie in a bigger ring, the ``coordinate_ring``:: @@ -591,7 +591,7 @@ cdef class CategoryObject(SageObject): sage: F.base_ring() Rational Field sage: F.__class__.base_ring - + sage: E = CombinatorialFreeModule(ZZ, [1,2,3]) sage: F = CombinatorialFreeModule(ZZ, [2,3,4]) @@ -599,7 +599,7 @@ cdef class CategoryObject(SageObject): sage: H.base_ring() Integer Ring sage: H.__class__.base_ring - + .. TODO:: diff --git a/src/sage/structure/factory.pyx b/src/sage/structure/factory.pyx index 4e656be479f..a4a13186973 100644 --- a/src/sage/structure/factory.pyx +++ b/src/sage/structure/factory.pyx @@ -550,7 +550,7 @@ cdef class UniqueFactory(SageObject): sage: a = test_factory(1, 2) Making object (1, 2) sage: test_factory.reduce_data(a) - (, + (, (, (...), (1, 2), diff --git a/src/sage/structure/list_clone.pyx b/src/sage/structure/list_clone.pyx index 50e57b34ca9..125f32762ef 100644 --- a/src/sage/structure/list_clone.pyx +++ b/src/sage/structure/list_clone.pyx @@ -934,7 +934,7 @@ cdef class ClonableArray(ClonableElement): sage: loads(dumps(el)) [1, 2, 4] sage: t = el.__reduce__(); t - (, + (, (, , [1, 2, 4], @@ -1720,7 +1720,7 @@ cdef class ClonableIntArray(ClonableElement): sage: loads(dumps(el)) [1, 2, 4] sage: t = el.__reduce__(); t - (, + (, (, , [1, 2, 4], diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 2c9c1ba894c..903a8541460 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -6363,6 +6363,7 @@ cdef class Expression(Expression_abc): sage: type(u._unpack_operands()[0]) <... 'tuple'> """ + from sage.symbolic.expression import unpack_operands return unpack_operands(self) def operands(self): diff --git a/src/sage/symbolic/function.pyx b/src/sage/symbolic/function.pyx index 24cb7adc6c1..0baedce69c0 100644 --- a/src/sage/symbolic/function.pyx +++ b/src/sage/symbolic/function.pyx @@ -141,6 +141,11 @@ is attempted, and after that ``sin()`` which succeeds:: from sage.structure.sage_object cimport SageObject from sage.structure.element cimport Element, parent, Expression from sage.misc.lazy_attribute import lazy_attribute +from .expression import ( + call_registered_function, find_registered_function, register_or_update_function, + get_sfunction_from_hash +) +from .expression import get_sfunction_from_serial as get_sfunction_from_serial from sage.structure.coerce cimport (coercion_model, py_scalar_to_element, is_numpy_type, is_mpmath_type) @@ -258,8 +263,6 @@ cdef class Function(SageObject): f(x) """ - from .expression import register_or_update_function - self._serial = register_or_update_function(self, self._name, self._latex_name, self._nargs, self._evalf_params_first, False) @@ -541,8 +544,6 @@ cdef class Function(SageObject): if not isinstance(a, Expression): raise TypeError("arguments must be symbolic expressions") - from .expression import call_registered_function - return call_registered_function(self._serial, self._nargs, args, hold, not symbolic_input, SR) @@ -837,8 +838,6 @@ cdef class GinacFunction(BuiltinFunction): preserved_arg=preserved_arg, alt_name=alt_name) cdef _is_registered(self): - from .expression import find_registered_function, get_sfunction_from_serial - # Since this is function is defined in C++, it is already in # ginac's function registry fname = self._ginac_name if self._ginac_name is not None else self._name @@ -846,8 +845,6 @@ cdef class GinacFunction(BuiltinFunction): return bool(get_sfunction_from_serial(self._serial)) cdef _register_function(self): - from .expression import register_or_update_function - # We don't need to add anything to GiNaC's function registry # However, if any custom methods were provided in the python class, # we should set the properties of the function_options object @@ -1097,8 +1094,6 @@ cdef class BuiltinFunction(Function): sage: loads(dumps(cot)) == cot # trac #15138 True """ - from .expression import find_registered_function, get_sfunction_from_serial - # check if already defined cdef unsigned int serial @@ -1196,8 +1191,6 @@ cdef class SymbolicFunction(Function): evalf_params_first) cdef _is_registered(SymbolicFunction self): - from .expression import get_sfunction_from_hash - # see if there is already a SymbolicFunction with the same state cdef long myhash = self._hash_() cdef SymbolicFunction sfunc = get_sfunction_from_hash(myhash) diff --git a/src/sage/symbolic/getitem_impl.pxi b/src/sage/symbolic/getitem_impl.pxi index ff8a9d94138..72bf9d5f0e5 100644 --- a/src/sage/symbolic/getitem_impl.pxi +++ b/src/sage/symbolic/getitem_impl.pxi @@ -182,7 +182,7 @@ cdef class OperandsWrapper(SageObject): TESTS:: sage: (x^2).op.__reduce__() - (, (x^2,)) + (, (x^2,)) sage: loads(dumps((x^2).op)) Operands of x^2 """ diff --git a/src/sage/symbolic/ring.pyx b/src/sage/symbolic/ring.pyx index 77002bef25c..2036a7331d4 100644 --- a/src/sage/symbolic/ring.pyx +++ b/src/sage/symbolic/ring.pyx @@ -52,11 +52,8 @@ from sage.structure.coerce cimport is_numpy_type import sage.rings.abc from sage.rings.integer_ring import ZZ -# is_SymbolicVariable used to be defined here; re-export it here lazily -cpdef bint is_SymbolicVariable(x): - from sage.symbolic.expression import _is_SymbolicVariable - - return _is_SymbolicVariable(x) +# is_SymbolicVariable used to be defined here; re-export it +from sage.symbolic.expression import _is_SymbolicVariable as is_SymbolicVariable import keyword import operator diff --git a/src/sage_docbuild/conf.py b/src/sage_docbuild/conf.py index a369978cf1d..4c880cdca63 100644 --- a/src/sage_docbuild/conf.py +++ b/src/sage_docbuild/conf.py @@ -40,12 +40,12 @@ # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ + 'sage_docbuild.ext.inventory_builder', + 'sage_docbuild.ext.multidocs', + 'sage_docbuild.ext.sage_autodoc', 'sphinx.ext.todo', 'sphinx.ext.extlinks', 'sphinx.ext.mathjax', - 'sage_docbuild.ext.sage_autodoc', - 'sage_docbuild.ext.inventory_builder', - 'sage_docbuild.ext.multidocs', 'IPython.sphinxext.ipython_directive', 'matplotlib.sphinxext.plot_directive', 'jupyter_sphinx', diff --git a/src/sage_setup/cython_options.py b/src/sage_setup/cython_options.py index a878fb754e4..086aa070ca9 100644 --- a/src/sage_setup/cython_options.py +++ b/src/sage_setup/cython_options.py @@ -10,11 +10,6 @@ def compiler_directives(profile: bool): auto_pickle=False, # Do not create __test__ dictionary automatically from docstrings autotestdict=False, - # When enabled, functions will bind to an instance when looked up as a - # class attribute (hence the name) and will emulate the attributes of - # Python functions, including introspections like argument names and - # annotations - binding=True, # Do not check for division by 0 (this is about 35% quicker than with check) cdivision=True, # Embed a textual copy of the call signature in the docstring (to support tools like IPython) From 24299ced2b38daef48a2002d24c1766c80d2d0e9 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Tue, 8 Nov 2022 16:26:22 +0000 Subject: [PATCH 416/632] work around SR being inexact, fix a silly doctest --- src/sage/matrix/matrix2.pyx | 45 ++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index 6c437e42c17..f33254c5c1b 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -90,6 +90,7 @@ from sage.categories.all import Fields, IntegralDomains from sage.rings.ring import is_Ring from sage.rings.number_field.number_field_base import is_NumberField from sage.rings.integer_ring import ZZ, is_IntegerRing +from sage.symbolic.ring import SR from sage.rings.integer import Integer from sage.rings.rational_field import QQ, is_RationalField import sage.rings.abc @@ -799,7 +800,7 @@ cdef class Matrix(Matrix1): sage: RF = RealField(52) sage: B = matrix(RF, 2, 2, 1) sage: A = matrix(RF, [[0.24, 1, 0], [1, 0, 0]]) - sage: 0 < (A * A.solve_right(B) - B).norm() < 1e-14 + sage: 0 <= (A * A.solve_right(B) - B).norm() < 1e-14 True Over the inexact ring ``SR``, we can still verify the solution @@ -1536,19 +1537,15 @@ cdef class Matrix(Matrix1): Beware that the ``exact`` algorithm is not numerically stable, but the default ``numpy`` algorithm is:: - sage: M = matrix(RR, 3, 3, [1,2,3,1/3,2/3,3/3,1/5,2/5,3/5]) - sage: M.pseudoinverse() # tol 1e-15 - [0.0620518477661335 0.0206839492553778 0.0124103695532267] - [ 0.124103695532267 0.0413678985107557 0.0248207391064534] - [ 0.186155543298400 0.0620518477661335 0.0372311086596801] - sage: M.pseudoinverse(algorithm="numpy") # tol 1e-15 - [0.0620518477661335 0.0206839492553778 0.0124103695532267] - [ 0.124103695532267 0.0413678985107557 0.0248207391064534] - [ 0.186155543298400 0.0620518477661335 0.0372311086596801] - sage: M.pseudoinverse(algorithm="exact") - [ 0.125000000000000 0.0625000000000000 0.0312500000000000] - [ 0.250000000000000 0.125000000000000 0.0625000000000000] - [ 0.000000000000000 0.000000000000000 0.0625000000000000] + sage: M = matrix.hilbert(12,ring=RR) + sage: (~M*M).norm() # a considerable error + 1.3... + sage: Mx = M.pseudoinverse(algorithm="exact") + sage: (Mx*M).norm() # huge error + 11.5... + sage: Mx = M.pseudoinverse(algorithm="numpy") + sage: (Mx*M).norm() # still OK + 1.00... When multiplying the given matrix with the pseudoinverse, the result is symmetric for the ``exact`` algorithm or hermitian @@ -1582,6 +1579,11 @@ cdef class Matrix(Matrix1): [-1286742750677287/643371375338643 1000799917193445/1000799917193444] [ 519646110850445/346430740566963 -300239975158034/600479950316067] + Although it is not too far off:: + + sage: (~M-M.pseudoinverse(algorithm="numpy")).norm() < 1e-14 + True + TESTS:: sage: M.pseudoinverse(algorithm="exact") @@ -7606,12 +7608,13 @@ cdef class Matrix(Matrix1): Check that :trac:`34724` is fixed (indirect doctest):: - sage: a=RR(6.12323399573677e-17) + sage: a=6.12323399573677e-17 sage: m=matrix(RR,[[-a, -1.72508242466029], [ 0.579682446302195, a]]) sage: (~m*m).norm() 1.0 """ self.check_mutability() + basring = self.base_ring() if algorithm == 'default': from sage.categories.discrete_valuation import DiscreteValuationFields @@ -7621,20 +7624,22 @@ cdef class Matrix(Matrix1): # In general, we would like to do so in any rank one valuation ring, # but this should be done by introducing a category of general valuation rings and fields, # which we don't have at the moment - elif self.base_ring() in DiscreteValuationFields(): + elif basring in DiscreteValuationFields(): try: - self.base_ring().one().abs() + basring.one().abs() algorithm = 'scaled_partial_pivoting' except (AttributeError, TypeError): algorithm = 'scaled_partial_pivoting_valuation' + elif (basring is SR) or basring.is_exact(): + algorithm = 'classical' else: try: - self.base_ring(1/2).abs() + (basring(0.42)).abs() algorithm = 'scaled_partial_pivoting' except (AttributeError, ArithmeticError, TypeError): algorithm = 'classical' try: - if self.base_ring() in _Fields: + if basring in _Fields: if algorithm in ['classical', 'partial_pivoting', 'scaled_partial_pivoting', 'scaled_partial_pivoting_valuation']: self._echelon_in_place(algorithm) elif algorithm == 'strassen': @@ -7646,7 +7651,7 @@ cdef class Matrix(Matrix1): kwds['algorithm'] = algorithm return self._echelonize_ring(**kwds) except ArithmeticError as msg: - raise NotImplementedError("%s\nEchelon form not implemented over '%s'."%(msg,self.base_ring())) + raise NotImplementedError("%s\nEchelon form not implemented over '%s'."%(msg,basring)) def echelon_form(self, algorithm="default", cutoff=0, **kwds): r""" From cce62c225276687c4f9e331688bd638085e2dbcd Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Tue, 8 Nov 2022 17:41:51 +0000 Subject: [PATCH 417/632] use subclassed Matrix_symbolic_dense --- src/sage/matrix/matrix2.pyx | 3 +-- src/sage/matrix/matrix_symbolic_dense.pyx | 3 +++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/matrix/matrix2.pyx b/src/sage/matrix/matrix2.pyx index f33254c5c1b..4e4cdff7efe 100644 --- a/src/sage/matrix/matrix2.pyx +++ b/src/sage/matrix/matrix2.pyx @@ -90,7 +90,6 @@ from sage.categories.all import Fields, IntegralDomains from sage.rings.ring import is_Ring from sage.rings.number_field.number_field_base import is_NumberField from sage.rings.integer_ring import ZZ, is_IntegerRing -from sage.symbolic.ring import SR from sage.rings.integer import Integer from sage.rings.rational_field import QQ, is_RationalField import sage.rings.abc @@ -7630,7 +7629,7 @@ cdef class Matrix(Matrix1): algorithm = 'scaled_partial_pivoting' except (AttributeError, TypeError): algorithm = 'scaled_partial_pivoting_valuation' - elif (basring is SR) or basring.is_exact(): + elif basring.is_exact(): algorithm = 'classical' else: try: diff --git a/src/sage/matrix/matrix_symbolic_dense.pyx b/src/sage/matrix/matrix_symbolic_dense.pyx index 9409ff5c1e8..2eca331a6a2 100644 --- a/src/sage/matrix/matrix_symbolic_dense.pyx +++ b/src/sage/matrix/matrix_symbolic_dense.pyx @@ -166,6 +166,9 @@ cdef maxima from sage.calculus.calculus import symbolic_expression_from_maxima_string, maxima cdef class Matrix_symbolic_dense(Matrix_generic_dense): + def echelonize(self, algorithm="classical", cutoff=0, **kwds): + return super().echelonize(algorithm="classical", cutoff=cutoff, **kwds) + def eigenvalues(self, extend=True): """ Compute the eigenvalues by solving the characteristic From 75026c0d2ab259a01854695c9fdf115c90c5dd8d Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Tue, 8 Nov 2022 12:15:48 -0700 Subject: [PATCH 418/632] trac 34733 noncommutative quo_rem --- src/sage/rings/polynomial/polynomial_element.pyx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index cea15d34764..243bbff5553 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -11662,6 +11662,17 @@ cdef class Polynomial_generic_dense(Polynomial): ... ZeroDivisionError: division by zero polynomial + Polynomials over noncommutative rings are also allowed + (after :trac:`34733`):: + + sage: HH = QuaternionAlgebra(QQ, -1, -1) + sage: P. = HH[] + sage: f = P.random_element(5) + sage: g = P.random_element((0, 5)) + sage: q,r = f.quo_rem(g) + sage: f == q*g + r + True + TESTS: The following shows that :trac:`16649` is indeed fixed. :: @@ -11709,7 +11720,7 @@ cdef class Polynomial_generic_dense(Polynomial): convert = True if convert: for k from m-n >= k >= 0: - q = inv * x[n+k-1] + q = x[n+k-1] * inv try: q = R(q) except TypeError: @@ -11719,7 +11730,7 @@ cdef class Polynomial_generic_dense(Polynomial): quo.append(q) else: for k from m-n >= k >= 0: - q = inv * x[n+k-1] + q = x[n+k-1] * inv for j from n+k-2 >= j >= k: x[j] -= q * y[j-k] quo.append(q) From aa74d4badcbe6841833ff983ad5f756c5ac78752 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Wed, 9 Nov 2022 12:17:56 +0900 Subject: [PATCH 419/632] More edits --- src/sage_docbuild/ext/sage_autodoc.py | 57 ++++++++++++++------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/src/sage_docbuild/ext/sage_autodoc.py b/src/sage_docbuild/ext/sage_autodoc.py index 418cf67dbd6..b36ed2ee25e 100644 --- a/src/sage_docbuild/ext/sage_autodoc.py +++ b/src/sage_docbuild/ext/sage_autodoc.py @@ -3,7 +3,7 @@ This is :mod:`sphinx.ext.autodoc` extension modified for Sage objects. -The original headline of :mod:`sphinx.ext.autodoc`: +The original header of :mod:`sphinx.ext.autodoc`: Extension to create automatic documentation from code docstrings. @@ -11,11 +11,13 @@ the doctree, thus avoiding duplication between docstrings and documentation for those who like elaborate docstrings. -Presently this module is based on :mod:`sphinx.ext.autodoc` from Sphinx version 5.3.0. -The upstream original source file is `sphinx/ext/autodoc/__init__.py `_. +This module is currently based on :mod:`sphinx.ext.autodoc` from Sphinx version +5.3.0. Compare against the upstream original source file +`sphinx/ext/autodoc/__init__.py +`_. In the source file of this module, major modifications are delimited by a pair -of comment dividers. To lessen maintenance burdens, we aim at reducing those modifications. +of comment dividers. To lessen maintenance burden, we aim at reducing the modifications. AUTHORS: @@ -510,8 +512,6 @@ def format_signature(self, **kwargs: Any) -> str: Let the user process it via the ``autodoc-process-signature`` event. """ - #if 'Expression.numerical_approx' in self.name: - # from celery.contrib import rdb; rdb.set_trace() if self.args is not None: # signature given explicitly args = "(%s)" % self.args @@ -1338,10 +1338,10 @@ def format_args(self, **kwargs: Any) -> str: try: self.env.app.emit('autodoc-before-process-signature', self.object, False) - # -------------------------------------------- + # ---------------------------------------------------------------- # Trac #9976: Support the _sage_argspec_ attribute which makes it # possible to get argument specification of decorated callables in - # documentation correct. See e.g. sage.misc.decorators.sage_wraps + # documentation correct. See e.g. sage.misc.decorators.sage_wraps obj = self.object if hasattr(obj, "_sage_argspec_"): @@ -1362,7 +1362,7 @@ def format_args(self, **kwargs: Any) -> str: if argspec is None: return None args = sage_formatargspec(*argspec) - # -------------------------------------------- + # ---------------------------------------------------------------- except TypeError as exc: logger.warning(__("Failed to get a function signature for %s: %s"), self.fullname, exc) @@ -1542,15 +1542,10 @@ def import_object(self, raiseerror: bool = False) -> bool: if ret: if hasattr(self.object, '__name__'): self.doc_as_attr = (self.objpath[-1] != self.object.__name__) - # ------------------------------------------------------------------ - # Trac #27692: - # - # For Python 3, we make use of self.object.__qualname__. - # - # Notes from trac #7448: - # - # The original goal of this was that if some class is aliased, the - # alias is generated as a link rather than duplicated. For example in + # ------------------------------------------------------------------- + # Trac #27692, #7448: The original goal of this was that if some + # class is aliased, the alias is generated as a link rather than + # duplicated. For example in # # class A: # pass @@ -1608,7 +1603,7 @@ def import_object(self, raiseerror: bool = False) -> bool: cls = getattr(cls, part, None) self.doc_as_attr = (self.objpath != qualname_parts and self.object is cls) - # ------------------------------------------------------------------ + # ------------------------------------------------------------------- else: self.doc_as_attr = True return ret @@ -2291,13 +2286,13 @@ def format_args(self, **kwargs: Any) -> str: kwargs.setdefault('unqualified_typehints', True) # ----------------------------------------------------------------- - # Trac #9976: This function has been rewritten to support the - # _sage_argspec_ attribute which makes it possible to get argument - # specification of decorated callables in documentation correct. - # See e.g. sage.misc.decorators.sage_wraps. + # Trac #9976: Support the _sage_argspec_ attribute which makes it + # possible to get argument specification of decorated callables in + # documentation correct. See e.g. sage.misc.decorators.sage_wraps. # # Note, however, that sage.misc.sageinspect.sage_getargspec already - # uses a method _sage_argspec_, that only works on objects, not on classes, though. + # uses a method _sage_argspec_, that only works on objects, not on + # classes, though. obj = self.object if hasattr(obj, "_sage_argspec_"): argspec = obj._sage_argspec_() @@ -2341,6 +2336,11 @@ def add_directive_header(self, sig: str) -> None: def document_members(self, all_members: bool = False) -> None: pass + # ------------------------------------------------------------------------ + # Trac #34730: The format_signature() of the class MethodDocumenter + # supports overloaded methods via inspect.signature(), which does not work + # with Sage yet. Hence the method was removed from here. + # ------------------------------------------------------------------------ def merge_default_value(self, actual: Signature, overload: Signature) -> Signature: """Merge default values of actual implementation to the overload variants.""" @@ -2665,8 +2665,9 @@ def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: ) -> bool: if isinstance(parent, ModuleDocumenter): return False - # ------------------------------------------------------------------- - # Trac: Do not pass objects of class CachedMethodCallerNoArgs as attributes. + # --------------------------------------------------------------------- + # Trac #34730: Do not pass objects of the class CachedMethodCaller as + # attributes. # # sage: from sphinx.util import inspect # sage: A = AlgebrasWithBasis(QQ).example() @@ -2678,14 +2679,14 @@ def can_document_member(cls, member: Any, membername: str, isattr: bool, parent: # sage: inspect.isroutine(member) # True # - from sage.misc.classcall_metaclass import ClasscallMetaclass if inspect.isattributedescriptor(member) and not inspect.isroutine(member): return True # Trac #26522: Pass objects of classes that inherit ClasscallMetaclass # as attributes rather than method descriptors. + from sage.misc.classcall_metaclass import ClasscallMetaclass if isinstance(type(member), ClasscallMetaclass): return True - # ------------------------------------------------------------------- + # --------------------------------------------------------------------- elif not inspect.isroutine(member) and not isinstance(member, type): return True else: From af97acc4a3e7f5087e56235887a760147636c5bc Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Tue, 8 Nov 2022 22:53:33 -0700 Subject: [PATCH 420/632] add another doctest --- .../rings/polynomial/polynomial_element_generic.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/sage/rings/polynomial/polynomial_element_generic.py b/src/sage/rings/polynomial/polynomial_element_generic.py index 868a5f691f9..64fcf65b210 100644 --- a/src/sage/rings/polynomial/polynomial_element_generic.py +++ b/src/sage/rings/polynomial/polynomial_element_generic.py @@ -824,6 +824,16 @@ def quo_rem(self, other): sage: f.quo_rem(g) (-y^5 + 2*y^2, y^3 - 2*x^2*y^2 - y) + Polynomials over noncommutative rings are also allowed:: + + sage: HH = QuaternionAlgebra(QQ, -1, -1) + sage: P. = PolynomialRing(HH, sparse=True) + sage: f = P.random_element(5) + sage: g = P.random_element((0, 5)) + sage: q, r = f.quo_rem(g) + sage: f == q*g + r + True + TESTS:: sage: P. = PolynomialRing(ZZ, sparse=True) From 51dfa8676979bd4e3e373fdbdf9df023a84ca16e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 9 Nov 2022 13:58:35 +0100 Subject: [PATCH 421/632] some details in hyperelliptic_padic_field --- .../hyperelliptic_padic_field.py | 61 ++++++++----------- 1 file changed, 24 insertions(+), 37 deletions(-) diff --git a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py index 373394b20af..a79843c0e99 100644 --- a/src/sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py +++ b/src/sage/schemes/hyperelliptic_curves/hyperelliptic_padic_field.py @@ -1,15 +1,15 @@ """ Hyperelliptic curves over a `p`-adic field """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 Robert Bradshaw # # 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.rings.all import (PowerSeriesRing, PolynomialRing, ZZ, QQ, @@ -27,10 +27,10 @@ class HyperellipticCurve_padic_field(hyperelliptic_generic.HyperellipticCurve_generic, ProjectivePlaneCurve_field): -# The functions below were prototyped at the 2007 Arizona Winter School by -# Robert Bradshaw and Ralf Gerkmann, working with Miljan Brakovevic and -# Kiran Kedlaya -# All of the below is with respect to the Monsky Washnitzer cohomology. + # The functions below were prototyped at the 2007 Arizona Winter School by + # Robert Bradshaw and Ralf Gerkmann, working with Miljan Brakovevic and + # Kiran Kedlaya + # All of the below is with respect to the Monsky Washnitzer cohomology. def local_analytic_interpolation(self, P, Q): """ @@ -158,7 +158,7 @@ def weierstrass_points(self): raise NotImplementedError() return [self((0,1,0))] + [self((x, 0, 1)) for x in f.roots(multiplicities=False)] - def is_in_weierstrass_disc(self,P): + def is_in_weierstrass_disc(self, P): """ Checks if `P` is in a Weierstrass disc @@ -186,12 +186,9 @@ def is_in_weierstrass_disc(self,P): - Jennifer Balakrishnan (2010-02) """ - if (P[1].valuation() == 0 and P != self(0,1,0)): - return False - else: - return True + return not (P[1].valuation() == 0 and P != self(0, 1, 0)) - def is_weierstrass(self,P): + def is_weierstrass(self, P): """ Checks if `P` is a Weierstrass point (i.e., fixed by the hyperelliptic involution) @@ -218,12 +215,8 @@ def is_weierstrass(self,P): AUTHOR: - Jennifer Balakrishnan (2010-02) - """ - if (P[1] == 0 or P[2] ==0): - return True - else: - return False + return (P[1] == 0 or P[2] == 0) def find_char_zero_weier_point(self, Q): """ @@ -260,7 +253,7 @@ def find_char_zero_weier_point(self, Q): if self.is_same_disc(P,Q): return P - def residue_disc(self,P): + def residue_disc(self, P): """ Gives the residue disc of `P` @@ -306,7 +299,7 @@ def residue_disc(self,P): else: return HF(0,1,0) - def is_same_disc(self,P,Q): + def is_same_disc(self, P, Q): """ Checks if `P,Q` are in same residue disc @@ -326,10 +319,7 @@ def is_same_disc(self,P,Q): sage: HK.is_same_disc(Q,S) False """ - if self.residue_disc(P) == self.residue_disc(Q): - return True - else: - return False + return self.residue_disc(P) == self.residue_disc(Q) def tiny_integrals(self, F, P, Q): r""" @@ -1001,12 +991,12 @@ def newton_sqrt(self, f, x0, prec): - Jennifer Balakrishnan """ z = x0 - loop_prec = (log(RR(prec))/log(RR(2))).ceil() + loop_prec = log(RR(prec), 2).ceil() for i in range(loop_prec): - z = (z + f/z) / 2 + z = (z + f / z) / 2 return z - def curve_over_ram_extn(self,deg): + def curve_over_ram_extn(self, deg): r""" Return ``self`` over `\QQ_p(p^(1/deg))`. @@ -1123,7 +1113,7 @@ def P_to_S(self, P, S): val = [I(S[1]) for I in integrals] return vector(val) - def coleman_integral_P_to_S(self,w,P,S): + def coleman_integral_P_to_S(self, w, P, S): r""" Given a finite Weierstrass point `P` and a point `S` in the same disc, computes the Coleman integral `\int_P^S w` @@ -1168,7 +1158,7 @@ def coleman_integral_P_to_S(self,w,P,S): int_sing_a = int_sing(S[1]) return int_sing_a - def S_to_Q(self,S,Q): + def S_to_Q(self, S, Q): r""" Given `S` a point on self over an extension field, computes the Coleman integrals `\{\int_S^Q x^i dx/2y \}_{i=0}^{2g-1}` @@ -1248,13 +1238,12 @@ def S_to_Q(self,S,Q): b = V(L) M_sys = matrix(K, M_frob).transpose() - 1 B = (~M_sys) - v = [c.valuation() for c in B.list()] - vv = min(v) + vv = min(c.valuation() for c in B.list()) B = (p**(-vv)*B).change_ring(K) B = p**(vv)*B return B*(b-S_to_FS-FQ_to_Q) - def coleman_integral_S_to_Q(self,w,S,Q): + def coleman_integral_S_to_Q(self, w, S, Q): r""" Compute the Coleman integral `\int_S^Q w` @@ -1293,7 +1282,6 @@ def coleman_integral_S_to_Q(self,w,S,Q): AUTHOR: - Jennifer Balakrishnan - """ import sage.schemes.hyperelliptic_curves.monsky_washnitzer as monsky_washnitzer K = self.base_ring() @@ -1350,10 +1338,9 @@ def coleman_integral_from_weierstrass_via_boundary(self, w, P, Q, d): AUTHOR: - Jennifer Balakrishnan - """ HJ = self.curve_over_ram_extn(d) - S = self.get_boundary_point(HJ,P) - P_to_S = self.coleman_integral_P_to_S(w,P,S) - S_to_Q = HJ.coleman_integral_S_to_Q(w,S,Q) + S = self.get_boundary_point(HJ, P) + P_to_S = self.coleman_integral_P_to_S(w, P, S) + S_to_Q = HJ.coleman_integral_S_to_Q(w, S, Q) return P_to_S + S_to_Q From 0c7da0a8b7a7199c6a1e3247cf8d476e5bdb3478 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Wed, 9 Nov 2022 13:36:13 +0000 Subject: [PATCH 422/632] hardcode "classical" algorithm, add a doctest --- src/sage/matrix/matrix_symbolic_dense.pyx | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/sage/matrix/matrix_symbolic_dense.pyx b/src/sage/matrix/matrix_symbolic_dense.pyx index 2eca331a6a2..8774848f2d7 100644 --- a/src/sage/matrix/matrix_symbolic_dense.pyx +++ b/src/sage/matrix/matrix_symbolic_dense.pyx @@ -166,8 +166,20 @@ cdef maxima from sage.calculus.calculus import symbolic_expression_from_maxima_string, maxima cdef class Matrix_symbolic_dense(Matrix_generic_dense): - def echelonize(self, algorithm="classical", cutoff=0, **kwds): - return super().echelonize(algorithm="classical", cutoff=cutoff, **kwds) + def echelonize(self, **kwds): + """ + Echelonize using the classical algorithm. + + + TESTS:: + + sage: m = matrix([[cos(pi/5), sin(pi/5)], [-sin(pi/5), cos(pi/5)]]) + sage: m.echelonize(); m + [1 0] + [0 1] + """ + + return super().echelonize(algorithm="classical", **kwds) def eigenvalues(self, extend=True): """ From c0eaf1e9a7ad01bbf8c6a464afbfee438cc0c62f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 9 Nov 2022 14:56:47 +0100 Subject: [PATCH 423/632] cleanup for QQ-curves --- src/sage/schemes/elliptic_curves/Qcurves.py | 47 +++++++++++++-------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/Qcurves.py b/src/sage/schemes/elliptic_curves/Qcurves.py index bf6b7b0d36d..4c9e8f3718a 100644 --- a/src/sage/schemes/elliptic_curves/Qcurves.py +++ b/src/sage/schemes/elliptic_curves/Qcurves.py @@ -9,7 +9,6 @@ The code here implements the algorithm of Cremona and Najman presented in [CrNa2020]_. """ - ############################################################################## # Copyright (C) 2020-2021 John Cremona # @@ -24,10 +23,10 @@ # # https://www.gnu.org/licenses/ ############################################################################## - from sage.rings.rational_field import QQ from sage.rings.polynomial.polynomial_ring import polygen + def is_Q_curve(E, maxp=100, certificate=False, verbose=False): r""" Return whether ``E`` is a `\QQ`-curve, with optional certificate. @@ -195,16 +194,25 @@ def is_Q_curve(E, maxp=100, certificate=False, verbose=False): 'core_poly': x^2 - 840064*x + 1593413632, 'r': 1, 'rho': 1} + + TESTS:: + + sage: E = EllipticCurve([GF(5)(t) for t in [2,3,5,7,11]]) + sage: is_Q_curve(E) + Traceback (most recent call last): + ... + TypeError: Elliptic Curve defined by ... must be an elliptic curve + defined over a number field """ from sage.rings.number_field.number_field_base import is_NumberField if verbose: - print("Checking whether {} is a Q-curve".format(E)) + print(f"Checking whether {E} is a Q-curve") try: assert is_NumberField(E.base_field()) except (AttributeError, AssertionError): - raise TypeError("{} must be an elliptic curve defined over a number field in is_Q_curve()") + raise TypeError(f"{E} must be an elliptic curve defined over a number field") from sage.rings.integer_ring import ZZ from sage.arith.functions import lcm @@ -224,7 +232,7 @@ def is_Q_curve(E, maxp=100, certificate=False, verbose=False): # test for CM for d, f, j in cm_j_invariants_and_orders(QQ): if jE == j: - return True, {'CM': d*f**2} + return True, {'CM': d * f**2} # else not CM return True, {'CM': ZZ(0), 'r': ZZ(0), 'rho': ZZ(0), 'N': ZZ(1), 'core_poly': polygen(QQ)} else: @@ -234,7 +242,7 @@ def is_Q_curve(E, maxp=100, certificate=False, verbose=False): flag, df = is_cm_j_invariant(jE) if flag: d, f = df - D = d*f**2 + D = d * f**2 if verbose: print("Yes: E is CM (discriminant {})".format(D)) if certificate: @@ -246,15 +254,15 @@ def is_Q_curve(E, maxp=100, certificate=False, verbose=False): K = E.base_field() jpoly = jE.minpoly() - if jpoly.degree() Date: Wed, 9 Nov 2022 15:14:21 +0100 Subject: [PATCH 424/632] use pari for roots of unity --- src/sage/rings/number_field/number_field.py | 40 ++++++++++----------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index a2f0e3d047c..4febd05f45d 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -7388,13 +7388,13 @@ def zeta(self, n=2, all=False): sage: K.zeta(2, all=True) [-1] sage: K.zeta(3) - 1/2*z - 1/2 + -1/2*z - 1/2 sage: K.zeta(3, all=True) - [1/2*z - 1/2, -1/2*z - 1/2] + [-1/2*z - 1/2, 1/2*z - 1/2] sage: K.zeta(4) Traceback (most recent call last): ... - ValueError: There are no 4th roots of unity in self. + ValueError: there are no 4th roots of unity in self :: @@ -7407,7 +7407,7 @@ def zeta(self, n=2, all=False): sage: K.zeta(3) Traceback (most recent call last): ... - ValueError: There are no 3rd roots of unity in self. + ValueError: there are no 3rd roots of unity in self sage: K.zeta(3,all=True) [] @@ -7434,28 +7434,26 @@ def zeta(self, n=2, all=False): if n == 1: if all: return [K.one()] - else: - return K.one() + return K.one() elif n == 2: if all: return [K(-1)] - else: - return K(-1) + return K(-1) - # First check if the degree of K is compatible with an - # inclusion QQ(\zeta_n) -> K. + # First check if the degree of K is compatible + # with an inclusion QQ(\zeta_n) -> K. if sage.arith.all.euler_phi(n).divides(K.absolute_degree()): - # Factor the n-th cyclotomic polynomial over K. - f = K.pari_polynomial('y') - factors = f.nffactor(pari.polcyclo(n)).component(1) - roots = (K(-g.polcoef(0)) for g in factors if g.poldegree() == 1) - if all: - return list(roots) - try: - return next(roots) - except StopIteration: - pass - raise ValueError("There are no %s roots of unity in self." % n.ordinal_str()) + w, zeta_w = self.pari_nf().nfrootsof1() + w = w.sage() + zeta_w = K(zeta_w) + if not w % n: + zeta_n = zeta_w**(w // n) + if all: + return [zeta_n**i for i in n.coprime_integers(n)] + return zeta_n + if all: + return [] + raise ValueError("there are no %s roots of unity in self" % n.ordinal_str()) def zeta_order(self): r""" From d86dd0c79ca6b59dbfae69960fac3a60d0bc8485 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 9 Nov 2022 16:45:11 +0100 Subject: [PATCH 425/632] less use of gap.eval in linear_code --- src/sage/coding/linear_code.py | 58 +++++++++++++++++++--------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/src/sage/coding/linear_code.py b/src/sage/coding/linear_code.py index e8e32f82c95..c19b83779d8 100644 --- a/src/sage/coding/linear_code.py +++ b/src/sage/coding/linear_code.py @@ -586,8 +586,8 @@ def assmus_mattson_designs(self, t, mode=None): if mode=="verbose": for w in nonzerowts: print("The weight w={} codewords of C* form a t-(v,k,lambda) design, where\n \ - t={}, v={}, k={}, lambda={}. \nThere are {} block of this design.".format(\ - w,t,n,w,wts[w]*binomial(w,t)//binomial(n,t),wts[w])) + t={}, v={}, k={}, lambda={}. \nThere are {} block of this design.".format( + w,t,n,w,wts[w]*binomial(w,t)//binomial(n,t),wts[w])) wtsp = Cp.weight_distribution() dp = min([i for i in range(1,len(wtsp)) if wtsp[i]!=0]) nonzerowtsp = [i for i in range(len(wtsp)) if wtsp[i]!=0 and i<=n-t and i>=dp] @@ -595,8 +595,8 @@ def assmus_mattson_designs(self, t, mode=None): if mode=="verbose": for w in nonzerowtsp: print("The weight w={} codewords of C* form a t-(v,k,lambda) design, where\n \ - t={}, v={}, k={}, lambda={}. \nThere are {} block of this design.".format(\ - w,t,n,w,wts[w]*binomial(w,t)//binomial(n,t),wts[w])) + t={}, v={}, k={}, lambda={}. \nThere are {} block of this design.".format( + w,t,n,w,wts[w]*binomial(w,t)//binomial(n,t),wts[w])) if s<=d-t: des = [[t,(n,w,wts[w]*binomial(w,t)//binomial(n,t))] for w in nonzerowts] ans = ans + ["weights from C: ",nonzerowts,"designs from C: ",des] @@ -1370,7 +1370,7 @@ def minimum_distance(self, algorithm=None): # This is done only if algorithm is None. if algorithm not in (None, "gap", "guava"): raise ValueError("The algorithm argument must be one of None, " - "'gap' or 'guava'; got '{0}'".format(algorithm)) + "'gap' or 'guava'; got '{0}'".format(algorithm)) F = self.base_ring() q = F.order() @@ -1380,14 +1380,14 @@ def minimum_distance(self, algorithm=None): "of size at most 256") G = self.generator_matrix() - if (q == 2 or q == 3) and algorithm=="guava": + if (q == 2 or q == 3) and algorithm == "guava": gap.load_package("guava") C = gap(G).GeneratorMatCode(gap(F)) d = C.MinimumWeight() return ZZ(d) return self._minimum_weight_codeword(algorithm).hamming_weight() - def _minimum_weight_codeword(self, algorithm = None): + def _minimum_weight_codeword(self, algorithm=None): r""" Return a minimum weight codeword of ``self``. @@ -1431,7 +1431,7 @@ def _minimum_weight_codeword(self, algorithm = None): current_randstate().set_seed_gap() - if algorithm=="guava": + if algorithm == "guava": GapPackage("guava", spkg="gap_packages").require() gap.load_package("guava") from sage.interfaces.gap import gfq_gap_to_sage @@ -1464,8 +1464,11 @@ def _minimum_weight_codeword(self, algorithm = None): def module_composition_factors(self, gp): r""" - Prints the GAP record of the Meataxe composition factors module in - Meataxe notation. This uses GAP but not Guava. + Print the GAP record of the Meataxe composition factors module. + + This is displayed in Meataxe notation. + + This uses GAP but not Guava. EXAMPLES:: @@ -1473,25 +1476,29 @@ def module_composition_factors(self, gp): sage: G = MS([[1,0,0,0,1,1,1,0],[0,1,1,1,0,0,0,0],[0,0,0,0,0,0,0,1],[0,0,0,0,0,1,0,0]]) sage: C = LinearCode(G) sage: gp = C.permutation_automorphism_group() - - Now type "C.module_composition_factors(gp)" to get the record printed. + sage: C.module_composition_factors(gp) + [ rec( + IsIrreducible := true, + IsOverFiniteField := true, + ...) ] """ + from sage.libs.gap.libgap import libgap F = self.base_ring() - q = F.order() gens = gp.gens() G = self.generator_matrix() n = len(G.columns()) - MS = MatrixSpace(F,n,n) - mats = [] # initializing list of mats by which the gens act on self + MS = MatrixSpace(F, n, n) + mats = [] # initializing list of mats by which the gens act on self Fn = VectorSpace(F, n) - W = Fn.subspace_with_basis(G.rows()) # this is self + W = Fn.subspace_with_basis(G.rows()) # this is self for g in gens: p = MS(g.matrix()) - m = [W.coordinate_vector(r*p) for r in G.rows()] + m = [W.coordinate_vector(r * p) for r in G.rows()] mats.append(m) - mats_str = str(gap([[list(r) for r in m] for m in mats])) - gap.eval("M:=GModuleByMats("+mats_str+", GF("+str(q)+"))") - print(gap("MTX.CompositionFactors( M )")) + mats_gap = libgap(mats) + M_gap = mats_gap.GModuleByMats(F) + compo = libgap.function_factory('MTX.CompositionFactors') + print(compo(M_gap)) def permutation_automorphism_group(self, algorithm="partition"): r""" @@ -1619,7 +1626,7 @@ def permutation_automorphism_group(self, algorithm="partition"): gap.eval("matCwt:=List(Cwt,c->VectorCodeword(c))") # for each i until stop = 1) if gap("Length(matCwt)") > 0: A = gap("MatrixAutomorphisms(matCwt)") - G2 = gap("Intersection2(%s,%s)"%(str(A).replace("\n",""),str(Gp).replace("\n",""))) # bottleneck 3 + G2 = gap("Intersection2(%s,%s)"%(str(A).replace("\n",""),str(Gp).replace("\n",""))) # bottleneck 3 Gp = G2 if Gp.Size()==1: return PermutationGroup([()]) @@ -1830,7 +1837,7 @@ def weight_distribution(self, algorithm=None): q = self.base_ring().order() z = 'Z(%s)*%s'%(q, [0]*self.length()) # GAP zero vector as a string _ = gap.eval("w:=DistancesDistributionMatFFEVecFFE("+Gmat+", GF("+str(q)+"),"+z+")") - v = [eval(gap.eval("w["+str(i)+"]")) for i in range(1,self.length()+2)] # because GAP returns vectors in compressed form + v = [eval(gap.eval("w["+str(i)+"]")) for i in range(1,self.length()+2)] # because GAP returns vectors in compressed form return v elif algorithm=="binary": from sage.coding.binary_code import weight_dist @@ -2144,7 +2151,7 @@ def e(i): return G -############################ linear codes python class ######################## +# ########################### linear codes python class ######################## class LinearCode(AbstractLinearCode): r""" @@ -2773,7 +2780,7 @@ def _build_lookup_table(self): # distance 1 gracefully zero_syndrome = vector(F,[F.zero()]*(n-k)) zero_syndrome.set_immutable() - lookup = { zero_syndrome : vector(F,[F.zero()]*n) } + lookup = {zero_syndrome: vector(F,[F.zero()]*n)} error_position_tables = [cartesian_product([l]*i) for i in range(1, t+1)] first_collision = True #Filling the lookup table @@ -2814,7 +2821,7 @@ def _build_lookup_table(self): # Update decoder types depending on whether we are decoding beyond d/2 if self._code_minimum_distance: if self._maximum_error_weight == (self._code_minimum_distance-1)//2: - self._decoder_type.update({"minimum-distance","always-succeed"}) + self._decoder_type.update({"minimum-distance", "always-succeed"}) else: # then t > (d-1)/2 self._decoder_type.add("might-error") @@ -2822,7 +2829,6 @@ def _build_lookup_table(self): self._decoder_type.add("always-succeed") return lookup - def decode_to_code(self, r): r""" Corrects the errors in ``word`` and returns a codeword. From d16faf2ec7c8ed880cd65469304cb8935559b1e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 9 Nov 2022 17:08:24 +0100 Subject: [PATCH 426/632] tiny details in symbolic min and max --- src/sage/functions/min_max.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/functions/min_max.py b/src/sage/functions/min_max.py index f783de0ba96..2454ef9434f 100644 --- a/src/sage/functions/min_max.py +++ b/src/sage/functions/min_max.py @@ -1,9 +1,9 @@ r""" Symbolic minimum and maximum -Sage provides a symbolic maximum and minimum due to the fact that the Python -builtin max and min are not able to deal with variables as users might expect. -These functions wait to evaluate if there are variables. +Sage provides a symbolic maximum and minimum due to the fact that the +Python builtin `\max` and `\min` are not able to deal with variables +as users might expect. These functions wait to evaluate if there are variables. Here you can see some differences:: @@ -24,14 +24,13 @@ max(x, 5) sage: min_symbolic(3, 5, x) min(x, 3) - """ ############################################################################### # Sage: Open Source Mathematical Software # Copyright (C) 2010 Burcin Erocal # Distributed under the terms of the GNU General Public License (GPL), # version 2 or any later version. The full text of the GPL is available at: -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ ############################################################################### from sage.symbolic.function import BuiltinFunction @@ -40,6 +39,7 @@ from builtins import max as builtin_max, min as builtin_min + class MinMax_base(BuiltinFunction): def eval_helper(self, this_f, builtin_f, initial_val, args): """ @@ -53,7 +53,6 @@ def eval_helper(self, this_f, builtin_f, initial_val, args): min(x, 3) sage: min_symbolic([5.0r]) # indirect doctest 5.0 - """ # __call__ ensures that if args is a singleton, the element is iterable arg_is_iter = False @@ -151,12 +150,13 @@ def __call__(self, *args, **kwds): except ValueError: pass + class MaxSymbolic(MinMax_base): def __init__(self): r""" Symbolic `\max` function. - The Python builtin `\max` function doesn't work as expected when symbolic + The Python builtin `\max` function does not work as expected when symbolic expressions are given as arguments. This function delays evaluation until all symbolic arguments are substituted with values. @@ -236,7 +236,6 @@ def _evalf_(self, *args, **kwds): ... sage: r.n() # abs tol 1e-8 0.873911256504955 - """ return max_symbolic(args) @@ -249,7 +248,7 @@ def __init__(self): r""" Symbolic `\min` function. - The Python builtin `\min` function doesn't work as expected when symbolic + The Python builtin `\min` function does not work as expected when symbolic expressions are given as arguments. This function delays evaluation until all symbolic arguments are substituted with values. @@ -323,4 +322,5 @@ def _evalf_(self, *args, **kwds): """ return min_symbolic(args) + min_symbolic = MinSymbolic() From ec99933aeb934a2310e5436e9b19b4367cb857f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 9 Nov 2022 17:14:57 +0100 Subject: [PATCH 427/632] fix doctest --- src/sage/rings/number_field/order.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/number_field/order.py b/src/sage/rings/number_field/order.py index 6eca89ed8dd..22f838a9781 100644 --- a/src/sage/rings/number_field/order.py +++ b/src/sage/rings/number_field/order.py @@ -867,12 +867,12 @@ def zeta(self, n=2, all=False): sage: F. = NumberField(x**2+3) sage: F.ring_of_integers().zeta(6) - 1/2*alpha + 1/2 + -1/2*alpha + 1/2 sage: O = F.order([3*alpha]) sage: O.zeta(3) Traceback (most recent call last): ... - ArithmeticError: There are no 3rd roots of unity in self. + ArithmeticError: there are no 3rd roots of unity in self """ roots_in_field = self.number_field().zeta(n, True) roots_in_self = [self(x) for x in roots_in_field if x in self] @@ -880,7 +880,7 @@ def zeta(self, n=2, all=False): if all: return [] else: - raise ArithmeticError("There are no %s roots of unity in self." % n.ordinal_str()) + raise ArithmeticError("there are no %s roots of unity in self" % n.ordinal_str()) if all: return roots_in_self else: From b50426fcfe2a979d44de50b0069a9ee2d0eecf8d Mon Sep 17 00:00:00 2001 From: Dave Witte Morris Date: Wed, 9 Nov 2022 11:54:23 -0700 Subject: [PATCH 428/632] minor spacing improvement --- src/sage/rings/polynomial/polynomial_element.pyx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 243bbff5553..dc915077cf2 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -11648,7 +11648,7 @@ cdef class Polynomial_generic_dense(Polynomial): sage: R. = P[] sage: f = y^10 + R.random_element(9) sage: g = y^5 + R.random_element(4) - sage: q,r = f.quo_rem(g) + sage: q, r = f.quo_rem(g) sage: f == q*g + r True sage: g = x*y^5 @@ -11669,7 +11669,7 @@ cdef class Polynomial_generic_dense(Polynomial): sage: P. = HH[] sage: f = P.random_element(5) sage: g = P.random_element((0, 5)) - sage: q,r = f.quo_rem(g) + sage: q, r = f.quo_rem(g) sage: f == q*g + r True @@ -11685,7 +11685,7 @@ cdef class Polynomial_generic_dense(Polynomial): sage: h.quo_rem(f) ((-1/13*x^2 - x)*y^2 + (-x^2 + 3*x - 155/4)*y - x - 1, 0) sage: h += (2/3*x^2-3*x+1)*y + 7/17*x+6/5 - sage: q,r = h.quo_rem(f) + sage: q, r = h.quo_rem(f) sage: h == q*f + r and r.degree() < f.degree() True From 90655c789251c71d4f273788fe4a8ef005caf5e6 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 9 Nov 2022 23:45:06 +0100 Subject: [PATCH 429/632] fix a good order of the supported findstat collections --- src/sage/databases/findstat.py | 323 +++++++++++++++++---------------- 1 file changed, 165 insertions(+), 158 deletions(-) diff --git a/src/sage/databases/findstat.py b/src/sage/databases/findstat.py index 70429a47ac0..aea8953b5b1 100644 --- a/src/sage/databases/findstat.py +++ b/src/sage/databases/findstat.py @@ -256,7 +256,7 @@ def mapping(sigma): from sage.combinat.words.word import Word from sage.combinat.words.words import Words from sage.combinat.words.abstract_word import Word_class -from sage.combinat.colored_permutations import SignedPermutations +from sage.combinat.colored_permutations import SignedPermutation, SignedPermutations from sage.combinat.plane_partition import PlanePartition from sage.combinat.decorated_permutation import DecoratedPermutation, DecoratedPermutations @@ -4486,158 +4486,159 @@ def name(self, style="singular"): "element_to_string", "elements_on_level", # return all elements on given level "element_level", # return level of a given element - "is_element"]) # return whether element is member of this collection (and, ideally, of no other collection) - -_SupportedFindStatCollections = { - "Permutations": - _SupportedFindStatCollection(lambda x: Permutation(literal_eval(x)), - str, - Permutations, - lambda x: x.size(), - lambda x: isinstance(x, Permutation)), - "BinaryWords": - _SupportedFindStatCollection(lambda x: Word((int(e) for e in str(x)), alphabet=[0,1]), - str, - lambda x: Words([0,1], length=x), - lambda x: x.length(), - lambda x: isinstance(x, Word_class)), - - "AlternatingSignMatrices": - _SupportedFindStatCollection(lambda x: AlternatingSignMatrix(literal_eval(x)), - lambda x: str(list(map(list, x.to_matrix().rows()))), - AlternatingSignMatrices, - lambda x: x.to_matrix().nrows(), - lambda x: isinstance(x, AlternatingSignMatrix)), - "BinaryTrees": - _SupportedFindStatCollection(lambda x: BinaryTree(str(x)), - str, - BinaryTrees, - lambda x: x.node_number(), - lambda x: isinstance(x, BinaryTree)), - "Cores": - _SupportedFindStatCollection(lambda x: Core(*literal_eval(x)), - lambda X: "( " + X._repr_() + ", " + str(X.k()) + " )", - lambda x: Cores(x[1], x[0]), - lambda x: (x.length(), x.k()), - lambda x: isinstance(x, Core)), - "DyckPaths": - _SupportedFindStatCollection(lambda x: DyckWord(literal_eval(x)), - lambda x: str(list(DyckWord(x))), - DyckWords, - lambda x: x.semilength(), - lambda x: isinstance(x, DyckWord)), - "FiniteCartanTypes": - _SupportedFindStatCollection(lambda x: CartanType(*literal_eval(str(x))), - str, - _finite_irreducible_cartan_types_by_rank, - lambda x: x.rank(), - lambda x: isinstance(x, CartanType_abstract)), - "GelfandTsetlinPatterns": - _SupportedFindStatCollection(lambda x: GelfandTsetlinPattern(literal_eval(x)), - str, - lambda x: (P - for la in Partitions(x[1], max_length=x[0]) - for P in GelfandTsetlinPatterns(top_row=la + [0]*(x[0]-len(la)))), - lambda x: (len(x[0]), sum(x[0])), - lambda x: (x == GelfandTsetlinPatterns - or isinstance(x, GelfandTsetlinPattern))), - "Graphs": - _SupportedFindStatCollection(lambda x: (lambda E, V: Graph([list(range(V)), - lambda i,j: (i,j) in E or (j,i) in E], - immutable=True))(*literal_eval(x)), - lambda X: str((X.edges(labels=False, sort=True), X.num_verts())), - lambda x: (g.copy(immutable=True) for g in graphs(x, copy=False)), - lambda x: x.num_verts(), - lambda x: isinstance(x, Graph)), - "IntegerCompositions": - _SupportedFindStatCollection(lambda x: Composition(literal_eval(x)), - str, - Compositions, - lambda x: x.size(), - lambda x: isinstance(x, Composition)), - "IntegerPartitions": - _SupportedFindStatCollection(lambda x: Partition(literal_eval(x)), - str, - Partitions, - lambda x: x.size(), - lambda x: isinstance(x, Partition)), - "OrderedTrees": - _SupportedFindStatCollection(lambda x: OrderedTree(literal_eval(x)), - str, - OrderedTrees, - lambda x: x.node_number(), - lambda x: isinstance(x, OrderedTree)), - "ParkingFunctions": - _SupportedFindStatCollection(lambda x: ParkingFunction(literal_eval(x)), - str, - ParkingFunctions, - len, - lambda x: isinstance(x, ParkingFunction)), - "PerfectMatchings": - _SupportedFindStatCollection(lambda x: PerfectMatching(literal_eval(x)), - str, - PerfectMatchings, - lambda x: x.size(), - lambda x: isinstance(x, PerfectMatching)), - "Posets": - _SupportedFindStatCollection(lambda x: (lambda R, E: Poset((list(range(E)), R)))(*literal_eval(x)), - lambda X: str((sorted(X._hasse_diagram.cover_relations()), - len(X._hasse_diagram.vertices(sort=False)))), - Posets, - lambda x: x.cardinality(), - lambda x: isinstance(x, FinitePoset)), - "StandardTableaux": - _SupportedFindStatCollection(lambda x: StandardTableau(literal_eval(x)), - str, - StandardTableaux, - lambda x: x.size(), - lambda x: isinstance(x, StandardTableau)), - "SemistandardTableaux": # apparently, isinstance(x, SemistandardTableau) is True for StandardTableaux x - _SupportedFindStatCollection(lambda x: SemistandardTableau(literal_eval(x)), - str, - lambda x: (T for T in SemistandardTableaux(size=x[0], max_entry=x[1]) - if max(T.entries()) == x[1]), - lambda x: (x.size(), max(x.entries())), - lambda x: isinstance(x, SemistandardTableau) and not isinstance(x, StandardTableau)), - "SetPartitions": - _SupportedFindStatCollection(lambda x: SetPartition(literal_eval(x.replace('{','[').replace('}',']'))), - str, - SetPartitions, - lambda x: x.size(), - lambda x: isinstance(x, SetPartition)), - "SkewPartitions": - _SupportedFindStatCollection(lambda x: SkewPartition(literal_eval(x)), - str, - SkewPartitions, - lambda x: x.size(), - lambda x: isinstance(x, SkewPartition)), - "SignedPermutations": - _SupportedFindStatCollection(lambda x: SignedPermutations(len(literal_eval(x)))(list(literal_eval(x))), - str, - SignedPermutations, - lambda x: len(list(x)), - lambda x: isinstance(x, SignedPermutations.Element)), - "PlanePartitions": - _SupportedFindStatCollection(lambda x: PlanePartition(literal_eval(x)), - lambda X: str(list(X)).replace(" ",""), - _plane_partitions_by_size, - lambda x: sum(sum(la) for la in x), - lambda x: isinstance(x, PlanePartition)), - "DecoratedPermutations": - _SupportedFindStatCollection(lambda x: DecoratedPermutation([v if v > 0 else (i if v == 0 else -i) - for i, v in enumerate(literal_eval(x.replace("+","0").replace("-","-1")), 1)]), - lambda x: "[" + ",".join((str(v) if abs(v) != i else ("+" if v > 0 else "-") - for i, v in enumerate(x, 1))) + "]", - DecoratedPermutations, - lambda x: x.size(), - lambda x: isinstance(x, DecoratedPermutation)), - "Lattices": - _SupportedFindStatCollection(lambda x: (lambda R, E: LatticePoset((list(range(E)), R)))(*literal_eval(x)), - lambda X: str((sorted(X._hasse_diagram.cover_relations()), - len(X._hasse_diagram.vertices(sort=False)))), - _finite_lattices, - lambda x: x.cardinality(), - lambda x: isinstance(x, FiniteLatticePoset))} + "is_element"]) # return whether element is member of this collection + +# this dictionary must be sorted so that subclasses come before +# superclasses, eg., "StandardTableaux" before "SemistandardTableaux" +_SupportedFindStatCollections = OrderedDict([ + ("Permutations", + _SupportedFindStatCollection(lambda x: Permutation(literal_eval(x)), + str, + Permutations, + lambda x: x.size(), + lambda x: isinstance(x, Permutation))), + ("BinaryWords", + _SupportedFindStatCollection(lambda x: Word((int(e) for e in str(x)), alphabet=[0,1]), + str, + lambda x: Words([0,1], length=x), + lambda x: x.length(), + lambda x: isinstance(x, Word_class))), + ("AlternatingSignMatrices", + _SupportedFindStatCollection(lambda x: AlternatingSignMatrix(literal_eval(x)), + lambda x: str(list(map(list, x.to_matrix().rows()))), + AlternatingSignMatrices, + lambda x: x.to_matrix().nrows(), + lambda x: isinstance(x, AlternatingSignMatrix))), + ("BinaryTrees", + _SupportedFindStatCollection(lambda x: BinaryTree(str(x)), + str, + BinaryTrees, + lambda x: x.node_number(), + lambda x: isinstance(x, BinaryTree))), + ("Cores", + _SupportedFindStatCollection(lambda x: Core(*literal_eval(x)), + lambda X: "( " + X._repr_() + ", " + str(X.k()) + " )", + lambda x: Cores(x[1], x[0]), + lambda x: (x.length(), x.k()), + lambda x: isinstance(x, Core))), + ("DyckPaths", + _SupportedFindStatCollection(lambda x: DyckWord(literal_eval(x)), + lambda x: str(list(DyckWord(x))), + DyckWords, + lambda x: x.semilength(), + lambda x: isinstance(x, DyckWord))), + ("FiniteCartanTypes", + _SupportedFindStatCollection(lambda x: CartanType(*literal_eval(str(x))), + str, + _finite_irreducible_cartan_types_by_rank, + lambda x: x.rank(), + lambda x: isinstance(x, CartanType_abstract))), + ("GelfandTsetlinPatterns", + _SupportedFindStatCollection(lambda x: GelfandTsetlinPattern(literal_eval(x)), + str, + lambda x: (P + for la in Partitions(x[1], max_length=x[0]) + for P in GelfandTsetlinPatterns(top_row=la + [0]*(x[0]-len(la)))), + lambda x: (len(x[0]), sum(x[0])), + lambda x: (x == GelfandTsetlinPatterns + or isinstance(x, GelfandTsetlinPattern)))), + ("Graphs", + _SupportedFindStatCollection(lambda x: (lambda E, V: Graph([list(range(V)), + lambda i,j: (i,j) in E or (j,i) in E], + immutable=True))(*literal_eval(x)), + lambda X: str((X.edges(labels=False, sort=True), X.num_verts())), + lambda x: (g.copy(immutable=True) for g in graphs(x, copy=False)), + lambda x: x.num_verts(), + lambda x: isinstance(x, Graph))), + ("IntegerPartitions", + _SupportedFindStatCollection(lambda x: Partition(literal_eval(x)), + str, + Partitions, + lambda x: x.size(), + lambda x: isinstance(x, Partition))), + ("IntegerCompositions", + _SupportedFindStatCollection(lambda x: Composition(literal_eval(x)), + str, + Compositions, + lambda x: x.size(), + lambda x: isinstance(x, Composition))), + ("OrderedTrees", + _SupportedFindStatCollection(lambda x: OrderedTree(literal_eval(x)), + str, + OrderedTrees, + lambda x: x.node_number(), + lambda x: isinstance(x, OrderedTree))), + ("ParkingFunctions", + _SupportedFindStatCollection(lambda x: ParkingFunction(literal_eval(x)), + str, + ParkingFunctions, + len, + lambda x: isinstance(x, ParkingFunction))), + ("Lattices", + _SupportedFindStatCollection(lambda x: (lambda R, E: LatticePoset((list(range(E)), R)))(*literal_eval(x)), + lambda X: str((sorted(X._hasse_diagram.cover_relations()), + len(X._hasse_diagram.vertices(sort=False)))), + _finite_lattices, + lambda x: x.cardinality(), + lambda x: isinstance(x, FiniteLatticePoset))), + ("Posets", + _SupportedFindStatCollection(lambda x: (lambda R, E: Poset((list(range(E)), R)))(*literal_eval(x)), + lambda X: str((sorted(X._hasse_diagram.cover_relations()), + len(X._hasse_diagram.vertices(sort=False)))), + Posets, + lambda x: x.cardinality(), + lambda x: isinstance(x, FinitePoset))), + ("StandardTableaux", + _SupportedFindStatCollection(lambda x: StandardTableau(literal_eval(x)), + str, + StandardTableaux, + lambda x: x.size(), + lambda x: isinstance(x, StandardTableau))), + ("SemistandardTableaux", + _SupportedFindStatCollection(lambda x: SemistandardTableau(literal_eval(x)), + str, + lambda x: (T for T in SemistandardTableaux(size=x[0], max_entry=x[1]) + if max(T.entries()) == x[1]), + lambda x: (x.size(), max(x.entries())), + lambda x: isinstance(x, SemistandardTableau))), + ("PerfectMatchings", + _SupportedFindStatCollection(lambda x: PerfectMatching(literal_eval(x)), + str, + PerfectMatchings, + lambda x: x.size(), + lambda x: isinstance(x, PerfectMatching))), + ("SetPartitions", + _SupportedFindStatCollection(lambda x: SetPartition(literal_eval(x.replace('{','[').replace('}',']'))), + str, + SetPartitions, + lambda x: x.size(), + lambda x: isinstance(x, SetPartition))), + ("SkewPartitions", + _SupportedFindStatCollection(lambda x: SkewPartition(literal_eval(x)), + str, + SkewPartitions, + lambda x: x.size(), + lambda x: isinstance(x, SkewPartition))), + ("SignedPermutations", + _SupportedFindStatCollection(lambda x: SignedPermutations(len(literal_eval(x)))(list(literal_eval(x))), + str, + SignedPermutations, + lambda x: len(list(x)), + lambda x: isinstance(x, SignedPermutation))), + ("PlanePartitions", + _SupportedFindStatCollection(lambda x: PlanePartition(literal_eval(x)), + lambda X: str(list(X)).replace(" ",""), + _plane_partitions_by_size, + lambda x: sum(sum(la) for la in x), + lambda x: isinstance(x, PlanePartition))), + ("DecoratedPermutations", + _SupportedFindStatCollection(lambda x: DecoratedPermutation([v if v > 0 else (i if v == 0 else -i) + for i, v in enumerate(literal_eval(x.replace("+","0").replace("-","-1")), 1)]), + lambda x: "[" + ",".join((str(v) if abs(v) != i else ("+" if v > 0 else "-") + for i, v in enumerate(x, 1))) + "]", + DecoratedPermutations, + lambda x: x.size(), + lambda x: isinstance(x, DecoratedPermutation)))]) class FindStatCollections(UniqueRepresentation, Parent): @@ -4689,9 +4690,8 @@ def __init__(self): """ fields = "LevelsWithSizes,Name,NamePlural,NameWiki" url = FINDSTAT_API_COLLECTIONS + "?fields=" + fields - d = _get_json(url, object_pairs_hook=OrderedDict) - self._findstat_collections = d["included"]["Collections"] - for id, data in self._findstat_collections.items(): + d = _get_json(url, object_pairs_hook=OrderedDict)["included"]["Collections"] + for id, data in d.items(): data["LevelsWithSizes"] = OrderedDict((literal_eval(level), size) for level, size in data["LevelsWithSizes"].items()) if data["NameWiki"] in _SupportedFindStatCollections: @@ -4706,7 +4706,14 @@ def __init__(self): # fields = "SageCodeElementToString,SageCodeElementsOnLevel,SageCodeStringToElement" # url = FINDSTAT_API_COLLECTIONS + id + "?fields=" + fields # print(json.load(urlopen(url))["included"]["Collections"][id]) + def position(item): + try: + return tuple(_SupportedFindStatCollections).index(item[1]["NameWiki"]) + except ValueError: + return len(_SupportedFindStatCollections) + self._findstat_collections = OrderedDict(sorted(d.items(), + key=position)) Parent.__init__(self, category=Sets()) def _element_constructor_(self, entry): @@ -4733,7 +4740,6 @@ def _element_constructor_(self, entry): Cc0012: Perfect matchings, Cc0013: Cores, Cc0014: Posets, - Cc0014: Posets, Cc0017: Alternating sign matrices, Cc0018: Gelfand-Tsetlin patterns, Cc0019: Semistandard tableaux, @@ -4745,7 +4751,8 @@ def _element_constructor_(self, entry): Cc0025: Plane partitions, Cc0026: Decorated permutations, Cc0027: Signed permutations, - Cc0028: Skew partitions] + Cc0028: Skew partitions, + Cc0029: Lattices] sage: FindStatCollection(Permutation([1,2,3])) # optional -- internet Cc0001: Permutations From ccb9962208e0896d57de53c00c890a9ebd9cff27 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Wed, 9 Nov 2022 21:08:29 -0500 Subject: [PATCH 430/632] src/sage/modular/quasimodform/: update the default names in polynomial_ring, fix documentation, fix doctests --- src/sage/modular/quasimodform/element.py | 11 ++- src/sage/modular/quasimodform/ring.py | 107 ++++++++++++++++++----- 2 files changed, 94 insertions(+), 24 deletions(-) diff --git a/src/sage/modular/quasimodform/element.py b/src/sage/modular/quasimodform/element.py index 0fde12cc248..37f5b51ed7a 100644 --- a/src/sage/modular/quasimodform/element.py +++ b/src/sage/modular/quasimodform/element.py @@ -74,13 +74,16 @@ class QuasiModularFormsElement(ModuleElement): sage: F.polynomial() E2*E4*E6 + E4^2 + E2^2 - If the group is not the full modular group, the names of the generators will - of the form ``ABCk`` where ``k`` is the weight of the form ``ABC``:: + If the group is not the full modular group, the default names of the + generators are given by ``Ek_i`` and ``Sk_i`` to denote the `i`-th basis + element of the weight `k` Eisenstein subspace and cuspidal subspace + respectively (for more details, see the documentation of + :meth:`~sage.modular.quasimodform.ring.QuasiModularFormsRing.polynomial_ring`) :: sage: QM = QuasiModularForms(Gamma1(4)) sage: F = (QM.0^4)*(QM.1^3) + QM.3 sage: F.polynomial() - -512*E2^4*B2^3 + E2^4*A3^2 + 48*E2^4*B3^2 + A3 + -512*E2^4*E2_1^3 + E2^4*E3_0^2 + 48*E2^4*E3_1^2 + E3_0 """ def __init__(self, parent, polynomial): r""" @@ -413,7 +416,7 @@ def polynomial(self, names=None): sage: QM.ngens() 5 sage: (QM.0 + QM.1 + QM.2*QM.1 + QM.3*QM.4).polynomial() - B3*A4 + A2*A3 + E2 + A2 + E3_1*E4_0 + E2_0*E3_0 + E2 + E2_0 """ P = self.parent().polynomial_ring(names) diff --git a/src/sage/modular/quasimodform/ring.py b/src/sage/modular/quasimodform/ring.py index be1a14b2d96..d557568239c 100644 --- a/src/sage/modular/quasimodform/ring.py +++ b/src/sage/modular/quasimodform/ring.py @@ -129,15 +129,15 @@ sage: QM.ngens() 5 sage: QM.polynomial_ring() - Multivariate Polynomial Ring in E2, A2, B2, A3, B3 over Rational Field + Multivariate Polynomial Ring in E2, E2_0, E2_1, E3_0, E3_1 over Rational Field sage: (QM.0 + QM.1*QM.0^2 + QM.3 + QM.4^3).polynomial() - B3^3 + E2^2*A2 + A3 + E2 + E3_1^3 + E2^2*E2_0 + E3_0 + E2 One can also convert a multivariate polynomial into a quasimodular form:: sage: QM.polynomial_ring().inject_variables() - Defining E2, A2, B2, A3, B3 - sage: QM.from_polynomial(B3^3 + E2^2*A2 + A3 + E2) + Defining E2, E2_0, E2_1, E3_0, E3_1 + sage: QM.from_polynomial(E3_1^3 + E2^2*E2_0 + E3_0 + E2) 3 - 72*q + 396*q^2 + 2081*q^3 + 19752*q^4 + 98712*q^5 + O(q^6) .. NOTE:: @@ -605,8 +605,18 @@ def polynomial_ring(self, names=None): - ``names`` (str, default: ``None``) -- a list or tuple of names (strings), or a comma separated string. Defines the names for the generators of the multivariate polynomial ring. The default names are - of the form ``ABCk`` where ``k`` is a number corresponding to the - weight of the form ``ABC``. + of the following form: + + - ``E2`` denotes the weight 2 Eisenstein series; + + - ``Ek_i`` and ``Sk_i`` denote the `i`-th basis element of the weight + `k` Eisenstein subspace and cuspidal subspace respectively; + + - If the level is one, the default names are ``E2``, ``E4`` and + ``E6``; + + - In any other cases, we use the letters ``Fk``, ``Gk``, ``Hk``, ..., + ``FFk``, ``FGk``, ... to denote any generator of weight `k`. OUTPUT: A multivariate polynomial ring in the variables ``names`` @@ -623,30 +633,87 @@ def polynomial_ring(self, names=None): 4 sage: E6.degree() 6 - sage: QM = QuasiModularForms(Gamma1(9)) - sage: QM.polynomial_ring() - Multivariate Polynomial Ring in E2, A2, B2, C2, D2, F2, G2, H2, A3, B3, C3, D3, F3, G3, H3, AA3, AB3, AC3 over Rational Field - sage: QM = QuasiModularForms(Gamma0(6)) - sage: QM.polynomial_ring(names="F") - Multivariate Polynomial Ring in F0, F1, F2, F3 over Rational Field + + Example when the level is not one:: + + sage: QM = QuasiModularForms(Gamma0(29)) + sage: P_29 = QM.polynomial_ring() + sage: P_29 + Multivariate Polynomial Ring in E2, F2, S2_0, S2_1, E4_0, F4, G4, H4 over Rational Field + sage: P_29.inject_variables() + Defining E2, F2, S2_0, S2_1, E4_0, F4, G4, H4 + sage: F2.degree() + 2 + sage: E4_0.degree() + 4 + + The name ``Sk_i`` stands for the `i`-th basis element of the cuspidal subspace of weight `k`:: + + sage: F2 = QM.from_polynomial(S2_0) + sage: F2.qexp(10) + q - q^4 - q^5 - q^6 + 2*q^7 - 2*q^8 - 2*q^9 + O(q^10) + sage: CuspForms(Gamma0(29), 2).0.qexp(10) + q - q^4 - q^5 - q^6 + 2*q^7 - 2*q^8 - 2*q^9 + O(q^10) + sage: F2 == CuspForms(Gamma0(29), 2).0 + True + + The name ``Ek_i`` stands for the `i`-th basis element of the Eisenstein subspace of weight `k`:: + + sage: F4 = QM.from_polynomial(E4_0) + sage: F4.qexp(30) + 1 + 240*q^29 + O(q^30) + sage: EisensteinForms(Gamma0(29), 4).0.qexp(30) + 1 + 240*q^29 + O(q^30) + sage: F4 == EisensteinForms(Gamma0(29), 4).0 + True + + One may also choose the name of the variables:: + + sage: QM = QuasiModularForms(1) + sage: QM.polynomial_ring(names="P, Q, R") + Multivariate Polynomial Ring in P, Q, R over Rational Field """ - weights = [f.weight() for f in self.__modular_forms_subring.gen_forms()] + M = self.__modular_forms_subring + gens = self.__modular_forms_subring.gen_forms() + weights = [f.weight() for f in gens] + gens = iter(gens) if names is None: if self.group() == Gamma0(1): names = ["E2", "E4", "E6"] else: names = ["E2"] - letters = "ABCDFGH" + letters = "FGHIJK" for unique_weight in set(weights): same_weights = [k for k in weights if k == unique_weight] # create all the names of the form: - # A, B, C, D, F, G, H, AA, AB, AC, AD,..., AAA, AAB, AAC,... - # the letter E is reserved for the weight 2 Eisenstein series + # F, G, H, I, J, K, FF, FG, FH,..., FFF, FFG,... + # the letters E and S are reserved for basis elements of the + # Eisenstein subspaces and cuspidal subspaces respectively. iter_names = (product(letters, repeat=r) - for r in range(1, len(same_weights)//7 + 2)) + for r in range(1, len(same_weights)//len(letters) + 2)) iter_names = chain(*iter_names) for k in same_weights: - names.append("".join(next(iter_names)) + str(k)) + form = next(gens) + Mk = self.__modular_forms_subring.modular_forms_of_weight(k) + if form.is_eisenstein(): + Ek_basis = Mk.eisenstein_subspace().basis() + # check if form is a basis element of the Eisenstein subspace of weight k + try: + n = Ek_basis.index(form) + name = f"E{str(k)}_{str(n)}" + except ValueError: + name = "".join(next(iter_names)) + str(k) + elif form.is_cuspidal(): + Sk_basis = Mk.cuspidal_subspace().basis() + # check if form is a basis element of the cuspidal subspace of weight k + try: + n = Sk_basis.index(form) + name = f"S{str(k)}_{str(n)}" + except ValueError: + name = "".join(next(iter_names)) + str(k) + else: + name = "".join(next(iter_names)) + str(k) + names.append(name) weights.insert(0, 2) # add the weight 2 Eisenstein series return PolynomialRing(self.base_ring(), len(weights), names, order=TermOrder('wdeglex', weights)) @@ -680,10 +747,10 @@ def from_polynomial(self, polynomial): sage: QM = QuasiModularForms(Gamma0(2)) sage: P = QM.polynomial_ring() sage: P.inject_variables() - Defining E2, A2, A4 + Defining E2, E2_0, E4_0 sage: QM.from_polynomial(E2) 1 - 24*q - 72*q^2 - 96*q^3 - 168*q^4 - 144*q^5 + O(q^6) - sage: QM.from_polynomial(E2 + A4*A2) == QM.0 + QM.2*QM.1 + sage: QM.from_polynomial(E2 + E4_0*E2_0) == QM.0 + QM.2*QM.1 True Naturally, the number of variable must not exceed the number of generators:: From dabdf8924269ea9127990a8aeaa15474db8afeee Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Wed, 9 Nov 2022 23:48:18 -0500 Subject: [PATCH 431/632] src/doc/en/developer/sage_manuals.rst: fix dead links --- src/doc/en/developer/sage_manuals.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/en/developer/sage_manuals.rst b/src/doc/en/developer/sage_manuals.rst index a640cc2df24..bdbf10cfb3b 100644 --- a/src/doc/en/developer/sage_manuals.rst +++ b/src/doc/en/developer/sage_manuals.rst @@ -8,7 +8,7 @@ The Sage Manuals Sage's manuals are written in `ReST `_ (reStructuredText), and generated with the software `Sphinx -`_: +`_: .. LIST-TABLE:: :widths: 4 12 @@ -84,7 +84,7 @@ The documentation can contain links toward modules, classes, or methods, e.g.:: For links toward classes, methods, or function, replace **:mod:** by **:class:**, **:meth:** or **:func:** respectively. See `Sphinx' documentation -`_. +`_. **Short links:** the link ``:func:`~sage.mod1.mod2.mod3.func1``` is equivalent to ``:func:`func1 ```: the function's name will be @@ -195,7 +195,7 @@ Two **help** commands which give plenty of documentation for the ``sage sage --docbuild -H # a more comprehensive one **Output formats:** All output formats supported by Sphinx (e.g. pdf) can be -used in Sage. See ``_. +used in Sage. See ``_. **Broken links:** in order to build the documentation while reporting the broken links that it contains, use the ``--warn-links`` flag. Note that Sphinx will not From bdc07ac7c38417e221ad4ae894010ed302a9b26c Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Thu, 10 Nov 2022 00:38:11 -0500 Subject: [PATCH 432/632] src/doc/en/developer/sage_manuals.rst: change hyperlink --- src/doc/en/developer/sage_manuals.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/en/developer/sage_manuals.rst b/src/doc/en/developer/sage_manuals.rst index bdbf10cfb3b..f4892b02742 100644 --- a/src/doc/en/developer/sage_manuals.rst +++ b/src/doc/en/developer/sage_manuals.rst @@ -84,7 +84,7 @@ The documentation can contain links toward modules, classes, or methods, e.g.:: For links toward classes, methods, or function, replace **:mod:** by **:class:**, **:meth:** or **:func:** respectively. See `Sphinx' documentation -`_. +`_. **Short links:** the link ``:func:`~sage.mod1.mod2.mod3.func1``` is equivalent to ``:func:`func1 ```: the function's name will be From 09a9c1c9da25b7c0a8c089d5355c742ee3ea014b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 10 Nov 2022 12:19:20 +0100 Subject: [PATCH 433/632] add doctest for fixed bug --- src/sage/rings/number_field/number_field.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 4febd05f45d..4625386d227 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -7417,6 +7417,12 @@ def zeta(self, n=2, all=False): sage: K. = NumberField(1/2*x^2 + 1/6) sage: K.zeta(3) -3/2*a - 1/2 + + TESTS:: + + sage: K = NumberField(x**60+691*x**12-25,'a') + sage: K.zeta(15,all=True) + [] """ try: return self._unit_group.zeta(n, all) From d097f7769f94889054e7f57e6bcd27654de0a41e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 10 Nov 2022 13:51:55 +0100 Subject: [PATCH 434/632] detail about min and max --- src/sage/functions/min_max.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/functions/min_max.py b/src/sage/functions/min_max.py index 2454ef9434f..28419136e52 100644 --- a/src/sage/functions/min_max.py +++ b/src/sage/functions/min_max.py @@ -2,7 +2,7 @@ Symbolic minimum and maximum Sage provides a symbolic maximum and minimum due to the fact that the -Python builtin `\max` and `\min` are not able to deal with variables +Python builtin :func:`max` and :func:`min` are not able to deal with variables as users might expect. These functions wait to evaluate if there are variables. Here you can see some differences:: @@ -156,7 +156,7 @@ def __init__(self): r""" Symbolic `\max` function. - The Python builtin `\max` function does not work as expected when symbolic + The Python builtin :func:`max` function does not work as expected when symbolic expressions are given as arguments. This function delays evaluation until all symbolic arguments are substituted with values. @@ -248,7 +248,7 @@ def __init__(self): r""" Symbolic `\min` function. - The Python builtin `\min` function does not work as expected when symbolic + The Python builtin :func:`min` function does not work as expected when symbolic expressions are given as arguments. This function delays evaluation until all symbolic arguments are substituted with values. From f567abf978ca328f67595743b5248ee42485b41e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 10 Nov 2022 14:22:24 +0100 Subject: [PATCH 435/632] get rid of some usage of "long" in pyx files --- .../tutorial-programming-python.rst | 2 +- src/sage/arith/functions.pyx | 2 +- src/sage/arith/long.pxd | 9 ++------- src/sage/crypto/boolean_function.pyx | 4 ++-- .../bounded_integer_sequences.pyx | 4 ++-- src/sage/ext/fast_callable.pyx | 2 +- src/sage/groups/perm_gps/permgroup_element.pyx | 5 ++--- src/sage/libs/singular/function.pyx | 4 +--- src/sage/matrix/args.pyx | 4 ++-- src/sage/matrix/matrix_gfpn_dense.pyx | 2 +- src/sage/matrix/matrix_integer_dense.pyx | 2 +- src/sage/misc/sage_input.py | 10 ++++------ src/sage/modules/free_module_element.pyx | 8 ++++---- src/sage/modules/vector_mod2_dense.pyx | 2 +- src/sage/rings/complex_double.pyx | 2 +- src/sage/rings/complex_mpc.pyx | 4 ++-- src/sage/rings/complex_mpfr.pyx | 2 +- src/sage/rings/finite_rings/element_givaro.pyx | 2 +- .../rings/finite_rings/element_ntl_gf2e.pyx | 2 +- .../rings/finite_rings/element_pari_ffelt.pyx | 2 +- src/sage/rings/integer.pyx | 4 ++-- .../number_field/number_field_element.pyx | 2 +- src/sage/rings/polynomial/pbori/pbori.pyx | 2 +- src/sage/rings/polynomial/plural.pyx | 15 +++------------ .../polynomial_integer_dense_flint.pyx | 4 ++-- src/sage/rings/power_series_ring_element.pyx | 2 +- src/sage/rings/real_mpfi.pyx | 6 +++--- src/sage/rings/real_mpfr.pyx | 4 ++-- src/sage/stats/intlist.pyx | 2 +- src/sage/stats/time_series.pyx | 2 +- src/sage/structure/coerce.pyx | 14 +++++++------- src/sage/symbolic/expression.pyx | 13 ++++++------- src/sage/symbolic/pynac_impl.pxi | 18 +++++++++--------- 33 files changed, 71 insertions(+), 91 deletions(-) diff --git a/src/doc/en/thematic_tutorials/tutorial-programming-python.rst b/src/doc/en/thematic_tutorials/tutorial-programming-python.rst index 50bff7decd2..5cafdf1ea1b 100644 --- a/src/doc/en/thematic_tutorials/tutorial-programming-python.rst +++ b/src/doc/en/thematic_tutorials/tutorial-programming-python.rst @@ -53,7 +53,7 @@ The *standard types* are :class:`bool`, :class:`int`, :class:`list`, * The type :class:`bool` (*booleans*) has two values: ``True`` and ``False``. The boolean operators are denoted by their names ``or``, ``and``, ``not``. -* The Python types :class:`int` and :class:`long` are used to +* The Python type :class:`int` is used to represent integers of limited size. To handle arbitrary large integers with exact arithmetic, Sage uses its own type named :class:`Integer`. diff --git a/src/sage/arith/functions.pyx b/src/sage/arith/functions.pyx index 8d1d25528f8..2fe15bbb974 100644 --- a/src/sage/arith/functions.pyx +++ b/src/sage/arith/functions.pyx @@ -195,7 +195,7 @@ cpdef LCM_list(v): sig_check() if isinstance(elt, Integer): x = elt - elif isinstance(elt, (int, long)): + elif isinstance(elt, int): x = Integer(elt) else: # The result is no longer an Integer, pass to generic code diff --git a/src/sage/arith/long.pxd b/src/sage/arith/long.pxd index b0c80f61480..e3a9f1586e5 100644 --- a/src/sage/arith/long.pxd +++ b/src/sage/arith/long.pxd @@ -194,15 +194,10 @@ cdef inline long dig(const digit* D, int n): cdef inline bint integer_check_long_py(x, long* value, int* err): """ Part of ``integer_check_long`` in ``long.pxd``, checking only for - Python objects of type ``int`` and ``long``. See that function for + Python objects of type ``int``. See that function for documentation and tests. """ - if not isinstance(x, long): - if isinstance(x, int): - # This can happen only on Python 2 - value[0] = PyInt_AS_LONG(x) - err[0] = 0 - return 1 + if not isinstance(x, int): err[0] = ERR_TYPE return 0 diff --git a/src/sage/crypto/boolean_function.pyx b/src/sage/crypto/boolean_function.pyx index 1584ca252ac..a9ea665475c 100644 --- a/src/sage/crypto/boolean_function.pyx +++ b/src/sage/crypto/boolean_function.pyx @@ -321,7 +321,7 @@ cdef class BooleanFunction(SageObject): bitset_set(self._truth_table, i) reed_muller(self._truth_table.bits, ZZ(self._truth_table.limbs).exact_log(2) ) - elif isinstance(x, (int,long,Integer) ): + elif isinstance(x, (int, Integer)): # initialisation to the zero function self._nvariables = ZZ(x) bitset_init(self._truth_table, (1<= self._truth_table.size: raise IndexError("index out of bound") return bitset_in(self._truth_table, x) diff --git a/src/sage/data_structures/bounded_integer_sequences.pyx b/src/sage/data_structures/bounded_integer_sequences.pyx index f307ebd01d1..724f296eae2 100644 --- a/src/sage/data_structures/bounded_integer_sequences.pyx +++ b/src/sage/data_structures/bounded_integer_sequences.pyx @@ -77,7 +77,7 @@ cimported in Cython modules: - ``cdef size_t biseq_getitem_py(biseq_t S, mp_size_t index)`` - Return ``S[index]`` as Python ``int`` or ``long``, without checking margins. + Return ``S[index]`` as Python ``int``, without checking margins. - ``cdef biseq_inititem(biseq_t S, mp_size_t index, size_t item)`` @@ -291,7 +291,7 @@ cdef inline size_t biseq_getitem(biseq_t S, mp_size_t index): cdef biseq_getitem_py(biseq_t S, mp_size_t index): """ - Get item ``S[index]`` as a Python ``int`` or ``long``, without + Get item ``S[index]`` as a Python ``int``, without checking margins. """ diff --git a/src/sage/ext/fast_callable.pyx b/src/sage/ext/fast_callable.pyx index 9cd023601e8..d21bddd5deb 100644 --- a/src/sage/ext/fast_callable.pyx +++ b/src/sage/ext/fast_callable.pyx @@ -983,7 +983,7 @@ cdef class Expression: # flag.) cdef Expression es - if isinstance(o, (int, long, Integer)): + if isinstance(o, (int, Integer)): es = s return ExpressionIPow(es._etb, s, o) else: diff --git a/src/sage/groups/perm_gps/permgroup_element.pyx b/src/sage/groups/perm_gps/permgroup_element.pyx index 620ca2a71bd..c0fbd8d15db 100644 --- a/src/sage/groups/perm_gps/permgroup_element.pyx +++ b/src/sage/groups/perm_gps/permgroup_element.pyx @@ -1061,11 +1061,10 @@ cdef class PermutationGroupElement(MultiplicativeGroupElement): # current behavior where if you pass in an integer which # is not in the domain of the permutation group, then that # integer itself will be returned. - if isinstance(i, (long, int, Integer)): + if isinstance(i, (int, Integer)): return i - - if not isinstance(i,(list,tuple,str)): + if not isinstance(i, (list, tuple, str)): raise ValueError("must be in the domain or a list, tuple or string") permuted = [i[self.perm[j]] for j from 0 <= j < self.n] diff --git a/src/sage/libs/singular/function.pyx b/src/sage/libs/singular/function.pyx index 74595b7c367..bb5d38ce80d 100644 --- a/src/sage/libs/singular/function.pyx +++ b/src/sage/libs/singular/function.pyx @@ -1394,9 +1394,7 @@ The Singular documentation for '%s' is given below. ring2 = a.parent() elif is_sage_wrapper_for_singular_ring(a): ring2 = a - elif isinstance(a, int) or\ - isinstance(a, long) or\ - isinstance(a, basestring): + elif isinstance(a, (int, str)): continue elif isinstance(a, Matrix_integer_dense): continue diff --git a/src/sage/matrix/args.pyx b/src/sage/matrix/args.pyx index 9b7ce991295..9e738312c27 100644 --- a/src/sage/matrix/args.pyx +++ b/src/sage/matrix/args.pyx @@ -1223,7 +1223,7 @@ cdef class MatrixArgs: return self.sequence_type() if isinstance(self.entries, dict): return MA_ENTRIES_MAPPING - if isinstance(self.entries, (int, long, float, complex)): + if isinstance(self.entries, (int, float, complex)): return MA_ENTRIES_SCALAR # Note: some objects are callable, iterable and act like a @@ -1296,7 +1296,7 @@ cdef class MatrixArgs: return MA_ENTRIES_SEQ_SEQ else: return MA_ENTRIES_SEQ_FLAT - if isinstance(x, (int, long, float, complex)): + if isinstance(x, (int, float, complex)): return MA_ENTRIES_SEQ_FLAT if isinstance(x, Element) and element_is_scalar(x): return MA_ENTRIES_SEQ_FLAT diff --git a/src/sage/matrix/matrix_gfpn_dense.pyx b/src/sage/matrix/matrix_gfpn_dense.pyx index e5fdcca4e3f..b3a61ae417e 100644 --- a/src/sage/matrix/matrix_gfpn_dense.pyx +++ b/src/sage/matrix/matrix_gfpn_dense.pyx @@ -1937,7 +1937,7 @@ def mtx_unpickle(f, int nr, int nc, data, bint m): # in the following line, we use a helper function that would return bytes, # regardless whether the input is bytes or str. cdef bytes Data = str_to_bytes(data, encoding='latin1') - if isinstance(f, (int, long)): + if isinstance(f, int): # This is for old pickles created with the group cohomology spkg MS = MatrixSpace(GF(f, 'z'), nr, nc, implementation=Matrix_gfpn_dense) else: diff --git a/src/sage/matrix/matrix_integer_dense.pyx b/src/sage/matrix/matrix_integer_dense.pyx index 3b8db1ec73f..00292d2e8fa 100644 --- a/src/sage/matrix/matrix_integer_dense.pyx +++ b/src/sage/matrix/matrix_integer_dense.pyx @@ -1639,7 +1639,7 @@ cdef class Matrix_integer_dense(Matrix_dense): from .matrix_modn_dense_float import MAX_MODULUS as MAX_MODULUS_FLOAT from .matrix_modn_dense_double import MAX_MODULUS as MAX_MODULUS_DOUBLE - if isinstance(moduli, (int, long, Integer)): + if isinstance(moduli, (int, Integer)): return self._mod_int(moduli) elif isinstance(moduli, list): moduli = MultiModularBasis(moduli) diff --git a/src/sage/misc/sage_input.py b/src/sage/misc/sage_input.py index b49763dbf27..a50bf15d708 100644 --- a/src/sage/misc/sage_input.py +++ b/src/sage/misc/sage_input.py @@ -107,7 +107,7 @@ The ``int`` method on :class:`SageInputBuilder` returns a SIE for an integer that is always represented in the simple way, without coercions. (So, depending on the preparser mode, it might read in as an -:class:`~sage.rings.integer.Integer`, an ``int``, or a ``long``.):: +:class:`~sage.rings.integer.Integer` or an ``int``.):: sage: test_qq_formatter(qq_sage_input_v2) [-ZZ(5)/7, -ZZ(5)/7, -5/7, -5/7, ZZ(3)/1, ZZ(3)/1, 3/1, 3/1] @@ -463,8 +463,6 @@ def __call__(self, x, coerced=False): return SIE_literal_stringrep(self, str(x)) if isinstance(x, int): - # For longs that don't fit in an int, we just use the int - # code; it will get extended to long automatically. if self._preparse is True: if x < 0: return -SIE_literal_stringrep(self, str(-x) + 'r') @@ -548,12 +546,12 @@ def int(self, n): r""" Return a raw SIE from the integer ``n`` - As it is raw, it may read back as a Sage Integer, a Python int or a - Python long, depending on its size and whether the preparser is enabled. + As it is raw, it may read back as a Sage Integer or a Python int, + depending on its size and whether the preparser is enabled. INPUT: - - ``n`` - a Sage Integer, a Python int or a Python long + - ``n`` -- a Sage Integer or a Python int EXAMPLES:: diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index 67991cebd52..c1e1424bfe8 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -371,7 +371,7 @@ def vector(arg0, arg1=None, arg2=None, sparse=None, immutable=False): ... TypeError: cannot convert 2-dimensional array to a vector - If any of the arguments to vector have Python type int, long, real, + If any of the arguments to vector have Python type int, real, or complex, they will first be coerced to the appropriate Sage objects. This fixes :trac:`3847`. :: @@ -475,7 +475,7 @@ def vector(arg0, arg1=None, arg2=None, sparse=None, immutable=False): # We first efficiently handle the important special case of the zero vector # over a ring. See trac 11657. # !! PLEASE DO NOT MOVE THIS CODE LOWER IN THIS FUNCTION !! - arg1_integer = isinstance(arg1, (int, long, Integer)) + arg1_integer = isinstance(arg1, (int, Integer)) if arg2 is None and is_Ring(arg0) and arg1_integer: M = FreeModule(arg0, arg1, bool(sparse)) v = M.zero_vector() @@ -898,14 +898,14 @@ def random_vector(ring, degree=None, *args, **kwds): ... ValueError: degree of a random vector must be non-negative, not -9 """ - if isinstance(ring, (Integer, int, long)): + if isinstance(ring, (Integer, int)): if not degree is None: arglist = list(args) arglist.insert(0, degree) args = tuple(arglist) degree = ring ring = ZZ - if not isinstance(degree,(Integer, int, long)): + if not isinstance(degree, (Integer, int)): raise TypeError("degree of a random vector must be an integer, not %s" % degree) if degree < 0: raise ValueError("degree of a random vector must be non-negative, not %s" % degree) diff --git a/src/sage/modules/vector_mod2_dense.pyx b/src/sage/modules/vector_mod2_dense.pyx index 9d57897febe..370e7040cbd 100644 --- a/src/sage/modules/vector_mod2_dense.pyx +++ b/src/sage/modules/vector_mod2_dense.pyx @@ -200,7 +200,7 @@ cdef class Vector_mod2_dense(free_module_element.FreeModuleElement): raise TypeError("x must be a list of the right length") for i in range(len(x)): xi = x[i] - if isinstance(xi, (IntegerMod_int, int, long, Integer)): + if isinstance(xi, (IntegerMod_int, int, Integer)): # the if/else statement is because in some compilers, (-1)%2 is -1 mzd_write_bit(self._entries, 0, i, 1 if xi%2 else 0) elif isinstance(xi, Rational): diff --git a/src/sage/rings/complex_double.pyx b/src/sage/rings/complex_double.pyx index f0c70c67f82..af568e65e17 100644 --- a/src/sage/rings/complex_double.pyx +++ b/src/sage/rings/complex_double.pyx @@ -380,7 +380,7 @@ cdef class ComplexDoubleField_class(sage.rings.abc.ComplexDoubleField): return x elif isinstance(x, tuple): return ComplexDoubleElement(x[0], x[1]) - elif isinstance(x, (float, int, long)): + elif isinstance(x, (float, int)): return ComplexDoubleElement(x, 0) elif isinstance(x, complex): return ComplexDoubleElement(x.real, x.imag) diff --git a/src/sage/rings/complex_mpc.pyx b/src/sage/rings/complex_mpc.pyx index 2a5e3048fbc..5de92be1a3d 100644 --- a/src/sage/rings/complex_mpc.pyx +++ b/src/sage/rings/complex_mpc.pyx @@ -869,7 +869,7 @@ cdef class MPComplexNumber(sage.structure.element.FieldElement): elif isinstance(z, Integer): mpc_set_z(self.value, (z).value, rnd) return - elif isinstance(z, (int, long)): + elif isinstance(z, int): mpc_set_si(self.value, z, rnd) return else: @@ -1623,7 +1623,7 @@ cdef class MPComplexNumber(sage.structure.element.FieldElement): x = self z = x._new() - if isinstance(right, (int,long)): + if isinstance(right, int): mpc_pow_si(z.value, x.value, right, (x._parent).__rnd) elif isinstance(right, Integer): mpc_pow_z(z.value, x.value, (right).value, (x._parent).__rnd) diff --git a/src/sage/rings/complex_mpfr.pyx b/src/sage/rings/complex_mpfr.pyx index 5083f66a89d..dd90e39488c 100644 --- a/src/sage/rings/complex_mpfr.pyx +++ b/src/sage/rings/complex_mpfr.pyx @@ -1714,7 +1714,7 @@ cdef class ComplexNumber(sage.structure.element.FieldElement): sage: float(5)^(0.5 + 14.1347251*I) -1.62414637645790 - 1.53692828324508*I """ - if isinstance(right, (int, long, Integer)): + if isinstance(right, (int, Integer)): return RingElement.__pow__(self, right) try: diff --git a/src/sage/rings/finite_rings/element_givaro.pyx b/src/sage/rings/finite_rings/element_givaro.pyx index 930eccbe57b..342c5f08090 100644 --- a/src/sage/rings/finite_rings/element_givaro.pyx +++ b/src/sage/rings/finite_rings/element_givaro.pyx @@ -378,7 +378,7 @@ cdef class Cache_givaro(Cache_base): else: raise TypeError("unable to coerce from a finite field other than the prime subfield") - elif isinstance(e, (int, Integer, long)) or is_IntegerMod(e): + elif isinstance(e, (int, Integer) or is_IntegerMod(e): try: e_int = e % self.characteristic() self.objectptr.initi(res, e_int) diff --git a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx index 96e4e3df8d9..c36adac770f 100644 --- a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx +++ b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx @@ -434,7 +434,7 @@ cdef class Cache_ntl_gf2e(Cache_base): if number < 0 or number >= self._order: raise TypeError("n must be between 0 and self.order()") - if isinstance(number, int) or isinstance(number, long): + if isinstance(number, int): if not number: n = 0 else: diff --git a/src/sage/rings/finite_rings/element_pari_ffelt.pyx b/src/sage/rings/finite_rings/element_pari_ffelt.pyx index ee0f9b3c4f5..3bf5c62ff32 100644 --- a/src/sage/rings/finite_rings/element_pari_ffelt.pyx +++ b/src/sage/rings/finite_rings/element_pari_ffelt.pyx @@ -395,7 +395,7 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): x_GEN = _new_GEN_from_mpz_t((x).value) self.construct(_INT_to_FFELT(g, x_GEN)) - elif isinstance(x, int) or isinstance(x, long): + elif isinstance(x, int): g = (self._parent._gen_pari).g x = objtogen(x) sig_on() diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index 0312f3f99fb..be8d08412fd 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -1184,7 +1184,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): .. NOTE:: '0x' is *not* prepended to the result like is done by the - corresponding Python function on ``int`` or ``long``. This is for + corresponding Python function on ``int``. This is for efficiency sake--adding and stripping the string wastes time; since this function is used for conversions from integers to other C-library structures, it is important @@ -1208,7 +1208,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): .. NOTE:: '0' (or '0o') is *not* prepended to the result like is done by the - corresponding Python function on ``int`` or ``long``. This is for + corresponding Python function on ``int``. This is for efficiency sake--adding and stripping the string wastes time; since this function is used for conversions from integers to other C-library structures, it is important diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index b78bc33078c..30f9c8ba57f 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -308,7 +308,7 @@ cdef class NumberFieldElement(FieldElement): self.__fld_numerator, self.__fld_denominator = parent.absolute_polynomial_ntl() cdef ZZ_c coeff - if isinstance(f, (int, long, Integer_sage)): + if isinstance(f, (int, Integer_sage)): # set it up and exit immediately # fast pathway mpz_to_ZZ(&coeff, (ZZ(f)).value) diff --git a/src/sage/rings/polynomial/pbori/pbori.pyx b/src/sage/rings/polynomial/pbori/pbori.pyx index 41850904621..153e69854a7 100644 --- a/src/sage/rings/polynomial/pbori/pbori.pyx +++ b/src/sage/rings/polynomial/pbori/pbori.pyx @@ -2213,7 +2213,7 @@ class BooleanMonomialMonoid(UniqueRepresentation, Monoid_class): self.base_ring().has_coerce_map_from(other.parent()) and \ self.base_ring()(other).is_one(): return self._one_element - elif isinstance(other, (int, long)) and other % 2: + elif isinstance(other, int) and other % 2: return self._one_element elif isinstance(other, (list, set)): diff --git a/src/sage/rings/polynomial/plural.pyx b/src/sage/rings/polynomial/plural.pyx index 9a996550975..c0931168995 100644 --- a/src/sage/rings/polynomial/plural.pyx +++ b/src/sage/rings/polynomial/plural.pyx @@ -569,19 +569,10 @@ cdef class NCPolynomialRing_plural(Ring): _n = sa2si(base_ring(element), _ring) _p = p_NSet(_n, _ring) - # and longs - elif isinstance(element, long): - if isinstance(base_ring, FiniteField_prime_modn): - element = element % self.base_ring().characteristic() - _p = p_ISet(int(element),_ring) - else: - _n = sa2si(base_ring(element),_ring) - _p = p_NSet(_n, _ring) - else: - raise NotImplementedError("not able to interpret "+repr(element) + - " of type "+ repr(type(element)) + - " as noncommutative polynomial") ### ?????? + raise NotImplementedError(f"not able to interpret {element)}" + f" of type {type(element)}" + " as noncommutative polynomial") # ???? return new_NCP(self, _p) cpdef _coerce_map_from_(self, S): diff --git a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx index bde23684615..2df63d29c14 100644 --- a/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_integer_dense_flint.pyx @@ -367,7 +367,7 @@ cdef class Polynomial_integer_dense_flint(Polynomial): interpreted as polynomial composition or evaluation by this method. - If the argument is not simply an integer (``int``, ``long`` or + If the argument is not simply an integer (``int`` or ``Integer``) a real number (``RealNumber``) a real interval (``RealIntervalFieldElement``) or a polynomial (of the same type as ``self``), the call is passed on to the generic implementation in the @@ -428,7 +428,7 @@ cdef class Polynomial_integer_dense_flint(Polynomial): fmpz_clear(z_fmpz) sig_off() return z - if isinstance(x0, (int, long)): + if isinstance(x0, int): x0 = Integer(x0) if isinstance(x0, Integer): a = x0 diff --git a/src/sage/rings/power_series_ring_element.pyx b/src/sage/rings/power_series_ring_element.pyx index 7cf46f7f170..2422521fba8 100644 --- a/src/sage/rings/power_series_ring_element.pyx +++ b/src/sage/rings/power_series_ring_element.pyx @@ -1119,7 +1119,7 @@ cdef class PowerSeries(AlgebraElement): T^2 + O(T^3) """ from sage.rings.power_series_ring import PowerSeriesRing - if isinstance(other, (int, Integer, long)): + if isinstance(other, (int, Integer)): return PowerSeriesRing(IntegerModRing(other), self.variable())(self) raise NotImplementedError("Mod on power series ring elements not defined except modulo an integer.") diff --git a/src/sage/rings/real_mpfi.pyx b/src/sage/rings/real_mpfi.pyx index a9e4b1eed16..5f1494000f7 100644 --- a/src/sage/rings/real_mpfi.pyx +++ b/src/sage/rings/real_mpfi.pyx @@ -2912,7 +2912,7 @@ cdef class RealIntervalFieldElement(RingElement): sage: RIF(1.0) << 32 4294967296 """ - if isinstance(x, RealIntervalFieldElement) and isinstance(y, (int,long, Integer)): + if isinstance(x, RealIntervalFieldElement) and isinstance(y, (int, Integer)): return x._lshift_(y) return sage.structure.element.bin_op(x, y, operator.lshift) @@ -2944,7 +2944,7 @@ cdef class RealIntervalFieldElement(RingElement): 0.062500000000000000? """ if isinstance(x, RealIntervalFieldElement) and \ - isinstance(y, (int,long,Integer)): + isinstance(y, (int, Integer)): return x._rshift_(y) return sage.structure.element.bin_op(x, y, operator.rshift) @@ -4411,7 +4411,7 @@ cdef class RealIntervalFieldElement(RingElement): """ if exponent == 2: return self.square() - if isinstance(exponent, (int, long, Integer)): + if isinstance(exponent, (int, Integer)): q, r = divmod (exponent, 2) if r == 0: # x^(2q) = (x^q)^2 xq = RingElement.__pow__(self, q) diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index c9214785c3c..09ace5ec1eb 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -678,7 +678,7 @@ cdef class RealField_class(sage.rings.abc.RealField): - Any MPFR real field with precision that is as large as this one - - int, long, integer, and rational rings. + - int, integer, and rational rings. - the field of algebraic reals @@ -6074,7 +6074,7 @@ cdef class int_toRR(Map): cdef long x_long cdef mpz_t x_mpz - if not isinstance(x, (int, long)): + if not isinstance(x, int): x = int(x) integer_check_long_py(x, &x_long, &err) diff --git a/src/sage/stats/intlist.pyx b/src/sage/stats/intlist.pyx index 6de4f36d70c..f29d6519d97 100644 --- a/src/sage/stats/intlist.pyx +++ b/src/sage/stats/intlist.pyx @@ -95,7 +95,7 @@ cdef class IntList: [1, -2] """ cdef TimeSeries T - if isinstance(values, (int,long,Integer)): + if isinstance(values, (int, Integer)): self._length = values values = None elif isinstance(values, TimeSeries): diff --git a/src/sage/stats/time_series.pyx b/src/sage/stats/time_series.pyx index 2624f7ebf2a..c90ea577bc8 100644 --- a/src/sage/stats/time_series.pyx +++ b/src/sage/stats/time_series.pyx @@ -128,7 +128,7 @@ cdef class TimeSeries: cdef cnumpy.ndarray np cdef double *np_data cdef unsigned int j - if isinstance(values, (int, long, Integer)): + if isinstance(values, (int, Integer)): self._length = values values = None elif isinstance(values, (Vector_real_double_dense, cnumpy.ndarray)): diff --git a/src/sage/structure/coerce.pyx b/src/sage/structure/coerce.pyx index 227a62cb3ef..86664258c8c 100644 --- a/src/sage/structure/coerce.pyx +++ b/src/sage/structure/coerce.pyx @@ -150,7 +150,7 @@ cpdef py_scalar_parent(py_type): sage: py_scalar_parent(gmpy2.mpc) Complex Double Field """ - if issubclass(py_type, int) or issubclass(py_type, long): + if issubclass(py_type, int): import sage.rings.integer_ring return sage.rings.integer_ring.ZZ if py_type is FractionType: @@ -266,7 +266,7 @@ cpdef py_scalar_to_element(x): """ if isinstance(x, Element): return x - elif isinstance(x, (int, long)): + elif isinstance(x, int): from sage.rings.integer import Integer return Integer(x) elif type(x) is FractionType: @@ -344,7 +344,7 @@ cpdef bint parent_is_integers(P) except -1: 2*f """ if isinstance(P, type): - if issubclass(P, int) or issubclass(P, long): + if issubclass(P, int): return True elif is_numpy_type(P): from numpy import integer @@ -969,7 +969,7 @@ cdef class CoercionModel: res = self.division_parent(res) return all, res - if isinstance(yp, Parent) and xp in [int, long, float, complex, bool]: + if isinstance(yp, Parent) and xp in [int, float, complex, bool]: mor = yp._internal_coerce_map_from(xp) if mor is not None: mor = mor.__copy__() @@ -982,7 +982,7 @@ cdef class CoercionModel: elif type(xp) is type: all.append("Left operand is not Sage element, will try _sage_.") - if isinstance(xp, Parent) and yp in [int, long, float, complex, bool]: + if isinstance(xp, Parent) and yp in [int, float, complex, bool]: mor = xp._internal_coerce_map_from(yp) if mor is not None: mor = mor.__copy__() @@ -1331,8 +1331,8 @@ cdef class CoercionModel: return x_elt,y_elt self._coercion_error(x, x_map, x_elt, y, y_map, y_elt) - cdef bint x_numeric = isinstance(x, (int, long, float, complex)) - cdef bint y_numeric = isinstance(y, (int, long, float, complex)) + cdef bint x_numeric = isinstance(x, (int, float, complex)) + cdef bint y_numeric = isinstance(y, (int, float, complex)) if not x_numeric and is_numpy_type(type(x)): import numpy diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index 903a8541460..da90a64c885 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -4712,7 +4712,7 @@ cdef class Expression(Expression_abc): return self.gradient() else: raise ValueError("No differentiation variable specified.") - if not isinstance(deg, (int, long, sage.rings.integer.Integer)) \ + if not isinstance(deg, (int, sage.rings.integer.Integer)) \ or deg < 1: raise TypeError("argument deg should be an integer >= 1.") cdef Expression symbol = self.coerce_in(symb) @@ -11290,10 +11290,11 @@ cdef class Expression(Expression_abc): def canonicalize_radical(self): r""" - Choose a canonical branch of the given expression. The square - root, cube root, natural log, etc. functions are multi-valued. The - ``canonicalize_radical()`` method will choose *one* of these values - based on a heuristic. + Choose a canonical branch of the given expression. + + The square root, cube root, natural log, etc. functions are + multi-valued. The ``canonicalize_radical()`` method will + choose *one* of these values based on a heuristic. For example, ``sqrt(x^2)`` has two values: ``x``, and ``-x``. The ``canonicalize_radical()`` function will choose @@ -13722,8 +13723,6 @@ cpdef new_Expression(parent, x): from sage.symbolic.constants import NaN return NaN exp = x - elif isinstance(x, long): - exp = x elif isinstance(x, int): exp = GEx(x) elif x is infinity: diff --git a/src/sage/symbolic/pynac_impl.pxi b/src/sage/symbolic/pynac_impl.pxi index d5934b565bc..0af6aae7fe1 100644 --- a/src/sage/symbolic/pynac_impl.pxi +++ b/src/sage/symbolic/pynac_impl.pxi @@ -1018,7 +1018,7 @@ cdef py_real(x): sage: py_real(complex(2,2)) 2.0 """ - if isinstance(x, (float, int, long)): + if isinstance(x, (float, int)): return x elif isinstance(x, complex): return x.real @@ -1118,7 +1118,7 @@ cdef py_conjugate(x): cdef bint py_is_rational(x): return (type(x) is Rational or type(x) is Integer or - isinstance(x, (int, long))) + isinstance(x, int)) cdef bint py_is_equal(x, y): @@ -1156,7 +1156,7 @@ cdef bint py_is_integer(x): sage: py_is_integer(3.0r) False """ - if isinstance(x, (int, long, Integer)): + if isinstance(x, (int, Integer)): return True if not isinstance(x, Element): return False @@ -1220,7 +1220,7 @@ def py_is_crational_for_doctest(x): cdef bint py_is_real(a): - if isinstance(a, (int, long, Integer, float)): + if isinstance(a, (int, Integer, float)): return True try: P = parent(a) @@ -1246,7 +1246,7 @@ cdef bint py_is_prime(n): cdef bint py_is_exact(x): - if isinstance(x, (int, long, Integer)): + if isinstance(x, (int, Integer)): return True if not isinstance(x, Element): return False @@ -1281,7 +1281,7 @@ cdef py_numer(n): sage: py_numer(no_numer()) 42 """ - if isinstance(n, (int, long, Integer)): + if isinstance(n, (int, Integer)): return n try: return n.numerator() @@ -1319,7 +1319,7 @@ cdef py_denom(n): sage: py_denom(2/3*i) 3 """ - if isinstance(n, (int, long, Integer)): + if isinstance(n, (int, Integer)): return 1 try: return n.denominator() @@ -1441,7 +1441,7 @@ cdef py_tgamma(x): sage: py_tgamma(1/2) 1.77245385090552 """ - if isinstance(x, (int, long)): + if isinstance(x, int): x = float(x) if type(x) is float: return math.tgamma(PyFloat_AS_DOUBLE(x)) @@ -1759,7 +1759,7 @@ cdef py_log(x): """ cdef gsl_complex res cdef double real, imag - if isinstance(x, (int, long)): + if isinstance(x, int): x = float(x) if type(x) is float: real = PyFloat_AS_DOUBLE(x) From 605968da44842a6351f93f69dbe68088ac9d749c Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Thu, 10 Nov 2022 17:06:38 +0000 Subject: [PATCH 436/632] numerical noise fixes we also made some tests use norm(), and shortened inputs using variables --- src/sage/geometry/polyhedron/library.py | 24 ++++++------------- src/sage/graphs/generic_graph.py | 2 +- .../schemes/projective/projective_space.py | 2 +- .../riemann_surfaces/riemann_surface.py | 6 ++--- .../polynomes_doctest.py | 2 +- 5 files changed, 13 insertions(+), 23 deletions(-) diff --git a/src/sage/geometry/polyhedron/library.py b/src/sage/geometry/polyhedron/library.py index 880eb4ee008..e54d8a7efd1 100644 --- a/src/sage/geometry/polyhedron/library.py +++ b/src/sage/geometry/polyhedron/library.py @@ -391,38 +391,28 @@ def gale_transform_to_primal(vectors, base_ring=None, backend=None): One can specify the base ring:: - sage: gale_transform_to_primal( - ....: [(1,1), (-1,-1), (1,0), - ....: (-1,0), (1,-1), (-2,1)]) + sage: p = [(1,1), (-1,-1), (1,0), (-1,0), (1,-1), (-2,1)] + sage: gtpp = gale_transform_to_primal(p); gtpp [(16, -35, 54), (24, 10, 31), (-15, 50, -60), (-25, 0, 0), (0, -25, 0), (0, 0, -25)] - sage: gale_transform_to_primal( - ....: [(1,1),(-1,-1),(1,0),(-1,0),(1,-1),(-2,1)], base_ring=RDF) - [(-0.6400000000000001, 1.4, -2.1600000000000006), - (-0.9600000000000002, -0.39999999999999997, -1.2400000000000002), - (0.6000000000000001, -2.0, 2.4000000000000004), - (1.0, 0.0, 0.0), - (0.0, 1.0, 0.0), - (0.0, 0.0, 1.0)] + sage: (matrix(RDF, gtpp)/25 + + ....: matrix(gale_transform_to_primal(p, base_ring=RDF))).norm() < 1e-15 + True One can also specify the backend to be used internally:: - sage: gale_transform_to_primal( - ....: [(1,1), (-1,-1), (1,0), - ....: (-1,0), (1,-1), (-2,1)], backend='field') + sage: gale_transform_to_primal(p, backend='field') [(48, -71, 88), (84, -28, 99), (-77, 154, -132), (-55, 0, 0), (0, -55, 0), (0, 0, -55)] - sage: gale_transform_to_primal( # optional - pynormaliz - ....: [(1,1), (-1,-1), (1,0), - ....: (-1,0), (1,-1), (-2,1)], backend='normaliz') + sage: gale_transform_to_primal(p, backend='normaliz') # optional - pynormaliz [(16, -35, 54), (24, 10, 31), (-15, 50, -60), diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index f2c94041c51..be31830500a 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -24154,7 +24154,7 @@ def katz_centrality(self, alpha, u=None): other nodes. :: sage: G = DiGraph({1: [10], 2:[10,11], 3:[10,11], 4:[], 5:[11, 4], 6:[11], 7:[10,11], 8:[10,11], 9:[10], 10:[11, 5, 8], 11:[6]}) - sage: G.katz_centrality(.85) + sage: G.katz_centrality(.85) # rel tol 1e-14 {1: 0.000000000000000, 2: 0.000000000000000, 3: 0.000000000000000, diff --git a/src/sage/schemes/projective/projective_space.py b/src/sage/schemes/projective/projective_space.py index eeb5dc589ca..2813bf4de24 100644 --- a/src/sage/schemes/projective/projective_space.py +++ b/src/sage/schemes/projective/projective_space.py @@ -1392,7 +1392,7 @@ def point_transformation_matrix(self, points_source, points_target, normalize=Tr sage: P1.=ProjectiveSpace(RR, 2) sage: points_source=[P1([1, 4, 1]), P1([1, 2, 2]), P1([3, 5, 1]), P1([1, -1, 1])] sage: points_target=[P1([5, -2, 7]), P1([3, -2, 3]), P1([6, -5, 9]), P1([3, 6, 7])] - sage: P1.point_transformation_matrix(points_source, points_target) + sage: P1.point_transformation_matrix(points_source, points_target) # abs tol 1e-13 [-0.0619047619047597 -0.609523809523810 -0.119047619047621] [ 0.853968253968253 0.0380952380952380 0.0412698412698421] [ -0.214285714285712 -0.933333333333333 0.280952380952379] diff --git a/src/sage/schemes/riemann_surfaces/riemann_surface.py b/src/sage/schemes/riemann_surfaces/riemann_surface.py index facd90d0425..b219cb975e2 100644 --- a/src/sage/schemes/riemann_surfaces/riemann_surface.py +++ b/src/sage/schemes/riemann_surfaces/riemann_surface.py @@ -268,9 +268,9 @@ def numerical_inverse(C): ....: -0.091587 + 0.19276*I, ....: 3.9443e-31 + 0.38552*I]) sage: from sage.schemes.riemann_surfaces.riemann_surface import numerical_inverse - sage: max(abs(c) for c in (C^(-1)*C-C^0).list()) < 1e-10 - False - sage: max(abs(c) for c in (numerical_inverse(C)*C-C^0).list()) < 1e-10 + sage: 3e-16 < (C^-1*C-C^0).norm() < 1e-15 + True + sage: (numerical_inverse(C)*C-C^0).norm() < 3e-16 True """ R = C.parent() diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/polynomes_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/polynomes_doctest.py index 9b3eade3687..219afcd22fd 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/polynomes_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/polynomes_doctest.py @@ -257,7 +257,7 @@ Sage example in ./polynomes.tex, line 1531:: sage: C = ComplexField(15) - sage: Frac(C['x'])(r).partial_fraction_decomposition() + sage: Frac(C['x'])(r).partial_fraction_decomposition() #abs tol 2e-4 (x^4 - x^2 + 6.000, [0.5312/(x - 1.000), 0.06250/(x^2 - 2.000*x + 1.000), 4.385*I/(x - 1.732*I), (-4.385*I)/(x + 1.732*I), (-0.5312)/(x + 1.000), 0.06250/(x^2 + 2.000*x + 1.000)]) From e8cebc3b2a473343d3a07c5d5b6102265f265b4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 10 Nov 2022 18:25:34 +0100 Subject: [PATCH 437/632] fix typo --- src/sage/rings/finite_rings/element_givaro.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/finite_rings/element_givaro.pyx b/src/sage/rings/finite_rings/element_givaro.pyx index 342c5f08090..920fdf273fc 100644 --- a/src/sage/rings/finite_rings/element_givaro.pyx +++ b/src/sage/rings/finite_rings/element_givaro.pyx @@ -378,7 +378,7 @@ cdef class Cache_givaro(Cache_base): else: raise TypeError("unable to coerce from a finite field other than the prime subfield") - elif isinstance(e, (int, Integer) or is_IntegerMod(e): + elif isinstance(e, (int, Integer)) or is_IntegerMod(e): try: e_int = e % self.characteristic() self.objectptr.initi(res, e_int) From 39ad89060c6931db6049620fdf9dc408941abb3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 11 Nov 2022 08:24:17 +0100 Subject: [PATCH 438/632] fix factorial of type int --- src/sage/functions/other.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/functions/other.py b/src/sage/functions/other.py index 1ce7569919f..eff27f95bc6 100644 --- a/src/sage/functions/other.py +++ b/src/sage/functions/other.py @@ -1518,7 +1518,7 @@ def _eval_(self, x): sage: type(factorial(float(3.2))) """ - if isinstance(x, Integer): + if isinstance(x, (int, Integer)): try: return x.factorial() except OverflowError: From 966f1596adefde7a85bab6c8c5b3f4b67537216f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 11 Nov 2022 17:09:48 +0100 Subject: [PATCH 439/632] small fixes --- src/sage/rings/polynomial/plural.pyx | 4 ++-- src/sage/symbolic/expression.pyx | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/polynomial/plural.pyx b/src/sage/rings/polynomial/plural.pyx index c0931168995..3209824ec88 100644 --- a/src/sage/rings/polynomial/plural.pyx +++ b/src/sage/rings/polynomial/plural.pyx @@ -570,9 +570,9 @@ cdef class NCPolynomialRing_plural(Ring): _p = p_NSet(_n, _ring) else: - raise NotImplementedError(f"not able to interpret {element)}" + raise NotImplementedError(f"not able to interpret {element}" f" of type {type(element)}" - " as noncommutative polynomial") # ???? + " as noncommutative polynomial") # ??? return new_NCP(self, _p) cpdef _coerce_map_from_(self, S): diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index da90a64c885..e59469135f0 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -13724,7 +13724,10 @@ cpdef new_Expression(parent, x): return NaN exp = x elif isinstance(x, int): - exp = GEx(x) + try: + exp = GEx(x) + except OverflowError: + exp = x elif x is infinity: return new_Expression_from_GEx(parent, g_Infinity) elif x is minus_infinity: From 1208b95955567bcb24e060675c3134853047c727 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 11 Nov 2022 18:14:59 +0100 Subject: [PATCH 440/632] adapt doctests to new website --- src/sage/databases/findstat.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/sage/databases/findstat.py b/src/sage/databases/findstat.py index aea8953b5b1..fa3b282a688 100644 --- a/src/sage/databases/findstat.py +++ b/src/sage/databases/findstat.py @@ -118,13 +118,14 @@ advertise yet another way to pass values to FindStat:: sage: r = findstat(Permutations, lambda pi: pi.saliances()[0], depth=2); r # optional -- internet - 0: St000740oMp00087 with offset 1 (quality [100, 100]) + 0: St000740oMp00066 with offset 1 (quality [100, 100]) + 1: St000740oMp00087 with offset 1 (quality [100, 100]) ... Note that some of the matches are up to a global offset. For example, we have:: - sage: r[0].info() # optional -- internet + sage: r[1].info() # optional -- internet after adding 1 to every value and applying Mp00087: inverse first fundamental transformation: Permutations -> Permutations @@ -431,10 +432,10 @@ def _get_json(url, **kwargs): EXAMPLES:: sage: from sage.databases.findstat import _get_json, FINDSTAT_API_MAPS - sage: _get_json(FINDSTAT_API_MAPS + "?xxx=yyy") # optional -- internet + sage: _get_json(FINDSTAT_API_MAPS + "?fields=yyy") # optional -- internet Traceback (most recent call last): ... - ValueError: E005: On filtering maps, the following parameters are not allowed: [u'xxx']. + ValueError: E018: Unknown fields in Map, Combinatorial map: to semistandard tableau via monotone triangles: ['yyy'] """ response = requests.get(url) if response.ok: @@ -454,10 +455,10 @@ def _post_json(url, data, **kwargs): EXAMPLES:: sage: from sage.databases.findstat import _post_json, FINDSTAT_API_STATISTICS - sage: _post_json(FINDSTAT_API_STATISTICS, {"xxx": "yyy"}) # optional -- internet + sage: _post_json(FINDSTAT_API_STATISTICS, {"fields": "yyy"}) # optional -- internet Traceback (most recent call last): ... - ValueError: E005: On filtering statistics, the following parameters are not allowed: ['xxx']. + ValueError: E018: Unknown fields in Statistic, Combinatorial statistic: St000001: ['yyy'] """ response = requests.post(url, data=data) if response.ok: From 14b858914fbe8de9aef016093192ae47bb04a57f Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Fri, 11 Nov 2022 18:37:13 +0100 Subject: [PATCH 441/632] remove OrderedDict --- src/sage/databases/findstat.py | 328 +++++++++++++++++---------------- 1 file changed, 166 insertions(+), 162 deletions(-) diff --git a/src/sage/databases/findstat.py b/src/sage/databases/findstat.py index fa3b282a688..92773dcc2a8 100644 --- a/src/sage/databases/findstat.py +++ b/src/sage/databases/findstat.py @@ -222,7 +222,6 @@ def mapping(sigma): from sage.databases.oeis import FancyTuple from ast import literal_eval -from collections import OrderedDict from copy import deepcopy import re import webbrowser @@ -1853,7 +1852,7 @@ def first_terms(self): if self._first_terms_cache is None: self._first_terms_cache = self._fetch_first_terms() # a shallow copy suffices - tuples are immutable - return OrderedDict(self._first_terms_cache) + return dict(self._first_terms_cache) def _first_terms_raw(self, max_values): """ @@ -2656,11 +2655,11 @@ def first_terms(self, max_values=FINDSTAT_MAX_SUBMISSION_VALUES): 0: St000041 (quality [99, 100]) 1: St000042 (quality [99, 100]) sage: r.first_terms() # optional -- internet - OrderedDict([([], 0), ([(1, 2)], 0)]) + {[]: 0, [(1, 2)]: 0} """ - return OrderedDict(itertools.islice(((objs[0], vals[0]) - for objs, vals in self._known_terms - if len(vals) == 1), max_values)) + return dict(itertools.islice(((objs[0], vals[0]) + for objs, vals in self._known_terms + if len(vals) == 1), max_values)) def _first_terms_raw(self, max_values): """ @@ -2694,7 +2693,7 @@ def _generating_functions_dict(self, sage: q = findstat(data, depth=0); q # optional -- internet 0: St000054 (quality [100, 100]) sage: q.first_terms() # optional -- internet - OrderedDict([([1, 2], 1)]) + {[1, 2]: 1} sage: q.generating_functions() # optional -- internet, indirect doctest {3: 2*q^3 + 2*q^2 + 2*q} """ @@ -4233,7 +4232,7 @@ def levels_with_sizes(self): sage: from sage.databases.findstat import FindStatCollection sage: cc = FindStatCollection("Perfect Matchings") # optional -- internet sage: cc.levels_with_sizes() # optional -- internet - OrderedDict([(2, 1), (4, 3), (6, 15), (8, 105), (10, 945)]) + {2: 1, 4: 3, 6: 15, 8: 105, 10: 945} """ return self._data["LevelsWithSizes"] @@ -4491,155 +4490,155 @@ def name(self, style="singular"): # this dictionary must be sorted so that subclasses come before # superclasses, eg., "StandardTableaux" before "SemistandardTableaux" -_SupportedFindStatCollections = OrderedDict([ - ("Permutations", - _SupportedFindStatCollection(lambda x: Permutation(literal_eval(x)), - str, - Permutations, - lambda x: x.size(), - lambda x: isinstance(x, Permutation))), - ("BinaryWords", - _SupportedFindStatCollection(lambda x: Word((int(e) for e in str(x)), alphabet=[0,1]), - str, - lambda x: Words([0,1], length=x), - lambda x: x.length(), - lambda x: isinstance(x, Word_class))), - ("AlternatingSignMatrices", - _SupportedFindStatCollection(lambda x: AlternatingSignMatrix(literal_eval(x)), - lambda x: str(list(map(list, x.to_matrix().rows()))), - AlternatingSignMatrices, - lambda x: x.to_matrix().nrows(), - lambda x: isinstance(x, AlternatingSignMatrix))), - ("BinaryTrees", - _SupportedFindStatCollection(lambda x: BinaryTree(str(x)), - str, - BinaryTrees, - lambda x: x.node_number(), - lambda x: isinstance(x, BinaryTree))), - ("Cores", - _SupportedFindStatCollection(lambda x: Core(*literal_eval(x)), - lambda X: "( " + X._repr_() + ", " + str(X.k()) + " )", - lambda x: Cores(x[1], x[0]), - lambda x: (x.length(), x.k()), - lambda x: isinstance(x, Core))), - ("DyckPaths", - _SupportedFindStatCollection(lambda x: DyckWord(literal_eval(x)), - lambda x: str(list(DyckWord(x))), - DyckWords, - lambda x: x.semilength(), - lambda x: isinstance(x, DyckWord))), - ("FiniteCartanTypes", - _SupportedFindStatCollection(lambda x: CartanType(*literal_eval(str(x))), - str, - _finite_irreducible_cartan_types_by_rank, - lambda x: x.rank(), - lambda x: isinstance(x, CartanType_abstract))), - ("GelfandTsetlinPatterns", - _SupportedFindStatCollection(lambda x: GelfandTsetlinPattern(literal_eval(x)), - str, - lambda x: (P - for la in Partitions(x[1], max_length=x[0]) - for P in GelfandTsetlinPatterns(top_row=la + [0]*(x[0]-len(la)))), - lambda x: (len(x[0]), sum(x[0])), - lambda x: (x == GelfandTsetlinPatterns - or isinstance(x, GelfandTsetlinPattern)))), - ("Graphs", - _SupportedFindStatCollection(lambda x: (lambda E, V: Graph([list(range(V)), - lambda i,j: (i,j) in E or (j,i) in E], - immutable=True))(*literal_eval(x)), - lambda X: str((X.edges(labels=False, sort=True), X.num_verts())), - lambda x: (g.copy(immutable=True) for g in graphs(x, copy=False)), - lambda x: x.num_verts(), - lambda x: isinstance(x, Graph))), - ("IntegerPartitions", - _SupportedFindStatCollection(lambda x: Partition(literal_eval(x)), - str, - Partitions, - lambda x: x.size(), - lambda x: isinstance(x, Partition))), - ("IntegerCompositions", - _SupportedFindStatCollection(lambda x: Composition(literal_eval(x)), - str, - Compositions, - lambda x: x.size(), - lambda x: isinstance(x, Composition))), - ("OrderedTrees", - _SupportedFindStatCollection(lambda x: OrderedTree(literal_eval(x)), - str, - OrderedTrees, - lambda x: x.node_number(), - lambda x: isinstance(x, OrderedTree))), - ("ParkingFunctions", - _SupportedFindStatCollection(lambda x: ParkingFunction(literal_eval(x)), - str, - ParkingFunctions, - len, - lambda x: isinstance(x, ParkingFunction))), - ("Lattices", - _SupportedFindStatCollection(lambda x: (lambda R, E: LatticePoset((list(range(E)), R)))(*literal_eval(x)), - lambda X: str((sorted(X._hasse_diagram.cover_relations()), - len(X._hasse_diagram.vertices(sort=False)))), - _finite_lattices, - lambda x: x.cardinality(), - lambda x: isinstance(x, FiniteLatticePoset))), - ("Posets", - _SupportedFindStatCollection(lambda x: (lambda R, E: Poset((list(range(E)), R)))(*literal_eval(x)), - lambda X: str((sorted(X._hasse_diagram.cover_relations()), - len(X._hasse_diagram.vertices(sort=False)))), - Posets, - lambda x: x.cardinality(), - lambda x: isinstance(x, FinitePoset))), - ("StandardTableaux", - _SupportedFindStatCollection(lambda x: StandardTableau(literal_eval(x)), - str, - StandardTableaux, - lambda x: x.size(), - lambda x: isinstance(x, StandardTableau))), - ("SemistandardTableaux", - _SupportedFindStatCollection(lambda x: SemistandardTableau(literal_eval(x)), - str, - lambda x: (T for T in SemistandardTableaux(size=x[0], max_entry=x[1]) - if max(T.entries()) == x[1]), - lambda x: (x.size(), max(x.entries())), - lambda x: isinstance(x, SemistandardTableau))), - ("PerfectMatchings", - _SupportedFindStatCollection(lambda x: PerfectMatching(literal_eval(x)), - str, - PerfectMatchings, - lambda x: x.size(), - lambda x: isinstance(x, PerfectMatching))), - ("SetPartitions", - _SupportedFindStatCollection(lambda x: SetPartition(literal_eval(x.replace('{','[').replace('}',']'))), - str, - SetPartitions, - lambda x: x.size(), - lambda x: isinstance(x, SetPartition))), - ("SkewPartitions", - _SupportedFindStatCollection(lambda x: SkewPartition(literal_eval(x)), - str, - SkewPartitions, - lambda x: x.size(), - lambda x: isinstance(x, SkewPartition))), - ("SignedPermutations", - _SupportedFindStatCollection(lambda x: SignedPermutations(len(literal_eval(x)))(list(literal_eval(x))), - str, - SignedPermutations, - lambda x: len(list(x)), - lambda x: isinstance(x, SignedPermutation))), - ("PlanePartitions", - _SupportedFindStatCollection(lambda x: PlanePartition(literal_eval(x)), - lambda X: str(list(X)).replace(" ",""), - _plane_partitions_by_size, - lambda x: sum(sum(la) for la in x), - lambda x: isinstance(x, PlanePartition))), - ("DecoratedPermutations", - _SupportedFindStatCollection(lambda x: DecoratedPermutation([v if v > 0 else (i if v == 0 else -i) - for i, v in enumerate(literal_eval(x.replace("+","0").replace("-","-1")), 1)]), - lambda x: "[" + ",".join((str(v) if abs(v) != i else ("+" if v > 0 else "-") - for i, v in enumerate(x, 1))) + "]", - DecoratedPermutations, - lambda x: x.size(), - lambda x: isinstance(x, DecoratedPermutation)))]) +_SupportedFindStatCollections = { + "Permutations": + _SupportedFindStatCollection(lambda x: Permutation(literal_eval(x)), + str, + Permutations, + lambda x: x.size(), + lambda x: isinstance(x, Permutation)), + "BinaryWords": + _SupportedFindStatCollection(lambda x: Word((int(e) for e in str(x)), alphabet=[0,1]), + str, + lambda x: Words([0,1], length=x), + lambda x: x.length(), + lambda x: isinstance(x, Word_class)), + "AlternatingSignMatrices": + _SupportedFindStatCollection(lambda x: AlternatingSignMatrix(literal_eval(x)), + lambda x: str(list(map(list, x.to_matrix().rows()))), + AlternatingSignMatrices, + lambda x: x.to_matrix().nrows(), + lambda x: isinstance(x, AlternatingSignMatrix)), + "BinaryTrees": + _SupportedFindStatCollection(lambda x: BinaryTree(str(x)), + str, + BinaryTrees, + lambda x: x.node_number(), + lambda x: isinstance(x, BinaryTree)), + "Cores": + _SupportedFindStatCollection(lambda x: Core(*literal_eval(x)), + lambda X: "( " + X._repr_() + ", " + str(X.k()) + " )", + lambda x: Cores(x[1], x[0]), + lambda x: (x.length(), x.k()), + lambda x: isinstance(x, Core)), + "DyckPaths": + _SupportedFindStatCollection(lambda x: DyckWord(literal_eval(x)), + lambda x: str(list(DyckWord(x))), + DyckWords, + lambda x: x.semilength(), + lambda x: isinstance(x, DyckWord)), + "FiniteCartanTypes": + _SupportedFindStatCollection(lambda x: CartanType(*literal_eval(str(x))), + str, + _finite_irreducible_cartan_types_by_rank, + lambda x: x.rank(), + lambda x: isinstance(x, CartanType_abstract)), + "GelfandTsetlinPatterns": + _SupportedFindStatCollection(lambda x: GelfandTsetlinPattern(literal_eval(x)), + str, + lambda x: (P + for la in Partitions(x[1], max_length=x[0]) + for P in GelfandTsetlinPatterns(top_row=la + [0]*(x[0]-len(la)))), + lambda x: (len(x[0]), sum(x[0])), + lambda x: (x == GelfandTsetlinPatterns + or isinstance(x, GelfandTsetlinPattern))), + "Graphs": + _SupportedFindStatCollection(lambda x: (lambda E, V: Graph([list(range(V)), + lambda i,j: (i,j) in E or (j,i) in E], + immutable=True))(*literal_eval(x)), + lambda X: str((X.edges(labels=False, sort=True), X.num_verts())), + lambda x: (g.copy(immutable=True) for g in graphs(x, copy=False)), + lambda x: x.num_verts(), + lambda x: isinstance(x, Graph)), + "IntegerPartitions": + _SupportedFindStatCollection(lambda x: Partition(literal_eval(x)), + str, + Partitions, + lambda x: x.size(), + lambda x: isinstance(x, Partition)), + "IntegerCompositions": + _SupportedFindStatCollection(lambda x: Composition(literal_eval(x)), + str, + Compositions, + lambda x: x.size(), + lambda x: isinstance(x, Composition)), + "OrderedTrees": + _SupportedFindStatCollection(lambda x: OrderedTree(literal_eval(x)), + str, + OrderedTrees, + lambda x: x.node_number(), + lambda x: isinstance(x, OrderedTree)), + "ParkingFunctions": + _SupportedFindStatCollection(lambda x: ParkingFunction(literal_eval(x)), + str, + ParkingFunctions, + len, + lambda x: isinstance(x, ParkingFunction)), + "Lattices": + _SupportedFindStatCollection(lambda x: (lambda R, E: LatticePoset((list(range(E)), R)))(*literal_eval(x)), + lambda X: str((sorted(X._hasse_diagram.cover_relations()), + len(X._hasse_diagram.vertices(sort=False)))), + _finite_lattices, + lambda x: x.cardinality(), + lambda x: isinstance(x, FiniteLatticePoset)), + "Posets": + _SupportedFindStatCollection(lambda x: (lambda R, E: Poset((list(range(E)), R)))(*literal_eval(x)), + lambda X: str((sorted(X._hasse_diagram.cover_relations()), + len(X._hasse_diagram.vertices(sort=False)))), + Posets, + lambda x: x.cardinality(), + lambda x: isinstance(x, FinitePoset)), + "StandardTableaux": + _SupportedFindStatCollection(lambda x: StandardTableau(literal_eval(x)), + str, + StandardTableaux, + lambda x: x.size(), + lambda x: isinstance(x, StandardTableau)), + "SemistandardTableaux": + _SupportedFindStatCollection(lambda x: SemistandardTableau(literal_eval(x)), + str, + lambda x: (T for T in SemistandardTableaux(size=x[0], max_entry=x[1]) + if max(T.entries()) == x[1]), + lambda x: (x.size(), max(x.entries())), + lambda x: isinstance(x, SemistandardTableau)), + "PerfectMatchings": + _SupportedFindStatCollection(lambda x: PerfectMatching(literal_eval(x)), + str, + PerfectMatchings, + lambda x: x.size(), + lambda x: isinstance(x, PerfectMatching)), + "SetPartitions": + _SupportedFindStatCollection(lambda x: SetPartition(literal_eval(x.replace('{','[').replace('}',']'))), + str, + SetPartitions, + lambda x: x.size(), + lambda x: isinstance(x, SetPartition)), + "SkewPartitions": + _SupportedFindStatCollection(lambda x: SkewPartition(literal_eval(x)), + str, + SkewPartitions, + lambda x: x.size(), + lambda x: isinstance(x, SkewPartition)), + "SignedPermutations": + _SupportedFindStatCollection(lambda x: SignedPermutations(len(literal_eval(x)))(list(literal_eval(x))), + str, + SignedPermutations, + lambda x: len(list(x)), + lambda x: isinstance(x, SignedPermutation)), + "PlanePartitions": + _SupportedFindStatCollection(lambda x: PlanePartition(literal_eval(x)), + lambda X: str(list(X)).replace(" ",""), + _plane_partitions_by_size, + lambda x: sum(sum(la) for la in x), + lambda x: isinstance(x, PlanePartition)), + "DecoratedPermutations": + _SupportedFindStatCollection(lambda x: DecoratedPermutation([v if v > 0 else (i if v == 0 else -i) + for i, v in enumerate(literal_eval(x.replace("+","0").replace("-","-1")), 1)]), + lambda x: "[" + ",".join((str(v) if abs(v) != i else ("+" if v > 0 else "-") + for i, v in enumerate(x, 1))) + "]", + DecoratedPermutations, + lambda x: x.size(), + lambda x: isinstance(x, DecoratedPermutation))} class FindStatCollections(UniqueRepresentation, Parent): @@ -4691,10 +4690,10 @@ def __init__(self): """ fields = "LevelsWithSizes,Name,NamePlural,NameWiki" url = FINDSTAT_API_COLLECTIONS + "?fields=" + fields - d = _get_json(url, object_pairs_hook=OrderedDict)["included"]["Collections"] + d = _get_json(url, object_pairs_hook=dict)["included"]["Collections"] for id, data in d.items(): - data["LevelsWithSizes"] = OrderedDict((literal_eval(level), size) - for level, size in data["LevelsWithSizes"].items()) + data["LevelsWithSizes"] = {literal_eval(level): size + for level, size in data["LevelsWithSizes"].items()} if data["NameWiki"] in _SupportedFindStatCollections: data["Code"] = _SupportedFindStatCollections[data["NameWiki"]] else: @@ -4713,8 +4712,7 @@ def position(item): except ValueError: return len(_SupportedFindStatCollections) - self._findstat_collections = OrderedDict(sorted(d.items(), - key=position)) + self._findstat_collections = dict(sorted(d.items(), key=position)) Parent.__init__(self, category=Sets()) def _element_constructor_(self, entry): @@ -4783,6 +4781,12 @@ def _element_constructor_(self, entry): sage: len(cc.first_terms(lambda x: x.edges(labels=False, sort=False)).list()) # optional -- internet 4 + + Check that we can override the automatic detection:: + + sage: qu = findstat("Semistandardtableaux", [(T, len(T)) for n in range(1,5) for T in StandardTableaux(n)], depth=1) + sage: qu[0] + St000010oMp00077 (quality [100, 100]) """ if isinstance(entry, self.Element): return entry From 0ac99053f878bf0396b2cad71491ef804f69e520 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 11 Nov 2022 10:51:58 -0800 Subject: [PATCH 442/632] build/pkgs/cypari: Update to 2.1.3 --- build/pkgs/cypari/checksums.ini | 6 +++--- build/pkgs/cypari/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/cypari/checksums.ini b/build/pkgs/cypari/checksums.ini index d81625b5867..998c8a5e962 100644 --- a/build/pkgs/cypari/checksums.ini +++ b/build/pkgs/cypari/checksums.ini @@ -1,5 +1,5 @@ tarball=cypari2-VERSION.tar.gz -sha1=2a3039aa6bd690206cb58d4c39aef21e736eacf1 -md5=6267c0dace847160763dc1777a390c0a -cksum=3639046443 +sha1=7208fd9d0b636ca7704d3b7d1167bc7c9af2637c +md5=605b157123bd8a498d53572e8096bb8c +cksum=2809951255 upstream_url=https://pypi.io/packages/source/c/cypari2/cypari2-VERSION.tar.gz diff --git a/build/pkgs/cypari/package-version.txt b/build/pkgs/cypari/package-version.txt index eca07e4c1a8..ac2cdeba013 100644 --- a/build/pkgs/cypari/package-version.txt +++ b/build/pkgs/cypari/package-version.txt @@ -1 +1 @@ -2.1.2 +2.1.3 From 0da93be72bf6457fb26301742449c38b06543285 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Fri, 11 Nov 2022 11:53:02 -0800 Subject: [PATCH 443/632] trac 34741: suppress warnings about "dylib ... was built for newer macOS ..." during doctesting. --- src/sage/doctest/parsing.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sage/doctest/parsing.py b/src/sage/doctest/parsing.py index 27fb25a273a..7c54a17fd93 100644 --- a/src/sage/doctest/parsing.py +++ b/src/sage/doctest/parsing.py @@ -1022,6 +1022,11 @@ def do_fixup(self, want, got): got = chained_fixup_warning_regex.sub('', got) did_fixup = True + if "newer macOS version" in got: + newer_macOS_version_regex = re.compile(r'.*dylib \(.*\) was built for newer macOS version \(.*\) than being linked \(.*\)') + got = newer_macOS_version_regex.sub('', got) + did_fixup = True + if "insufficient permissions" in got: sympow_cache_warning_regex = re.compile(r'\*\*WARNING\*\* /var/cache/sympow/datafiles/le64 yields insufficient permissions') got = sympow_cache_warning_regex.sub('', got) From f185d689989a661cf77e22471a56277f61a18dd5 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 11 Nov 2022 13:48:30 -0800 Subject: [PATCH 444/632] .github/workflows/ci-macos.yml: Update tested macos versions, remove test of outdated python from python.org --- .github/workflows/ci-macos.yml | 29 +++-------------------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/.github/workflows/ci-macos.yml b/.github/workflows/ci-macos.yml index d2df2268199..1921fc0d066 100644 --- a/.github/workflows/ci-macos.yml +++ b/.github/workflows/ci-macos.yml @@ -42,12 +42,8 @@ jobs: # python3_xcode is only accepted if enough packages are available from the system # --> to test "minimal", we would need https://trac.sagemath.org/ticket/30949 tox_env: [homebrew-macos-usrlocal-minimal, homebrew-macos-usrlocal-standard, homebrew-macos-usrlocal-maximal, homebrew-macos-usrlocal-python3_xcode-standard, conda-forge-macos-minimal, conda-forge-macos-standard, conda-forge-macos-maximal] - # As of 2021-12, default xcode - # - on macos-10.15: 12.4 - # - on macos-latest (= macos-11): 13.1 - # https://github.com/actions/virtual-environments/blob/main/images/macos/macos-10.15-Readme.md#xcode xcode_version_factor: [default] - os: [ macos-10.15, macos-latest ] + os: [ macos-11, macos-12 ] env: TOX_ENV: local-${{ matrix.tox_env }} LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-tox-local-${{ matrix.tox_env }}-${{ matrix.os }}-xcode_${{ matrix.xcode_version_factor }} @@ -158,22 +154,10 @@ jobs: fail-fast: false max-parallel: 4 matrix: - os: [ macos-10.15, macos-11.0 ] - tox_system_factor: [macos-nobootstrap, macos-nobootstrap-python3_pythonorg] + os: [ macos-11, macos-12 ] + tox_system_factor: [macos-nobootstrap] tox_packages_factor: [minimal] - # As of 2021-03, default is 12.4 - # https://github.com/actions/virtual-environments/blob/main/images/macos/macos-10.15-Readme.md#xcode xcode_version_factor: [default] - include: - # Test xcode 11.7 only on macos-10.15 - - tox_system_factor: macos-nobootstrap - tox_packages_factor: minimal - xcode_version_factor: 11.7 - os: macos-10.15 - - tox_system_factor: macos-nobootstrap-python3_pythonorg - tox_packages_factor: minimal - xcode_version_factor: 11.7 - os: macos-10.15 env: TOX_ENV: local-${{ matrix.tox_system_factor }}-${{ matrix.tox_packages_factor }} LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-tox-local-${{ matrix.tox_system_factor }}-${{ matrix.tox_packages_factor }}-xcode_${{ matrix.xcode_version_factor }} @@ -198,13 +182,6 @@ jobs: - name: Install test prerequisites run: | sudo /usr/bin/python3 -m pip install tox - - name: Install python3 from python.org - # As of 2020-03-30 (https://github.com/actions/virtual-environments/blob/master/images/macos/macos-10.15-Readme.md), - # Python 3.7.7 is installed on GitHub Actions runners. But we install our own copy from the python.org binary package. - run: | - curl -o python3.pkg https://www.python.org/ftp/python/3.7.7/python-3.7.7-macosx10.9.pkg - sudo installer -verbose -pkg python3.pkg -target / - if: contains(matrix.tox_system_factor, 'python3_pythonorg') - name: Build and test with tox # We use a high parallelization on purpose in order to catch possible parallelization bugs in the build scripts. # For doctesting, we use a lower parallelization to avoid timeouts. From 278058b3ac0d3862c82c431d1c6dbe4dbdd8ed8c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 11 Nov 2022 19:06:22 -0800 Subject: [PATCH 445/632] build/pkgs/topcom/spkg-install.in: Do not run autotools --- build/pkgs/topcom/spkg-install.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build/pkgs/topcom/spkg-install.in b/build/pkgs/topcom/spkg-install.in index 53eb50b1b98..561925248d7 100644 --- a/build/pkgs/topcom/spkg-install.in +++ b/build/pkgs/topcom/spkg-install.in @@ -1,4 +1,9 @@ cd src + +# Avoid running autotools. Patch no_vendor_cddlib.patch makes changes +# both to input and output. +touch configure Makefile.in + ./configure \ --prefix="$SAGE_LOCAL" \ --libdir="$SAGE_LOCAL/lib" \ From 91704bbd12bdb01c4137d1d0dd51b68499258d26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 12 Nov 2022 09:34:06 +0100 Subject: [PATCH 446/632] switch back to --- src/sage/symbolic/expression.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/symbolic/expression.pyx b/src/sage/symbolic/expression.pyx index e59469135f0..5efc0d792bc 100644 --- a/src/sage/symbolic/expression.pyx +++ b/src/sage/symbolic/expression.pyx @@ -13725,7 +13725,7 @@ cpdef new_Expression(parent, x): exp = x elif isinstance(x, int): try: - exp = GEx(x) + exp = GEx(x) except OverflowError: exp = x elif x is infinity: From 54d036f01b50958dd224ed5d8791b88fdd31c527 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sat, 12 Nov 2022 09:55:26 +0100 Subject: [PATCH 447/632] add forgotten # optional -- internet --- src/sage/databases/findstat.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/databases/findstat.py b/src/sage/databases/findstat.py index 92773dcc2a8..0eff62eba25 100644 --- a/src/sage/databases/findstat.py +++ b/src/sage/databases/findstat.py @@ -4784,8 +4784,9 @@ def _element_constructor_(self, entry): Check that we can override the automatic detection:: - sage: qu = findstat("Semistandardtableaux", [(T, len(T)) for n in range(1,5) for T in StandardTableaux(n)], depth=1) - sage: qu[0] + sage: l = [(T, len(T)) for n in range(1,5) for T in StandardTableaux(n)] # optional -- internet + sage: qu = findstat("Semistandardtableaux", l, depth=1) # optional -- internet + sage: qu[0] # optional -- internet St000010oMp00077 (quality [100, 100]) """ if isinstance(entry, self.Element): From 712322b42271ce01ec3e2bf2ab74a332d5db7db9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 12 Nov 2022 21:39:33 +0100 Subject: [PATCH 448/632] modernize super in root_systems --- src/sage/combinat/root_system/cartan_type.py | 10 +++---- .../combinat/root_system/coxeter_matrix.py | 4 +-- src/sage/combinat/root_system/coxeter_type.py | 21 +++++++------ src/sage/combinat/root_system/fusion_ring.py | 11 +++---- .../non_symmetric_macdonald_polynomials.py | 9 +++--- .../combinat/root_system/pieri_factors.py | 10 +++---- .../root_system/reflection_group_complex.py | 30 +++++++++---------- src/sage/combinat/root_system/root_system.py | 11 ++++--- .../combinat/root_system/type_A_infinity.py | 19 +++++------- src/sage/combinat/root_system/type_dual.py | 10 +++---- src/sage/combinat/root_system/type_folded.py | 6 ++-- src/sage/combinat/root_system/weight_space.py | 16 +++++----- .../combinat/root_system/weyl_characters.py | 12 ++++---- src/sage/combinat/root_system/weyl_group.py | 4 +-- 14 files changed, 83 insertions(+), 90 deletions(-) diff --git a/src/sage/combinat/root_system/cartan_type.py b/src/sage/combinat/root_system/cartan_type.py index dafe2f62366..f78be79bab5 100644 --- a/src/sage/combinat/root_system/cartan_type.py +++ b/src/sage/combinat/root_system/cartan_type.py @@ -465,7 +465,7 @@ .. TODO:: Should those indexes come before the introduction? """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2007 Mike Hansen , # Copyright (C) 2008-2009 Nicolas M. Thiery , # @@ -473,8 +473,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.misc.cachefunc import cached_method from sage.misc.abstract_method import abstract_method @@ -1875,7 +1875,7 @@ def _ascii_art_node(self, label): if (label == self.special_node() and self.options('mark_special_node') in ['printing', 'both']): return self.options('special_node_str') - return super(CartanType_affine, self)._ascii_art_node(label) + return super()._ascii_art_node(label) def _latex_draw_node(self, x, y, label, position="below=4pt"): r""" @@ -1896,7 +1896,7 @@ def _latex_draw_node(self, x, y, label, position="below=4pt"): fill = 'black' else: fill = 'white' - return super(CartanType_affine, self)._latex_draw_node(x, y, label, position, fill) + return super()._latex_draw_node(x, y, label, position, fill) def is_finite(self): """ diff --git a/src/sage/combinat/root_system/coxeter_matrix.py b/src/sage/combinat/root_system/coxeter_matrix.py index 8eb2f743fa5..9ae647552e2 100644 --- a/src/sage/combinat/root_system/coxeter_matrix.py +++ b/src/sage/combinat/root_system/coxeter_matrix.py @@ -205,7 +205,7 @@ def __classcall_private__(cls, data=None, index_set=None, coxeter_type=None, else: index_set = tuple(range(1, n + 1)) if len(set(index_set)) != n: - raise ValueError("the given index set is not valid") + raise ValueError("the given index set is not valid") return cls._from_matrix(data, coxeter_type, index_set, coxeter_type_check) @@ -653,7 +653,7 @@ def _repr_option(self, key): """ if key == 'ascii_art' or key == 'element_ascii_art': return self._matrix.nrows() > 1 - return super(CoxeterMatrix, self)._repr_option(key) + return super()._repr_option(key) def _latex_(self): r""" diff --git a/src/sage/combinat/root_system/coxeter_type.py b/src/sage/combinat/root_system/coxeter_type.py index 9507858663a..9c10c3c04db 100644 --- a/src/sage/combinat/root_system/coxeter_type.py +++ b/src/sage/combinat/root_system/coxeter_type.py @@ -1,7 +1,7 @@ """ Coxeter Types """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2015 Travis Scrimshaw , # 2015 Jean-Philippe Labbe , # @@ -14,8 +14,8 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.abstract_method import abstract_method from sage.misc.cachefunc import cached_method @@ -54,7 +54,7 @@ def __classcall_private__(cls, *x): except (ValueError, TypeError): pass - if len(x) == 1: # In case the input is similar to CoxeterType([['A',2]]) + if len(x) == 1: # In case the input is similar to CoxeterType([['A',2]]) return CoxeterType(x[0]) raise NotImplementedError("Coxeter types not from Cartan types not yet implemented") @@ -173,12 +173,12 @@ def _samples(self): Coxeter type of ['F', 4, 1], Coxeter type of ['G', 2, 1], Coxeter type of ['A', 1, 1]] """ - finite = [CoxeterType(t) for t in [['A', 1], ['A', 5], ['B', 1], ['B', 5], - ['C', 1], ['C', 5], ['D', 4], ['D', 5], - ['E', 6], ['E', 7], ['E', 8], ['F', 4], - ['H', 3], ['H', 4], ['I', 10]]] + finite = [CoxeterType(t) for t in [['A', 1], ['A', 5], ['B', 1], ['B', 5], + ['C', 1], ['C', 5], ['D', 4], ['D', 5], + ['E', 6], ['E', 7], ['E', 8], ['F', 4], + ['H', 3], ['H', 4], ['I', 10]]] - affine = [CoxeterType(t) for t in [['A', 2, 1], ['B', 5, 1], + affine = [CoxeterType(t) for t in [['A', 2, 1], ['B', 5, 1], ['C', 5, 1], ['D', 5, 1], ['E', 6, 1], ['E', 7, 1], ['E', 8, 1], ['F', 4, 1], ['G', 2, 1], ['A', 1, 1]]] @@ -433,8 +433,7 @@ def __classcall_private__(cls, cartan_type): sage: C1 is C2 True """ - return super(CoxeterTypeFromCartanType, cls).__classcall__(cls, - CartanType(cartan_type)) + return super().__classcall__(cls, CartanType(cartan_type)) def __init__(self, cartan_type): """ diff --git a/src/sage/combinat/root_system/fusion_ring.py b/src/sage/combinat/root_system/fusion_ring.py index b261550db21..9df6a3b870e 100644 --- a/src/sage/combinat/root_system/fusion_ring.py +++ b/src/sage/combinat/root_system/fusion_ring.py @@ -20,6 +20,7 @@ from sage.rings.number_field.number_field import CyclotomicField from sage.misc.cachefunc import cached_method + class FusionRing(WeylCharacterRing): r""" Return the Fusion Ring (Verlinde Algebra) of level ``k``. @@ -318,10 +319,10 @@ def __classcall__(cls, ct, k, base_ring=ZZ, prefix=None, style="coroots", conjug sage: E81 = FusionRing('E8', 1) sage: TestSuite(E81).run() """ - return super(FusionRing, cls).__classcall__(cls, ct, base_ring=base_ring, - prefix=prefix, style=style, k=k, - conjugate=conjugate, - cyclotomic_order=cyclotomic_order) + return super().__classcall__(cls, ct, base_ring=base_ring, + prefix=prefix, style=style, k=k, + conjugate=conjugate, + cyclotomic_order=cyclotomic_order) def _test_verlinde(self, **options): """ @@ -337,7 +338,7 @@ def _test_verlinde(self, **options): i0 = self.one() from sage.misc.misc import some_tuples B = self.basis() - for x,y,z in some_tuples(B, 3, tester._max_runs): + for x, y, z in some_tuples(B, 3, tester._max_runs): v = sum(self.s_ij(x,w) * self.s_ij(y,w) * self.s_ij(z,w) / self.s_ij(i0,w) for w in B) tester.assertEqual(v, c * self.N_ijk(x,y,z)) diff --git a/src/sage/combinat/root_system/non_symmetric_macdonald_polynomials.py b/src/sage/combinat/root_system/non_symmetric_macdonald_polynomials.py index 2e52b7fd7ee..74b5ff34eec 100644 --- a/src/sage/combinat/root_system/non_symmetric_macdonald_polynomials.py +++ b/src/sage/combinat/root_system/non_symmetric_macdonald_polynomials.py @@ -1203,7 +1203,7 @@ def __classcall__(cls, KL, q='q', q1='q1', q2='q2', normalized=True): q = K(q) q1 = K(q1) q2 = K(q2) - return super(NonSymmetricMacdonaldPolynomials, cls).__classcall__(cls, KL, q, q1, q2, normalized) + return super().__classcall__(cls, KL, q, q1, q2, normalized) def __init__(self, KL, q, q1, q2, normalized): r""" @@ -1581,12 +1581,11 @@ def __getitem__(self, mu): """ muaff = self._L.embed_at_level(mu, 0) if not all(muaff.scalar(coroot) in ZZ for coroot in self._L.simple_coroots()): - raise ValueError("%s does not lift to a level 0 element of the affine weight lattice"%muaff) - return super(NonSymmetricMacdonaldPolynomials, self).__getitem__(mu) - + raise ValueError("%s does not lift to a level 0 element of the affine weight lattice" % muaff) + return super().__getitem__(mu) @cached_method - def rho_prime(self): # Should be rho_prime_check + def rho_prime(self): # Should be rho_prime_check r""" Return the level 0 sum of the classical fundamental weights in `L'`. diff --git a/src/sage/combinat/root_system/pieri_factors.py b/src/sage/combinat/root_system/pieri_factors.py index c2e098af316..e91fc9b0948 100644 --- a/src/sage/combinat/root_system/pieri_factors.py +++ b/src/sage/combinat/root_system/pieri_factors.py @@ -7,8 +7,8 @@ # Nicolas M. Thiery # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#****************************************************************************** +# https://www.gnu.org/licenses/ +# ***************************************************************************** from sage.misc.cachefunc import cached_method from sage.misc.constant_function import ConstantFunction @@ -325,7 +325,7 @@ def maximal_elements(self): ct_aff = ct.dual().affine() - max_elts_affine = WeylGroup(ct_aff).pieri_factors().maximal_elements() + max_elts_affine = WeylGroup(ct_aff).pieri_factors().maximal_elements() for w in max_elts_affine: if 0 not in w.reduced_word(): @@ -542,7 +542,7 @@ def __classcall__(cls, W, min_length=0, max_length=infinity, max_support = frozenset(max_support) min_length = max(min_length, len(min_support)) max_length = min(len(max_support), max_length, len(W.index_set()) - 1) - return super(PieriFactors_type_A_affine, cls).__classcall__(cls, W, min_length, max_length, min_support, max_support) + return super().__classcall__(cls, W, min_length, max_length, min_support, max_support) def __init__(self, W, min_length, max_length, min_support, max_support): r""" @@ -656,7 +656,7 @@ def _test_maximal_elements(self, **options): if self._min_length > 0 or self._max_length < len(self.W.index_set())-1 or self._max_support != frozenset(index_set): tester.info("\n Strict subset of the Pieri factors; skipping test") return - return super(PieriFactors_type_A_affine, self)._test_maximal_elements(**options) + return super()._test_maximal_elements(**options) def __contains__(self, w): r""" diff --git a/src/sage/combinat/root_system/reflection_group_complex.py b/src/sage/combinat/root_system/reflection_group_complex.py index 8ae19776db4..bebb3dc9f04 100644 --- a/src/sage/combinat/root_system/reflection_group_complex.py +++ b/src/sage/combinat/root_system/reflection_group_complex.py @@ -185,15 +185,15 @@ - Christian Stump (2015): initial version """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2011-2016 Christian Stump # # 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.misc.cachefunc import cached_method, cached_function from sage.misc.misc_c import prod @@ -236,26 +236,26 @@ def __init__(self, W_types, index_set=None, hyperplane_index_set=None, reflectio W_components = [] reflection_type = [] for W_type in W_types: - if W_type == (1,1,1): + if W_type == (1, 1, 1): raise ValueError("the one element group is not considered a reflection group") elif W_type in ZZ: - call_str = 'ComplexReflectionGroup(%s)'%W_type - elif isinstance(W_type,CartanMatrix): - call_str = 'PermRootGroup(IdentityMat(%s),%s)'%(W_type._rank,str(W_type._M._gap_())) + call_str = 'ComplexReflectionGroup(%s)' % W_type + elif isinstance(W_type, CartanMatrix): + call_str = 'PermRootGroup(IdentityMat(%s),%s)' % (W_type._rank, str(W_type._M._gap_())) elif is_Matrix(W_type): - call_str = 'PermRootGroup(IdentityMat(%s),%s)'%(W_type._rank,str(W_type._gap_())) - elif W_type in ZZ or ( isinstance(W_type, tuple) and len(W_type) == 3 ): - call_str = 'ComplexReflectionGroup%s'%str(W_type) + call_str = 'PermRootGroup(IdentityMat(%s),%s)' % (W_type._rank, str(W_type._gap_())) + elif W_type in ZZ or (isinstance(W_type, tuple) and len(W_type) == 3): + call_str = 'ComplexReflectionGroup%s' % str(W_type) else: if W_type[0] == "I": - call_str = 'CoxeterGroup("I",2,%s)'%W_type[1] + call_str = 'CoxeterGroup("I",2,%s)' % W_type[1] else: - call_str = 'CoxeterGroup("%s",%s)'%W_type + call_str = 'CoxeterGroup("%s",%s)' % W_type W_components.append(gap3(call_str)) X = list(W_components[-1].ReflectionType()) if len(X) > 1: - raise ValueError("input data %s is invalid"%W_type) + raise ValueError("input data %s is invalid" % W_type) X = X[0] type_dict = {} type_dict["series"] = X.series.sage() @@ -1389,7 +1389,7 @@ def braid_relations(self): [[[1, 2, 1], [2, 1, 2]], [[1, 3], [3, 1]], [[2, 3, 2], [3, 2, 3]]] """ if self.is_real(): - return super(ComplexReflectionGroup,self).braid_relations() + return super().braid_relations() else: return self._gap_group.BraidRelations().sage() @@ -1910,7 +1910,7 @@ def coxeter_number(self, chi=None): 24 """ if chi is None: - return super(ComplexReflectionGroup, self).coxeter_number() + return super().coxeter_number() G = self.gens() cox_chi = 0 diff --git a/src/sage/combinat/root_system/root_system.py b/src/sage/combinat/root_system/root_system.py index 9f4764f9d23..4112165885e 100644 --- a/src/sage/combinat/root_system/root_system.py +++ b/src/sage/combinat/root_system/root_system.py @@ -4,7 +4,7 @@ See :ref:`sage.combinat.root_system.all` for an overview. """ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2007 Mike Hansen , # Justin Walker # 2008-2009 Nicolas M. Thiery @@ -12,8 +12,8 @@ # Distributed under the terms of the GNU General Public License (GPL) # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# *************************************************************************** # Design largely inspired from MuPAD-Combinat from sage.structure.sage_object import SageObject from sage.structure.unique_representation import UniqueRepresentation @@ -311,7 +311,7 @@ def __classcall__(cls, cartan_type, as_dual_of=None): sage: RootSystem(["B",3], as_dual_of=None) is RootSystem("B3") True """ - return super(RootSystem, cls).__classcall__(cls, CartanType(cartan_type), as_dual_of) + return super().__classcall__(cls, CartanType(cartan_type), as_dual_of) def __init__(self, cartan_type, as_dual_of=None): """ @@ -338,7 +338,6 @@ def __init__(self, cartan_type, as_dual_of=None): self.dual_side = True self.dual = as_dual_of - def _test_root_lattice_realizations(self, **options): """ Runs tests on all the root lattice realizations of this root @@ -507,7 +506,7 @@ def root_poset(self, restricted=False, facade=False): sage: Phi.cover_relations() [[alpha[2], alpha[1] + alpha[2]], [alpha[1], alpha[1] + alpha[2]], [alpha[1] + alpha[2], alpha[1] + 2*alpha[2]]] """ - return self.root_lattice().root_poset(restricted=restricted,facade=facade) + return self.root_lattice().root_poset(restricted=restricted, facade=facade) def coroot_lattice(self): """ diff --git a/src/sage/combinat/root_system/type_A_infinity.py b/src/sage/combinat/root_system/type_A_infinity.py index 21237bc48c8..ddd0b352d1f 100644 --- a/src/sage/combinat/root_system/type_A_infinity.py +++ b/src/sage/combinat/root_system/type_A_infinity.py @@ -1,15 +1,12 @@ """ Root system data for type A infinity """ - -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2016 Andrew Mathas # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#***************************************************************************** - - +# https://www.gnu.org/licenses/ +# *************************************************************************** from .cartan_type import CartanType_standard, CartanType_simple from sage.rings.integer_ring import ZZ @@ -57,7 +54,7 @@ def __init__(self, index_set): sage: TestSuite(ct).run() """ - super(CartanType, self).__init__() + super().__init__() self.letter = 'A' self.n = index_set @@ -106,11 +103,11 @@ def ascii_art(self, label=lambda i: i, node=None): node = self._ascii_art_node if self.n == ZZ: - ret = '..---'+'---'.join(node(label(i)) for i in range(7))+'---..\n' - ret += ' '+''.join("{:4}".format(label(i)) for i in range(-3,4)) + ret = '..---' + '---'.join(node(label(i)) for i in range(7)) + '---..\n' + ret += ' ' + ''.join("{:4}".format(label(i)) for i in range(-3, 4)) else: - ret = '---'.join(node(label(i)) for i in range(7))+'---..\n' - ret += '0'+''.join("{:4}".format(label(i)) for i in range(1,7)) + ret = '---'.join(node(label(i)) for i in range(7)) + '---..\n' + ret += '0' + ''.join("{:4}".format(label(i)) for i in range(1, 7)) return ret diff --git a/src/sage/combinat/root_system/type_dual.py b/src/sage/combinat/root_system/type_dual.py index 3c9e48397fe..ac02044aedf 100644 --- a/src/sage/combinat/root_system/type_dual.py +++ b/src/sage/combinat/root_system/type_dual.py @@ -675,20 +675,20 @@ def _default_folded_cartan_type(self): """ from sage.combinat.root_system.type_folded import CartanTypeFolded letter = self._type.type() - if letter == 'BC': # A_{2n}^{(2)\dagger} + if letter == 'BC': # A_{2n}^{(2)\dagger} n = self._type.classical().rank() return CartanTypeFolded(self, ['A', 2*n - 1, 1], [[0]] + [[i, 2*n-i] for i in range(1, n)] + [[n]]) - if letter == 'B': # A_{2n-1}^{(2)} + if letter == 'B': # A_{2n-1}^{(2)} n = self._type.classical().rank() return CartanTypeFolded(self, ['D', n + 1, 1], [[i] for i in range(n)] + [[n, n+1]]) - if letter == 'C': # D_{n+1}^{(2)} + if letter == 'C': # D_{n+1}^{(2)} n = self._type.classical().rank() return CartanTypeFolded(self, ['A', 2*n-1, 1], [[0]] + [[i, 2*n-i] for i in range(1, n)] + [[n]]) - if letter == 'F': # E_6^{(2)} + if letter == 'F': # E_6^{(2)} return CartanTypeFolded(self, ['E', 6, 1], [[0], [2], [4], [3, 5], [1, 6]]) - if letter == 'G': # D_4^{(3)} + if letter == 'G': # D_4^{(3)} return CartanTypeFolded(self, ['D', 4, 1], [[0], [1, 3, 4], [2]]) return super(CartanType, self)._default_folded_cartan_type() diff --git a/src/sage/combinat/root_system/type_folded.py b/src/sage/combinat/root_system/type_folded.py index b8ef07d88e2..bfb0d1513c6 100644 --- a/src/sage/combinat/root_system/type_folded.py +++ b/src/sage/combinat/root_system/type_folded.py @@ -178,13 +178,13 @@ def __classcall_private__(cls, cartan_type, virtual, orbit): virtual = CartanType(virtual) if isinstance(orbit, dict): i_set = cartan_type.index_set() - orb = [None]*len(i_set) - for k,v in orbit.items(): + orb = [None] * len(i_set) + for k, v in orbit.items(): orb[i_set.index(k)] = tuple(v) orbit = tuple(orb) else: orbit = tuple(map(tuple, orbit)) - return super(CartanTypeFolded, cls).__classcall__(cls, cartan_type, virtual, orbit) + return super().__classcall__(cls, cartan_type, virtual, orbit) def __init__(self, cartan_type, folding_of, orbit): """ diff --git a/src/sage/combinat/root_system/weight_space.py b/src/sage/combinat/root_system/weight_space.py index 03409e334f9..ab59db061fe 100644 --- a/src/sage/combinat/root_system/weight_space.py +++ b/src/sage/combinat/root_system/weight_space.py @@ -1,12 +1,12 @@ """ Weight lattices and weight spaces """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2008-2009 Nicolas M. Thiery # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.cachefunc import cached_method from sage.sets.family import Family @@ -14,6 +14,7 @@ from .weight_lattice_realizations import WeightLatticeRealizations import functools + class WeightSpace(CombinatorialFreeModule): r""" INPUT: @@ -152,8 +153,7 @@ def __classcall_private__(cls, root_system, base_ring, extended=False): sage: WeightSpace(R, QQ) is WeightSpace(R, QQ, False) True """ - return super(WeightSpace, cls).__classcall__(cls, root_system, base_ring, extended) - + return super().__classcall__(cls, root_system, base_ring, extended) def __init__(self, root_system, base_ring, extended): """ @@ -383,8 +383,7 @@ def _repr_term(self, m): """ if m == "delta": return "deltacheck" if self.root_system.dual_side else "delta" - else: - return super(WeightSpace, self)._repr_term(m) + return super()._repr_term(m) def _latex_term(self, m): r""" @@ -402,8 +401,7 @@ def _latex_term(self, m): """ if m == "delta": return "\\delta^\\vee" if self.root_system.dual_side else "\\delta" - else: - return super(WeightSpace, self)._latex_term(m) + return super()._latex_term(m) @cached_method def _to_classical_on_basis(self, i): diff --git a/src/sage/combinat/root_system/weyl_characters.py b/src/sage/combinat/root_system/weyl_characters.py index af95fbce583..ec4afd893d1 100644 --- a/src/sage/combinat/root_system/weyl_characters.py +++ b/src/sage/combinat/root_system/weyl_characters.py @@ -103,10 +103,10 @@ def __classcall__(cls, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, c ct = CartanType(ct) if prefix is None: if ct.is_atomic(): - prefix = ct[0]+str(ct[1]) + prefix = ct[0] + str(ct[1]) else: prefix = repr(ct) - return super(WeylCharacterRing, cls).__classcall__(cls, ct, base_ring=base_ring, prefix=prefix, style=style, k=k, conjugate=conjugate, cyclotomic_order=cyclotomic_order) + return super().__classcall__(cls, ct, base_ring=base_ring, prefix=prefix, style=style, k=k, conjugate=conjugate, cyclotomic_order=cyclotomic_order) def __init__(self, ct, base_ring=ZZ, prefix=None, style="lattice", k=None, conjugate=False, cyclotomic_order=None): """ @@ -170,7 +170,7 @@ def next_level(wt): self._m_g = 2 else: self._m_g = 3 - if ct[0] in ['B','F']: + if ct[0] in ['B', 'F']: self._nf = 2 else: self._nf = 1 @@ -448,7 +448,7 @@ def __call__(self, *args): # object which can't be coerced into self if len(args) > 1: args = (args,) - return super(WeylCharacterRing, self).__call__(*args) + return super().__call__(*args) def _element_constructor_(self, weight): """ @@ -1733,7 +1733,7 @@ def __classcall__(cls, parent, prefix=None): sage: a3.cartan_type(), a3.base_ring(), a3.parent() (['A', 3], Integer Ring, The Weyl Character Ring of Type A3 with Integer Ring coefficients) """ - return super(WeightRing, cls).__classcall__(cls, parent, prefix=prefix) + return super().__classcall__(cls, parent, prefix=prefix) def __init__(self, parent, prefix): """ @@ -1818,7 +1818,7 @@ def __call__(self, *args): # object which can't be coerced into self if len(args) > 1: args = (args,) - return super(WeightRing, self).__call__(*args) + return super().__call__(*args) def _element_constructor_(self, weight): """ diff --git a/src/sage/combinat/root_system/weyl_group.py b/src/sage/combinat/root_system/weyl_group.py index efcdce97384..a8c13419c4b 100644 --- a/src/sage/combinat/root_system/weyl_group.py +++ b/src/sage/combinat/root_system/weyl_group.py @@ -217,7 +217,7 @@ class WeylGroup_gens(UniqueRepresentation, @staticmethod def __classcall__(cls, domain, prefix=None): - return super(WeylGroup_gens, cls).__classcall__(cls, domain, prefix) + return super().__classcall__(cls, domain, prefix) def __init__(self, domain, prefix): """ @@ -1016,7 +1016,7 @@ def __classcall__(cls, cartan_type, prefix=None): sage: W1 is W2 True """ - return super(WeylGroup_permutation, cls).__classcall__(cls, CartanType(cartan_type), prefix) + return super().__classcall__(cls, CartanType(cartan_type), prefix) def __init__(self, cartan_type, prefix): """ From 30baa084e97e47798795f13dccd279464aca308f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 12 Nov 2022 21:45:37 +0100 Subject: [PATCH 449/632] modern super in root_systems, step 2 --- src/sage/combinat/root_system/ambient_space.py | 6 +++--- src/sage/combinat/root_system/associahedron.py | 6 +++--- src/sage/combinat/root_system/coxeter_group.py | 7 ++++--- .../root_system/extended_affine_weyl_group.py | 6 +++--- .../root_system/hecke_algebra_representation.py | 7 ++++--- .../root_system/integrable_representations.py | 9 ++++----- .../root_system/reflection_group_element.pyx | 6 +++--- src/sage/combinat/root_system/root_space.py | 13 +++++++------ src/sage/combinat/root_system/type_dual.py | 2 +- src/sage/combinat/root_system/type_marked.py | 2 +- 10 files changed, 33 insertions(+), 31 deletions(-) diff --git a/src/sage/combinat/root_system/ambient_space.py b/src/sage/combinat/root_system/ambient_space.py index b1499f4eb4d..4b88f44ad9e 100644 --- a/src/sage/combinat/root_system/ambient_space.py +++ b/src/sage/combinat/root_system/ambient_space.py @@ -1,13 +1,13 @@ r""" Ambient lattices and ambient spaces """ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2008-2009 Daniel Bump # Copyright (C) 2008-2013 Nicolas M. Thiery # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# *************************************************************************** from sage.misc.cachefunc import cached_method from sage.combinat.free_module import CombinatorialFreeModule from .weight_lattice_realizations import WeightLatticeRealizations diff --git a/src/sage/combinat/root_system/associahedron.py b/src/sage/combinat/root_system/associahedron.py index c6765e71eb9..5b4fae15bcb 100644 --- a/src/sage/combinat/root_system/associahedron.py +++ b/src/sage/combinat/root_system/associahedron.py @@ -11,14 +11,14 @@ - Christian Stump """ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2011-2012 Christian Stump # # Distributed under the terms of the GNU General Public License (GPL) # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# *************************************************************************** from sage.geometry.polyhedron.backend_ppl import Polyhedron_QQ_ppl from sage.geometry.polyhedron.backend_normaliz import Polyhedron_QQ_normaliz from sage.geometry.polyhedron.backend_cdd import Polyhedron_QQ_cdd diff --git a/src/sage/combinat/root_system/coxeter_group.py b/src/sage/combinat/root_system/coxeter_group.py index 7ca11e52a14..67962feafff 100644 --- a/src/sage/combinat/root_system/coxeter_group.py +++ b/src/sage/combinat/root_system/coxeter_group.py @@ -1,18 +1,19 @@ """ Coxeter Groups """ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2010 Nicolas Thiery # # Distributed under the terms of the GNU General Public License (GPL) # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# *************************************************************************** from sage.combinat.root_system.weyl_group import WeylGroup from sage.combinat.root_system.reflection_group_real import ReflectionGroup from sage.combinat.root_system.cartan_type import CartanType + def CoxeterGroup(data, implementation="reflection", base_ring=None, index_set=None): """ Return an implementation of the Coxeter group given by ``data``. diff --git a/src/sage/combinat/root_system/extended_affine_weyl_group.py b/src/sage/combinat/root_system/extended_affine_weyl_group.py index bd02024afc6..3edde88317d 100644 --- a/src/sage/combinat/root_system/extended_affine_weyl_group.py +++ b/src/sage/combinat/root_system/extended_affine_weyl_group.py @@ -10,7 +10,7 @@ - Nicolas M. Thiery (2012): initial version - Mark Shimozono (2013): twisted affine root systems, multiple realizations, GL_n """ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2012 Daniel Bump , # 2012 Daniel Orr # 2012 Anne Schilling @@ -23,8 +23,8 @@ # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# *************************************************************************** from sage.combinat.root_system.cartan_type import CartanType from sage.combinat.root_system.weyl_group import WeylGroup from sage.categories.groups import Groups diff --git a/src/sage/combinat/root_system/hecke_algebra_representation.py b/src/sage/combinat/root_system/hecke_algebra_representation.py index bde28234214..20d8d293337 100644 --- a/src/sage/combinat/root_system/hecke_algebra_representation.py +++ b/src/sage/combinat/root_system/hecke_algebra_representation.py @@ -1,13 +1,13 @@ r""" Hecke algebra representations """ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2013 Nicolas M. Thiery # Anne Schilling # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# *************************************************************************** import functools from sage.misc.abstract_method import abstract_method from sage.misc.cachefunc import cached_method @@ -19,6 +19,7 @@ from sage.rings.infinity import infinity from sage.rings.integer_ring import ZZ + class HeckeAlgebraRepresentation(WithEqualityById, SageObject): r""" A representation of an (affine) Hecke algebra given by the action of the `T` generators diff --git a/src/sage/combinat/root_system/integrable_representations.py b/src/sage/combinat/root_system/integrable_representations.py index 6aedd4aefb2..d96f34963f8 100644 --- a/src/sage/combinat/root_system/integrable_representations.py +++ b/src/sage/combinat/root_system/integrable_representations.py @@ -1,16 +1,14 @@ """ Integrable Representations of Affine Lie Algebras """ - -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2014, 2105 Daniel Bump # Travis Scrimshaw # Valentin Buciumas # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#***************************************************************************** - +# https://www.gnu.org/licenses/ +# *************************************************************************** from sage.structure.unique_representation import UniqueRepresentation from sage.structure.category_object import CategoryObject from sage.categories.modules import Modules @@ -20,6 +18,7 @@ from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet from sage.combinat.root_system.weyl_characters import WeylCharacterRing + # TODO: Make this a proper parent and implement actions class IntegrableRepresentation(UniqueRepresentation, CategoryObject): r""" diff --git a/src/sage/combinat/root_system/reflection_group_element.pyx b/src/sage/combinat/root_system/reflection_group_element.pyx index c08a6001524..b724132637b 100644 --- a/src/sage/combinat/root_system/reflection_group_element.pyx +++ b/src/sage/combinat/root_system/reflection_group_element.pyx @@ -11,7 +11,7 @@ AUTHORS: - Christian Stump (initial version 2011--2015) - Travis Scrimshaw (14-03-2017): moved element code """ -#***************************************************************************** +# *************************************************************************** # Copyright (C) 2011-2016 Christian Stump # Copyright (C) 2017 Travis Scrimshaw # @@ -19,8 +19,8 @@ AUTHORS: # 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.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute diff --git a/src/sage/combinat/root_system/root_space.py b/src/sage/combinat/root_system/root_space.py index d52b5d7a8f0..d4e6f8746d2 100644 --- a/src/sage/combinat/root_system/root_space.py +++ b/src/sage/combinat/root_system/root_space.py @@ -1,13 +1,12 @@ """ Root lattices and root spaces """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2008-2009 Nicolas M. Thiery # # Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#***************************************************************************** - +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.cachefunc import cached_method, cached_in_parent_method from sage.rings.integer_ring import ZZ from sage.combinat.free_module import CombinatorialFreeModule @@ -183,9 +182,9 @@ def _to_root_lattice(self, x): .. TODO:: generalize diagonal module morphisms to implement this """ try: - return self.root_system.root_lattice().sum_of_terms( (i, ZZ(c)) for (i,c) in x) + return self.root_system.root_lattice().sum_of_terms((i, ZZ(c)) for i, c in x) except TypeError: - raise ValueError("%s does not have integral coefficients"%x) + raise ValueError("%s does not have integral coefficients" % x) @cached_method def _to_classical_on_basis(self, i): @@ -231,6 +230,7 @@ def basis_value(basis, i): return basis[i] return self.module_morphism(on_basis = functools.partial(basis_value, basis) , codomain=L) + class RootSpaceElement(CombinatorialFreeModule.Element): def scalar(self, lambdacheck): """ @@ -461,4 +461,5 @@ def to_ambient(self): """ return self.parent().to_ambient_space_morphism()(self) + RootSpace.Element = RootSpaceElement diff --git a/src/sage/combinat/root_system/type_dual.py b/src/sage/combinat/root_system/type_dual.py index ac02044aedf..ff5c6bd5a3e 100644 --- a/src/sage/combinat/root_system/type_dual.py +++ b/src/sage/combinat/root_system/type_dual.py @@ -691,4 +691,4 @@ def _default_folded_cartan_type(self): return CartanTypeFolded(self, ['E', 6, 1], [[0], [2], [4], [3, 5], [1, 6]]) if letter == 'G': # D_4^{(3)} return CartanTypeFolded(self, ['D', 4, 1], [[0], [1, 3, 4], [2]]) - return super(CartanType, self)._default_folded_cartan_type() + return super()._default_folded_cartan_type() diff --git a/src/sage/combinat/root_system/type_marked.py b/src/sage/combinat/root_system/type_marked.py index 0a506de4165..bb18256dbc6 100644 --- a/src/sage/combinat/root_system/type_marked.py +++ b/src/sage/combinat/root_system/type_marked.py @@ -82,7 +82,7 @@ def __classcall__(cls, ct, marked_nodes): if any(node not in ct.index_set() for node in marked_nodes): raise ValueError("invalid marked node") marked_nodes = tuple(sorted(marked_nodes)) - return super(CartanType, cls).__classcall__(cls, ct, marked_nodes) + return super().__classcall__(cls, ct, marked_nodes) def __init__(self, ct, marked_nodes): """ From 49259be43ec5f1872948e1a183c4878f19ce2d2a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 12 Nov 2022 14:44:38 -0800 Subject: [PATCH 450/632] SageIpythonConfiguration.colors: Use default from SageTerminalInteractiveShell instead of forcing LightBG --- src/sage/repl/configuration.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sage/repl/configuration.py b/src/sage/repl/configuration.py index 1f2aeb27692..99e51699868 100644 --- a/src/sage/repl/configuration.py +++ b/src/sage/repl/configuration.py @@ -81,7 +81,10 @@ def colors(self): sage: sage_ipython_config.simple_prompt() True """ - return 'LightBG' if self._allow_ansi() else 'NoColor' + if not self._allow_ansi(): + return 'NoColor' + from sage.repl.interpreter import SageTerminalInteractiveShell + return SageTerminalInteractiveShell.colors.default() def simple_prompt(self): """ From 1801f626544ccd1f112c6e6ac3527a6b99920fad Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 26 Sep 2022 10:14:51 -0700 Subject: [PATCH 451/632] VectorFieldModule: Add tensor_product, tensor_power --- .../differentiable/diff_form_module.py | 28 +++++ .../differentiable/tensorfield_module.py | 6 + .../differentiable/vectorfield_module.py | 116 +++++++++++++++++- 3 files changed, 144 insertions(+), 6 deletions(-) diff --git a/src/sage/manifolds/differentiable/diff_form_module.py b/src/sage/manifolds/differentiable/diff_form_module.py index 37feeaa7650..4b9a7cae78c 100644 --- a/src/sage/manifolds/differentiable/diff_form_module.py +++ b/src/sage/manifolds/differentiable/diff_form_module.py @@ -47,6 +47,7 @@ from sage.manifolds.differentiable.diff_form import DiffForm, DiffFormParal from sage.manifolds.differentiable.tensorfield import TensorField from sage.manifolds.differentiable.tensorfield_paral import TensorFieldParal +from sage.manifolds.differentiable.vectorfield_module import VectorFieldModule class DiffFormModule(UniqueRepresentation, Parent): @@ -534,6 +535,33 @@ def base_module(self): """ return self._vmodule + def tensor_type(self): + r""" + Return the tensor type of ``self`` if ``self`` is a module of 1-forms. + + In this case, the pair `(0, 1)` is returned, indicating that the module + is identified with the dual of the base module. + + For differential forms of other degrees, an exception is raised. + + EXAMPLES:: + + sage: M = Manifold(3, 'M') + sage: M.diff_form_module(1).tensor_type() + (0, 1) + sage: M.diff_form_module(2).tensor_type() + Traceback (most recent call last): + ... + NotImplementedError + """ + if self._degree == 1: + return (0, 1) + raise NotImplementedError + + tensor_power = VectorFieldModule.tensor_power + + tensor = tensor_product = VectorFieldModule.tensor_product + def degree(self): r""" Return the degree of the differential forms in ``self``. diff --git a/src/sage/manifolds/differentiable/tensorfield_module.py b/src/sage/manifolds/differentiable/tensorfield_module.py index cbc73f9e520..d845ae03766 100644 --- a/src/sage/manifolds/differentiable/tensorfield_module.py +++ b/src/sage/manifolds/differentiable/tensorfield_module.py @@ -50,6 +50,8 @@ MultivectorFieldParal) from sage.manifolds.differentiable.automorphismfield import (AutomorphismField, AutomorphismFieldParal) +from sage.manifolds.differentiable.vectorfield_module import VectorFieldModule + class TensorFieldModule(UniqueRepresentation, Parent): r""" @@ -561,6 +563,10 @@ def tensor_type(self): """ return self._tensor_type + tensor_power = VectorFieldModule.tensor_power + + tensor = tensor_product = VectorFieldModule.tensor_product + @cached_method def zero(self): """ diff --git a/src/sage/manifolds/differentiable/vectorfield_module.py b/src/sage/manifolds/differentiable/vectorfield_module.py index 580eec814ab..8c023117eab 100644 --- a/src/sage/manifolds/differentiable/vectorfield_module.py +++ b/src/sage/manifolds/differentiable/vectorfield_module.py @@ -506,6 +506,34 @@ def destination_map(self): """ return self._dest_map + def base_module(self): + r""" + Return the module on which ``self`` is constructed, namely ``self`` itself. + + EXAMPLES:: + + sage: M = Manifold(2, 'M') + sage: XM = M.vector_field_module() + sage: XM.base_module() is XM + True + + """ + return self + + def tensor_type(self): + r""" + Return the tensor type of ``self``, the pair `(1, 0)`. + + EXAMPLES:: + + sage: M = Manifold(2, 'M') + sage: XM = M.vector_field_module() + sage: XM.tensor_type() + (1, 0) + + """ + return (1, 0) + def tensor_module(self, k, l, *, sym=None, antisym=None): r""" Return the module of type-`(k,l)` tensors on ``self``. @@ -731,8 +759,8 @@ def general_linear_group(self): self._general_linear_group = AutomorphismFieldGroup(self) return self._general_linear_group - def tensor(self, tensor_type, name=None, latex_name=None, sym=None, - antisym=None, specific_type=None): + def _tensor(self, tensor_type, name=None, latex_name=None, sym=None, + antisym=None, specific_type=None): r""" Construct a tensor on ``self``. @@ -776,10 +804,6 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, sage: XM.tensor((1,2), name='t') Tensor field t of type (1,2) on the 2-dimensional differentiable manifold M - sage: XM.tensor((1,0), name='a') - Vector field a on the 2-dimensional differentiable manifold M - sage: XM.tensor((0,2), name='a', antisym=(0,1)) - 2-form a on the 2-dimensional differentiable manifold M .. SEEALSO:: @@ -824,6 +848,86 @@ def tensor(self, tensor_type, name=None, latex_name=None, sym=None, self, tensor_type, name=name, latex_name=latex_name, sym=sym, antisym=antisym) + tensor_power = FiniteRankFreeModule.tensor_power + + tensor_product = FiniteRankFreeModule.tensor_product + + def tensor(self, *args, **kwds): + r""" + Construct a tensor field on the domain of ``self`` or a tensor product of ``self`` with other modules. + + If ``args`` consist of other parents, just delegate to :meth:`tensor_product`. + + Otherwise, construct a tensor (i.e., a tensor field on the domain of + the vector field module) from the following input. + + INPUT: + + - ``tensor_type`` -- pair (k,l) with k being the contravariant rank + and l the covariant rank + - ``name`` -- (string; default: ``None``) name given to the tensor + - ``latex_name`` -- (string; default: ``None``) LaTeX symbol to denote + the tensor; if none is provided, the LaTeX symbol is set to ``name`` + - ``sym`` -- (default: ``None``) a symmetry or a list of symmetries + among the tensor arguments: each symmetry is described by a tuple + containing the positions of the involved arguments, with the + convention position=0 for the first argument; for instance: + + * ``sym=(0,1)`` for a symmetry between the 1st and 2nd arguments + * ``sym=[(0,2),(1,3,4)]`` for a symmetry between the 1st and 3rd + arguments and a symmetry between the 2nd, 4th and 5th arguments + + - ``antisym`` -- (default: ``None``) antisymmetry or list of + antisymmetries among the arguments, with the same convention + as for ``sym`` + - ``specific_type`` -- (default: ``None``) specific subclass of + :class:`~sage.manifolds.differentiable.tensorfield.TensorField` for + the output + + OUTPUT: + + - instance of + :class:`~sage.manifolds.differentiable.tensorfield.TensorField` + representing the tensor defined on the vector field module with the + provided characteristics + + EXAMPLES:: + + sage: M = Manifold(2, 'M') + sage: XM = M.vector_field_module() + sage: XM.tensor((1,2), name='t') + Tensor field t of type (1,2) on the 2-dimensional differentiable + manifold M + sage: XM.tensor((1,0), name='a') + Vector field a on the 2-dimensional differentiable manifold M + sage: XM.tensor((0,2), name='a', antisym=(0,1)) + 2-form a on the 2-dimensional differentiable manifold M + + Delegation to :meth:`tensor_product`:: + + sage: M = Manifold(2, 'M') + sage: XM = M.vector_field_module() + sage: XM.tensor(XM) + Module T^(2,0)(M) of type-(2,0) tensors fields on the 2-dimensional differentiable manifold M + sage: XM.tensor(XM, XM.dual(), XM) + Module T^(3,1)(M) of type-(3,1) tensors fields on the 2-dimensional differentiable manifold M + sage: XM.tensor(XM).tensor(XM.dual().tensor(XM.dual())) + Traceback (most recent call last): + ... + AttributeError: 'TensorFieldModule_with_category' object has no attribute '_basis_sym' + + .. SEEALSO:: + + :class:`~sage.manifolds.differentiable.tensorfield.TensorField` + for more examples and documentation. + """ + # Until https://trac.sagemath.org/ticket/30373 is done, + # TensorProductFunctor._functor_name is "tensor", so this method + # also needs to double as the tensor product construction + if isinstance(args[0], Parent): + return self.tensor_product(*args, **kwds) + return self._tensor(*args, **kwds) + def alternating_contravariant_tensor(self, degree, name=None, latex_name=None): r""" From 676772c0dde6391c48cdc2e142d56f6c95479e54 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 26 Sep 2022 19:59:04 -0700 Subject: [PATCH 452/632] src/sage/manifolds/differentiable/vectorfield_module.py (VectorFieldModule_abstract): New --- .../differentiable/diff_form_module.py | 8 ++----- .../differentiable/tensorfield_module.py | 8 ++----- .../differentiable/vectorfield_module.py | 23 ++++++++++++++----- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/sage/manifolds/differentiable/diff_form_module.py b/src/sage/manifolds/differentiable/diff_form_module.py index 4b9a7cae78c..5a337b6c72c 100644 --- a/src/sage/manifolds/differentiable/diff_form_module.py +++ b/src/sage/manifolds/differentiable/diff_form_module.py @@ -47,10 +47,10 @@ from sage.manifolds.differentiable.diff_form import DiffForm, DiffFormParal from sage.manifolds.differentiable.tensorfield import TensorField from sage.manifolds.differentiable.tensorfield_paral import TensorFieldParal -from sage.manifolds.differentiable.vectorfield_module import VectorFieldModule +from sage.manifolds.differentiable.vectorfield_module import VectorFieldModule_abstract -class DiffFormModule(UniqueRepresentation, Parent): +class DiffFormModule(VectorFieldModule_abstract): r""" Module of differential forms of a given degree `p` (`p`-forms) along a differentiable manifold `U` with values on a differentiable manifold `M`. @@ -558,10 +558,6 @@ def tensor_type(self): return (0, 1) raise NotImplementedError - tensor_power = VectorFieldModule.tensor_power - - tensor = tensor_product = VectorFieldModule.tensor_product - def degree(self): r""" Return the degree of the differential forms in ``self``. diff --git a/src/sage/manifolds/differentiable/tensorfield_module.py b/src/sage/manifolds/differentiable/tensorfield_module.py index d845ae03766..0479792a82e 100644 --- a/src/sage/manifolds/differentiable/tensorfield_module.py +++ b/src/sage/manifolds/differentiable/tensorfield_module.py @@ -50,10 +50,10 @@ MultivectorFieldParal) from sage.manifolds.differentiable.automorphismfield import (AutomorphismField, AutomorphismFieldParal) -from sage.manifolds.differentiable.vectorfield_module import VectorFieldModule +from sage.manifolds.differentiable.vectorfield_module import VectorFieldModule_abstract -class TensorFieldModule(UniqueRepresentation, Parent): +class TensorFieldModule(VectorFieldModule_abstract): r""" Module of tensor fields of a given type `(k,l)` along a differentiable manifold `U` with values on a differentiable manifold `M`, via a @@ -563,10 +563,6 @@ def tensor_type(self): """ return self._tensor_type - tensor_power = VectorFieldModule.tensor_power - - tensor = tensor_product = VectorFieldModule.tensor_product - @cached_method def zero(self): """ diff --git a/src/sage/manifolds/differentiable/vectorfield_module.py b/src/sage/manifolds/differentiable/vectorfield_module.py index 8c023117eab..1d850014fab 100644 --- a/src/sage/manifolds/differentiable/vectorfield_module.py +++ b/src/sage/manifolds/differentiable/vectorfield_module.py @@ -53,14 +53,29 @@ from sage.rings.integer import Integer from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation -from sage.tensor.modules.finite_rank_free_module import FiniteRankFreeModule +from sage.tensor.modules.finite_rank_free_module import ( + FiniteRankFreeModule_abstract, + FiniteRankFreeModule, +) if TYPE_CHECKING: from sage.manifolds.differentiable.diff_map import DiffMap from sage.manifolds.differentiable.manifold import DifferentiableManifold -class VectorFieldModule(UniqueRepresentation, Parent): +class VectorFieldModule_abstract(UniqueRepresentation, Parent): + r""" + Abstract base class for modules of vector fields. + """ + + tensor_power = FiniteRankFreeModule_abstract.tensor_power + + tensor_product = FiniteRankFreeModule_abstract.tensor_product + + tensor = FiniteRankFreeModule_abstract.tensor + + +class VectorFieldModule(VectorFieldModule_abstract): r""" Module of vector fields along a differentiable manifold `U` with values on a differentiable manifold `M`, via a differentiable @@ -848,10 +863,6 @@ def _tensor(self, tensor_type, name=None, latex_name=None, sym=None, self, tensor_type, name=name, latex_name=latex_name, sym=sym, antisym=antisym) - tensor_power = FiniteRankFreeModule.tensor_power - - tensor_product = FiniteRankFreeModule.tensor_product - def tensor(self, *args, **kwds): r""" Construct a tensor field on the domain of ``self`` or a tensor product of ``self`` with other modules. From 610545e6e2f83b6200675fd708cfb47b280413cb Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 27 Sep 2022 14:11:38 -0700 Subject: [PATCH 453/632] TensorFreeModule.tensor_factors: Go through methods tensor_type, base_module --- src/sage/tensor/modules/tensor_free_module.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index b04e8581148..823a9482383 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -382,12 +382,14 @@ def tensor_factors(self): Dual of the Rank-3 free module M over the Integer Ring, Dual of the Rank-3 free module M over the Integer Ring] """ - if self._tensor_type == (0,1): # case of the dual + tensor_type = self.tensor_type() + if tensor_type == (0,1): # case of the dual raise NotImplementedError - factors = [self._fmodule] * self._tensor_type[0] - dmodule = self._fmodule.dual() - if self._tensor_type[1]: - factors += [dmodule] * self._tensor_type[1] + bmodule = self.base_module() + factors = [bmodule] * tensor_type[0] + dmodule = bmodule.dual() + if tensor_type[1]: + factors += [dmodule] * tensor_type[1] return factors #### Parent Methods From a6ba1011bb5d8601ed0acf1dbc845297042ea1a1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 27 Sep 2022 14:11:53 -0700 Subject: [PATCH 454/632] TensorFieldModule.tensor_factors: New --- src/sage/manifolds/differentiable/tensorfield_module.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sage/manifolds/differentiable/tensorfield_module.py b/src/sage/manifolds/differentiable/tensorfield_module.py index 0479792a82e..3c3aa7d71f2 100644 --- a/src/sage/manifolds/differentiable/tensorfield_module.py +++ b/src/sage/manifolds/differentiable/tensorfield_module.py @@ -228,6 +228,12 @@ class TensorFieldModule(VectorFieldModule_abstract): [1 0] [0 1] + TESTS:: + + sage: T11.tensor_factors() + [Module X(M) of vector fields on the 2-dimensional differentiable manifold M, + Module Omega^1(M) of 1-forms on the 2-dimensional differentiable manifold M] + """ Element = TensorField @@ -288,6 +294,8 @@ def __init__(self, vector_field_module, tensor_type): self._dest_map = dest_map self._ambient_domain = vector_field_module._ambient_domain + tensor_factors = TensorFreeModule.tensor_factors + #### Parent methods def _element_constructor_(self, comp=[], frame=None, name=None, From 8e0bf363b0ffab0336e4218fa986fac3bcef4b48 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 27 Sep 2022 17:29:27 -0700 Subject: [PATCH 455/632] TensorFieldModule: Put it in the category Modules.TensorProducts --- src/sage/manifolds/differentiable/tensorfield.py | 4 ++-- src/sage/manifolds/differentiable/tensorfield_module.py | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/sage/manifolds/differentiable/tensorfield.py b/src/sage/manifolds/differentiable/tensorfield.py index a2aad4d4937..db4a2a05aa1 100644 --- a/src/sage/manifolds/differentiable/tensorfield.py +++ b/src/sage/manifolds/differentiable/tensorfield.py @@ -176,8 +176,8 @@ class TensorField(ModuleElementWithMutability): Module T^(0,2)(S^2) of type-(0,2) tensors fields on the 2-dimensional differentiable manifold S^2 sage: t.parent().category() - Category of modules over Algebra of differentiable scalar fields on the - 2-dimensional differentiable manifold S^2 + Category of tensor products of modules over Algebra of differentiable scalar fields + on the 2-dimensional differentiable manifold S^2 The parent of `t` is not a free module, for the sphere `S^2` is not parallelizable:: diff --git a/src/sage/manifolds/differentiable/tensorfield_module.py b/src/sage/manifolds/differentiable/tensorfield_module.py index 3c3aa7d71f2..4bb33109c25 100644 --- a/src/sage/manifolds/differentiable/tensorfield_module.py +++ b/src/sage/manifolds/differentiable/tensorfield_module.py @@ -125,8 +125,8 @@ class TensorFieldModule(VectorFieldModule_abstract): `T^{(2,0)}(M)` is a module over the algebra `C^k(M)`:: sage: T20.category() - Category of modules over Algebra of differentiable scalar fields on the - 2-dimensional differentiable manifold M + Category of tensor products of modules over Algebra of differentiable scalar fields + on the 2-dimensional differentiable manifold M sage: T20.base_ring() is M.scalar_field_algebra() True @@ -237,7 +237,7 @@ class TensorFieldModule(VectorFieldModule_abstract): """ Element = TensorField - def __init__(self, vector_field_module, tensor_type): + def __init__(self, vector_field_module, tensor_type, category=None): r""" Construct a module of tensor fields taking values on a (a priori) not parallelizable differentiable manifold. @@ -289,7 +289,8 @@ def __init__(self, vector_field_module, tensor_type): # the member self._ring is created for efficiency (to avoid calls to # self.base_ring()): self._ring = domain.scalar_field_algebra() - Parent.__init__(self, base=self._ring, category=Modules(self._ring)) + category = Modules(self._ring).TensorProducts().or_subcategory(category) + Parent.__init__(self, base=self._ring, category=category) self._domain = domain self._dest_map = dest_map self._ambient_domain = vector_field_module._ambient_domain From 20bd0d3f7ced5dc76e1b9032903254cf9aeb6236 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 27 Sep 2022 17:50:31 -0700 Subject: [PATCH 456/632] src/sage/manifolds/differentiable/tensorfield_module.py: Update copyright according to git blame -w --date=format:%Y FILE | sort -k2 --- src/sage/manifolds/differentiable/tensorfield_module.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/sage/manifolds/differentiable/tensorfield_module.py b/src/sage/manifolds/differentiable/tensorfield_module.py index 4bb33109c25..dd9634f066b 100644 --- a/src/sage/manifolds/differentiable/tensorfield_module.py +++ b/src/sage/manifolds/differentiable/tensorfield_module.py @@ -27,9 +27,11 @@ """ # ***************************************************************************** -# Copyright (C) 2015 Eric Gourgoulhon -# Copyright (C) 2015 Michal Bejger -# Copyright (C) 2016 Travis Scrimshaw +# Copyright (C) 2015-2018 Eric Gourgoulhon +# 2015 Michal Bejger +# 2016 Travis Scrimshaw +# 2020 Michael Jung +# 2022 Matthias Koeppe # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of From 38e6b3e414c15dde36ba3e7957539e7727d8a14d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 29 Sep 2022 16:53:19 -0700 Subject: [PATCH 457/632] sage.tensor.modules.reflexive_module: New, refactor finite rank free modules and vector field modules through the new classes --- .../differentiable/tensorfield_module.py | 7 +- .../differentiable/vectorfield_module.py | 47 +-- .../tensor/modules/finite_rank_free_module.py | 193 +----------- src/sage/tensor/modules/reflexive_module.py | 276 ++++++++++++++++++ src/sage/tensor/modules/tensor_free_module.py | 29 +- 5 files changed, 298 insertions(+), 254 deletions(-) create mode 100644 src/sage/tensor/modules/reflexive_module.py diff --git a/src/sage/manifolds/differentiable/tensorfield_module.py b/src/sage/manifolds/differentiable/tensorfield_module.py index dd9634f066b..75344239d0c 100644 --- a/src/sage/manifolds/differentiable/tensorfield_module.py +++ b/src/sage/manifolds/differentiable/tensorfield_module.py @@ -43,6 +43,7 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent from sage.categories.modules import Modules +from sage.tensor.modules.reflexive_module import ReflexiveModule_tensor from sage.tensor.modules.tensor_free_module import TensorFreeModule from sage.manifolds.differentiable.tensorfield import TensorField from sage.manifolds.differentiable.tensorfield_paral import TensorFieldParal @@ -55,7 +56,7 @@ from sage.manifolds.differentiable.vectorfield_module import VectorFieldModule_abstract -class TensorFieldModule(VectorFieldModule_abstract): +class TensorFieldModule(ReflexiveModule_tensor, VectorFieldModule_abstract): r""" Module of tensor fields of a given type `(k,l)` along a differentiable manifold `U` with values on a differentiable manifold `M`, via a @@ -297,8 +298,6 @@ def __init__(self, vector_field_module, tensor_type, category=None): self._dest_map = dest_map self._ambient_domain = vector_field_module._ambient_domain - tensor_factors = TensorFreeModule.tensor_factors - #### Parent methods def _element_constructor_(self, comp=[], frame=None, name=None, @@ -601,7 +600,7 @@ def zero(self): #*********************************************************************** -class TensorFieldFreeModule(TensorFreeModule): +class TensorFieldFreeModule(TensorFreeModule, VectorFieldModule_abstract): r""" Free module of tensor fields of a given type `(k,l)` along a differentiable manifold `U` with values on a parallelizable manifold `M`, diff --git a/src/sage/manifolds/differentiable/vectorfield_module.py b/src/sage/manifolds/differentiable/vectorfield_module.py index 1d850014fab..3effe04cbb7 100644 --- a/src/sage/manifolds/differentiable/vectorfield_module.py +++ b/src/sage/manifolds/differentiable/vectorfield_module.py @@ -53,9 +53,10 @@ from sage.rings.integer import Integer from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation -from sage.tensor.modules.finite_rank_free_module import ( - FiniteRankFreeModule_abstract, - FiniteRankFreeModule, +from sage.tensor.modules.finite_rank_free_module import FiniteRankFreeModule +from sage.tensor.modules.reflexive_module import ( + ReflexiveModule_abstract, + ReflexiveModule_base ) if TYPE_CHECKING: @@ -63,19 +64,13 @@ from sage.manifolds.differentiable.manifold import DifferentiableManifold -class VectorFieldModule_abstract(UniqueRepresentation, Parent): +class VectorFieldModule_abstract(UniqueRepresentation, ReflexiveModule_abstract): r""" Abstract base class for modules of vector fields. """ - tensor_power = FiniteRankFreeModule_abstract.tensor_power - tensor_product = FiniteRankFreeModule_abstract.tensor_product - - tensor = FiniteRankFreeModule_abstract.tensor - - -class VectorFieldModule(VectorFieldModule_abstract): +class VectorFieldModule(ReflexiveModule_base, VectorFieldModule_abstract): r""" Module of vector fields along a differentiable manifold `U` with values on a differentiable manifold `M`, via a differentiable @@ -521,34 +516,6 @@ def destination_map(self): """ return self._dest_map - def base_module(self): - r""" - Return the module on which ``self`` is constructed, namely ``self`` itself. - - EXAMPLES:: - - sage: M = Manifold(2, 'M') - sage: XM = M.vector_field_module() - sage: XM.base_module() is XM - True - - """ - return self - - def tensor_type(self): - r""" - Return the tensor type of ``self``, the pair `(1, 0)`. - - EXAMPLES:: - - sage: M = Manifold(2, 'M') - sage: XM = M.vector_field_module() - sage: XM.tensor_type() - (1, 0) - - """ - return (1, 0) - def tensor_module(self, k, l, *, sym=None, antisym=None): r""" Return the module of type-`(k,l)` tensors on ``self``. @@ -1305,7 +1272,7 @@ def poisson_tensor( #****************************************************************************** -class VectorFieldFreeModule(FiniteRankFreeModule): +class VectorFieldFreeModule(FiniteRankFreeModule, VectorFieldModule_abstract): r""" Free module of vector fields along a differentiable manifold `U` with values on a parallelizable manifold `M`, via a differentiable map diff --git a/src/sage/tensor/modules/finite_rank_free_module.py b/src/sage/tensor/modules/finite_rank_free_module.py index 61a44d022d4..534c99d0b3f 100644 --- a/src/sage/tensor/modules/finite_rank_free_module.py +++ b/src/sage/tensor/modules/finite_rank_free_module.py @@ -548,8 +548,14 @@ class :class:`~sage.modules.free_module.FreeModule_generic` from sage.tensor.modules.free_module_alt_form import FreeModuleAltForm from sage.tensor.modules.free_module_element import FiniteRankFreeModuleElement from sage.tensor.modules.free_module_tensor import FreeModuleTensor +from sage.tensor.modules.reflexive_module import ( + ReflexiveModule_abstract, + ReflexiveModule_base, + ReflexiveModule_dual, +) -class FiniteRankFreeModule_abstract(UniqueRepresentation, Parent): + +class FiniteRankFreeModule_abstract(UniqueRepresentation, ReflexiveModule_abstract): r""" Abstract base class for free modules of finite rank over a commutative ring. """ @@ -619,130 +625,6 @@ def _latex_(self): else: return self._latex_name - def tensor_power(self, n): - r""" - Return the ``n``-fold tensor product of ``self``. - - EXAMPLES:: - - sage: M = FiniteRankFreeModule(QQ, 2) - sage: M.tensor_power(3) - Free module of type-(3,0) tensors on the 2-dimensional vector space over the Rational Field - sage: M.tensor_module(1,2).tensor_power(3) - Free module of type-(3,6) tensors on the 2-dimensional vector space over the Rational Field - """ - tensor_type = self.tensor_type() - return self.base_module().tensor_module(n * tensor_type[0], n * tensor_type[1]) - - def tensor_product(self, *others): - r""" - Return the tensor product of ``self`` and ``others``. - - EXAMPLES:: - - sage: M = FiniteRankFreeModule(QQ, 2) - sage: M.tensor_product(M) - Free module of type-(2,0) tensors on the 2-dimensional vector space over the Rational Field - sage: M.tensor_product(M.dual()) - Free module of type-(1,1) tensors on the 2-dimensional vector space over the Rational Field - sage: M.dual().tensor_product(M, M.dual()) - Free module of type-(1,2) tensors on the 2-dimensional vector space over the Rational Field - sage: M.tensor_product(M.tensor_module(1,2)) - Free module of type-(2,2) tensors on the 2-dimensional vector space over the Rational Field - sage: M.tensor_module(1,2).tensor_product(M) - Free module of type-(2,2) tensors on the 2-dimensional vector space over the Rational Field - sage: M.tensor_module(1,1).tensor_product(M.tensor_module(1,2)) - Free module of type-(2,3) tensors on the 2-dimensional vector space over the Rational Field - - sage: Sym2M = M.tensor_module(2, 0, sym=range(2)); Sym2M - Free module of fully symmetric type-(2,0) tensors on the 2-dimensional vector space over the Rational Field - sage: Sym01x23M = Sym2M.tensor_product(Sym2M); Sym01x23M - Free module of type-(4,0) tensors on the 2-dimensional vector space over the Rational Field, - with symmetry on the index positions (0, 1), with symmetry on the index positions (2, 3) - sage: Sym01x23M._index_maps - ((0, 1), (2, 3)) - - sage: N = M.tensor_module(3, 3, sym=[1, 2], antisym=[3, 4]); N - Free module of type-(3,3) tensors on the 2-dimensional vector space over the Rational Field, - with symmetry on the index positions (1, 2), - with antisymmetry on the index positions (3, 4) - sage: NxN = N.tensor_product(N); NxN - Free module of type-(6,6) tensors on the 2-dimensional vector space over the Rational Field, - with symmetry on the index positions (1, 2), with symmetry on the index positions (4, 5), - with antisymmetry on the index positions (6, 7), with antisymmetry on the index positions (9, 10) - sage: NxN._index_maps - ((0, 1, 2, 6, 7, 8), (3, 4, 5, 9, 10, 11)) - """ - from sage.modules.free_module_element import vector - from .comp import CompFullySym, CompFullyAntiSym, CompWithSym - - base_module = self.base_module() - if not all(module.base_module() == base_module for module in others): - raise NotImplementedError('all factors must be tensor modules over the same base module') - factors = [self] + list(others) - result_tensor_type = sum(vector(factor.tensor_type()) for factor in factors) - result_sym = [] - result_antisym = [] - # Keep track of reordering of the contravariant and covariant indices - # (compatible with FreeModuleTensor.__mul__) - index_maps = [] - running_indices = vector([0, result_tensor_type[0]]) - for factor in factors: - tensor_type = factor.tensor_type() - index_map = tuple(i + running_indices[0] for i in range(tensor_type[0])) - index_map += tuple(i + running_indices[1] for i in range(tensor_type[1])) - index_maps.append(index_map) - - if tensor_type[0] + tensor_type[1] > 1: - basis_sym = factor._basis_sym() - all_indices = tuple(range(tensor_type[0] + tensor_type[1])) - if isinstance(basis_sym, CompFullySym): - sym = [all_indices] - antisym = [] - elif isinstance(basis_sym, CompFullyAntiSym): - sym = [] - antisym = [all_indices] - elif isinstance(basis_sym, CompWithSym): - sym = basis_sym._sym - antisym = basis_sym._antisym - else: - sym = antisym = [] - - def map_isym(isym): - return tuple(index_map[i] for i in isym) - - result_sym.extend(tuple(index_map[i] for i in isym) for isym in sym) - result_antisym.extend(tuple(index_map[i] for i in isym) for isym in antisym) - - running_indices += vector(tensor_type) - - result = base_module.tensor_module(*result_tensor_type, - sym=result_sym, antisym=result_antisym) - result._index_maps = tuple(index_maps) - return result - - def tensor(self, *args, **kwds): - # Until https://trac.sagemath.org/ticket/30373 is done, - # TensorProductFunctor._functor_name is "tensor", so here we delegate. - r""" - Return the tensor product of ``self`` and ``others``. - - This method is invoked when :class:`~sage.categories.tensor.TensorProductFunctor` - is applied to parents. - - It just delegates to :meth:`tensor_product`. - - EXAMPLES:: - - sage: M = FiniteRankFreeModule(QQ, 2); M - 2-dimensional vector space over the Rational Field - sage: M20 = M.tensor_module(2, 0); M20 - Free module of type-(2,0) tensors on the 2-dimensional vector space over the Rational Field - sage: tensor([M20, M20]) - Free module of type-(4,0) tensors on the 2-dimensional vector space over the Rational Field - """ - return self.tensor_product(*args, **kwds) - def rank(self) -> int: r""" Return the rank of the free module ``self``. @@ -1092,7 +974,7 @@ def _test_isomorphism_with_fixed_basis(self, **options): tester.assertEqual(morphism.codomain().rank(), self.rank()) -class FiniteRankFreeModule(FiniteRankFreeModule_abstract): +class FiniteRankFreeModule(ReflexiveModule_base, FiniteRankFreeModule_abstract): r""" Free module of finite rank over a commutative ring. @@ -3430,34 +3312,8 @@ def identity_map(self, name='Id', latex_name=None): self._identity_map.set_name(name=name, latex_name=latex_name) return self._identity_map - def base_module(self): - r""" - Return the free module on which ``self`` is constructed, namely ``self`` itself. - - EXAMPLES:: - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: M.base_module() is M - True - - """ - return self - - def tensor_type(self): - r""" - Return the tensor type of ``self``, the pair `(1, 0)`. - - EXAMPLES:: - - sage: M = FiniteRankFreeModule(ZZ, 3) - sage: M.tensor_type() - (1, 0) - - """ - return (1, 0) - - -class FiniteRankDualFreeModule(FiniteRankFreeModule_abstract): +class FiniteRankDualFreeModule(ReflexiveModule_dual, FiniteRankFreeModule_abstract): r""" Dual of a free module of finite rank over a commutative ring. @@ -3584,24 +3440,6 @@ def __init__(self, fmodule, name=None, latex_name=None): latex_name=latex_name) fmodule._all_modules.add(self) - def construction(self): - r""" - Return the functorial construction of ``self``. - - This implementation just returns ``None``, as no functorial construction is implemented. - - TESTS:: - - sage: from sage.tensor.modules.ext_pow_free_module import ExtPowerDualFreeModule - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: e = M.basis('e') - sage: A = M.dual() - sage: A.construction() is None - True - """ - # No construction until we extend VectorFunctor with a parameter 'dual' - return None - #### Parent methods def _element_constructor_(self, comp=[], basis=None, name=None, @@ -3736,16 +3574,3 @@ def base_module(self): """ return self._fmodule - - def tensor_type(self): - r""" - Return the tensor type of ``self``. - - EXAMPLES:: - - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: M.dual().tensor_type() - (0, 1) - - """ - return (0, 1) diff --git a/src/sage/tensor/modules/reflexive_module.py b/src/sage/tensor/modules/reflexive_module.py new file mode 100644 index 00000000000..84fbc673017 --- /dev/null +++ b/src/sage/tensor/modules/reflexive_module.py @@ -0,0 +1,276 @@ +r""" +Base class for reflexive modules +""" + +from sage.misc.abstract_method import abstract_method +from sage.structure.parent import Parent + + +class ReflexiveModule_abstract(Parent): + r""" + Abstract base class for reflexive modules. + """ + + @abstract_method(optional=True) + def tensor_type(self): + r""" + Return the tensor type of ``self``. + + OUTPUT: + + - pair `(k,l)` such that ``self`` is the module tensor product + `T^{(k,l)}(M)`, where `M` is the :meth:`base_module` of ``self``. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3) + sage: T = M.tensor_module(1, 2) + sage: T.tensor_type() + (1, 2) + """ + + @abstract_method + def base_module(self): + r""" + Return the module on which ``self`` is constructed. + """ + + def dual(self): + r""" + Return the dual module. + """ + k, l = self.tensor_type() + return self.base_module().tensor_module(l, k) + + def tensor(self, *args, **kwds): + # Until https://trac.sagemath.org/ticket/30373 is done, + # TensorProductFunctor._functor_name is "tensor", so here we delegate. + r""" + Return the tensor product of ``self`` and ``others``. + + This method is invoked when :class:`~sage.categories.tensor.TensorProductFunctor` + is applied to parents. + + It just delegates to :meth:`tensor_product`. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(QQ, 2); M + 2-dimensional vector space over the Rational Field + sage: M20 = M.tensor_module(2, 0); M20 + Free module of type-(2,0) tensors on the 2-dimensional vector space over the Rational Field + sage: tensor([M20, M20]) + Free module of type-(4,0) tensors on the 2-dimensional vector space over the Rational Field + """ + return self.tensor_product(*args, **kwds) + + def tensor_power(self, n): + r""" + Return the ``n``-fold tensor product of ``self``. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(QQ, 2) + sage: M.tensor_power(3) + Free module of type-(3,0) tensors on the 2-dimensional vector space over the Rational Field + sage: M.tensor_module(1,2).tensor_power(3) + Free module of type-(3,6) tensors on the 2-dimensional vector space over the Rational Field + """ + tensor_type = self.tensor_type() + return self.base_module().tensor_module(n * tensor_type[0], n * tensor_type[1]) + + def tensor_product(self, *others): + r""" + Return the tensor product of ``self`` and ``others``. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(QQ, 2) + sage: M.tensor_product(M) + Free module of type-(2,0) tensors on the 2-dimensional vector space over the Rational Field + sage: M.tensor_product(M.dual()) + Free module of type-(1,1) tensors on the 2-dimensional vector space over the Rational Field + sage: M.dual().tensor_product(M, M.dual()) + Free module of type-(1,2) tensors on the 2-dimensional vector space over the Rational Field + sage: M.tensor_product(M.tensor_module(1,2)) + Free module of type-(2,2) tensors on the 2-dimensional vector space over the Rational Field + sage: M.tensor_module(1,2).tensor_product(M) + Free module of type-(2,2) tensors on the 2-dimensional vector space over the Rational Field + sage: M.tensor_module(1,1).tensor_product(M.tensor_module(1,2)) + Free module of type-(2,3) tensors on the 2-dimensional vector space over the Rational Field + + sage: Sym2M = M.tensor_module(2, 0, sym=range(2)); Sym2M + Free module of fully symmetric type-(2,0) tensors on the 2-dimensional vector space over the Rational Field + sage: Sym01x23M = Sym2M.tensor_product(Sym2M); Sym01x23M + Free module of type-(4,0) tensors on the 2-dimensional vector space over the Rational Field, + with symmetry on the index positions (0, 1), with symmetry on the index positions (2, 3) + sage: Sym01x23M._index_maps + ((0, 1), (2, 3)) + + sage: N = M.tensor_module(3, 3, sym=[1, 2], antisym=[3, 4]); N + Free module of type-(3,3) tensors on the 2-dimensional vector space over the Rational Field, + with symmetry on the index positions (1, 2), + with antisymmetry on the index positions (3, 4) + sage: NxN = N.tensor_product(N); NxN + Free module of type-(6,6) tensors on the 2-dimensional vector space over the Rational Field, + with symmetry on the index positions (1, 2), with symmetry on the index positions (4, 5), + with antisymmetry on the index positions (6, 7), with antisymmetry on the index positions (9, 10) + sage: NxN._index_maps + ((0, 1, 2, 6, 7, 8), (3, 4, 5, 9, 10, 11)) + """ + from sage.modules.free_module_element import vector + from .comp import CompFullySym, CompFullyAntiSym, CompWithSym + + base_module = self.base_module() + if not all(module.base_module() == base_module for module in others): + raise NotImplementedError('all factors must be tensor modules over the same base module') + factors = [self] + list(others) + result_tensor_type = sum(vector(factor.tensor_type()) for factor in factors) + result_sym = [] + result_antisym = [] + # Keep track of reordering of the contravariant and covariant indices + # (compatible with FreeModuleTensor.__mul__) + index_maps = [] + running_indices = vector([0, result_tensor_type[0]]) + for factor in factors: + tensor_type = factor.tensor_type() + index_map = tuple(i + running_indices[0] for i in range(tensor_type[0])) + index_map += tuple(i + running_indices[1] for i in range(tensor_type[1])) + index_maps.append(index_map) + + if tensor_type[0] + tensor_type[1] > 1: + basis_sym = factor._basis_sym() + all_indices = tuple(range(tensor_type[0] + tensor_type[1])) + if isinstance(basis_sym, CompFullySym): + sym = [all_indices] + antisym = [] + elif isinstance(basis_sym, CompFullyAntiSym): + sym = [] + antisym = [all_indices] + elif isinstance(basis_sym, CompWithSym): + sym = basis_sym._sym + antisym = basis_sym._antisym + else: + sym = antisym = [] + + def map_isym(isym): + return tuple(index_map[i] for i in isym) + + result_sym.extend(tuple(index_map[i] for i in isym) for isym in sym) + result_antisym.extend(tuple(index_map[i] for i in isym) for isym in antisym) + + running_indices += vector(tensor_type) + + result = base_module.tensor_module(*result_tensor_type, + sym=result_sym, antisym=result_antisym) + result._index_maps = tuple(index_maps) + return result + + +class ReflexiveModule_base: + + def base_module(self): + r""" + Return the free module on which ``self`` is constructed, namely ``self`` itself. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: M.base_module() is M + True + + sage: M = Manifold(2, 'M') + sage: XM = M.vector_field_module() + sage: XM.base_module() is XM + True + """ + return self + + def tensor_type(self): + r""" + Return the tensor type of ``self``, the pair `(1, 0)`. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3) + sage: M.tensor_type() + (1, 0) + + sage: M = Manifold(2, 'M') + sage: XM = M.vector_field_module() + sage: XM.tensor_type() + (1, 0) + """ + return (1, 0) + + def dual(self): + r""" + Return the dual module. + """ + return self.tensor_module(0, 1) + + @abstract_method + def tensor_module(self, k, l, **kwds): + r""" + Return the module of all tensors of type `(k, l)` defined on ``self``. + """ + + +class ReflexiveModule_dual: + + def tensor_type(self): + r""" + Return the tensor type of ``self``. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: M.dual().tensor_type() + (0, 1) + + """ + return (0, 1) + + def construction(self): + r""" + Return the functorial construction of ``self``. + + This implementation just returns ``None``, as no functorial construction is implemented. + + TESTS:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: e = M.basis('e') + sage: A = M.dual() + sage: A.construction() is None + True + """ + return None + + +class ReflexiveModule_tensor: + + def tensor_factors(self): + r""" + Return the tensor factors of this tensor module. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: T = M.tensor_module(2, 3) + sage: T.tensor_factors() + [Rank-3 free module M over the Integer Ring, + Rank-3 free module M over the Integer Ring, + Dual of the Rank-3 free module M over the Integer Ring, + Dual of the Rank-3 free module M over the Integer Ring, + Dual of the Rank-3 free module M over the Integer Ring] + """ + tensor_type = self.tensor_type() + if tensor_type == (0,1): # case of the dual + raise NotImplementedError + bmodule = self.base_module() + factors = [bmodule] * tensor_type[0] + dmodule = bmodule.dual() + if tensor_type[1]: + factors += [dmodule] * tensor_type[1] + return factors diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index 823a9482383..ecaf4c46886 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -67,9 +67,11 @@ from sage.tensor.modules.free_module_morphism import \ FiniteRankFreeModuleMorphism from sage.tensor.modules.free_module_automorphism import FreeModuleAutomorphism +from sage.tensor.modules.reflexive_module import ReflexiveModule_tensor + from .tensor_free_submodule_basis import TensorFreeSubmoduleBasis_sym -class TensorFreeModule(FiniteRankFreeModule_abstract): +class TensorFreeModule(FiniteRankFreeModule_abstract, ReflexiveModule_tensor): r""" Class for the free modules over a commutative ring `R` that are tensor products of a given free module `M` over `R` with itself and its @@ -367,31 +369,6 @@ def __init__(self, fmodule, tensor_type, name=None, latex_name=None, category=No super().__init__(fmodule._ring, rank, name=name, latex_name=latex_name, category=category) fmodule._all_modules.add(self) - def tensor_factors(self): - r""" - Return the tensor factors of this tensor module. - - EXAMPLES:: - - sage: M = FiniteRankFreeModule(ZZ, 3, name='M') - sage: T = M.tensor_module(2, 3) - sage: T.tensor_factors() - [Rank-3 free module M over the Integer Ring, - Rank-3 free module M over the Integer Ring, - Dual of the Rank-3 free module M over the Integer Ring, - Dual of the Rank-3 free module M over the Integer Ring, - Dual of the Rank-3 free module M over the Integer Ring] - """ - tensor_type = self.tensor_type() - if tensor_type == (0,1): # case of the dual - raise NotImplementedError - bmodule = self.base_module() - factors = [bmodule] * tensor_type[0] - dmodule = bmodule.dual() - if tensor_type[1]: - factors += [dmodule] * tensor_type[1] - return factors - #### Parent Methods def _element_constructor_(self, comp=[], basis=None, name=None, From 04a4daa81badfde762256b27734b423ca3763449 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 29 Sep 2022 16:58:50 -0700 Subject: [PATCH 458/632] src/sage/tensor/modules/reflexive_module.py: Make all classes subclasses of ..._abstract --- src/sage/tensor/modules/reflexive_module.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/tensor/modules/reflexive_module.py b/src/sage/tensor/modules/reflexive_module.py index 84fbc673017..92725c2d97a 100644 --- a/src/sage/tensor/modules/reflexive_module.py +++ b/src/sage/tensor/modules/reflexive_module.py @@ -167,7 +167,7 @@ def map_isym(isym): return result -class ReflexiveModule_base: +class ReflexiveModule_base(ReflexiveModule_abstract): def base_module(self): r""" @@ -216,7 +216,7 @@ def tensor_module(self, k, l, **kwds): """ -class ReflexiveModule_dual: +class ReflexiveModule_dual(ReflexiveModule_abstract): def tensor_type(self): r""" @@ -248,7 +248,7 @@ def construction(self): return None -class ReflexiveModule_tensor: +class ReflexiveModule_tensor(ReflexiveModule_abstract): def tensor_factors(self): r""" From 53e944a6a330ab81ea77fceab7d05d6552545a08 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 1 Oct 2022 22:59:53 -0700 Subject: [PATCH 459/632] src/sage/tensor/modules/reflexive_module.py: Add documentation --- src/sage/tensor/modules/reflexive_module.py | 113 +++++++++++++++++++- 1 file changed, 111 insertions(+), 2 deletions(-) diff --git a/src/sage/tensor/modules/reflexive_module.py b/src/sage/tensor/modules/reflexive_module.py index 92725c2d97a..06a4b58d28f 100644 --- a/src/sage/tensor/modules/reflexive_module.py +++ b/src/sage/tensor/modules/reflexive_module.py @@ -1,5 +1,5 @@ r""" -Base class for reflexive modules +Base classes for reflexive modules """ from sage.misc.abstract_method import abstract_method @@ -9,6 +9,51 @@ class ReflexiveModule_abstract(Parent): r""" Abstract base class for reflexive modules. + + An `R`-module `M` is *reflexive* if the natural map from `M` to its double + dual `M^{**}` is an isomorphism. + + In the category of `R`-modules, the dual module `M^*` is + the `R`-module of linear functionals $\phi:\ M \longrightarrow R$. + However, we do not make the assumption that the dual module + (obtained by :meth:`dual`) is in the category :class:`Homsets`. + + We identify the double dual `M^{**}` with `M`. + + Tensor products of reflexive modules are reflexive. We identify all + tensor products of `k` copies of `M` and `l` copies of `M^*` and + denote it by `T^{(k,l)}(M)`. The :meth:`tensor_type` of such a tensor + product is the pair `(k, l)`, and `M` is called its :meth:`base_module`. + + There are three abstract subclasses: + + - :class:`ReflexiveModule_base` is the base class for implementations + of base modules `M`. + + - :class:`ReflexiveModule_dual` is the base class for implementations + of duals `M^*`. + + - :class:`ReflexiveModule_tensor` is the base class for implementations + of tensor modules `T^{(k,l)}(M)`. + + TESTS:: + + sage: from sage.tensor.modules.reflexive_module import ( + ....: ReflexiveModule_abstract, ReflexiveModule_base, + ....: ReflexiveModule_dual, ReflexiveModule_tensor) + sage: M = FiniteRankFreeModule(ZZ, 3) + sage: isinstance(M, ReflexiveModule_abstract) + True + sage: isinstance(M, ReflexiveModule_base) + True + sage: isinstance(M.dual(), ReflexiveModule_abstract) + True + sage: isinstance(M.dual(), ReflexiveModule_dual) + True + sage: isinstance(M.tensor_module(1, 1), ReflexiveModule_abstract) + True + sage: isinstance(M.tensor_module(1, 1), ReflexiveModule_tensor) + True """ @abstract_method(optional=True) @@ -33,11 +78,33 @@ def tensor_type(self): def base_module(self): r""" Return the module on which ``self`` is constructed. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3) + sage: M.base_module() is M + True + sage: M.dual().base_module() is M + True + sage: M.tensor_module(1, 2).base_module() is M + True """ def dual(self): r""" Return the dual module. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3) + sage: M.dual() + Dual of the Rank-3 free module over the Integer Ring + sage: M.dual().dual() + Rank-3 free module over the Integer Ring + sage: M.tensor_module(1, 2) + Free module of type-(1,2) tensors on the Rank-3 free module over the Integer Ring + sage: M.tensor_module(1, 2).dual() + Free module of type-(2,1) tensors on the Rank-3 free module over the Integer Ring """ k, l = self.tensor_type() return self.base_module().tensor_module(l, k) @@ -168,6 +235,16 @@ def map_isym(isym): class ReflexiveModule_base(ReflexiveModule_abstract): + r""" + Abstract base class for reflexive modules that are base modules. + + TESTS:: + + sage: from sage.tensor.modules.reflexive_module import ReflexiveModule_base + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: isinstance(M, ReflexiveModule_base) + True + """ def base_module(self): r""" @@ -206,6 +283,12 @@ def tensor_type(self): def dual(self): r""" Return the dual module. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: M.dual() + Dual of the Rank-3 free module M over the Integer Ring """ return self.tensor_module(0, 1) @@ -213,10 +296,26 @@ def dual(self): def tensor_module(self, k, l, **kwds): r""" Return the module of all tensors of type `(k, l)` defined on ``self``. + + EXAMPLES:: + + sage: M = FiniteRankFreeModule(ZZ, 3) + sage: M.tensor_module(1, 2) + Free module of type-(1,2) tensors on the Rank-3 free module over the Integer Ring """ class ReflexiveModule_dual(ReflexiveModule_abstract): + r""" + Abstract base class for reflexive modules that are the duals of base modules. + + TESTS:: + + sage: from sage.tensor.modules.reflexive_module import ReflexiveModule_dual + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: isinstance(M.dual(), ReflexiveModule_dual) + True + """ def tensor_type(self): r""" @@ -227,7 +326,6 @@ def tensor_type(self): sage: M = FiniteRankFreeModule(ZZ, 3, name='M') sage: M.dual().tensor_type() (0, 1) - """ return (0, 1) @@ -245,10 +343,21 @@ def construction(self): sage: A.construction() is None True """ + # Until https://trac.sagemath.org/ticket/34605 is done return None class ReflexiveModule_tensor(ReflexiveModule_abstract): + r""" + Abstract base class for reflexive modules that are tensor products of base modules. + + TESTS:: + + sage: from sage.tensor.modules.reflexive_module import ReflexiveModule_tensor + sage: M = FiniteRankFreeModule(ZZ, 3, name='M') + sage: isinstance(M.tensor_module(1, 1), ReflexiveModule_tensor) + True + """ def tensor_factors(self): r""" From f107a775fd44066f3abc931c01257f3b13a53f2b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 3 Oct 2022 04:31:25 -0700 Subject: [PATCH 460/632] src/doc/en/reference/tensor_free_modules/tensors.rst: Add reflexive_module --- src/doc/en/reference/tensor_free_modules/tensors.rst | 2 ++ src/sage/tensor/modules/reflexive_module.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/doc/en/reference/tensor_free_modules/tensors.rst b/src/doc/en/reference/tensor_free_modules/tensors.rst index 434ea734191..65b7786a1cb 100644 --- a/src/doc/en/reference/tensor_free_modules/tensors.rst +++ b/src/doc/en/reference/tensor_free_modules/tensors.rst @@ -4,6 +4,8 @@ Tensors .. toctree:: :maxdepth: 2 + sage/tensor/modules/reflexive_module + sage/tensor/modules/tensor_free_module sage/tensor/modules/tensor_free_submodule diff --git a/src/sage/tensor/modules/reflexive_module.py b/src/sage/tensor/modules/reflexive_module.py index 06a4b58d28f..f969d547811 100644 --- a/src/sage/tensor/modules/reflexive_module.py +++ b/src/sage/tensor/modules/reflexive_module.py @@ -14,7 +14,7 @@ class ReflexiveModule_abstract(Parent): dual `M^{**}` is an isomorphism. In the category of `R`-modules, the dual module `M^*` is - the `R`-module of linear functionals $\phi:\ M \longrightarrow R$. + the `R`-module of linear functionals `\phi:\ M \longrightarrow R`. However, we do not make the assumption that the dual module (obtained by :meth:`dual`) is in the category :class:`Homsets`. From d2228bd2e747c68fecb69617b49d4cad988682e1 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 4 Oct 2022 11:05:10 -0700 Subject: [PATCH 461/632] src/sage/tensor/modules/tensor_free_module.py (TensorFreeModule): Make MRO consistent with FiniteRankFreeModule --- src/sage/tensor/modules/tensor_free_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index ecaf4c46886..3dd3a573c89 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -71,7 +71,7 @@ from .tensor_free_submodule_basis import TensorFreeSubmoduleBasis_sym -class TensorFreeModule(FiniteRankFreeModule_abstract, ReflexiveModule_tensor): +class TensorFreeModule(ReflexiveModule_tensor, FiniteRankFreeModule_abstract): r""" Class for the free modules over a commutative ring `R` that are tensor products of a given free module `M` over `R` with itself and its From 8a2866bf17cf8119b719741c0b9e0574fee58a04 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 4 Oct 2022 14:35:13 -0700 Subject: [PATCH 462/632] get rid of VectorFieldModule_abstract --- src/sage/manifolds/differentiable/diff_form_module.py | 6 ++++-- .../manifolds/differentiable/tensorfield_module.py | 5 ++--- .../manifolds/differentiable/vectorfield_module.py | 10 ++-------- 3 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/sage/manifolds/differentiable/diff_form_module.py b/src/sage/manifolds/differentiable/diff_form_module.py index 5a337b6c72c..04d85a0817d 100644 --- a/src/sage/manifolds/differentiable/diff_form_module.py +++ b/src/sage/manifolds/differentiable/diff_form_module.py @@ -47,10 +47,10 @@ from sage.manifolds.differentiable.diff_form import DiffForm, DiffFormParal from sage.manifolds.differentiable.tensorfield import TensorField from sage.manifolds.differentiable.tensorfield_paral import TensorFieldParal -from sage.manifolds.differentiable.vectorfield_module import VectorFieldModule_abstract +from sage.tensor.modules.reflexive_module import ReflexiveModule_abstract -class DiffFormModule(VectorFieldModule_abstract): +class DiffFormModule(UniqueRepresentation, Parent): r""" Module of differential forms of a given degree `p` (`p`-forms) along a differentiable manifold `U` with values on a differentiable manifold `M`. @@ -535,6 +535,8 @@ def base_module(self): """ return self._vmodule + tensor = tensor_product = ReflexiveModule_abstract.tensor_product + def tensor_type(self): r""" Return the tensor type of ``self`` if ``self`` is a module of 1-forms. diff --git a/src/sage/manifolds/differentiable/tensorfield_module.py b/src/sage/manifolds/differentiable/tensorfield_module.py index 75344239d0c..6347189848f 100644 --- a/src/sage/manifolds/differentiable/tensorfield_module.py +++ b/src/sage/manifolds/differentiable/tensorfield_module.py @@ -53,10 +53,9 @@ MultivectorFieldParal) from sage.manifolds.differentiable.automorphismfield import (AutomorphismField, AutomorphismFieldParal) -from sage.manifolds.differentiable.vectorfield_module import VectorFieldModule_abstract -class TensorFieldModule(ReflexiveModule_tensor, VectorFieldModule_abstract): +class TensorFieldModule(UniqueRepresentation, ReflexiveModule_tensor): r""" Module of tensor fields of a given type `(k,l)` along a differentiable manifold `U` with values on a differentiable manifold `M`, via a @@ -600,7 +599,7 @@ def zero(self): #*********************************************************************** -class TensorFieldFreeModule(TensorFreeModule, VectorFieldModule_abstract): +class TensorFieldFreeModule(TensorFreeModule): r""" Free module of tensor fields of a given type `(k,l)` along a differentiable manifold `U` with values on a parallelizable manifold `M`, diff --git a/src/sage/manifolds/differentiable/vectorfield_module.py b/src/sage/manifolds/differentiable/vectorfield_module.py index 3effe04cbb7..8cd62dacc8c 100644 --- a/src/sage/manifolds/differentiable/vectorfield_module.py +++ b/src/sage/manifolds/differentiable/vectorfield_module.py @@ -64,13 +64,7 @@ from sage.manifolds.differentiable.manifold import DifferentiableManifold -class VectorFieldModule_abstract(UniqueRepresentation, ReflexiveModule_abstract): - r""" - Abstract base class for modules of vector fields. - """ - - -class VectorFieldModule(ReflexiveModule_base, VectorFieldModule_abstract): +class VectorFieldModule(UniqueRepresentation, ReflexiveModule_base): r""" Module of vector fields along a differentiable manifold `U` with values on a differentiable manifold `M`, via a differentiable @@ -1272,7 +1266,7 @@ def poisson_tensor( #****************************************************************************** -class VectorFieldFreeModule(FiniteRankFreeModule, VectorFieldModule_abstract): +class VectorFieldFreeModule(FiniteRankFreeModule): r""" Free module of vector fields along a differentiable manifold `U` with values on a parallelizable manifold `M`, via a differentiable map From 9861b026a44c57743775b32cb741a969e13d8786 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Wed, 26 Oct 2022 20:27:23 -0700 Subject: [PATCH 463/632] trac 34652: import NN directly rather than lazily --- src/sage/categories/sets_cat.py | 6 +++--- src/sage/combinat/cluster_complex.py | 2 +- src/sage/combinat/crystals/tensor_product.py | 2 +- src/sage/combinat/integer_lists/nn.py | 2 +- src/sage/combinat/integer_vector.py | 2 +- src/sage/combinat/integer_vectors_mod_permgroup.py | 2 +- src/sage/combinat/interval_posets.py | 2 +- src/sage/combinat/non_decreasing_parking_function.py | 2 +- src/sage/combinat/partition.py | 2 +- src/sage/combinat/partition_kleshchev.py | 2 +- src/sage/combinat/partition_tuple.py | 2 +- src/sage/combinat/path_tableaux/semistandard.py | 2 +- src/sage/combinat/posets/poset_examples.py | 4 ++-- src/sage/combinat/root_system/cartan_type.py | 2 +- src/sage/combinat/root_system/root_lattice_realizations.py | 2 +- src/sage/combinat/root_system/type_super_A.py | 2 +- src/sage/combinat/sine_gordon.py | 2 +- src/sage/combinat/symmetric_group_algebra.py | 2 +- src/sage/combinat/tableau_tuple.py | 2 +- src/sage/combinat/words/abstract_word.py | 2 +- src/sage/rings/semirings/all.py | 6 +----- src/sage/sets/non_negative_integers.py | 1 + 22 files changed, 25 insertions(+), 28 deletions(-) diff --git a/src/sage/categories/sets_cat.py b/src/sage/categories/sets_cat.py index 13ea3b0338f..99a24c496c0 100644 --- a/src/sage/categories/sets_cat.py +++ b/src/sage/categories/sets_cat.py @@ -1878,13 +1878,13 @@ def image(self, domain_subset=None): cls = ImageSubobject return cls(self, domain_subset) + # Lazy imports to avoid circularity issues. Enumerated = LazyImport('sage.categories.enumerated_sets', 'EnumeratedSets', at_startup=True) - Facade = LazyImport('sage.categories.facade_sets', 'FacadeSets') Finite = LazyImport('sage.categories.finite_sets', 'FiniteSets', at_startup=True) Topological = LazyImport('sage.categories.topological_spaces', 'TopologicalSpaces', 'Topological', at_startup=True) - Metric = LazyImport('sage.categories.metric_spaces', 'MetricSpaces', - 'Metric', at_startup=True) + from sage.categories.facade_sets import FacadeSets as Facade + from sage.categories.metric_spaces import MetricSpaces as Metric class Infinite(CategoryWithAxiom): diff --git a/src/sage/combinat/cluster_complex.py b/src/sage/combinat/cluster_complex.py index 31562e6bc80..ba5645b14ca 100644 --- a/src/sage/combinat/cluster_complex.py +++ b/src/sage/combinat/cluster_complex.py @@ -50,7 +50,7 @@ from sage.categories.coxeter_groups import CoxeterGroups from sage.combinat.root_system.coxeter_group import CoxeterGroup from sage.combinat.subword_complex import SubwordComplex, SubwordComplexFacet -from sage.rings.semirings.all import NN +from sage.rings.semirings.non_negative_integer_semiring import NN class ClusterComplexFacet(SubwordComplexFacet): diff --git a/src/sage/combinat/crystals/tensor_product.py b/src/sage/combinat/crystals/tensor_product.py index 18d80008ccb..1675cad6eff 100644 --- a/src/sage/combinat/crystals/tensor_product.py +++ b/src/sage/combinat/crystals/tensor_product.py @@ -51,7 +51,7 @@ TensorProductOfSuperCrystalsElement, TensorProductOfQueerSuperCrystalsElement) from sage.misc.flatten import flatten from sage.structure.element import get_coercion_model -from sage.rings.semirings.all import NN +from sage.rings.semirings.non_negative_integer_semiring import NN from sage.arith.misc import integer_trunc as trunc diff --git a/src/sage/combinat/integer_lists/nn.py b/src/sage/combinat/integer_lists/nn.py index 6fe0cb0adb5..4329c6164d9 100644 --- a/src/sage/combinat/integer_lists/nn.py +++ b/src/sage/combinat/integer_lists/nn.py @@ -1,6 +1,6 @@ from sage.sets.family import Family from sage.combinat.integer_lists import IntegerListsLex -from sage.rings.semirings.all import NN +from sage.rings.semirings.non_negative_integer_semiring import NN from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets diff --git a/src/sage/combinat/integer_vector.py b/src/sage/combinat/integer_vector.py index 352ec520d4d..2b88a6e3e1e 100644 --- a/src/sage/combinat/integer_vector.py +++ b/src/sage/combinat/integer_vector.py @@ -44,7 +44,7 @@ from sage.rings.infinity import PlusInfinity from sage.arith.all import binomial from sage.rings.integer_ring import ZZ -from sage.rings.semirings.all import NN +from sage.rings.semirings.non_negative_integer_semiring import NN from sage.rings.integer import Integer def is_gale_ryser(r,s): diff --git a/src/sage/combinat/integer_vectors_mod_permgroup.py b/src/sage/combinat/integer_vectors_mod_permgroup.py index 26559396b39..229b5b28146 100644 --- a/src/sage/combinat/integer_vectors_mod_permgroup.py +++ b/src/sage/combinat/integer_vectors_mod_permgroup.py @@ -11,7 +11,7 @@ # **************************************************************************** from sage.structure.unique_representation import UniqueRepresentation -from sage.rings.semirings.all import NN +from sage.rings.semirings.non_negative_integer_semiring import NN from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets diff --git a/src/sage/combinat/interval_posets.py b/src/sage/combinat/interval_posets.py index cd0846e3d6e..7ccede75e8b 100644 --- a/src/sage/combinat/interval_posets.py +++ b/src/sage/combinat/interval_posets.py @@ -48,7 +48,7 @@ from sage.misc.latex import latex from sage.misc.lazy_attribute import lazy_attribute from sage.rings.integer import Integer -from sage.rings.semirings.all import NN +from sage.rings.semirings.non_negative_integer_semiring import NN from sage.sets.non_negative_integers import NonNegativeIntegers from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets from sage.sets.family import Family diff --git a/src/sage/combinat/non_decreasing_parking_function.py b/src/sage/combinat/non_decreasing_parking_function.py index b5ecf2bb8c0..9763793bb1b 100644 --- a/src/sage/combinat/non_decreasing_parking_function.py +++ b/src/sage/combinat/non_decreasing_parking_function.py @@ -38,7 +38,7 @@ from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets from sage.categories.sets_with_grading import SetsWithGrading from sage.categories.monoids import Monoids -from sage.rings.semirings.all import NN +from sage.rings.semirings.non_negative_integer_semiring import NN from sage.rings.integer import Integer from sage.structure.element import Element from sage.structure.parent import Parent diff --git a/src/sage/combinat/partition.py b/src/sage/combinat/partition.py index 844de2016da..6f52d1ae49d 100644 --- a/src/sage/combinat/partition.py +++ b/src/sage/combinat/partition.py @@ -307,7 +307,7 @@ from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from sage.rings.integer_ring import ZZ from sage.rings.rational_field import QQ -from sage.rings.semirings.all import NN +from sage.rings.semirings.non_negative_integer_semiring import NN from sage.arith.all import factorial, gcd from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.integer import Integer diff --git a/src/sage/combinat/partition_kleshchev.py b/src/sage/combinat/partition_kleshchev.py index 8fd46bc76fe..7c8344d71b5 100644 --- a/src/sage/combinat/partition_kleshchev.py +++ b/src/sage/combinat/partition_kleshchev.py @@ -85,7 +85,7 @@ from sage.misc.lazy_attribute import lazy_attribute from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from sage.rings.integer_ring import ZZ -from sage.rings.semirings.all import NN +from sage.rings.semirings.non_negative_integer_semiring import NN from sage.cpython.getattr import getattr_from_other_class from collections import defaultdict diff --git a/src/sage/combinat/partition_tuple.py b/src/sage/combinat/partition_tuple.py index 800e2a61c31..389063e0d24 100644 --- a/src/sage/combinat/partition_tuple.py +++ b/src/sage/combinat/partition_tuple.py @@ -267,7 +267,7 @@ class of modules for the algebras, which are generalisations of the Specht from sage.misc.cachefunc import cached_method from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from sage.rings.integer_ring import ZZ -from sage.rings.semirings.all import NN +from sage.rings.semirings.non_negative_integer_semiring import NN from sage.rings.integer import Integer from sage.sets.positive_integers import PositiveIntegers from sage.structure.parent import Parent diff --git a/src/sage/combinat/path_tableaux/semistandard.py b/src/sage/combinat/path_tableaux/semistandard.py index 55236fe9ef8..84de742f1d0 100644 --- a/src/sage/combinat/path_tableaux/semistandard.py +++ b/src/sage/combinat/path_tableaux/semistandard.py @@ -85,7 +85,7 @@ from sage.combinat.tableau import Tableau from sage.combinat.gelfand_tsetlin_patterns import GelfandTsetlinPattern from sage.combinat.partition import _Partitions -from sage.rings.semirings.all import NN +from sage.rings.semirings.non_negative_integer_semiring import NN ############################################################################### diff --git a/src/sage/combinat/posets/poset_examples.py b/src/sage/combinat/posets/poset_examples.py index 377fc569fde..7f1fcf1e633 100644 --- a/src/sage/combinat/posets/poset_examples.py +++ b/src/sage/combinat/posets/poset_examples.py @@ -623,7 +623,7 @@ def IntegerPartitionsDominanceOrder(n): [[4, 2], [5, 1]], [[5, 1], [6]]] """ - from sage.rings.semirings.all import NN + from sage.rings.semirings.non_negative_integer_semiring import NN if n not in NN: raise ValueError('n must be an integer') from sage.combinat.partition import Partitions, Partition @@ -973,7 +973,7 @@ def SetPartitions(n): sage: posets.SetPartitions(4) Finite lattice containing 15 elements """ - from sage.rings.semirings.all import NN + from sage.rings.semirings.non_negative_integer_semiring import NN if n not in NN: raise ValueError('n must be an integer') from sage.combinat.set_partition import SetPartitions diff --git a/src/sage/combinat/root_system/cartan_type.py b/src/sage/combinat/root_system/cartan_type.py index dafe2f62366..68c4d8ae15f 100644 --- a/src/sage/combinat/root_system/cartan_type.py +++ b/src/sage/combinat/root_system/cartan_type.py @@ -594,7 +594,7 @@ def __call__(self, *args): if isinstance(t, (CartanType_abstract, SuperCartanType_standard)): return t - from sage.rings.semirings.all import NN + from sage.rings.semirings.non_negative_integer_semiring import NN if isinstance(t, str): if "x" in t: from . import type_reducible diff --git a/src/sage/combinat/root_system/root_lattice_realizations.py b/src/sage/combinat/root_system/root_lattice_realizations.py index 8e97587945d..fd32dc072f1 100644 --- a/src/sage/combinat/root_system/root_lattice_realizations.py +++ b/src/sage/combinat/root_system/root_lattice_realizations.py @@ -3848,7 +3848,7 @@ def is_dominant_weight(self): # Or is_dominant_integral_weight? True """ alphacheck = self.parent().simple_coroots() - from sage.rings.semirings.all import NN + from sage.rings.semirings.non_negative_integer_semiring import NN return all(self.inner_product(alphacheck[i]) in NN for i in self.parent().index_set()) diff --git a/src/sage/combinat/root_system/type_super_A.py b/src/sage/combinat/root_system/type_super_A.py index 16df61caee3..c398812c087 100644 --- a/src/sage/combinat/root_system/type_super_A.py +++ b/src/sage/combinat/root_system/type_super_A.py @@ -442,7 +442,7 @@ def is_dominant_weight(self): """ alpha = self.parent().simple_roots() l = self.parent().cartan_type().symmetrizer() - from sage.rings.semirings.all import NN + from sage.rings.semirings.non_negative_integer_semiring import NN return all(l[i] * self.inner_product(alpha[i]) in NN for i in self.parent().index_set()) diff --git a/src/sage/combinat/sine_gordon.py b/src/sage/combinat/sine_gordon.py index c9a4ab2d8ba..52895b255c5 100644 --- a/src/sage/combinat/sine_gordon.py +++ b/src/sage/combinat/sine_gordon.py @@ -45,7 +45,7 @@ from sage.rings.integer_ring import ZZ from sage.rings.real_mpfr import RR -from sage.rings.semirings.all import NN +from sage.rings.semirings.non_negative_integer_semiring import NN from sage.functions.trig import cos, sin from sage.misc.lazy_import import lazy_import lazy_import("sage.plot.plot", "parametric_plot") diff --git a/src/sage/combinat/symmetric_group_algebra.py b/src/sage/combinat/symmetric_group_algebra.py index c3d67555a6b..b8a0bebab44 100644 --- a/src/sage/combinat/symmetric_group_algebra.py +++ b/src/sage/combinat/symmetric_group_algebra.py @@ -215,7 +215,7 @@ def SymmetricGroupAlgebra(R, W, category=None): sage: SGg.group().conjugacy_classes_representatives() [(), (1,2), (1,2,3)] """ - from sage.rings.semirings.all import NN + from sage.rings.semirings.non_negative_integer_semiring import NN if W in NN: W = Permutations(W) if category is None: diff --git a/src/sage/combinat/tableau_tuple.py b/src/sage/combinat/tableau_tuple.py index 34565ecb642..3deb562bbb6 100644 --- a/src/sage/combinat/tableau_tuple.py +++ b/src/sage/combinat/tableau_tuple.py @@ -231,7 +231,7 @@ from sage.arith.all import factorial from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from sage.rings.integer import Integer -from sage.rings.semirings.all import NN +from sage.rings.semirings.non_negative_integer_semiring import NN from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets from sage.sets.family import Family from sage.sets.positive_integers import PositiveIntegers diff --git a/src/sage/combinat/words/abstract_word.py b/src/sage/combinat/words/abstract_word.py index 80dab5e552b..ff3be9930ad 100644 --- a/src/sage/combinat/words/abstract_word.py +++ b/src/sage/combinat/words/abstract_word.py @@ -833,7 +833,7 @@ def delta(self): word: 1211222112112112221122211222112112112221... """ from sage.combinat.words.word import Word - from sage.rings.semirings.all import NN + from sage.rings.semirings.non_negative_integer_semiring import NN return Word(self._delta_iterator(), alphabet=NN) def _iterated_right_palindromic_closure_iterator(self, f=None): diff --git a/src/sage/rings/semirings/all.py b/src/sage/rings/semirings/all.py index 91594074088..b14da38328a 100644 --- a/src/sage/rings/semirings/all.py +++ b/src/sage/rings/semirings/all.py @@ -1,6 +1,2 @@ - -from sage.misc.lazy_import import lazy_import -lazy_import('sage.rings.semirings.non_negative_integer_semiring', - ['NonNegativeIntegerSemiring', 'NN']) - +from .non_negative_integer_semiring import NonNegativeIntegerSemiring, NN from .tropical_semiring import TropicalSemiring diff --git a/src/sage/sets/non_negative_integers.py b/src/sage/sets/non_negative_integers.py index 9b01ad6f3d4..9b5def119e0 100644 --- a/src/sage/sets/non_negative_integers.py +++ b/src/sage/sets/non_negative_integers.py @@ -95,6 +95,7 @@ def __contains__(self, elt): """ EXAMPLES:: + sage: NN = NonNegativeIntegers() sage: 1 in NN True sage: -1 in NN From 539119b1bc7d9fad671f4e5a8b1dc536cd718e0b Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Sat, 12 Nov 2022 17:41:49 -0800 Subject: [PATCH 464/632] trac 34652: Add categories/facade_sets to pkg/sagemath-objects/Manifest.in --- pkgs/sagemath-categories/MANIFEST.in.m4 | 1 + pkgs/sagemath-objects/MANIFEST.in | 1 + src/sage/categories/sets_cat.py | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/pkgs/sagemath-categories/MANIFEST.in.m4 b/pkgs/sagemath-categories/MANIFEST.in.m4 index 390c7e5759f..98d10a91fef 100644 --- a/pkgs/sagemath-categories/MANIFEST.in.m4 +++ b/pkgs/sagemath-categories/MANIFEST.in.m4 @@ -12,6 +12,7 @@ exclude sage/categories/basic.* exclude sage/categories/cartesian_product.* exclude sage/categories/category*.* exclude sage/categories/covariant_functorial_construction.* +exclude sage/categories/facade_sets.* exclude sage/categories/functor.* exclude sage/categories/homset.* exclude sage/categories/homsets.* diff --git a/pkgs/sagemath-objects/MANIFEST.in b/pkgs/sagemath-objects/MANIFEST.in index ffa9e9c7f10..eced95ea865 100644 --- a/pkgs/sagemath-objects/MANIFEST.in +++ b/pkgs/sagemath-objects/MANIFEST.in @@ -12,6 +12,7 @@ include sage/categories/basic.* include sage/categories/cartesian_product.* include sage/categories/category*.* include sage/categories/covariant_functorial_construction.* +include sage/categories/facade_sets.* include sage/categories/functor.* include sage/categories/homset.* include sage/categories/homsets.* diff --git a/src/sage/categories/sets_cat.py b/src/sage/categories/sets_cat.py index 99a24c496c0..9f9bab84e23 100644 --- a/src/sage/categories/sets_cat.py +++ b/src/sage/categories/sets_cat.py @@ -1883,8 +1883,9 @@ def image(self, domain_subset=None): Finite = LazyImport('sage.categories.finite_sets', 'FiniteSets', at_startup=True) Topological = LazyImport('sage.categories.topological_spaces', 'TopologicalSpaces', 'Topological', at_startup=True) + Metric = LazyImport('sage.categories.metric_spaces', 'MetricSpaces', + 'Metric', at_startup=True) from sage.categories.facade_sets import FacadeSets as Facade - from sage.categories.metric_spaces import MetricSpaces as Metric class Infinite(CategoryWithAxiom): From 6cc57984e6945c207289e2979f21eeb747843c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 13 Nov 2022 08:32:24 +0100 Subject: [PATCH 465/632] modernize super in algebras --- src/sage/algebras/clifford_algebra.py | 10 +- src/sage/algebras/commutative_dga.py | 12 +-- .../hecke_algebras/cubic_hecke_algebra.py | 19 ++-- .../hecke_algebras/cubic_hecke_base_ring.py | 26 +++-- .../hecke_algebras/cubic_hecke_matrix_rep.py | 97 +++++++++---------- 5 files changed, 79 insertions(+), 85 deletions(-) diff --git a/src/sage/algebras/clifford_algebra.py b/src/sage/algebras/clifford_algebra.py index be0db957cf6..feff6d3a343 100644 --- a/src/sage/algebras/clifford_algebra.py +++ b/src/sage/algebras/clifford_algebra.py @@ -6,8 +6,7 @@ - Travis Scrimshaw (2013-09-06): Initial version - Trevor K. Karn (2022-07-27): Rewrite basis indexing using FrozenBitset """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2013-2022 Travis Scrimshaw # (C) 2022 Trevor Karn # @@ -15,9 +14,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.misc.cachefunc import cached_method from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent @@ -631,7 +629,7 @@ def _element_constructor_(self, x): return self.element_class(self, {FrozenBitset((i,)): R.one() for i in x}) try: - return super(CliffordAlgebra, self)._element_constructor_(x) + return super()._element_constructor_(x) except TypeError: raise TypeError(f'do not know how to make {x=} an element of self') diff --git a/src/sage/algebras/commutative_dga.py b/src/sage/algebras/commutative_dga.py index a05ede46e52..0cac2ee3ebb 100644 --- a/src/sage/algebras/commutative_dga.py +++ b/src/sage/algebras/commutative_dga.py @@ -211,7 +211,7 @@ def __classcall__(cls, A, im_gens): raise ValueError("The given dictionary does not determine a degree 1 map") im_gens = tuple(im_gens.get(x, A.zero()) for x in A.gens()) - return super(Differential, cls).__classcall__(cls, A, im_gens) + return super().__classcall__(cls, A, im_gens) def __init__(self, A, im_gens): r""" @@ -998,9 +998,9 @@ def __classcall__(cls, base, names=None, degrees=None, R=None, I=None, category= for i in range(n) if is_odd(tot_degs[i])], side='twosided') - return super(GCAlgebra, cls).__classcall__(cls, base=base, names=names, - degrees=degrees, R=R, I=I, - category=category) + return super().__classcall__(cls, base=base, names=names, + degrees=degrees, R=R, I=I, + category=category) def __init__(self, base, R=None, I=None, names=None, degrees=None, category=None): """ @@ -1236,7 +1236,7 @@ def _coerce_map_from_(self, other): .gens()): return False return self.cover_ring().has_coerce_map_from(other.cover_ring()) - return super(GCAlgebra, self)._coerce_map_from_(other) + return super()._coerce_map_from_(other) def _element_constructor_(self, x, coerce=True): r""" @@ -1755,7 +1755,7 @@ def _coerce_map_from_(self, other): return False elif isinstance(other, GCAlgebra): # Not multigraded return False - return super(GCAlgebra_multigraded, self)._coerce_map_from_(other) + return super()._coerce_map_from_(other) def basis(self, n, total=False): """ diff --git a/src/sage/algebras/hecke_algebras/cubic_hecke_algebra.py b/src/sage/algebras/hecke_algebras/cubic_hecke_algebra.py index f3cdb5a6af8..29d444e745f 100644 --- a/src/sage/algebras/hecke_algebras/cubic_hecke_algebra.py +++ b/src/sage/algebras/hecke_algebras/cubic_hecke_algebra.py @@ -113,17 +113,15 @@ - Sebastian Oehms May 2020: initial version """ - -############################################################################## +# ########################################################################### # Copyright (C) 2020 Sebastian Oehms # # 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 warnings import warn from sage.combinat.free_module import CombinatorialFreeModule @@ -188,7 +186,8 @@ def __invert__(self): inverse_Tietze = () len_self = len(self_Tietze) - inverse_Tietze = tuple([-1*self_Tietze[len_self - i - 1] for i in range(len_self)]) + inverse_Tietze = tuple([-1 * self_Tietze[len_self - i - 1] + for i in range(len_self)]) P = self.parent() return P(inverse_Tietze) @@ -829,9 +828,9 @@ def __classcall_private__(cls, n=None, names='c', cubic_equation_parameters=None from sage.structure.category_object import normalize_names names = tuple(normalize_names(n, names)) - return super(CubicHeckeAlgebra, cls).__classcall__(cls, names, - cubic_equation_parameters=cubic_equation_parameters, - cubic_equation_roots=cubic_equation_roots) + return super().__classcall__(cls, names, + cubic_equation_parameters=cubic_equation_parameters, + cubic_equation_roots=cubic_equation_roots) def __init__(self, names, cubic_equation_parameters=None, cubic_equation_roots=None): r""" @@ -3272,7 +3271,7 @@ def cubic_hecke_subalgebra(self, nstrands=None): if nstrands == self._nstrands - 1 and self._cubic_hecke_subalgebra is not None: return self._cubic_hecke_subalgebra - names_red = names[:nstrands-1] + names_red = names[:nstrands - 1] if self.base_ring() == self.base_ring(generic=True): SubHeckeAlg = CubicHeckeAlgebra(names=names_red) else: diff --git a/src/sage/algebras/hecke_algebras/cubic_hecke_base_ring.py b/src/sage/algebras/hecke_algebras/cubic_hecke_base_ring.py index 1a21f5dcb9f..6388fa79e60 100644 --- a/src/sage/algebras/hecke_algebras/cubic_hecke_base_ring.py +++ b/src/sage/algebras/hecke_algebras/cubic_hecke_base_ring.py @@ -11,17 +11,15 @@ - Sebastian Oehms May 2020: initial version """ - -############################################################################## +# ########################################################################### # Copyright (C) 2020 Sebastian Oehms # # 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.category_object import normalize_names from sage.structure.element import get_coercion_model from sage.categories.action import Action @@ -152,7 +150,7 @@ def _act_(self, perm, pol): for key, value in pol.dict().items(): newkey = [0] * len(key) for pos, k in enumerate(key): - newkey[perm(pos+1)-1] = k + newkey[perm(pos + 1) - 1] = k pol_dict[tuple(newkey)] = value return self.domain()(pol_dict) @@ -313,7 +311,7 @@ def _element_constructor_(self, x, mon=None): from sage.interfaces.gap3 import GAP3Element if isinstance(x, GAP3Element): return self._convert_from_gap3_mvp(x) - return super(CubicHeckeExtensionRing, self)._element_constructor_(x, mon=mon) + return super()._element_constructor_(x, mon=mon) def _coerce_map_from_(self, R): r""" @@ -338,8 +336,8 @@ def _coerce_map_from_(self, R): markov = R.markov_trace_version() a, b, c, *rem = self.gens() iu = a + b + c - iv = a*b + a*c + b*c - iw = a*b*c + iv = a * b + a * c + b * c + iw = a * b * c im_gens = [iu, iv, iw] if markov: if self.markov_trace_version(): @@ -351,7 +349,7 @@ def _coerce_map_from_(self, R): else: embedding_into_extension_ring = R.hom(im_gens) return embedding_into_extension_ring - return super(CubicHeckeExtensionRing, self)._coerce_map_from_(R) + return super()._coerce_map_from_(R) def hom(self, im_gens, codomain=None, check=True, base_map=None): r""" @@ -383,13 +381,13 @@ def hom(self, im_gens, codomain=None, check=True, base_map=None): e3, *im_remain = im_gens hom_cycl_gen = self.base_ring().hom([e3], codomain=e3.parent(), check=check, base_map=base_map) verbose("hom_cycl_gen %s" % hom_cycl_gen, level=2) - return super(CubicHeckeExtensionRing, self).hom(im_remain, codomain=codomain, check=check, base_map=hom_cycl_gen) + return super().hom(im_remain, codomain=codomain, check=check, base_map=hom_cycl_gen) else: if base_map is None: raise ValueError('number of images must be four (inculding a ' 'third root of unity at first position) or a ' 'base_map (on %s) must be given' % self.base_ring()) - return super(CubicHeckeExtensionRing, self).hom(im_gens, codomain=codomain, check=check, base_map=base_map) + return super().hom(im_gens, codomain=codomain, check=check, base_map=base_map) def _an_element_(self): r""" @@ -1164,10 +1162,10 @@ def mirror_involution(self): if self._mirror is None: if self._is_markov_trace_version(): u, v, w, s = self.gens() - self._mirror = self.hom([v/w, u/w, ~w, ~s]) + self._mirror = self.hom([v / w, u / w, ~w, ~s]) else: u, v, w = self.gens() - self._mirror = self.hom([v/w, u/w, ~w]) + self._mirror = self.hom([v / w, u / w, ~w]) return self._mirror def create_specialization(self, im_cubic_equation_parameters, im_writhe_parameter=None): diff --git a/src/sage/algebras/hecke_algebras/cubic_hecke_matrix_rep.py b/src/sage/algebras/hecke_algebras/cubic_hecke_matrix_rep.py index b2d68fb2f6f..ef8ba7316c9 100644 --- a/src/sage/algebras/hecke_algebras/cubic_hecke_matrix_rep.py +++ b/src/sage/algebras/hecke_algebras/cubic_hecke_matrix_rep.py @@ -14,18 +14,17 @@ - Sebastian Oehms May 2020: initial version """ - -############################################################################## +# ########################################################################### # Copyright (C) 2020 Sebastian Oehms # # 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 enum import Enum + from sage.misc.cachefunc import cached_method from sage.misc.verbose import verbose from sage.rings.integer import Integer @@ -136,10 +135,10 @@ def number_of_representations(self, nstrands): raise ValueError("nstrands must be between 1 and 4") return self.value['num_rep'][nstrands - 1] - RegularLeft = {'split': False, 'regular': True, 'data': sc.regular_left, 'num_rep': [1, 1, 1, 1]} - RegularRight = {'split': False, 'regular': True, 'data': sc.regular_right, 'num_rep': [1, 1, 1, 1]} - SplitIrredMarin = {'split': True, 'regular': False, 'data': sc.split_irred, 'num_rep': [1, 3, 7, 24]} - SplitIrredChevie = {'split': True, 'regular': False, 'data': None, 'num_rep': [1, 3, 7, 24, 30]} + RegularLeft = {'split': False, 'regular': True, 'data': sc.regular_left, 'num_rep': [1, 1, 1, 1]} + RegularRight = {'split': False, 'regular': True, 'data': sc.regular_right, 'num_rep': [1, 1, 1, 1]} + SplitIrredMarin = {'split': True, 'regular': False, 'data': sc.split_irred, 'num_rep': [1, 3, 7, 24]} + SplitIrredChevie = {'split': True, 'regular': False, 'data': None, 'num_rep': [1, 3, 7, 24, 30]} # --------------------------------------------- @@ -275,42 +274,42 @@ def internal_index(self): # ------------------------------------------------------------------------------------------------- # absolutely irreducible representations corresponding to braids on 2 strands # ------------------------------------------------------------------------------------------------- - W2_100 = {'alt_name': 'Sa', 'dim': 1, 'ngens': 1, 'len_orbit': 3, 'gap_ind': 0, 'intern_ind': 0} - W2_001 = {'alt_name': 'Sc', 'dim': 1, 'ngens': 1, 'len_orbit': 3, 'gap_ind': 1, 'intern_ind': 1} - W2_010 = {'alt_name': 'Sb', 'dim': 1, 'ngens': 1, 'len_orbit': 3, 'gap_ind': 2, 'intern_ind': 2} + W2_100 = {'alt_name': 'Sa', 'dim': 1, 'ngens': 1, 'len_orbit': 3, 'gap_ind': 0, 'intern_ind': 0} + W2_001 = {'alt_name': 'Sc', 'dim': 1, 'ngens': 1, 'len_orbit': 3, 'gap_ind': 1, 'intern_ind': 1} + W2_010 = {'alt_name': 'Sb', 'dim': 1, 'ngens': 1, 'len_orbit': 3, 'gap_ind': 2, 'intern_ind': 2} # ------------------------------------------------------------------------------------------------- # absolutely irreducible representations corresponding to braids on 3 strands # ------------------------------------------------------------------------------------------------- - W3_100 = {'alt_name': 'Sa', 'dim': 1, 'ngens': 2, 'len_orbit': 3, 'gap_ind': 0, 'intern_ind': 0} - W3_001 = {'alt_name': 'Sc', 'dim': 1, 'ngens': 2, 'len_orbit': 3, 'gap_ind': 1, 'intern_ind': 1} - W3_010 = {'alt_name': 'Sb', 'dim': 1, 'ngens': 2, 'len_orbit': 3, 'gap_ind': 2, 'intern_ind': 2} + W3_100 = {'alt_name': 'Sa', 'dim': 1, 'ngens': 2, 'len_orbit': 3, 'gap_ind': 0, 'intern_ind': 0} + W3_001 = {'alt_name': 'Sc', 'dim': 1, 'ngens': 2, 'len_orbit': 3, 'gap_ind': 1, 'intern_ind': 1} + W3_010 = {'alt_name': 'Sb', 'dim': 1, 'ngens': 2, 'len_orbit': 3, 'gap_ind': 2, 'intern_ind': 2} - W3_011 = {'alt_name': 'Tbc', 'dim': 2, 'ngens': 2, 'len_orbit': 3, 'gap_ind': 3, 'intern_ind': 3} - W3_110 = {'alt_name': 'Tab', 'dim': 2, 'ngens': 2, 'len_orbit': 3, 'gap_ind': 4, 'intern_ind': 4} - W3_101 = {'alt_name': 'Tac', 'dim': 2, 'ngens': 2, 'len_orbit': 3, 'gap_ind': 5, 'intern_ind': 5} + W3_011 = {'alt_name': 'Tbc', 'dim': 2, 'ngens': 2, 'len_orbit': 3, 'gap_ind': 3, 'intern_ind': 3} + W3_110 = {'alt_name': 'Tab', 'dim': 2, 'ngens': 2, 'len_orbit': 3, 'gap_ind': 4, 'intern_ind': 4} + W3_101 = {'alt_name': 'Tac', 'dim': 2, 'ngens': 2, 'len_orbit': 3, 'gap_ind': 5, 'intern_ind': 5} - W3_111 = {'alt_name': 'V', 'dim': 3, 'ngens': 2, 'len_orbit': 1, 'gap_ind': 6, 'intern_ind': 6} + W3_111 = {'alt_name': 'V', 'dim': 3, 'ngens': 2, 'len_orbit': 1, 'gap_ind': 6, 'intern_ind': 6} # ------------------------------------------------------------------------------------------------- # absolutely irreducible representations corresponding to braids on 4 strands # ------------------------------------------------------------------------------------------------- - W4_100 = {'alt_name': 'Sa', 'dim': 1, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 0, 'intern_ind': 0} - W4_001 = {'alt_name': 'Sc', 'dim': 1, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 1, 'intern_ind': 1} - W4_010 = {'alt_name': 'Sb', 'dim': 1, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 2, 'intern_ind': 2} + W4_100 = {'alt_name': 'Sa', 'dim': 1, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 0, 'intern_ind': 0} + W4_001 = {'alt_name': 'Sc', 'dim': 1, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 1, 'intern_ind': 1} + W4_010 = {'alt_name': 'Sb', 'dim': 1, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 2, 'intern_ind': 2} - W4_011 = {'alt_name': 'Tbc', 'dim': 2, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 3, 'intern_ind': 3} - W4_110 = {'alt_name': 'Tab', 'dim': 2, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 4, 'intern_ind': 4} - W4_101 = {'alt_name': 'Tac', 'dim': 2, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 5, 'intern_ind': 5} + W4_011 = {'alt_name': 'Tbc', 'dim': 2, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 3, 'intern_ind': 3} + W4_110 = {'alt_name': 'Tab', 'dim': 2, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 4, 'intern_ind': 4} + W4_101 = {'alt_name': 'Tac', 'dim': 2, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 5, 'intern_ind': 5} - W4_111 = {'alt_name': 'V', 'dim': 3, 'ngens': 3, 'len_orbit': 1, 'gap_ind': 6, 'intern_ind': 6} + W4_111 = {'alt_name': 'V', 'dim': 3, 'ngens': 3, 'len_orbit': 1, 'gap_ind': 6, 'intern_ind': 6} - W4_120 = {'alt_name': 'Uba', 'dim': 3, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 7, 'intern_ind': 7} - W4_201 = {'alt_name': 'Uac', 'dim': 3, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 8, 'intern_ind': 8} - W4_012 = {'alt_name': 'Ucb', 'dim': 3, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 9, 'intern_ind': 9} - W4_102 = {'alt_name': 'Uca', 'dim': 3, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 10, 'intern_ind': 10} - W4_210 = {'alt_name': 'Uab', 'dim': 3, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 11, 'intern_ind': 11} - W4_021 = {'alt_name': 'Ubc', 'dim': 3, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 12, 'intern_ind': 12} + W4_120 = {'alt_name': 'Uba', 'dim': 3, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 7, 'intern_ind': 7} + W4_201 = {'alt_name': 'Uac', 'dim': 3, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 8, 'intern_ind': 8} + W4_012 = {'alt_name': 'Ucb', 'dim': 3, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 9, 'intern_ind': 9} + W4_102 = {'alt_name': 'Uca', 'dim': 3, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 10, 'intern_ind': 10} + W4_210 = {'alt_name': 'Uab', 'dim': 3, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 11, 'intern_ind': 11} + W4_021 = {'alt_name': 'Ubc', 'dim': 3, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 12, 'intern_ind': 12} W4_213 = {'alt_name': 'Vcab', 'dim': 6, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 13, 'intern_ind': 13} W4_132 = {'alt_name': 'Vbca', 'dim': 6, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 14, 'intern_ind': 14} @@ -319,11 +318,11 @@ def internal_index(self): W4_123 = {'alt_name': 'Vcba', 'dim': 6, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 17, 'intern_ind': 17} W4_312 = {'alt_name': 'Vacb', 'dim': 6, 'ngens': 3, 'len_orbit': 6, 'gap_ind': 18, 'intern_ind': 18} - W4_422 = {'alt_name': 'Wa', 'dim': 8, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 19, 'intern_ind': 19} - W4_224 = {'alt_name': 'Wc', 'dim': 8, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 20, 'intern_ind': 20} - W4_242 = {'alt_name': 'Wb', 'dim': 8, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 21, 'intern_ind': 21} + W4_422 = {'alt_name': 'Wa', 'dim': 8, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 19, 'intern_ind': 19} + W4_224 = {'alt_name': 'Wc', 'dim': 8, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 20, 'intern_ind': 20} + W4_242 = {'alt_name': 'Wb', 'dim': 8, 'ngens': 3, 'len_orbit': 3, 'gap_ind': 21, 'intern_ind': 21} - W4_333 = {'alt_name': 'X', 'dim': 9, 'ngens': 3, 'len_orbit': 2, 'gap_ind': 22, 'intern_ind': 22} + W4_333 = {'alt_name': 'X', 'dim': 9, 'ngens': 3, 'len_orbit': 2, 'gap_ind': 22, 'intern_ind': 22} W4_333bar = {'alt_name': 'Xbar', 'dim': 9, 'ngens': 3, 'len_orbit': 2, 'gap_ind': 23, 'intern_ind': 23} # ------------------------------------------------------------------------------------------------- @@ -340,7 +339,7 @@ def internal_index(self): W5_103 = {'alt_name': None, 'dim': 4, 'ngens': 4, 'len_orbit': 6, 'gap_ind': 7, 'intern_ind': 7} W5_310 = {'alt_name': None, 'dim': 4, 'ngens': 4, 'len_orbit': 6, 'gap_ind': 8, 'intern_ind': 8} - W5_203 = {'alt_name': None, 'dim': 5, 'ngens': 4, 'len_orbit': 6, 'gap_ind': 9, 'intern_ind': 9} + W5_203 = {'alt_name': None, 'dim': 5, 'ngens': 4, 'len_orbit': 6, 'gap_ind': 9, 'intern_ind': 9} W5_032 = {'alt_name': None, 'dim': 5, 'ngens': 4, 'len_orbit': 6, 'gap_ind': 10, 'intern_ind': 10} W5_320 = {'alt_name': None, 'dim': 5, 'ngens': 4, 'len_orbit': 6, 'gap_ind': 11, 'intern_ind': 11} W5_230 = {'alt_name': None, 'dim': 5, 'ngens': 4, 'len_orbit': 6, 'gap_ind': 12, 'intern_ind': 12} @@ -513,7 +512,7 @@ def __getitem__(self, item): elif isinstance(item, (Integer, int)): return self._get_block(item) - return super(CubicHeckeMatrixRep, self).__getitem__(item) + return super().__getitem__(item) @cached_method def block_diagonal_list(self): @@ -799,12 +798,12 @@ def __call__(self, entries=None, coerce=True, copy=None): """ from sage.algebras.hecke_algebras.cubic_hecke_algebra import CubicHeckeAlgebra if entries is None: - return super(CubicHeckeMatrixSpace, self).__call__(entries=entries, coerce=coerce, copy=copy) + return super().__call__(entries=entries, coerce=coerce, copy=copy) if not hasattr(entries, 'parent'): - return super(CubicHeckeMatrixSpace, self).__call__(entries=entries, coerce=coerce, copy=copy) + return super().__call__(entries=entries, coerce=coerce, copy=copy) ele_parent = entries.parent() if not isinstance(ele_parent, (CubicHeckeAlgebra, MatrixSpace)): - return super(CubicHeckeMatrixSpace, self).__call__(entries=entries, coerce=coerce, copy=copy) + return super().__call__(entries=entries, coerce=coerce, copy=copy) return self._element_constructor_(entries) @cached_method @@ -890,12 +889,12 @@ def invert_gen(matr): Return the inverse matrix of generators. """ cfs = ch_algebra.cubic_equation(as_coefficients=True, generic=True) - fac = - 1/cfs[0] - cf0, cf1, cf2, cf3 = [original_base_ring(cf*fac) for cf in cfs] + fac = - 1 / cfs[0] + cf0, cf1, cf2, cf3 = [original_base_ring(cf * fac) for cf in cfs] - matri = cf1*matr.parent().one() - matri += cf2*matr - matri += cf3*matr**2 + matri = cf1 * matr.parent().one() + matri += cf2 * matr + matri += cf3 * matr**2 d1, d2 = matr.dimensions() matrI = matrix(original_base_ring, d1, d2, lambda i, j: original_base_ring(matri[i, j])) return matrI @@ -912,7 +911,7 @@ def invert_gen(matr): num_rep = representation_type.number_of_representations(n) if representation_type == RepresentationType.SplitIrredChevie: - rep_list = [ch_algebra._fetch_matrix_list_from_chevie(i+1) for i in range(num_rep)] + rep_list = [ch_algebra._fetch_matrix_list_from_chevie(i + 1) for i in range(num_rep)] if gen_ind > 0: matrix_list = [rep[gen_ind - 1] for rep in rep_list] else: @@ -1003,7 +1002,7 @@ def zero(self): [0 0 0] ] """ - z = self.element_class(self, super(CubicHeckeMatrixSpace, self).zero()) + z = self.element_class(self, super().zero()) z._cubic_hecke_element = self._cubic_hecke_algebra.zero() z.set_immutable() return z @@ -1031,7 +1030,7 @@ def one(self): [0 0 1] ] """ - o = self.element_class(self, super(CubicHeckeMatrixSpace, self).one()) + o = self.element_class(self, super().one()) o._cubic_hecke_element = self._cubic_hecke_algebra.one() o.set_immutable() return o From 74601e6a3a42413400b2718ad2fed987028a30ca Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 13 Nov 2022 10:10:08 -0800 Subject: [PATCH 466/632] Remove unused import --- src/sage/manifolds/differentiable/vectorfield_module.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/sage/manifolds/differentiable/vectorfield_module.py b/src/sage/manifolds/differentiable/vectorfield_module.py index 8cd62dacc8c..ac8e7dc1290 100644 --- a/src/sage/manifolds/differentiable/vectorfield_module.py +++ b/src/sage/manifolds/differentiable/vectorfield_module.py @@ -54,10 +54,7 @@ from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation from sage.tensor.modules.finite_rank_free_module import FiniteRankFreeModule -from sage.tensor.modules.reflexive_module import ( - ReflexiveModule_abstract, - ReflexiveModule_base -) +from sage.tensor.modules.reflexive_module import ReflexiveModule_base if TYPE_CHECKING: from sage.manifolds.differentiable.diff_map import DiffMap From bcab6e59d96411cce472f5cc79ef1d29ae0c42d8 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Sun, 13 Nov 2022 10:24:52 -0800 Subject: [PATCH 467/632] trac 34741: add comment with ticket reference --- src/sage/doctest/parsing.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/doctest/parsing.py b/src/sage/doctest/parsing.py index 7c54a17fd93..26d793b96bf 100644 --- a/src/sage/doctest/parsing.py +++ b/src/sage/doctest/parsing.py @@ -1023,6 +1023,8 @@ def do_fixup(self, want, got): did_fixup = True if "newer macOS version" in got: + # :trac:`34741` -- suppress warning arising after + # upgrading from macOS 12.X to 13.X. newer_macOS_version_regex = re.compile(r'.*dylib \(.*\) was built for newer macOS version \(.*\) than being linked \(.*\)') got = newer_macOS_version_regex.sub('', got) did_fixup = True From d18036b4148283139e7c66fb57f92468c0470715 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 13 Nov 2022 16:42:51 -0800 Subject: [PATCH 468/632] build/pkgs/cmake: Update to 3.24.3 --- build/pkgs/cmake/checksums.ini | 6 +++--- build/pkgs/cmake/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/cmake/checksums.ini b/build/pkgs/cmake/checksums.ini index 64ac39d871a..c89abdf4277 100644 --- a/build/pkgs/cmake/checksums.ini +++ b/build/pkgs/cmake/checksums.ini @@ -1,5 +1,5 @@ tarball=cmake-VERSION.tar.gz -sha1=abbeedb49c153be4103eabc95f4ffd94440f4d61 -md5=f616604606184e3c7b870a57e68a7c3b -cksum=2102786355 +sha1=256d6a57a57fa6ceaacd6a2daf708baefd33850c +md5=226dd564164372f9f7d1e21e38e6e8c5 +cksum=2080281918 upstream_url=https://github.com/Kitware/CMake/releases/download/vVERSION/cmake-VERSION.tar.gz diff --git a/build/pkgs/cmake/package-version.txt b/build/pkgs/cmake/package-version.txt index 6075c9a9ff9..693bd59e3e6 100644 --- a/build/pkgs/cmake/package-version.txt +++ b/build/pkgs/cmake/package-version.txt @@ -1 +1 @@ -3.21.0 +3.24.3 From dce791c7544c32388d34c7bfadaede61e51a1e92 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 13 Nov 2022 16:44:50 -0800 Subject: [PATCH 469/632] build/pkgs/cmake/spkg-configure.m4: Increase minimum version from 3.4 to 3.10 (as found on ubuntu-bionic) --- build/pkgs/cmake/spkg-configure.m4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/pkgs/cmake/spkg-configure.m4 b/build/pkgs/cmake/spkg-configure.m4 index bc50446d0ef..8934bfc273f 100644 --- a/build/pkgs/cmake/spkg-configure.m4 +++ b/build/pkgs/cmake/spkg-configure.m4 @@ -1,11 +1,11 @@ SAGE_SPKG_CONFIGURE( [cmake], [ - AC_CACHE_CHECK([for cmake >= 3.4], [ac_cv_path_CMAKE], [ + AC_CACHE_CHECK([for cmake >= 3.10], [ac_cv_path_CMAKE], [ AC_PATH_PROGS_FEATURE_CHECK([CMAKE], [cmake], [ cmake_version=`$ac_path_CMAKE --version 2>&1 \ | $SED -n -e 's/cmake version *\([[0-9]]*\.[[0-9]]*\.[[0-9]]*\)/\1/p'` AS_IF([test -n "$cmake_version"], [ - AX_COMPARE_VERSION([$cmake_version], [ge], [3.4], [ + AX_COMPARE_VERSION([$cmake_version], [ge], [3.10], [ ac_cv_path_CMAKE="$ac_path_CMAKE" ac_path_CMAKE_found=: ]) From 74b4574ec0b9803abecfa348ea79c0cdc60f07e2 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 13 Nov 2022 20:23:23 -0800 Subject: [PATCH 470/632] build/pkgs/_python3.*/distros/debian.txt: Add python3.*-distutils --- build/pkgs/_python3.10/distros/debian.txt | 1 + build/pkgs/_python3.11/distros/debian.txt | 1 + build/pkgs/_python3.8/distros/debian.txt | 1 + build/pkgs/_python3.9/distros/debian.txt | 1 + 4 files changed, 4 insertions(+) diff --git a/build/pkgs/_python3.10/distros/debian.txt b/build/pkgs/_python3.10/distros/debian.txt index 9b7529828a1..a3e949f403c 100644 --- a/build/pkgs/_python3.10/distros/debian.txt +++ b/build/pkgs/_python3.10/distros/debian.txt @@ -1,3 +1,4 @@ python3.10 python3.10-dev +python3.10-distutils python3.10-venv diff --git a/build/pkgs/_python3.11/distros/debian.txt b/build/pkgs/_python3.11/distros/debian.txt index a3128e4751f..8877c2d2af0 100644 --- a/build/pkgs/_python3.11/distros/debian.txt +++ b/build/pkgs/_python3.11/distros/debian.txt @@ -1,3 +1,4 @@ python3.11 python3.11-dev +python3.11-distutils python3.11-venv diff --git a/build/pkgs/_python3.8/distros/debian.txt b/build/pkgs/_python3.8/distros/debian.txt index 11476fe0595..bf46e908ff6 100644 --- a/build/pkgs/_python3.8/distros/debian.txt +++ b/build/pkgs/_python3.8/distros/debian.txt @@ -1,3 +1,4 @@ python3.8 python3.8-dev +python3.8-distutils python3.8-venv diff --git a/build/pkgs/_python3.9/distros/debian.txt b/build/pkgs/_python3.9/distros/debian.txt index e113156b021..014b90fd8ad 100644 --- a/build/pkgs/_python3.9/distros/debian.txt +++ b/build/pkgs/_python3.9/distros/debian.txt @@ -1,3 +1,4 @@ python3.9 python3.9-dev +python3.9-distutils python3.9-venv From f53288683dc6c870c9ea0105c37c4b8f18b3cbfd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 13 Nov 2022 21:25:28 -0800 Subject: [PATCH 471/632] tox.ini: Add ubuntu-deadsnakes, python3.12 --- build/bin/write-dockerfile.sh | 8 ++++++-- build/pkgs/_python3.12/distros/arch.txt | 1 + build/pkgs/_python3.12/distros/cygwin.txt | 1 + build/pkgs/_python3.12/distros/debian.txt | 4 ++++ build/pkgs/_python3.12/distros/fedora.txt | 2 ++ build/pkgs/_python3.12/distros/freebsd.txt | 1 + build/pkgs/_python3.12/distros/homebrew.txt | 1 + build/pkgs/_python3.12/distros/macports.txt | 1 + build/pkgs/_python3.12/distros/opensuse.txt | 2 ++ tox.ini | 13 ++++++++----- 10 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 build/pkgs/_python3.12/distros/arch.txt create mode 100644 build/pkgs/_python3.12/distros/cygwin.txt create mode 100644 build/pkgs/_python3.12/distros/debian.txt create mode 100644 build/pkgs/_python3.12/distros/fedora.txt create mode 100644 build/pkgs/_python3.12/distros/freebsd.txt create mode 100644 build/pkgs/_python3.12/distros/homebrew.txt create mode 100644 build/pkgs/_python3.12/distros/macports.txt create mode 100644 build/pkgs/_python3.12/distros/opensuse.txt diff --git a/build/bin/write-dockerfile.sh b/build/bin/write-dockerfile.sh index 42eb5f53686..14cbaf786eb 100755 --- a/build/bin/write-dockerfile.sh +++ b/build/bin/write-dockerfile.sh @@ -63,11 +63,15 @@ EOF RUN sed -i.bak $DIST_UPGRADE /etc/apt/sources.list && apt-get update && DEBIAN_FRONTEND=noninteractive apt-get -y dist-upgrade EOF fi - if [ -n "$EXTRA_REPOSITORY" ]; then + if [ -n "$EXTRA_REPOSITORIES" ]; then cat < Date: Sun, 13 Nov 2022 21:45:47 -0800 Subject: [PATCH 472/632] build/pkgs/cmake/spkg-configure.m4: Increase minimum version to 3.11 --- build/pkgs/cmake/spkg-configure.m4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/pkgs/cmake/spkg-configure.m4 b/build/pkgs/cmake/spkg-configure.m4 index 8934bfc273f..ce36e8aa0cc 100644 --- a/build/pkgs/cmake/spkg-configure.m4 +++ b/build/pkgs/cmake/spkg-configure.m4 @@ -1,11 +1,11 @@ SAGE_SPKG_CONFIGURE( [cmake], [ - AC_CACHE_CHECK([for cmake >= 3.10], [ac_cv_path_CMAKE], [ + AC_CACHE_CHECK([for cmake >= 3.11], [ac_cv_path_CMAKE], [ AC_PATH_PROGS_FEATURE_CHECK([CMAKE], [cmake], [ cmake_version=`$ac_path_CMAKE --version 2>&1 \ | $SED -n -e 's/cmake version *\([[0-9]]*\.[[0-9]]*\.[[0-9]]*\)/\1/p'` AS_IF([test -n "$cmake_version"], [ - AX_COMPARE_VERSION([$cmake_version], [ge], [3.10], [ + AX_COMPARE_VERSION([$cmake_version], [ge], [3.11], [ ac_cv_path_CMAKE="$ac_path_CMAKE" ac_path_CMAKE_found=: ]) From 501a25411904ead8d9b097f465cd40b984eaa701 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 14 Nov 2022 16:20:02 +0100 Subject: [PATCH 473/632] Implement `characteristic` method for rings extensions See ticket #34692. --- src/sage/rings/ring_extension.pyx | 42 +++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/sage/rings/ring_extension.pyx b/src/sage/rings/ring_extension.pyx index 2b649359ad4..9f53d42f80f 100644 --- a/src/sage/rings/ring_extension.pyx +++ b/src/sage/rings/ring_extension.pyx @@ -1885,6 +1885,48 @@ cdef class RingExtension_generic(CommutativeAlgebra): parent = self.Hom(codomain, category=category) return RingExtensionHomomorphism(parent, im_gens, base_map, check) + def characteristic(self): + r""" + Return the characteristic of the extension as a ring. + + OUTPUT: + + A prime number. + + EXAMPLES:: + + sage: F = GF(5^2).over() # over GF(5) + sage: K = GF(5^4).over(F) + sage: L = GF(5^12).over(K) + sage: F.characteristic() + 5 + sage: K.characteristic() + 5 + sage: L.characteristic() + 5 + + :: + + sage: F = RR.over(ZZ) + sage: F.characteristic() + 0 + + :: + + sage: F = GF(11) + sage: A. = F[] + sage: K = Frac(F).over(F) + sage: K.characteristic() + 11 + + :: + + sage: R = Zp(7).over(ZZ) + sage: R.characteristic() + 0 + """ + return self._backend.characteristic() + # Fraction fields ################# From bcffa0cf0842ebef2403d5c8b6aa88514beb86e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 14 Nov 2022 17:18:21 +0100 Subject: [PATCH 474/632] Fix imprecision in documentation See David's comment: https://trac.sagemath.org/ticket/34692#comment:5. --- src/sage/rings/ring_extension.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/ring_extension.pyx b/src/sage/rings/ring_extension.pyx index 9f53d42f80f..bbc082d2e77 100644 --- a/src/sage/rings/ring_extension.pyx +++ b/src/sage/rings/ring_extension.pyx @@ -1891,7 +1891,7 @@ cdef class RingExtension_generic(CommutativeAlgebra): OUTPUT: - A prime number. + A prime number or zero. EXAMPLES:: From 675e24988a07f58d092a7ddb976c9e6ee04f3ba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 14 Nov 2022 18:03:12 +0100 Subject: [PATCH 475/632] Add tests for characteristic method See https://trac.sagemath.org/ticket/34692#comment:9. --- src/sage/rings/ring_extension.pyx | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/ring_extension.pyx b/src/sage/rings/ring_extension.pyx index bbc082d2e77..c761314585c 100644 --- a/src/sage/rings/ring_extension.pyx +++ b/src/sage/rings/ring_extension.pyx @@ -1921,9 +1921,21 @@ cdef class RingExtension_generic(CommutativeAlgebra): :: - sage: R = Zp(7).over(ZZ) - sage: R.characteristic() - 0 + sage: E = Fp(7).over(ZZ) + sage: E.characteristic() + 7 + + TESTS:: + + Ensure ticket :trac:`34692` is fixed. + + sage: Fq = GF(11) + sage: FqX. = Fq[] + sage: k = Frac(FqX) + sage: i = Hom(FqX, k).natural_map() + sage: K = k.over(i) + sage: K.frobenius_endomorphism() + Frobenius endomorphism x |--> x^11 of Fraction Field of Univariate Polynomial Ring in X over Finite Field of size 11 over its base """ return self._backend.characteristic() From ab2c211033e15fa5237b20690a715d50577d06f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 14 Nov 2022 18:52:42 +0100 Subject: [PATCH 476/632] Fix doctest See https://trac.sagemath.org/ticket/34692#comment:11. --- src/sage/rings/ring_extension.pyx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/ring_extension.pyx b/src/sage/rings/ring_extension.pyx index c761314585c..b493f57bed9 100644 --- a/src/sage/rings/ring_extension.pyx +++ b/src/sage/rings/ring_extension.pyx @@ -1921,19 +1921,18 @@ cdef class RingExtension_generic(CommutativeAlgebra): :: - sage: E = Fp(7).over(ZZ) + sage: E = GF(7).over(ZZ) sage: E.characteristic() 7 - TESTS:: + TESTS: - Ensure ticket :trac:`34692` is fixed. + Ensure ticket :trac:`34692` is fixed:: sage: Fq = GF(11) sage: FqX. = Fq[] sage: k = Frac(FqX) - sage: i = Hom(FqX, k).natural_map() - sage: K = k.over(i) + sage: K = k.over(FqX) sage: K.frobenius_endomorphism() Frobenius endomorphism x |--> x^11 of Fraction Field of Univariate Polynomial Ring in X over Finite Field of size 11 over its base """ From 1de311622a6412f45a2232ae4a51e9d13c75b464 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Wed, 16 Nov 2022 15:23:31 +0900 Subject: [PATCH 477/632] Remove old materials from tutorial --- src/doc/en/tutorial/latex.rst | 435 +++++++++------------------- src/doc/en/tutorial/programming.rst | 6 +- 2 files changed, 142 insertions(+), 299 deletions(-) diff --git a/src/doc/en/tutorial/latex.rst b/src/doc/en/tutorial/latex.rst index b9297a1cfab..aafaaea6ac5 100644 --- a/src/doc/en/tutorial/latex.rst +++ b/src/doc/en/tutorial/latex.rst @@ -2,14 +2,9 @@ Sage, LaTeX and Friends ********************************* -AUTHOR: Rob Beezer (2010-05-23) - -Sage and the LaTeX dialect of TeX have an -intensely synergistic relationship. This section aims to -introduce the variety of interactions, beginning with the most -basic and proceeding to the more unusual and arcane. (So you may -not want to read this entire section on your first pass through -this tutorial.) +Sage and the LaTeX dialect of TeX have an intensely synergistic relationship. +This section aims to introduce the variety of interactions, beginning with the +most basic and proceeding to the more unusual. Overview ======== @@ -18,41 +13,36 @@ It may be easiest to understand the various uses of LaTeX with a brief overview of the mechanics of the three principal methods employed by Sage. - #. Every "object" in Sage is required to have a LaTeX representation. - You can access this representation by executing, in the notebook or - at the sage command line, ``latex(foo)`` where ``foo`` is some object - in Sage. The output is a string that should render a reasonably accurate - representation of ``foo`` when used in TeX's math-mode (for example, - when enclosed between a pair of single dollar signs). Some examples of - this follow below. + #. Every "object" in Sage is required to have a LaTeX representation. You + can access this representation by executing ``latex(foo)`` where + ``foo`` is some object in Sage. The output is a string that should + render a reasonably accurate representation of ``foo`` when used in + TeX's math-mode (for example, when enclosed between a pair of single + dollar signs). Some examples of this follow below. In this way, Sage can be used effectively for constructing portions of a LaTeX document: create or compute an object in Sage, print ``latex()`` of the object and cut/paste it into your document. - #. The notebook interface is configured to use - `MathJax `_ - to render mathematics - cleanly in a web browser. MathJax is an open source JavaScript - display engine for mathematics that works in all modern - browsers. It is able to render a large, but not totally - complete, subset of TeX. It has no support for - things like complicated tables, sectioning or document - management, as it is oriented towards accurately rendering - "snippets" of TeX. Seemingly automatic rendering of math in the - notebook is provided by converting the ``latex()`` - representation of an object (as described above) into a form of - HTML palatable to MathJax. - - Since MathJax uses its own scalable fonts, it is superior to other methods that - rely on converting equations, or other snippets of TeX, into static inline images. - - #. At the Sage command-line, or in the notebook when LaTeX code is - more involved than MathJax can handle, a system-wide installation of - LaTeX can be employed. Sage includes almost everything you need to - build and use Sage, but a significant exception is TeX itself. So in these - situations you need to have TeX installed, along with some associated - conversion utilities, to utilize the full power. + #. The Jupyter notebook interface uses `MathJax `_ + to render mathematics cleanly in a web browser. You can start this + automatic rendering by executing ``%display latex`` (and stop by + executing ``%display plain``). + + MathJax is an open source JavaScript display engine for mathematics that + works in all modern browsers. It is able to render a large, but not + totally complete, subset of TeX. It has no support for things like + complicated tables, sectioning or document management, as it is oriented + towards accurately rendering "snippets" of TeX. Seemingly automatic + rendering of math in the notebook is provided by converting the + ``latex()`` representation of an object (as described above) into a form + of HTML palatable to MathJax. + + #. A system-wide installation of LaTeX can be employed. Sage includes + almost everything you need to build and use Sage, but a significant + exception is TeX itself. So in these situations you need to have + TeX installed, along with some associated conversion utilities, to + utilize the full power. Here we demonstrate some basic uses of the ``latex()`` function. :: @@ -72,11 +62,10 @@ Here we demonstrate some basic uses of the ``latex()`` function. :: -1 & -1 & -1 \end{array}\right) -Basic MathJax functionality is largely automatic in the notebook, but -we can partially demonstrate this support with the ``MathJax`` class. -The ``eval`` function of this class converts a Sage object to its -LaTeX representation and then wraps it in HTML that invokes the CSS -"math" class, which then employs MathJax. :: +Basic MathJax functionality is largely automatic in the notebook, but we can +partially demonstrate this support with the ``MathJax`` class. The object of +this class converts a Sage object to its LaTeX representation and then wraps it +in HTML. :: sage: from sage.misc.html import MathJax sage: mj = MathJax() @@ -94,65 +83,28 @@ LaTeX representation and then wraps it in HTML that invokes the CSS Basic Use ========= -As indicated in the overview, the simplest way to exploit Sage's -support of LaTeX is to use the ``latex()`` function to create -legitimate LaTeX code to represent mathematical objects. These -strings can then be incorporated into standalone LaTeX documents. -This works identically in the notebook and at the Sage command -line. - -At the other extreme is the ``view()`` command. At the Sage -command line the command ``view(foo)`` will create the LaTeX -representation of ``foo``, incorporate this into a simple LaTeX -document, and then process that document with your system-wide -TeX installation. Finally, the appropriate viewer will be called -to display the output from the TeX command. Which version of TeX -is used, and therefore the nature of the output and associated -viewer, can be customized (see :ref:`sec-custom-processing`). - -In the notebook, the ``view(foo)`` command creates the -appropriate combination of HTML and CSS so that MathJax will -render the LaTeX representation properly in the worksheet. To the -user, it simply creates a nicely formatted version of the output, -distinct from the default ASCII output of Sage. Not every -mathematical object in Sage has a LaTeX representation amenable to -the limited capabilities of MathJax. In these cases, the MathJax -interpretation can be bypassed, the system-wide TeX called -instead, and the subsequent output converted to a graphic image -for display in the worksheet. Affecting and controlling this -process is discussed below in the section -:ref:`sec-custom-generation`. - -The notebook has two other features for employing TeX. -The first is the "Typeset" button just above the first cell of a -worksheet, to the right of the four drop-down boxes. When -checked, any subsequent evaluations of cells will result in -output interpreted by MathJax, hence of a typeset quality. Note -that this effect is not retroactive -- previously evaluated cells -need to be re-evaluated. Essentially, checking the "Typeset" -button is identical to wrapping the output of each cell in the -``view()`` command. - -A second feature of the notebook is entering TeX as -part of annotating a worksheet. When the cursor is placed -between cells of a worksheet so that a blue bar appears, then a -shift-click will open a mini-word-processor, TinyMCE. This -allows for the entry of text, using a WSIWYG editor to create -HTML and CSS command for styled text. So it is possible to add -formatted text as commentary within a worksheet. However, text -between pairs of dollar signs, or pairs of double dollar signs is -interpreted by MathJax as inline or display math (respectively). +As indicated in the overview, the simplest way to exploit Sage's support of +LaTeX is to use the ``latex()`` function to create legitimate LaTeX code to +represent mathematical objects. These strings can then be incorporated into +standalone LaTeX documents. + +At the other extreme is the ``view()`` command. The command ``view(foo)`` will +create the LaTeX representation of ``foo``, incorporate this into a simple +LaTeX document, and then process that document with your system-wide TeX +installation. Finally, the appropriate viewer will be called to display the +output from the TeX command. Which version of TeX is used, and therefore the +nature of the output and associated viewer, can be customized (see +:ref:`sec-custom-processing`). .. _sec-custom-generation: Customizing LaTeX Generation ============================ -There are several ways to customize the actual LaTeX code generated by -the ``latex()`` command. In the notebook and at the Sage command-line -there is a pre-defined object named ``latex`` which has several methods, -which you can list by typing ``latex.``, followed by the :kbd:`Tab` key -(note the period). +There are several ways to customize the actual LaTeX code generated by the +``latex()`` command. There is a pre-defined object named ``latex`` which has +several methods, which you can list by typing ``latex.``, followed by the +:kbd:`Tab` key (note the period). A good example is the ``latex.matrix_delimiters`` method. It can be used to change the notation surrounding a matrix -- large parentheses, @@ -192,7 +144,7 @@ done in written work. This is accomplished by redefining the sage: latex(QQ) \Bold{Q} sage: from sage.misc.html import MathJax - sage: mj=MathJax() + sage: mj = MathJax() sage: mj(QQ) \[\newcommand{\Bold}[1]{\mathbf{#1}}\Bold{Q}\] sage: latex.blackboard_bold(True) @@ -200,10 +152,9 @@ done in written work. This is accomplished by redefining the \[\newcommand{\Bold}[1]{\mathbb{#1}}\Bold{Q}\] sage: latex.blackboard_bold(False) -It is possible to take advantage of the extensible nature of -TeX by adding in new macros and new packages. First, -individual macros can be added so that they are used when -MathJax interprets a snippet of TeX in the notebook. :: +It is possible to take advantage of the extensible nature of TeX by adding in +new macros and new packages. First, individual macros can be added so that +they are used when MathJax interprets a snippet of TeX in the notebook. :: sage: latex.extra_macros() '' @@ -215,18 +166,16 @@ MathJax interprets a snippet of TeX in the notebook. :: sage: latex(x+y) x + y sage: from sage.misc.html import MathJax - sage: mj=MathJax() + sage: mj = MathJax() sage: mj(x+y) \[\newcommand{\foo}{bar}x + y\] -Additional macros added this way will also be used in the event -that the system-wide version of TeX is called on -something larger than MathJax can handle. The command -``latex_extra_preamble`` is used to build the preamble of a -complete LaTeX document, so the following illustrates -how this is accomplished. As usual note the need for the -double-backslashes in the Python strings. :: - +Additional macros added this way will also be used in the event that the +system-wide version of TeX is called on something larger than MathJax can +handle. The command ``latex_extra_preamble`` is used to build the preamble of +a complete LaTeX document, so the following illustrates how this is +accomplished. As usual note the need for the double-backslashes in the Python +strings. :: sage: latex.extra_macros('') sage: latex.extra_preamble('') @@ -242,20 +191,16 @@ double-backslashes in the Python strings. :: \newcommand{\Bold}[1]{\mathbf{#1}} \newcommand{\foo}{bar} -Again, for larger or more complicated LaTeX -expressions, it is possible to add packages (or anything else) to -the preamble of the LaTeX file. Anything may be -incorporated into the preamble with the ``latex.add_to_preamble`` -command, and the specialized command -``latex.add_package_to_preamble_if_available`` will first check -if a certain package is actually available before trying to add -it to the preamble. - -Here we add the geometry package to the preamble and use it to -set the size of the region on the page that TeX will -use (effectively setting the margins). As usual, note the need -for the double-backslashes in the Python strings. :: +Again, for larger or more complicated LaTeX expressions, it is possible to add +packages (or anything else) to the preamble of the LaTeX file. Anything may be +incorporated into the preamble with the ``latex.add_to_preamble`` command, and +the specialized command ``latex.add_package_to_preamble_if_available`` will +first check if a certain package is actually available before trying to add it +to the preamble. +Here we add the geometry package to the preamble and use it to set the size of +the region on the page that TeX will use (effectively setting the margins). As +usual, note the need for the double-backslashes in the Python strings. :: sage: from sage.misc.latex import latex_extra_preamble sage: latex.extra_macros('') @@ -270,9 +215,9 @@ for the double-backslashes in the Python strings. :: ... \newcommand{\Bold}[1]{\mathbf{#1}} -A particular package may be added along with a check on its existence, -as follows. As an example, we just illustrate an attempt to add to -the preamble a package that presumably does not exist. :: +A particular package may be added along with a check on its existence, as +follows. As an example, we just illustrate an attempt to add to the preamble a +package that presumably does not exist. :: sage: latex.extra_preamble('') sage: latex.extra_preamble() @@ -289,94 +234,30 @@ the preamble a package that presumably does not exist. :: Customizing LaTeX Processing ============================ -It is also possible to control which variant of TeX is -used for system-wide invocations, thus also influencing the -nature of the output. - -The ``latex.engine()`` command can be used to control if the -system-wide executables ``latex``, ``pdflatex`` or ``xelatex`` -are employed for more complicated LaTeX expressions. -When ``view()`` is called from the sage command-line and the -engine is set to ``latex``, a dvi file is produced and Sage will -use a dvi viewer (like xdvi) to display the result. In contrast, -using ``view()`` at the Sage command-line, when the engine is set -to ``pdflatex``, will produce a PDF as the result and Sage will -call your system's utility for displaying PDF files (acrobat, -okular, evince, etc.). - -In the notebook, it is necessary to intervene in the decision as -to whether MathJax will interpret a snippet of TeX, or -if the LaTeX is complicated enough that the system-wide -installation of TeX should do the work instead. The -device is a list of strings, which if any one is discovered in a -piece of LaTeX code signal the notebook to bypass -MathJax and invoke latex (or whichever executable is set by the -``latex.engine()`` command). This list is managed by the -``latex.add_to_mathjax_avoid_list`` and -``latex.mathjax_avoid_list`` commands. :: - - sage: latex.mathjax_avoid_list([]) # not tested - sage: latex.mathjax_avoid_list() # not tested - [] - sage: latex.mathjax_avoid_list(['foo', 'bar']) # not tested - sage: latex.mathjax_avoid_list() # not tested - ['foo', 'bar'] - sage: latex.add_to_mathjax_avoid_list('tikzpicture') # not tested - sage: latex.mathjax_avoid_list() # not tested - ['foo', 'bar', 'tikzpicture'] - sage: latex.mathjax_avoid_list([]) # not tested - sage: latex.mathjax_avoid_list() # not tested - [] - -Suppose a LaTeX expression is produced in the notebook -with ``view()`` or while the "Typeset" button is checked, and -then recognized as requiring the external LaTeX -installation through the "mathjax avoid list." Then the selected -executable (as specified by ``latex.engine()``) will process the -LaTeX. However, instead of then spawning an external -viewer (which is the command-line behavior), Sage will attempt to -convert the result into a single, tightly-cropped image, which is -then inserted into the worksheet as the output of the cell. - -Just how this conversion proceeds depends on several factors -- -mostly which executable you have specified as the engine and -which conversion utilities are available on your system. Four -useful converters that will cover all eventualities are -``dvips``, ``ps2pdf``, ``dvipng`` and from the ``ImageMagick`` suite, -``convert``. The goal is to produce a PNG file as the output for -inclusion back into the worksheet. When a LaTeX -expression can be converted successfully to a dvi by the latex -engine, then dvipng should accomplish the conversion. If the -LaTeX expression and chosen engine creates a dvi with -specials that dvipng cannot handle, then dvips will create a -PostScript file. Such a PostScript file, or a PDF file created by -an engine such as ``pdflatex``, is then processed into a PNG with -the ``convert`` utility. The presence of two of these converters -can be tested with the ``have_dvipng()`` and ``have_convert()`` -routines. - -These conversions are done automatically if you have the necessary -converters installed; if not, then an error message is printed telling -you what's missing and where to download it. - -For a concrete example of how complicated LaTeX -expressions can be processed, see the example in the next section -(:ref:`sec-tkz-graph`) for using the LaTeX -``tkz-graph`` package to produce high-quality renderings of -combinatorial graphs. For other examples, there are some -pre-packaged test cases. To use these, it is necessary to import -the ``sage.misc.latex.latex_examples`` object, which is an -instance of the ``sage.misc.latex.LatexExamples`` class, as -illustrated below. This class currently has examples of -commutative diagrams, combinatorial graphs, knot theory and -pstricks, which respectively exercise the following packages: -xy, tkz-graph, xypic, pstricks. After the import, use -tab-completion on ``latex_examples`` to see the pre-packaged -examples. Calling each example will give you back some -explanation about what is required to make the example render -properly. To actually see the examples, it is necessary to use -``view()`` (once the preamble, engine, etc are all set properly). -:: +It is also possible to control which variant of TeX is used for system-wide +invocations, thus also influencing the nature of the output. + +The ``latex.engine()`` command can be used to control if the system-wide +executables ``latex``, ``pdflatex`` or ``xelatex`` are employed for more +complicated LaTeX expressions. When ``view()`` is called and the engine is set +to ``latex``, a dvi file is produced and Sage will use a dvi viewer (like xdvi) +to display the result. In contrast, using ``view()`` when the engine is set to +``pdflatex`` will produce a PDF as the result and Sage will call your system's +utility for displaying PDF files (acrobat, okular, evince, etc.). + +For a concrete example of how complicated LaTeX expressions can be processed, +see the example in the next section (:ref:`sec-tkz-graph`) for using the LaTeX +``tkz-graph`` package to produce high-quality renderings of combinatorial +graphs. For other examples, there are some pre-packaged test cases. To use +these, it is necessary to import the ``sage.misc.latex.latex_examples`` object, +which is an instance of the ``sage.misc.latex.LatexExamples`` class, as +illustrated below. This class currently has examples of commutative diagrams, +combinatorial graphs, knot theory and pstricks, which respectively exercise the +following packages: xy, tkz-graph, xypic, pstricks. After the import, use +tab-completion on ``latex_examples`` to see the pre-packaged examples. Calling +each example will give you back some explanation about what is required to make +the example render properly. To actually see the examples, it is necessary to +use ``view()`` (once the preamble, engine, etc are all set properly). :: sage: from sage.misc.latex import latex_examples sage: latex_examples.diagram() @@ -393,96 +274,58 @@ properly. To actually see the examples, it is necessary to use An Example: Combinatorial Graphs with tkz-graph =============================================== -High-quality illustrations of combinatorial graphs (henceforth -just "graphs") are possible with the ``tkz-graph`` package. -This package is built on top of the ``tikz`` front-end to the -``pgf`` library. So all of these components need to be part -of a system-wide TeX installation, and it may be possible -that these components may not be at their most current -versions as packaged in some TeX implementations. So for -best results, it could be necessary or advisable to install -these as part of your personal texmf tree. Creating, -maintaining and customizing a system-wide or personal TeX -installation is beyond the scope of this document, but it should -be easy to find instructions. The necessary files are listed in -:ref:`sec-system-wide-tex`. - -Thus, to start we need to insure that the relevant packages -are included by adding them to the preamble of the eventual -LaTeX document. The images of graphs do not form properly -when a dvi file is used as an intermediate format, so it is -best to set the latex engine to the ``pdflatex`` executable. -At this point a command like ``view(graphs.CompleteGraph(4))`` -should succeed at the Sage command-line and produce a PDF +High-quality illustrations of combinatorial graphs (henceforth just "graphs") +are possible with the ``tkz-graph`` package. This package is built on top of +the ``tikz`` front-end to the ``pgf`` library. So all of these components need +to be part of a system-wide TeX installation, and it may be possible that these +components may not be at their most current versions as packaged in some TeX +implementations. So for best results, it could be necessary or advisable to +install these as part of your personal texmf tree. Creating, maintaining and +customizing a system-wide or personal TeX installation is beyond the scope of +this document, but it should be easy to find instructions. The necessary files +are listed in :ref:`sec-system-wide-tex`. + +Thus, to start we need to insure that the relevant packages are included by +adding them to the preamble of the eventual LaTeX document. The images of +graphs do not form properly when a dvi file is used as an intermediate format, +so it is best to set the latex engine to the ``pdflatex`` executable. At this +point a command like ``view(graphs.CompleteGraph(4))`` should produce a PDF with an appropriate image of the complete graph `K_4`. -For a similar experience in the notebook, it is necessary -to disable MathJax processing of the LaTeX code for the graph -by using the "mathjax avoid list." Graphs are included with a -``tikzpicture`` environment, so this is a good choice for -a string to include in the avoidance list. Now, -``view(graphs.CompleteGraph(4))`` in a worksheet -should call pdflatex to create a PDF and then the -``convert`` utility will extract a PNG graphic to -insert into the output cell of the worksheet. -The following commands illustrate the steps to get -graphs processed by LaTeX in the notebook. :: - - sage: from sage.graphs.graph_latex import setup_latex_preamble - sage: setup_latex_preamble() - sage: latex.extra_preamble() # random - depends on system's TeX installation - '\\usepackage{tikz}\n\\usepackage{tkz-graph}\n\\usepackage{tkz-berge}\n' - sage: latex.engine('pdflatex') - sage: latex.add_to_mathjax_avoid_list('tikzpicture') # not tested - sage: latex.mathjax_avoid_list() # not tested - ['tikz', 'tikzpicture'] - -At this point, a command like ``view(graphs.CompleteGraph(4))`` -should produce a graphic version of the graph pasted into the -notebook, having used ``pdflatex`` to process ``tkz-graph`` -commands to realize the graph. Note that there is a variety of -options to affect how a graph is rendered in LaTeX via -``tkz-graph``, which is again outside the scope of this section, -see the section of the Reference manual titled "LaTeX Options for -Graphs" for instructions and details. +Note that there is a variety of options to affect how a graph is rendered in +LaTeX via ``tkz-graph``, which is again outside the scope of this section, see +the section of the Reference manual titled "LaTeX Options for Graphs" for +instructions and details. .. _sec-system-wide-tex: A Fully Capable TeX Installation ================================ -Many of the more advanced features of the integration of -TeX with Sage requires a system-wide installation of -TeX. Many versions of Linux have base TeX -packages based on TeX-live, for OSX there is -TeXshop and for Windows there is MikTeX. -The ``convert`` utility is part of the -`ImageMagick `_ suite (which -should be a package or an easy download), and the three -programs ``dvipng``, ``ps2pdf``, and ``dvips`` may be -included with your TeX distribution. The first two may -also be obtained, respectively, from -http://sourceforge.net/projects/dvipng/ and as part of + +Many of the more advanced features of the integration of TeX with Sage requires +a system-wide installation of TeX. Many versions of Linux have base TeX +packages based on TeX Live, for macOS there is TeXShop and for Windows there is +MiKTeX. The ``convert`` utility is part of the `ImageMagick +`_ suite (which should be a package or an easy +download), and the three programs ``dvipng``, ``ps2pdf``, and ``dvips`` may be +included with your TeX distribution. The first two may also be obtained, +respectively, from http://sourceforge.net/projects/dvipng/ and as part of `Ghostscript `_. -Rendering combinatorial graphs requires a recent version of the -PGF library, the file ``tkz-graph.sty`` from -https://www.ctan.org/pkg/tkz-graph, and the files ``tkz-arith.sty`` -and perhaps ``tkz-berge.sty`` from +Rendering combinatorial graphs requires a recent version of the PGF library, +the file ``tkz-graph.sty`` from https://www.ctan.org/pkg/tkz-graph, and the +files ``tkz-arith.sty`` and perhaps ``tkz-berge.sty`` from https://www.ctan.org/pkg/tkz-berge. -External Programs -================= - -There are three programs available to further integrate -TeX and Sage. The first is sagetex. A concise -description of sagetex is that it is a collection of -TeX macros that allow a LaTeX document to -include instructions to have Sage compute various objects and/or -format objects using the ``latex()`` support built in to Sage. -So as an intermediate step of compiling a LaTeX -document, all of the computational and LaTeX-formatting -features of Sage can be handled automatically. As an example, a -mathematics examination can maintain a correct correspondence -between questions and answers by using sagetex to have Sage -compute one from the other. See :ref:`sec-sagetex` for more -information. +SageTeX +======= + +SageTeX is a program available to further integrate TeX and Sage. A concise +description of SageTeX is that it is a collection of TeX macros that allow a +LaTeX document to include instructions to have Sage compute various objects +and/or format objects using the ``latex()`` support built into Sage. So as an +intermediate step of compiling a LaTeX document, all of the computational and +LaTeX-formatting features of Sage can be handled automatically. As an example, +a mathematics examination can maintain a correct correspondence between +questions and answers by using SageTeX to have Sage compute one from the other. +See :ref:`sec-sagetex` for more information. diff --git a/src/doc/en/tutorial/programming.rst b/src/doc/en/tutorial/programming.rst index f0f43e6f09f..57dac68ae1c 100644 --- a/src/doc/en/tutorial/programming.rst +++ b/src/doc/en/tutorial/programming.rst @@ -704,10 +704,9 @@ declares the :math:`1 \in \GF{5}` equal to :math:`1 \in \QQ`. Profiling ========= -Section Author: Martin Albrecht (malb@informatik.uni-bremen.de) - "Premature optimization is the root of all evil." - Donald Knuth +.. sectionauthor:: Martin Albrecht Sometimes it is useful to check for bottlenecks in code to understand which parts take the most computational time; this can @@ -764,7 +763,7 @@ closer examination: .. skip -:: +:: sage: %prun -r A*A sage: stats = _ @@ -810,3 +809,4 @@ On a system shell, type The output file ``cachegrind.out.42`` can now be examined with ``kcachegrind``. Please note that the naming convention ``cachegrind.out.XX`` needs to be obeyed. + From bb42d889e2647edbe74dd3598d0dd5aafc9a97fe Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Wed, 16 Nov 2022 21:38:58 +0900 Subject: [PATCH 478/632] A tiny fix for a glaring error --- src/doc/en/tutorial/introduction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/en/tutorial/introduction.rst b/src/doc/en/tutorial/introduction.rst index 10ebef9f5d6..44eecb132c3 100644 --- a/src/doc/en/tutorial/introduction.rst +++ b/src/doc/en/tutorial/introduction.rst @@ -99,7 +99,7 @@ Ways to Use Sage You can use Sage in several ways. -- **Notebook graphical interface:** run `sage -n jupyter`; see +- **Notebook graphical interface:** run ``sage -n jupyter``; see `the Jupyter documentation on-line `_, - **Interactive command line:** see :ref:`chapter-interactive_shell`, From cd0e58d4febe84d3d9b9d4472b26ecc179b1b53b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 16 Nov 2022 20:12:55 +0100 Subject: [PATCH 479/632] fix W391 in pyx files (no empty final lines) --- src/sage/coding/ag_code_decoders.pyx | 1 - src/sage/coding/binary_code.pyx | 3 --- src/sage/geometry/integral_points.pyx | 1 - src/sage/libs/arb/arith.pyx | 2 +- src/sage/libs/braiding.pyx | 2 -- src/sage/libs/eclib/homspace.pyx | 4 +--- src/sage/libs/flint/fmpz_poly.pyx | 2 -- src/sage/libs/homfly.pyx | 3 +-- src/sage/libs/ntl/ntl_GF2.pyx | 2 +- src/sage/libs/sirocco.pyx | 1 - src/sage/matrix/action.pyx | 1 - src/sage/matrix/change_ring.pyx | 5 +---- src/sage/matrix/echelon_matrix.pyx | 1 - src/sage/matrix/matrix_cyclo_dense.pyx | 1 - src/sage/matrix/matrix_rational_sparse.pyx | 1 - src/sage/matrix/strassen.pyx | 1 - src/sage/misc/derivative.pyx | 1 - src/sage/misc/instancedoc.pyx | 1 - src/sage/misc/lazy_attribute.pyx | 4 ++-- src/sage/misc/nested_class.pyx | 1 - src/sage/misc/parser.pyx | 1 - src/sage/monoids/free_abelian_monoid_element.pyx | 1 - src/sage/numerical/backends/cvxopt_backend.pyx | 1 - src/sage/numerical/mip.pyx | 3 +-- src/sage/rings/complex_number.pyx | 2 -- src/sage/rings/finite_rings/residue_field.pyx | 2 -- src/sage/rings/function_field/element.pyx | 1 - src/sage/rings/function_field/hermite_form_polynomial.pyx | 1 - src/sage/rings/noncommutative_ideals.pyx | 1 - .../rings/number_field/number_field_element_quadratic.pyx | 1 - src/sage/rings/number_field/number_field_morphisms.pyx | 1 - src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx | 1 - src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx | 1 - src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx | 2 +- src/sage/rings/padics/padic_ZZ_pX_element.pyx | 1 - src/sage/rings/padics/padic_capped_relative_element.pyx | 1 - src/sage/rings/padics/padic_ext_element.pyx | 1 - src/sage/rings/padics/padic_fixed_mod_element.pyx | 2 -- src/sage/rings/padics/padic_floating_point_element.pyx | 1 - src/sage/rings/padics/padic_relaxed_errors.pyx | 1 - src/sage/rings/polynomial/hilbert.pyx | 1 - src/sage/rings/polynomial/laurent_polynomial.pyx | 1 - src/sage/rings/polynomial/polynomial_compiled.pyx | 1 - src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx | 1 - src/sage/rings/polynomial/polynomial_zz_pex.pyx | 1 - src/sage/rings/polynomial/skew_polynomial_element.pyx | 1 - src/sage/rings/polynomial/skew_polynomial_finite_order.pyx | 2 -- src/sage/rings/polynomial/weil/weil_polynomials.pyx | 1 - src/sage/rings/power_series_pari.pyx | 1 - src/sage/rings/power_series_poly.pyx | 1 - src/sage/rings/puiseux_series_ring_element.pyx | 1 - src/sage/rings/real_lazy.pyx | 1 - src/sage/rings/semirings/tropical_semiring.pyx | 1 - src/sage/sat/solvers/satsolver.pyx | 1 - src/sage/stats/hmm/chmm.pyx | 1 - 55 files changed, 9 insertions(+), 71 deletions(-) diff --git a/src/sage/coding/ag_code_decoders.pyx b/src/sage/coding/ag_code_decoders.pyx index b1833b7a34a..54e3c1d8810 100644 --- a/src/sage/coding/ag_code_decoders.pyx +++ b/src/sage/coding/ag_code_decoders.pyx @@ -2636,4 +2636,3 @@ cdef class DifferentialAGCodeDecoder_K_extension(Decoder_K_extension): sage: TestSuite(circuit).run(skip='_test_pickling') # long time """ super().__init__(pls, G, Q, DifferentialAGCodeDecoder_K, verbose=verbose) - diff --git a/src/sage/coding/binary_code.pyx b/src/sage/coding/binary_code.pyx index 66192643aae..c493df314a1 100644 --- a/src/sage/coding/binary_code.pyx +++ b/src/sage/coding/binary_code.pyx @@ -4182,6 +4182,3 @@ cdef class BinaryCodeClassifier: sig_free(ortho_basis) sig_free(temp_basis) return output - - - diff --git a/src/sage/geometry/integral_points.pyx b/src/sage/geometry/integral_points.pyx index f8325eea1ed..4d39b436cae 100644 --- a/src/sage/geometry/integral_points.pyx +++ b/src/sage/geometry/integral_points.pyx @@ -1450,4 +1450,3 @@ cpdef print_cache(InequalityCollection inequality_collection): print('Cached next-to-inner loop: ' + str(ieq.coeff) + ' * x_0 + ' + str(ieq.coeff_next) + ' * x_1 + ' + str(ieq.cache_next) + ' >= 0') - diff --git a/src/sage/libs/arb/arith.pyx b/src/sage/libs/arb/arith.pyx index 31db27d6bb6..d885f97b238 100644 --- a/src/sage/libs/arb/arith.pyx +++ b/src/sage/libs/arb/arith.pyx @@ -57,6 +57,7 @@ def bernoulli(n): fmpq_clear(x) return q + def hilbert_class_polynomial(D): """ Return the Hilbert class polynomial for discriminant ``D`` using ``arb``. @@ -82,4 +83,3 @@ def hilbert_class_polynomial(D): poly = PolynomialRing(ZZ, "x", implementation="FLINT")() acb_modular_hilbert_class_poly(poly.__poly, n) return poly - diff --git a/src/sage/libs/braiding.pyx b/src/sage/libs/braiding.pyx index 9178d259b13..e8f37dad588 100644 --- a/src/sage/libs/braiding.pyx +++ b/src/sage/libs/braiding.pyx @@ -384,7 +384,6 @@ def sliding_circuits(braid): [[[0], [2, 1], [1, 2], [2]]], [[[0], [1, 2], [2], [2, 1]]], [[[0], [2, 1], [1], [1, 2]]]] - """ nstrands = braid.parent().strands() l = braid.Tietze() @@ -392,4 +391,3 @@ def sliding_circuits(braid): cdef list[list[list[list[int]]]] rop = SlidingCircuits(nstrands, l) sig_off() return rop - diff --git a/src/sage/libs/eclib/homspace.pyx b/src/sage/libs/eclib/homspace.pyx index 9868795ab20..3feea84fda7 100644 --- a/src/sage/libs/eclib/homspace.pyx +++ b/src/sage/libs/eclib/homspace.pyx @@ -283,6 +283,4 @@ cdef class ModularSymbols: inc(iter) MS = MatrixSpace(base_ring, n, sparse=True) # The next step is the bottleneck. - ans = MS(d) - return ans - + return MS(d) diff --git a/src/sage/libs/flint/fmpz_poly.pyx b/src/sage/libs/flint/fmpz_poly.pyx index d894478ba60..23df744b440 100644 --- a/src/sage/libs/flint/fmpz_poly.pyx +++ b/src/sage/libs/flint/fmpz_poly.pyx @@ -440,7 +440,6 @@ cdef class Fmpz_poly(SageObject): cdef long nn = n fmpz_poly_truncate(self.poly, nn) # mutating! - def _sage_(self, var='x'): """ Return self as an element of the sage ZZ[var]. @@ -456,4 +455,3 @@ cdef class Fmpz_poly(SageObject): """ from sage.rings.integer_ring import ZZ return ZZ[var](self.list()) - diff --git a/src/sage/libs/homfly.pyx b/src/sage/libs/homfly.pyx index 0493908fae2..550c5f02a65 100644 --- a/src/sage/libs/homfly.pyx +++ b/src/sage/libs/homfly.pyx @@ -102,9 +102,8 @@ def homfly_polynomial_dict(link): cdef Poly* c_output = homfly(link) sig_off() cdef int l = c_output.len - d = dict() + d = {} for i in range(l): ter = c_output.term[i] d[(int(ter.l), int(ter.m))] = int(ter.coef) return d - diff --git a/src/sage/libs/ntl/ntl_GF2.pyx b/src/sage/libs/ntl/ntl_GF2.pyx index 8af0d6caf9a..d6ada6cb100 100644 --- a/src/sage/libs/ntl/ntl_GF2.pyx +++ b/src/sage/libs/ntl/ntl_GF2.pyx @@ -266,6 +266,7 @@ def unpickle_class_value(cls, x): """ return cls(x) + def unpickle_class_args(cls, x): """ Here for unpickling. @@ -278,4 +279,3 @@ def unpickle_class_args(cls, x): """ return cls(*x) - diff --git a/src/sage/libs/sirocco.pyx b/src/sage/libs/sirocco.pyx index 5bd4062394d..117d714c5d5 100644 --- a/src/sage/libs/sirocco.pyx +++ b/src/sage/libs/sirocco.pyx @@ -292,4 +292,3 @@ cpdef list[list] contpath_comps(int deg, list values, double y0r, double y0i, li free(c_otherdegrees) free(c_othercoefs) return l - diff --git a/src/sage/matrix/action.pyx b/src/sage/matrix/action.pyx index 6d4d7853b5f..dc057d1dce5 100644 --- a/src/sage/matrix/action.pyx +++ b/src/sage/matrix/action.pyx @@ -580,4 +580,3 @@ cdef class MatrixSchemePointAction(MatrixMulAction): (2 : 1) """ return P._matrix_times_point_(mat, self._codomain) - diff --git a/src/sage/matrix/change_ring.pyx b/src/sage/matrix/change_ring.pyx index 33f962aebc0..c14d6540849 100644 --- a/src/sage/matrix/change_ring.pyx +++ b/src/sage/matrix/change_ring.pyx @@ -38,8 +38,5 @@ def integer_to_real_double_dense(Matrix_integer_dense A): S, None, None, None) for i from 0 <= i < A._nrows: for j from 0 <= j < A._ncols: - M.set_unsafe_double(i,j,A.get_unsafe_double(i,j)) + M.set_unsafe_double(i, j, A.get_unsafe_double(i, j)) return M - - - diff --git a/src/sage/matrix/echelon_matrix.pyx b/src/sage/matrix/echelon_matrix.pyx index 1b329b6a423..3fc43b485ca 100644 --- a/src/sage/matrix/echelon_matrix.pyx +++ b/src/sage/matrix/echelon_matrix.pyx @@ -151,4 +151,3 @@ def reduced_echelon_matrix_iterator(K, k, n, bint sparse=False, bint copy=True, yield m del v # hack: Python itertools reuses the tuple if nobody else uses it del pivots # hack: Python itertools reuses the tuple if nobody else uses it - diff --git a/src/sage/matrix/matrix_cyclo_dense.pyx b/src/sage/matrix/matrix_cyclo_dense.pyx index 47c279254c2..200d029947d 100644 --- a/src/sage/matrix/matrix_cyclo_dense.pyx +++ b/src/sage/matrix/matrix_cyclo_dense.pyx @@ -1961,4 +1961,3 @@ cdef class Matrix_cyclo_dense(Matrix_dense): if subdivide: M.subdivide([Anr*i for i in range(1,nr)], [Anc*i for i in range(1,nc)]) return M - diff --git a/src/sage/matrix/matrix_rational_sparse.pyx b/src/sage/matrix/matrix_rational_sparse.pyx index da3270d470d..cdc70b72163 100644 --- a/src/sage/matrix/matrix_rational_sparse.pyx +++ b/src/sage/matrix/matrix_rational_sparse.pyx @@ -805,4 +805,3 @@ cdef class Matrix_rational_sparse(Matrix_sparse): cdef mpq_t minus_one mpq_init(minus_one) mpq_set_si(minus_one, -1,1) - diff --git a/src/sage/matrix/strassen.pyx b/src/sage/matrix/strassen.pyx index cd343bd92e8..452fe3b2e1a 100644 --- a/src/sage/matrix/strassen.pyx +++ b/src/sage/matrix/strassen.pyx @@ -867,4 +867,3 @@ def test(n, m, R, c=2): ## sage: C == D ## True - diff --git a/src/sage/misc/derivative.pyx b/src/sage/misc/derivative.pyx index 60bb7c1e09b..45ee7a918c5 100644 --- a/src/sage/misc/derivative.pyx +++ b/src/sage/misc/derivative.pyx @@ -221,4 +221,3 @@ def multi_derivative(F, args): for arg in derivative_parse(args): F = F._derivative(arg) return F - diff --git a/src/sage/misc/instancedoc.pyx b/src/sage/misc/instancedoc.pyx index 574287fefe9..360d3f768b3 100644 --- a/src/sage/misc/instancedoc.pyx +++ b/src/sage/misc/instancedoc.pyx @@ -327,4 +327,3 @@ def instancedoc(cls): tp.tp_doc = NULL PyType_Modified(tp) return cls - diff --git a/src/sage/misc/lazy_attribute.pyx b/src/sage/misc/lazy_attribute.pyx index d5610294a68..23a7098564b 100644 --- a/src/sage/misc/lazy_attribute.pyx +++ b/src/sage/misc/lazy_attribute.pyx @@ -506,6 +506,7 @@ class lazy_attribute(_lazy_attribute): if hasattr(f, "__module__"): self.__module__ = f.__module__ + class lazy_class_attribute(lazy_attribute): """ A lazy class attribute for an class is like a usual class attribute, @@ -601,7 +602,6 @@ class lazy_class_attribute(lazy_attribute): """ result = self.f(cls) if result is NotImplemented: - return getattr(super(cls, cls),self.__name__) + return getattr(super(cls, cls), self.__name__) setattr(cls, self.__name__, result) return result - diff --git a/src/sage/misc/nested_class.pyx b/src/sage/misc/nested_class.pyx index 427dd8374e2..b601d6f7276 100644 --- a/src/sage/misc/nested_class.pyx +++ b/src/sage/misc/nested_class.pyx @@ -386,4 +386,3 @@ class A1: class A2: class A3: pass - diff --git a/src/sage/misc/parser.pyx b/src/sage/misc/parser.pyx index a365c3c79ea..b979bd5907c 100644 --- a/src/sage/misc/parser.pyx +++ b/src/sage/misc/parser.pyx @@ -1098,4 +1098,3 @@ cdef class LookupNameMaker: if self.fallback is not None: return self.fallback(name) raise NameError("Unknown variable: '{}'".format(name)) - diff --git a/src/sage/monoids/free_abelian_monoid_element.pyx b/src/sage/monoids/free_abelian_monoid_element.pyx index 232deeadac6..87c9f91f445 100644 --- a/src/sage/monoids/free_abelian_monoid_element.pyx +++ b/src/sage/monoids/free_abelian_monoid_element.pyx @@ -388,4 +388,3 @@ cdef class FreeAbelianMonoidElement(MonoidElement): """ cdef Py_ssize_t i return [_Integer_from_mpz(self._element_vector[i]) for i in range(self._n)] - diff --git a/src/sage/numerical/backends/cvxopt_backend.pyx b/src/sage/numerical/backends/cvxopt_backend.pyx index 59938bf175b..10358c3a8f9 100644 --- a/src/sage/numerical/backends/cvxopt_backend.pyx +++ b/src/sage/numerical/backends/cvxopt_backend.pyx @@ -1002,4 +1002,3 @@ cdef class CVXOPTBackend(GenericBackend): return self.param[name] else: self.param[name] = value - diff --git a/src/sage/numerical/mip.pyx b/src/sage/numerical/mip.pyx index c2ed1368837..5194b62ad5e 100644 --- a/src/sage/numerical/mip.pyx +++ b/src/sage/numerical/mip.pyx @@ -3576,7 +3576,7 @@ cdef class MIPVariable(SageObject): """ Implement the action of a matrix multiplying from the left. """ - result = dict() + result = {} for i, col in enumerate(m.columns()): x = self[i] x_index, = x.dict().keys() @@ -3585,4 +3585,3 @@ cdef class MIPVariable(SageObject): V = FreeModule(self._p.base_ring(), m.nrows()) T = self._p.linear_functions_parent().tensor(V) return T(result) - diff --git a/src/sage/rings/complex_number.pyx b/src/sage/rings/complex_number.pyx index e8e94e4331d..1715d2b910f 100644 --- a/src/sage/rings/complex_number.pyx +++ b/src/sage/rings/complex_number.pyx @@ -9,8 +9,6 @@ TESTS:: DeprecationWarning: the complex_number module is deprecated, please use sage.rings.complex_mpfr See http://trac.sagemath.org/24483 for details. """ - from sage.misc.superseded import deprecation from sage.rings.complex_mpfr import * deprecation(24483, "the complex_number module is deprecated, please use sage.rings.complex_mpfr") - diff --git a/src/sage/rings/finite_rings/residue_field.pyx b/src/sage/rings/finite_rings/residue_field.pyx index dbef75e9fe6..270f92a66e7 100644 --- a/src/sage/rings/finite_rings/residue_field.pyx +++ b/src/sage/rings/finite_rings/residue_field.pyx @@ -1919,5 +1919,3 @@ class ResidueFiniteField_ntl_gf2e(ResidueField_generic, FiniteField_ntl_gf2e): return FiniteField_ntl_gf2e._element_constructor_(self, x) except TypeError: return ResidueField_generic._element_constructor_(self, x) - - diff --git a/src/sage/rings/function_field/element.pyx b/src/sage/rings/function_field/element.pyx index 41a78f11846..404199f142f 100644 --- a/src/sage/rings/function_field/element.pyx +++ b/src/sage/rings/function_field/element.pyx @@ -1578,4 +1578,3 @@ cdef class FunctionFieldElement_rational(FunctionFieldElement): assert f.denominator() == 1 assert self._x.denominator() == 1 return self.parent()(self._x.numerator().inverse_mod(f.numerator())) - diff --git a/src/sage/rings/function_field/hermite_form_polynomial.pyx b/src/sage/rings/function_field/hermite_form_polynomial.pyx index 823ae0b55fa..dff24054110 100644 --- a/src/sage/rings/function_field/hermite_form_polynomial.pyx +++ b/src/sage/rings/function_field/hermite_form_polynomial.pyx @@ -184,4 +184,3 @@ def reversed_hermite_form(Matrix mat, bint transformation=False): if transformation: return U - diff --git a/src/sage/rings/noncommutative_ideals.pyx b/src/sage/rings/noncommutative_ideals.pyx index 29599c2ac0f..1533672534d 100644 --- a/src/sage/rings/noncommutative_ideals.pyx +++ b/src/sage/rings/noncommutative_ideals.pyx @@ -418,4 +418,3 @@ class Ideal_nc(Ideal_generic): return other.ring().ideal(other.gens(), side='twosided') raise NotImplementedError("cannot multiply non-commutative ideals") - diff --git a/src/sage/rings/number_field/number_field_element_quadratic.pyx b/src/sage/rings/number_field/number_field_element_quadratic.pyx index 04224f2f208..ed662749e35 100644 --- a/src/sage/rings/number_field/number_field_element_quadratic.pyx +++ b/src/sage/rings/number_field/number_field_element_quadratic.pyx @@ -3074,4 +3074,3 @@ cpdef bint is_sqrt_disc(Rational ad, Rational bd): mpz_clear(denom) return ret - diff --git a/src/sage/rings/number_field/number_field_morphisms.pyx b/src/sage/rings/number_field/number_field_morphisms.pyx index 9f77c9e3949..18e9341903b 100644 --- a/src/sage/rings/number_field/number_field_morphisms.pyx +++ b/src/sage/rings/number_field/number_field_morphisms.pyx @@ -775,4 +775,3 @@ cdef class CyclotomicFieldConversion(Map): return self.phi.preimage(M(x)) except ValueError: raise ValueError('Element {} has no image in the codomain'.format(x)) - diff --git a/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx index 731d27ec33c..8f0f2cbe446 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CA_element.pyx @@ -2351,4 +2351,3 @@ def make_ZZpXCAElement(parent, value, absprec, version): return ans else: raise ValueError("unknown unpickling version") - diff --git a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx index 463cc371031..adb0747fd4a 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_CR_element.pyx @@ -3284,4 +3284,3 @@ def make_ZZpXCRElement(parent, unit, ordp, relprec, version): return ans else: raise ValueError("unknown unpickling version") - diff --git a/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx index 9e182de50d5..6d6ab0f7040 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_FM_element.pyx @@ -1721,6 +1721,7 @@ cdef class pAdicZZpXFMElement(pAdicZZpXElement): """ return self.ext_p_list_precs(pos, self.prime_pow.ram_prec_cap) + def make_ZZpXFMElement(parent, f): """ Create a new ``pAdicZZpXFMElement`` out of an ``ntl_ZZ_pX`` ``f``, with @@ -1737,4 +1738,3 @@ def make_ZZpXFMElement(parent, f): True """ return pAdicZZpXFMElement(parent, f) - diff --git a/src/sage/rings/padics/padic_ZZ_pX_element.pyx b/src/sage/rings/padics/padic_ZZ_pX_element.pyx index a450f28de65..2fcc167d1f7 100644 --- a/src/sage/rings/padics/padic_ZZ_pX_element.pyx +++ b/src/sage/rings/padics/padic_ZZ_pX_element.pyx @@ -914,4 +914,3 @@ cdef get_val_prec(PowComputer_ext pp, a): print(py_tmp) raise TypeError("modulus must be a positive power of the appropriate prime") raise TypeError("unsupported type for list element: %s" % type(a)) - diff --git a/src/sage/rings/padics/padic_capped_relative_element.pyx b/src/sage/rings/padics/padic_capped_relative_element.pyx index e7dce3c3c51..b5843d467a7 100644 --- a/src/sage/rings/padics/padic_capped_relative_element.pyx +++ b/src/sage/rings/padics/padic_capped_relative_element.pyx @@ -610,4 +610,3 @@ def base_p_list(Integer n, bint pos, PowComputer_class prime_pow): cdef ExpansionIter expansion = ExpansionIter(dummy, n.exact_log(p) + 2, mode) mpz_set(expansion.curvalue, n.value) return trim_zeros(list(expansion)) - diff --git a/src/sage/rings/padics/padic_ext_element.pyx b/src/sage/rings/padics/padic_ext_element.pyx index 7cd211e5fd8..39f58efcd0b 100644 --- a/src/sage/rings/padics/padic_ext_element.pyx +++ b/src/sage/rings/padics/padic_ext_element.pyx @@ -509,4 +509,3 @@ cdef class pAdicExtElement(pAdicGenericElement): return R.residue_field()(self.expansion(0)) else: raise NotImplementedError("residue() not implemented in extensions for absprec larger than one") - diff --git a/src/sage/rings/padics/padic_fixed_mod_element.pyx b/src/sage/rings/padics/padic_fixed_mod_element.pyx index 8b45f6cea71..bf7fdd2debd 100644 --- a/src/sage/rings/padics/padic_fixed_mod_element.pyx +++ b/src/sage/rings/padics/padic_fixed_mod_element.pyx @@ -567,7 +567,6 @@ cdef class pAdicFixedModElement(FMElement): return ans - def make_pAdicFixedModElement(parent, value): """ Unpickles a fixed modulus element. @@ -580,4 +579,3 @@ def make_pAdicFixedModElement(parent, value): 2*5^2 + 3*5^3 """ return unpickle_fme_v2(pAdicFixedModElement, parent, value) - diff --git a/src/sage/rings/padics/padic_floating_point_element.pyx b/src/sage/rings/padics/padic_floating_point_element.pyx index 7cad653a214..13eb30a3abc 100644 --- a/src/sage/rings/padics/padic_floating_point_element.pyx +++ b/src/sage/rings/padics/padic_floating_point_element.pyx @@ -442,4 +442,3 @@ cdef class pAdicFloatingPointElement(FPElement): sig_off() return ans - diff --git a/src/sage/rings/padics/padic_relaxed_errors.pyx b/src/sage/rings/padics/padic_relaxed_errors.pyx index 9451c7df14d..ddadb111ca9 100644 --- a/src/sage/rings/padics/padic_relaxed_errors.pyx +++ b/src/sage/rings/padics/padic_relaxed_errors.pyx @@ -69,4 +69,3 @@ def raise_error(error, permissive=False): raise PrecisionError("not enough precision") if error & ERROR_ABANDON: raise PrecisionError("computation has been abandoned; try to increase precision") - diff --git a/src/sage/rings/polynomial/hilbert.pyx b/src/sage/rings/polynomial/hilbert.pyx index 9c5c5b1232c..0c49e1655c2 100644 --- a/src/sage/rings/polynomial/hilbert.pyx +++ b/src/sage/rings/polynomial/hilbert.pyx @@ -601,4 +601,3 @@ def hilbert_poincare_series(I, grading=None): if HP.leading_coefficient() >= 0: return HP / PR.prod([(1-t**d) for d in grading]) return (-HP) / (-PR.prod([(1-t**d) for d in grading])) - diff --git a/src/sage/rings/polynomial/laurent_polynomial.pyx b/src/sage/rings/polynomial/laurent_polynomial.pyx index 5e4188ed80f..703e8b59ab9 100644 --- a/src/sage/rings/polynomial/laurent_polynomial.pyx +++ b/src/sage/rings/polynomial/laurent_polynomial.pyx @@ -3763,4 +3763,3 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): if new_ring is not None: return new_ring(ans) return ans - diff --git a/src/sage/rings/polynomial/polynomial_compiled.pyx b/src/sage/rings/polynomial/polynomial_compiled.pyx index 3f01b3c8731..83f33a3a4d2 100644 --- a/src/sage/rings/polynomial/polynomial_compiled.pyx +++ b/src/sage/rings/polynomial/polynomial_compiled.pyx @@ -509,4 +509,3 @@ cdef class abc_pd(binary_pd): self.value = self.left.value * self.right.value + coeffs[self.index] pd_clean(self.left) pd_clean(self.right) - diff --git a/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx b/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx index d72a8076848..8fa744ff668 100644 --- a/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx +++ b/src/sage/rings/polynomial/polynomial_real_mpfr_dense.pyx @@ -778,4 +778,3 @@ def make_PolynomialRealDense(parent, data): 3.00000000000000*x^2 + 2.00000000000000*x + 1.00000000000000 """ return PolynomialRealDense(parent, data) - diff --git a/src/sage/rings/polynomial/polynomial_zz_pex.pyx b/src/sage/rings/polynomial/polynomial_zz_pex.pyx index 853ca0629c3..e31f3991497 100644 --- a/src/sage/rings/polynomial/polynomial_zz_pex.pyx +++ b/src/sage/rings/polynomial/polynomial_zz_pex.pyx @@ -443,4 +443,3 @@ cdef class Polynomial_ZZ_pEX(Polynomial_template): x^4 + x^3 + x """ return self.shift(-n) - diff --git a/src/sage/rings/polynomial/skew_polynomial_element.pyx b/src/sage/rings/polynomial/skew_polynomial_element.pyx index c47515141fd..2c0d42ff511 100644 --- a/src/sage/rings/polynomial/skew_polynomial_element.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_element.pyx @@ -690,4 +690,3 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): q.append(c) q.reverse() return (self._new_c(q, parent), self._new_c(a[:db], parent, 1)) - diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx index 951fcd22c97..3dd313aad05 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx @@ -572,8 +572,6 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): self._optbound = [section(x) for x in bound.list()] return center(self._optbound) - # TODO: # fast multiplication # reduced characteristic polynomial - diff --git a/src/sage/rings/polynomial/weil/weil_polynomials.pyx b/src/sage/rings/polynomial/weil/weil_polynomials.pyx index 2fd4ad9c84d..f2544ed77c8 100755 --- a/src/sage/rings/polynomial/weil/weil_polynomials.pyx +++ b/src/sage/rings/polynomial/weil/weil_polynomials.pyx @@ -585,4 +585,3 @@ class WeilPolynomials(): 158 """ return self.w.node_count() - diff --git a/src/sage/rings/power_series_pari.pyx b/src/sage/rings/power_series_pari.pyx index d4488574bab..7b9c2b5c783 100644 --- a/src/sage/rings/power_series_pari.pyx +++ b/src/sage/rings/power_series_pari.pyx @@ -953,4 +953,3 @@ cdef class PowerSeries_pari(PowerSeries): precision = self._prec f = self return PowerSeries_pari(self._parent, f.g.serreverse(), precision) - diff --git a/src/sage/rings/power_series_poly.pyx b/src/sage/rings/power_series_poly.pyx index affd674afda..a45943a4391 100644 --- a/src/sage/rings/power_series_poly.pyx +++ b/src/sage/rings/power_series_poly.pyx @@ -1293,4 +1293,3 @@ cdef class BaseRingFloorDivAction(Action): P = self.US() g = P.base_ring()(g) return type(x)(P, elt.__f // g, prec=prec, check=False) - diff --git a/src/sage/rings/puiseux_series_ring_element.pyx b/src/sage/rings/puiseux_series_ring_element.pyx index aff90ee1f89..7fe08d1b46d 100644 --- a/src/sage/rings/puiseux_series_ring_element.pyx +++ b/src/sage/rings/puiseux_series_ring_element.pyx @@ -1052,4 +1052,3 @@ cdef class PuiseuxSeries(AlgebraElement): 25*x^(23/2) + O(x^(27/2)) """ return self.__invert__() - diff --git a/src/sage/rings/real_lazy.pyx b/src/sage/rings/real_lazy.pyx index 02909654037..3d779b359ce 100644 --- a/src/sage/rings/real_lazy.pyx +++ b/src/sage/rings/real_lazy.pyx @@ -1748,4 +1748,3 @@ cdef class LazyWrapperMorphism(Morphism): else: e._value = x return e - diff --git a/src/sage/rings/semirings/tropical_semiring.pyx b/src/sage/rings/semirings/tropical_semiring.pyx index 17e25091f39..c96d7bccd6c 100644 --- a/src/sage/rings/semirings/tropical_semiring.pyx +++ b/src/sage/rings/semirings/tropical_semiring.pyx @@ -671,4 +671,3 @@ cdef class TropicalToTropical(Map): +infinity """ return self.codomain()((x)._val) - diff --git a/src/sage/sat/solvers/satsolver.pyx b/src/sage/sat/solvers/satsolver.pyx index d93181c142f..8b9b5398b98 100644 --- a/src/sage/sat/solvers/satsolver.pyx +++ b/src/sage/sat/solvers/satsolver.pyx @@ -395,4 +395,3 @@ def SAT(solver=None, *args, **kwds): return GlucoseSyrup(*args, **kwds) else: raise ValueError("Solver '{}' is not available".format(solver)) - diff --git a/src/sage/stats/hmm/chmm.pyx b/src/sage/stats/hmm/chmm.pyx index 11b3cfd52ff..ed38ffdbbf6 100644 --- a/src/sage/stats/hmm/chmm.pyx +++ b/src/sage/stats/hmm/chmm.pyx @@ -1559,4 +1559,3 @@ def unpickle_gaussian_mixture_hmm_v1(A, B, pi, mixture): m.pi = pi m.mixture = mixture return m - From 313d0fdf8e25e3799df87840e2efc5852acb750c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 16 Nov 2022 16:10:49 -0800 Subject: [PATCH 480/632] build/pkgs/networkx: Update to 2.8.8 --- build/pkgs/networkx/checksums.ini | 6 +++--- build/pkgs/networkx/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/networkx/checksums.ini b/build/pkgs/networkx/checksums.ini index fbcdd6b3771..00b6f1fa335 100644 --- a/build/pkgs/networkx/checksums.ini +++ b/build/pkgs/networkx/checksums.ini @@ -1,5 +1,5 @@ tarball=networkx-VERSION.tar.gz -sha1=ca2bfcee5662a59e44a8398feacbeee4ac3f86d6 -md5=d7e62d70785d92f523e7e317a1f70f0a -cksum=1611585872 +sha1=40e981041664856ba473c9079006367ed0d0e71b +md5=22139ab5a47818fa00cbaa91eb126381 +cksum=4201985987 upstream_url=https://pypi.io/packages/source/n/networkx/networkx-VERSION.tar.gz diff --git a/build/pkgs/networkx/package-version.txt b/build/pkgs/networkx/package-version.txt index bcd0f91fe45..80803faf1b9 100644 --- a/build/pkgs/networkx/package-version.txt +++ b/build/pkgs/networkx/package-version.txt @@ -1 +1 @@ -2.8.7 +2.8.8 From b50cd0b9b9fd1fb26e248669edc02a8fb2bf6150 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Wed, 16 Nov 2022 19:42:29 -0800 Subject: [PATCH 481/632] Remove module-level imports from sage.plot --- src/sage/calculus/transforms/dwt.pyx | 16 +++++++------- src/sage/calculus/transforms/fft.pyx | 5 +++-- src/sage/modular/overconvergent/genus0.py | 3 ++- .../probability/probability_distribution.pyx | 7 ++++--- .../elliptic_curves/ell_finite_field.py | 7 ++----- .../schemes/elliptic_curves/ell_generic.py | 21 ++++++++++--------- src/sage/schemes/elliptic_curves/ell_point.py | 9 ++++---- 7 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/sage/calculus/transforms/dwt.pyx b/src/sage/calculus/transforms/dwt.pyx index 6c34b1a288f..61e0f83d331 100644 --- a/src/sage/calculus/transforms/dwt.pyx +++ b/src/sage/calculus/transforms/dwt.pyx @@ -21,9 +21,6 @@ AUTHOR: # https://www.gnu.org/licenses/ # **************************************************************************** -import sage.plot.all - - def WaveletTransform(n, wavelet_type, wavelet_k): r""" This function initializes an GSLDoubleArray of length n which @@ -136,19 +133,20 @@ cdef class DiscreteWaveletTransform(GSLDoubleArray): def backward_transform(self): gsl_wavelet_transform_inverse(self.wavelet,self.data,self.stride,self.n,self.workspace) - def plot(self,xmin=None,xmax=None,**args): + def plot(self, xmin=None, xmax=None, **args): + from sage.plot.point import point + cdef int i cdef double x v = [] - point = sage.plot.all.point if xmin is None: x_min = 0 if xmax is None: - x_max=self.n - for i from x_min <=i < x_max: + x_max = self.n + for i from x_min <= i < x_max: x = self.data[i] - if i >0: - v.append(point([(i,x)],hue=(1,1,1),**args)) + if i > 0: + v.append(point([(i, x)], hue=(1, 1, 1), **args)) return sum(v) diff --git a/src/sage/calculus/transforms/fft.pyx b/src/sage/calculus/transforms/fft.pyx index c5a6108d222..1ea2cb6b2b2 100644 --- a/src/sage/calculus/transforms/fft.pyx +++ b/src/sage/calculus/transforms/fft.pyx @@ -22,11 +22,11 @@ AUTHORS: from cysignals.memory cimport sig_malloc, sig_free -import sage.plot.all import sage.libs.pari.all from sage.rings.integer import Integer from sage.rings.complex_mpfr import ComplexNumber + def FastFourierTransform(size, base_ring=None): """ Create an array for fast Fourier transform conversion using gsl. @@ -247,10 +247,11 @@ cdef class FastFourierTransform_complex(FastFourierTransform_base): Graphics object consisting of 2 graphics primitives """ + from sage.plot.point import point + cdef int i v = [] - point = sage.plot.all.point pi = sage.symbolic.constants.pi.n() I = sage.symbolic.constants.I.n() s = 1/(3*pi) # so arg gets scaled between -1/3 and 1/3. diff --git a/src/sage/modular/overconvergent/genus0.py b/src/sage/modular/overconvergent/genus0.py index 8f903f76b7c..972ef7eed45 100644 --- a/src/sage/modular/overconvergent/genus0.py +++ b/src/sage/modular/overconvergent/genus0.py @@ -186,7 +186,6 @@ from sage.modular.modform.j_invariant import j_invariant_qexp from sage.modules.free_module_element import vector from sage.modules.module import Module -from sage.plot.plot import plot from sage.rings.big_oh import O from sage.rings.infinity import Infinity from sage.rings.integer_ring import ZZ @@ -1652,6 +1651,8 @@ def valuation_plot(self, rmax=None): sage: f.valuation_plot() Graphics object consisting of 1 graphics primitive """ + from sage.plot.plot import plot + if rmax is None: rmax = ZZ(self.prime()) / ZZ(1 + self.prime()) return plot(self.r_ord, (0, rmax)) diff --git a/src/sage/probability/probability_distribution.pyx b/src/sage/probability/probability_distribution.pyx index a76518b7969..58a2209b45b 100644 --- a/src/sage/probability/probability_distribution.pyx +++ b/src/sage/probability/probability_distribution.pyx @@ -43,9 +43,9 @@ REFERENCES: import sys from cysignals.memory cimport sig_malloc, sig_free -import sage.plot.plot from sage.libs.gsl.all cimport * import sage.misc.prandom as random +import sage.rings.real_double from sage.modules.free_module_element import vector #TODO: Add more distributions available in gsl @@ -952,7 +952,7 @@ cdef class RealDistribution(ProbabilityDistribution): def plot(self, *args, **kwds): """ Plot the distribution function for the probability - distribution. Parameters to ``sage.plot.plot.plot.plot`` can be + distribution. Parameters to :func:`sage.plot.plot.plot` can be passed through ``*args`` and ``**kwds``. EXAMPLES:: @@ -960,8 +960,9 @@ cdef class RealDistribution(ProbabilityDistribution): sage: T = RealDistribution('uniform', [0, 2]) sage: P = T.plot() """ + from sage.plot.plot import plot + return plot(self.distribution_function, *args, **kwds) - return sage.plot.plot.plot(self.distribution_function, *args, **kwds) cdef class GeneralDiscreteDistribution(ProbabilityDistribution): """ diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index 31e193a64b6..14c7b9d7c26 100644 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -36,8 +36,6 @@ from sage.misc.cachefunc import cached_method from sage.groups.additive_abelian.additive_abelian_wrapper import AdditiveAbelianGroupWrapper -import sage.plot.all as plot - class EllipticCurve_finite_field(EllipticCurve_field, HyperellipticCurve_finite_field): r""" @@ -98,10 +96,9 @@ def plot(self, *args, **kwds): if not R.is_prime_field(): raise NotImplementedError - G = plot.Graphics() - G += plot.points([P[0:2] for P in self.points() if not P.is_zero()], *args, **kwds) + from sage.plot.point import points - return G + return points([P[0:2] for P in self.points() if not P.is_zero()], *args, **kwds) def _points_via_group_structure(self): """ diff --git a/src/sage/schemes/elliptic_curves/ell_generic.py b/src/sage/schemes/elliptic_curves/ell_generic.py index 154a581bc2a..559f71a9db4 100644 --- a/src/sage/schemes/elliptic_curves/ell_generic.py +++ b/src/sage/schemes/elliptic_curves/ell_generic.py @@ -61,9 +61,6 @@ from sage.rings.finite_rings.finite_field_base import FiniteField import sage.groups.additive_abelian.additive_abelian_group as groups import sage.groups.generic as generic -import sage.plot.all as plot -from sage.misc.lazy_import import lazy_import -lazy_import("sage.plot.plot", "generate_plot_points") from sage.arith.all import lcm import sage.rings.all as rings @@ -3052,24 +3049,28 @@ def f2(z): else: I.append((xmin, xmax, '=')) - g = plot.Graphics() + from sage.plot.graphics import Graphics + from sage.plot.line import line + from sage.plot.plot import generate_plot_points + + g = Graphics() plot_points = int(args.pop('plot_points',200)) adaptive_tolerance = args.pop('adaptive_tolerance',0.01) adaptive_recursion = args.pop('adaptive_recursion',5) randomize = args.pop('randomize',True) for j in range(len(I)): - a,b,shape = I[j] + a, b, shape = I[j] v = generate_plot_points(f1, (a, b), plot_points, adaptive_tolerance, adaptive_recursion, randomize) w = generate_plot_points(f2, (a, b), plot_points, adaptive_tolerance, adaptive_recursion, randomize) if shape == 'o': - g += plot.line(v + list(reversed(w)) + [v[0]], **args) + g += line(v + list(reversed(w)) + [v[0]], **args) elif shape == '<': - g += plot.line(list(reversed(v)) + w, **args) + g += line(list(reversed(v)) + w, **args) elif shape == '>': - g += plot.line(v + list(reversed(w)), **args) + g += line(v + list(reversed(w)), **args) else: - g += plot.line(v, **args) - g += plot.line(w, **args) + g += line(v, **args) + g += line(w, **args) return g @cached_method diff --git a/src/sage/schemes/elliptic_curves/ell_point.py b/src/sage/schemes/elliptic_curves/ell_point.py index 98c0dee32c7..28ea7b00592 100644 --- a/src/sage/schemes/elliptic_curves/ell_point.py +++ b/src/sage/schemes/elliptic_curves/ell_point.py @@ -118,8 +118,6 @@ import math -import sage.plot.all as plot - from sage.rings.padics.factory import Qp from sage.rings.padics.precision_error import PrecisionError @@ -597,10 +595,13 @@ def plot(self, **args): sage: P.plot(pointsize=30, rgbcolor=(1,0,0)) Graphics object consisting of 1 graphics primitive """ + from sage.plot.point import point + from sage.plot.text import text + if self.is_zero(): - return plot.text("$\\infty$", (-3, 3), **args) + return text("$\\infty$", (-3, 3), **args) else: - return plot.point((self[0], self[1]), **args) + return point((self[0], self[1]), **args) def _add_(self, right): """ From 90278a789175f4c204cd0f6861cfe795aad10b18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 17 Nov 2022 10:39:46 +0100 Subject: [PATCH 482/632] _repr_svg_ for Dyck words --- src/sage/combinat/dyck_word.py | 55 ++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/sage/combinat/dyck_word.py b/src/sage/combinat/dyck_word.py index 85fe966f70e..78fc75dda53 100644 --- a/src/sage/combinat/dyck_word.py +++ b/src/sage/combinat/dyck_word.py @@ -895,6 +895,61 @@ def _latex_(self) -> str: res += "\\end{tikzpicture}$}}" return res + def _repr_svg_(self) -> str: + """ + Return the svg picture of ``self``. + + This can be displayed by Jupyter. + + EXAMPLES:: + + sage: PP = DyckWords(6).random_element() + sage: PP._repr_svg_() + '' + """ + N = self.length() + width = 0.1 if N < 20 else N / 200 + resu = '' + resu += '' + resu += '" + hori_lines = [] + path = ['') + path.append('') + resu1 += " ".join(path) + hori_lines.append('') + resu3 += "".join(hori_lines) + + margin = 2 * width + resu += '\"{} {} {} {} \">'.format(-margin, -max_y - margin, + N + 2 * margin, max_y + 2 * margin) + + return resu + resu1 + resu3 + def plot(self, **kwds): """ Plot a Dyck word as a continuous path. From 096e22811a936af07c4b3b55974ca30f02e7360d Mon Sep 17 00:00:00 2001 From: Benjamin Hackl Date: Thu, 17 Nov 2022 06:50:31 -0700 Subject: [PATCH 483/632] trac 34756: appended /kernels to jupyter kernel paths --- src/doc/en/installation/launching.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/en/installation/launching.rst b/src/doc/en/installation/launching.rst index c7b4e1b35ec..be4d4041636 100644 --- a/src/doc/en/installation/launching.rst +++ b/src/doc/en/installation/launching.rst @@ -165,7 +165,7 @@ A command along the lines of .. CODE-BLOCK:: bash - ln -s $(sage -sh -c 'ls -d $SAGE_VENV/share/jupyter/kernels/sagemath') $HOME/.local/share/jupyter/sagemath-dev + ln -s $(sage -sh -c 'ls -d $SAGE_VENV/share/jupyter/kernels/sagemath') $HOME/.local/share/jupyter/kernels/sagemath-dev can then be used to create a symlink to the SageMath kernel description in a location where your own ``jupyter`` can find it. @@ -174,7 +174,7 @@ If you have installed SageMath from source, the alternative command .. CODE-BLOCK:: bash - ln -s $(sage -sh -c 'ls -d $SAGE_ROOT/venv/share/jupyter/kernels/sagemath') $HOME/.local/share/jupyter/sagemath-dev + ln -s $(sage -sh -c 'ls -d $SAGE_ROOT/venv/share/jupyter/kernels/sagemath') $HOME/.local/share/jupyter/kernels/sagemath-dev creates a symlink that will stay current even if you switch to a different Python version later. From 2dcf70c25eadc1af982ef53c836bd47e88c3b343 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 19 Nov 2022 08:44:32 +0100 Subject: [PATCH 484/632] some details in filtered simplicial complexes --- .../topology/filtered_simplicial_complex.py | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/sage/topology/filtered_simplicial_complex.py b/src/sage/topology/filtered_simplicial_complex.py index cbc0da0db16..4d860357904 100644 --- a/src/sage/topology/filtered_simplicial_complex.py +++ b/src/sage/topology/filtered_simplicial_complex.py @@ -90,6 +90,7 @@ from sage.rings.infinity import infinity from sage.misc.cachefunc import cached_method + class FilteredSimplicialComplex(SageObject): r""" Define a filtered complex. @@ -338,7 +339,7 @@ def filtration(self, s, filtration_value=None): else: self._insert(s, filtration_value) - def prune(self,threshold): + def prune(self, threshold): r""" Return a copy of the filtered complex, where simplices above the threshold value have been removed. @@ -367,7 +368,7 @@ def prune(self,threshold): return result_complex - @cached_method(key=lambda self,f,s,v:(f,s)) + @cached_method(key=lambda self, f, s, v: (f, s)) def _persistent_homology(self, field=2, strict=True, verbose=False): """ Compute the homology intervals of the complex. @@ -445,7 +446,7 @@ def key(s): # Initialize data structures for the algo self._marked = [False] * n self._T = [None] * n - intervals = [[] for i in range(self._dimension+1)] + intervals = [[] for i in range(self._dimension + 1)] self.pairs = [] self._strict = strict @@ -491,7 +492,7 @@ def _add_interval(self, s, t, intervals): Add a new interval (i.e. homology element). This method should not be called by users, it is used in - the ``_compute_persistence`` method. The simplex of + the ``_persistent_homology`` method. The simplex of death may be ``None``, in which case the interval is infinite. INPUT: @@ -544,7 +545,7 @@ def _remove_pivot_rows(self, s, simplices): This method implements the subroutine of the same name in [ZC2005]_. This method should not be called by users, - it is used in the ``compute_persistence`` method. + it is used in the ``_persistent_homology`` method. TESTS:: @@ -565,11 +566,11 @@ def _remove_pivot_rows(self, s, simplices): return d # Initialize the boundary chain - for (i, f) in enumerate(s.faces()): + for i, f in enumerate(s.faces()): d += (-1)**i * self._chaingroup(f) # Remove all unmarked elements - for (s, x_s) in d: + for s, x_s in d: j = self._index_of_simplex[s] if not self._marked[j]: d = d - x_s * self._chaingroup(s) @@ -586,7 +587,7 @@ def _remove_pivot_rows(self, s, simplices): c = self._T[max_index][1] q = c[t] - d = d - ((q**(-1))*c) + d = d - ((q**(-1)) * c) return d @@ -596,7 +597,7 @@ def _max_index(self, d): coefficient in ``d``. This method is called in ``_remove_pivot_rows`` and - ``compute_persistence``. It should not be called by users + ``_persistent_homology``. It should not be called by users outside of those methods. TESTS:: @@ -611,7 +612,7 @@ def _max_index(self, d): 6 """ currmax = -1 - for (s, x_s) in d: + for s, x_s in d: j = self._index_of_simplex[s] if j > currmax: currmax = j @@ -686,7 +687,7 @@ def betti_number(self, k, a, b, field=2, strict=True, verbose=None): if verbose is None: verbose = self._verbose intervals = self._persistent_homology(field, strict, verbose=verbose) - return Integer(sum(1 for (i, j) in intervals[k] + return Integer(sum(1 for i, j in intervals[k] if (i <= a and a + b < j) and a >= 0)) def _repr_(self): @@ -720,7 +721,7 @@ def _repr_(self): def _simplicial_(self): """ - Return the associated simplicial complex + Return the associated simplicial complex. All simplices of the filtered simplicial complex are included in the resulting simplicial complex. From dec76ef80dd7169d48858c2103592df9c9118ff2 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sat, 19 Nov 2022 15:50:58 +0100 Subject: [PATCH 485/632] implement _floordiv_ and gcd --- src/sage/combinat/sf/sfa.py | 116 ++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index 5cd0012cd34..a4abf9181fd 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -228,6 +228,7 @@ from sage.combinat.free_module import CombinatorialFreeModule from sage.matrix.constructor import matrix from sage.structure.factorization import Factorization +from sage.structure.element import parent from sage.misc.misc_c import prod from sage.data_structures.blas_dict import convert_remove_zeroes, linear_combination from copy import copy @@ -3090,6 +3091,120 @@ def factor(self, proof=None): factors = [(L(factor), exponent) for factor, exponent in factors] return Factorization(factors, unit=unit) + def _floordiv_(self, other): + """ + Perform division with remainder and return the quotient. + + INPUT: + + - ``right`` - something coercible to a symmetric function in + ``self.parent()`` + + EXAMPLES:: + + sage: e = SymmetricFunctions(ZZ).e() + sage: e[3,2,1] // e[2] + e[3, 1] + + """ + from sage.combinat.sf.multiplicative import SymmetricFunctionAlgebra_multiplicative + from sage.structure.element import get_coercion_model + cm = get_coercion_model() + L = cm.common_parent(self.parent(), parent(other)) + if isinstance(L, SymmetricFunctionAlgebra_multiplicative): + M = L + else: + M = L.realization_of().h() + self = M(self) + other = M(other) + + n1 = max((part[0] for part in self.support() if part), default=0) + n2 = max((part[0] for part in other.support() if part), default=0) + n = max(n1, n2, 1) + R = PolynomialRing(M.base_ring(), + ["v%s" % a for a in range(1, n + 1)]) + if n == 1: + p1 = R({part.to_exp(n)[0]: c for part, c in self}) + p2 = R({part.to_exp(n)[0]: c for part, c in other}) + else: + p1 = R({tuple(part.to_exp(n)): c for part, c in self}) + p2 = R({tuple(part.to_exp(n)): c for part, c in other}) + g = p1 // p2 + if n == 1: + g = {_Partitions.from_exp([e]): c + for e, c in g.dict().items()} + else: + g = {_Partitions.from_exp(e): c + for e, c in g.iterator_exp_coeff(False)} + return L(M.element_class(M, g)) + + def gcd(self, other): + """ + Return the greatest common divisor with ``other``. + + INPUT: + + - ``other`` -- the other symmetric function + + EXAMPLES:: + + sage: e = SymmetricFunctions(ZZ).e() + sage: A = 5*e[3] + e[2,1] + e[1] + sage: B = 7*e[2] + e[5,1] + sage: C = 3*e[1,1] + e[2] + sage: gcd(A*B^2, B*C) + 7*e[2] + e[5, 1] + + sage: p = SymmetricFunctions(ZZ).p() + sage: gcd(e[2,1], p[1,1]-p[2]) + e[2] + sage: gcd(p[2,1], p[3,2]-p[2,1]) + p[2] + + TESTS:: + + sage: s = SymmetricFunctions(ZZ).s() + sage: gcd(s(0), s[1]) + s[1] + + sage: gcd(s(0), s(1)) + s[] + + sage: gcd(s(9), s(6)) + 3*s[] + + """ + from sage.combinat.sf.multiplicative import SymmetricFunctionAlgebra_multiplicative + from sage.structure.element import get_coercion_model + cm = get_coercion_model() + L = cm.common_parent(self.parent(), parent(other)) + if isinstance(L, SymmetricFunctionAlgebra_multiplicative): + M = L + else: + M = L.realization_of().h() + self = M(self) + other = M(other) + + n1 = max((part[0] for part in self.support() if part), default=0) + n2 = max((part[0] for part in other.support() if part), default=0) + n = max(n1, n2, 1) + R = PolynomialRing(M.base_ring(), + ["v%s" % a for a in range(1, n + 1)]) + if n == 1: + p1 = R({part.to_exp(n)[0]: c for part, c in self}) + p2 = R({part.to_exp(n)[0]: c for part, c in other}) + else: + p1 = R({tuple(part.to_exp(n)): c for part, c in self}) + p2 = R({tuple(part.to_exp(n)): c for part, c in other}) + g = p1.gcd(p2) + if n == 1: + g = {_Partitions.from_exp([e]): c + for e, c in g.dict().items()} + else: + g = {_Partitions.from_exp(e): c + for e, c in g.iterator_exp_coeff(False)} + return L(M.element_class(M, g)) + def plethysm(self, x, include=None, exclude=None): r""" Return the outer plethysm of ``self`` with ``x``. @@ -3171,6 +3286,7 @@ def plethysm(self, x, include=None, exclude=None): Sage can also do the plethysm with an element in the completion:: + sage: s = SymmetricFunctions(QQ).s() sage: L = LazySymmetricFunctions(s) sage: f = s[2,1] sage: g = L(s[1]) / (1 - L(s[1])); g From 444d3f3f581a0f1cb2f3497664ffb46405182024 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 19 Nov 2022 10:36:46 -0800 Subject: [PATCH 486/632] src/sage/libs/fes.pyx: Remove --- pkgs/sagemath-standard/setup.py | 2 +- src/sage/libs/fes.pyx | 343 -------------------------------- src/setup.py | 2 +- 3 files changed, 2 insertions(+), 345 deletions(-) delete mode 100644 src/sage/libs/fes.pyx diff --git a/pkgs/sagemath-standard/setup.py b/pkgs/sagemath-standard/setup.py index ce9fd0a70aa..d03b63400f6 100755 --- a/pkgs/sagemath-standard/setup.py +++ b/pkgs/sagemath-standard/setup.py @@ -93,7 +93,7 @@ from sage.misc.package import is_package_installed_and_updated distributions = [''] optional_packages_with_extensions = ['mcqd', 'bliss', 'tdlib', - 'coxeter3', 'fes', 'sirocco', 'meataxe'] + 'coxeter3', 'sirocco', 'meataxe'] distributions += ['sagemath-{}'.format(pkg) for pkg in optional_packages_with_extensions if is_package_installed_and_updated(pkg)] diff --git a/src/sage/libs/fes.pyx b/src/sage/libs/fes.pyx deleted file mode 100644 index bf62c33e265..00000000000 --- a/src/sage/libs/fes.pyx +++ /dev/null @@ -1,343 +0,0 @@ -# distutils: language = c -# distutils: libraries = fes -# sage_setup: distribution = sagemath-fes - -""" -Binding for the FES library - -Finding solutions of systems of boolean equations by exhaustive -search, via the fes library. This is usually (much) faster than -computing a Groebner basis, except in special cases where the latter -is particularly easy. - -The FES library is presently only able to deal with polynomials in 64 -variables. Performing a full exhaustive search over 64 variables will -take a **long** time. The number of variables can be artificially -reduced to 64 by specializing some of them. - -Note that the FES library **requires** at least of the equations to be -non-linear. - -AUTHORS: - -- Charles Bouillaguet (2012-12-20) : initial version - -EXAMPLES: - -Random Degree-2 System:: - - sage: from sage.libs.fes import exhaustive_search # optional - FES - sage: n = 16 # optional - FES - sage: R = BooleanPolynomialRing(n, 'x') # optional - FES - sage: solution = dict(zip(R.gens(), VectorSpace(GF(2), n).random_element())) # optional - FES - - sage: F = [ R.random_element() for i in range(n+10) ] # optional - FES - sage: G = [ f + f.subs(solution) for f in F] # optional - FES - sage: sols = exhaustive_search(G) # optional - FES - sage: solution in sols # optional - FES - True - sage: len(sols) # optional - FES - 1 - -Cyclic benchmark:: - - sage: from sage.rings.ideal import Cyclic # optional - FES - sage: from sage.libs.fes import exhaustive_search # optional - FES - sage: n = 10 # optional - FES - sage: R = BooleanPolynomialRing(n, 'x') # optional - FES - sage: sols = exhaustive_search( Cyclic(R) ) # optional - FES - sage: len(sols) # optional - FES - 1 - sage: set(sols[0]) == set(R.gens()) # optional - FES - True - -REFERENCES: - -- [BCCCNSY2010]_ -""" - -#***************************************************************************** -# Copyright (C) 2012 Charles Bouillaguet -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** - -from libc.stdint cimport uint64_t -from cysignals.memory cimport sig_calloc, sig_free -from cysignals.signals cimport sig_on, sig_off - -cdef extern from "fes_interface.h": - ctypedef int (*solution_callback_t)(void *, uint64_t) - - void exhaustive_search_wrapper(int n, int n_eqs, int degree, int ***coeffs, solution_callback_t callback, void* callback_state, int verbose) - - -from sage.rings.integer import Integer -from sage.rings.infinity import Infinity -from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF - -from sage.structure.parent cimport Parent -from sage.structure.sequence import Sequence -from sage.rings.polynomial.multi_polynomial cimport MPolynomial -from sage.rings.polynomial.term_order import TermOrder -from sage.rings.polynomial.pbori.pbori import BooleanPolynomial, BooleanPolynomialRing -from sage.arith.all import binomial -from sage.combinat.subset import Subsets - -from sage.matrix.all import * -from sage.modules.all import * - - - -class InternalState: - verbose = False - sols = [] - max_sols = 0 - - -cdef int report_solution(void *_state, uint64_t i): -# This is the callback function which is invoked by the fes library -# each time a solution is found. ``i`` describes the solution (least -# significant bit gives the value of the first variable, most -# significant bit gives the value of the last variable). ``state`` is -# the pointer passed to the fes library initially. - cdef object state = _state - state.sols.append(i) - if state.verbose: - print("fes: solution {0} / {1} found : {2:x}".format(len(state.sols), state.max_sols, i)) - if (state.max_sols > 0 and state.max_sols >= len(state.sols)): - return 1 #stop the library - return 0 # keep going - - -def exhaustive_search(eqs, max_sols=Infinity, verbose=False): - r""" - Invokes the fes library to solve a system of boolean equation by - exhaustive search. - - INPUT: - - - ``eqs`` -- list of boolean equations - - - ``max_sols`` -- stop after this many solutions are found - - - ``verbose`` -- whether the library should display information about - its work - - NOTE: - - Using this function requires the optional package FES to be installed. - - EXAMPLES: - - A very simple example:: - - sage: from sage.libs.fes import exhaustive_search # optional - FES - sage: R. = BooleanPolynomialRing() # optional - FES - sage: exhaustive_search( [x*y + z + y + 1, x+y+z, x*y + y*z] ) # optional - FES - [{y: 0, z: 1, x: 1}] - - Another very simple example:: - - sage: R. = BooleanPolynomialRing() # optional - FES - sage: f = x*y*z + z + y + 1 # optional - FES - sage: sorted( exhaustive_search( [f, x+y+z, x*y + y*z] ) ) # optional - FES - [{w: 0, t: 0, y: 0, z: 1, x: 1}, {w: 0, t: 1, y: 0, z: 1, x: 1}, {w: 1, t: 0, y: 0, z: 1, x: 1}, {w: 1, t: 1, y: 0, z: 1, x: 1}] - - An example with several solutions:: - - sage: R. = BooleanPolynomialRing() # optional - FES - sage: eqs = [x*z + y*t + 1, x*y + y*z + z*t + t*x , x+y+z+1] # optional - FES - sage: sorted( exhaustive_search( eqs ) ) # optional - FES - [{t: 0, y: 1, z: 1, x: 1}, {t: 1, y: 1, z: 0, x: 0}] - - We voluntarily limit the number of solutions returned by the library:: - - sage: eqs = [x*z + y*t + 1, x*y + y*z + z*t + t*x , x+y+z+1] # optional - FES - sage: exhaustive_search(eqs, max_sols=1 ) # optional - FES - [{t: 0, y: 1, z: 1, x: 1}] - - .. SEEALSO:: - - This function should return the same solutions as - :meth:`~sage.rings.polynomial.pbori.BooleanPolynomialIdeal.variety` - on a :class:`~sage.rings.polynomial.pbori.BooleanPolynomialIdeal`. - - .. NOTE:: - - The order in which the solutions are returned is implementation - dependent ; it may vary accross machines, and may vary accross - calls to the function. - - """ - if eqs == []: - raise ValueError("No equations, no solutions") - - eqs = Sequence(eqs) - R = eqs.ring() - if R.base_ring() != GF(2): - raise ValueError("FES only deals with equations over GF(2)") - - degree = max( [f.degree() for f in eqs] ) - if degree <= 1: - raise ValueError("the FES library requires equations to be non-linear") - n = R.ngens() - - - # ------- initialize a data-structure to communicate the equations to the library - cdef int ***coeffs = sig_calloc(len(eqs), sizeof(int **)) - for e,f in enumerate(eqs): - coeffs[e] = sig_calloc(degree+1, sizeof(int *)) - for d in range(degree+1): - coeffs[e][d] = sig_calloc(binomial(n,d), sizeof(int)) - - for m in f: # we enumerate the monomials of f - d = m.degree() - temp_n = n - int_idx = 0 - prev_var = -1 - for idx in sorted(m.iterindex()): # iterate over all the variables in the monomial - j = idx - prev_var - 1 - for k in range(j): - int_idx += binomial(temp_n-k-1, d-1) - prev_var = idx - d -= 1 - temp_n -= 1 + j - coeffs[e][ m.degree() ][int_idx] = 1 - - internal_state = InternalState() - internal_state.verbose = verbose - internal_state.sols = [] - if max_sols == Infinity: - internal_state.max_sols = 0 - else: - internal_state.max_sols = max_sols - - # ------- runs the library - sig_on() - exhaustive_search_wrapper(n, len(eqs), degree, coeffs, report_solution, internal_state, verbose) - sig_off() - - # ------- frees memory occupied by the dense representation of the equations - if coeffs != NULL: - for e in range(len(eqs)): - if coeffs[e] != NULL: - for d in range(degree+1): - if coeffs[e][d] != NULL: - sig_free(coeffs[e][d]) - sig_free(coeffs[e]) - sig_free(coeffs) - - # ------ convert (packed) solutions to suitable format - dict_sols = [] - for i in internal_state.sols: - sol = dict([]) - for x in R.gens(): - sol[x] = GF(2)(i & 1) - i = i >> 1 - dict_sols.append( sol ) - - return dict_sols - - -def find_coordinate_change(As, max_tries=64): - """ - Tries to find a linear change of coordinates such that certain - coefficients of the quadratic forms As become zero. This in turn - makes the exhaustive search faster - - EXAMPLES: - - Testing that the function works:: - - sage: from sage.libs.fes import find_coordinate_change # optional - FES - sage: n = 40 # optional - FES - sage: K = GF(2) # optional - FES - sage: R = BooleanPolynomialRing(n, 'x') # optional - FES - sage: foo = [ MatrixSpace(GF(2), n, n).random_element() ] # optional - FES - sage: f = [ M + M.T for M in foo ] # optional - FES - sage: S = find_coordinate_change( f ) # optional - FES - sage: g = [ S.T*M*S for M in f ] # optional - FES - sage: vector ([ M[0,1] for M in g[:32]]).is_zero() # optional - FES - True - """ - n = As[0].nrows() - m = min(32, len(As)) - system = matrix(GF(2), m, n-2) - S = identity_matrix(GF(2), n) - Bs = As - - for foo in range(max_tries): - for i in range(m): - system[i] = Bs[i][0,2:] - RHS = vector(GF(2), m, [ Bs[i][0,1] for i in range(m) ]) - try: - solution = system.solve_right( RHS ) - S_prime = identity_matrix(GF(2), n) - S_prime[1,2:] = solution - return S*S_prime.T - except ValueError: - S.randomize() - while not S.is_invertible(): - S.randomize() - Bs = [ S.T*M*S for M in As ] - print("trying again...") - raise ValueError("Could not find suitable coordinate change") - - - - -def prepare_polynomials(f): - """ - Finds a linear combination of the equations that is faster to solve by FES - - INPUT: - - - ``f`` -- list of boolean equations - - EXAMPLES: - - We check that the function does what it is supposed to do:: - - sage: from sage.libs.fes import prepare_polynomials # optional - FES - sage: n = 35 # optional - FES - sage: K = GF(2) # optional - FES - sage: R = BooleanPolynomialRing(n, 'x') # optional - FES - sage: f = [ sum( [K.random_element() * R.gen(i) * R.gen(j) for i in range(n) for j in range(i)] ) \ - + sum( [K.random_element() * R.gen(i) for i in range(n) ] ) \ - + K.random_element() for l in range(n) ] # optional - FES - sage: g = prepare_polynomials(f) # optional - FES - sage: [x.lm() for x in g] # optional - FES, random - 0 - """ - if f == []: - return [] - excess = len(f) - 32 - - if excess <= 0: - return f - - # now, try to cancel the `excess` first monomials - s = Sequence(f) - - # switch to degrevlex if not already there - R = s.ring() - if R.term_order() != 'degrevlex': - R2 = BooleanPolynomialRing( R.ngens(), R.variable_names(), order='degrevlex') - s = Sequence( [R2(g) for g in s] ) - - monomials_in_s = list( s.monomials() ) - monomials_in_s.sort(reverse=True) - - m = matrix(R.base_ring(), [ [ g.monomial_coefficient(m) for m in monomials_in_s[:excess] ] for g in s ]) - # now find the linear combinations of the equations that kills the first `excess` monomials in all but `excess` equations - # todo, this is very likely suboptimal, but m.echelonize() does not return the transformation... - P, L, U = m.LU() - S = (P*L).I - result = Sequence( S * vector(s) ) - result.reverse() - return result diff --git a/src/setup.py b/src/setup.py index 948e4754513..f0dd4054bb7 100755 --- a/src/setup.py +++ b/src/setup.py @@ -80,7 +80,7 @@ # Exclude a few files if the corresponding distribution is not loaded optional_packages = ['mcqd', 'bliss', 'tdlib', - 'coxeter3', 'fes', 'sirocco', 'meataxe'] + 'coxeter3', 'sirocco', 'meataxe'] not_installed_packages = [package for package in optional_packages if not is_package_installed_and_updated(package)] From e7c8026920c146b96a2c2999139eded1825b3110 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 19 Nov 2022 10:43:19 -0800 Subject: [PATCH 487/632] src/sage/rings/polynomial/multi_polynomial_sequence.py: Remove use of fes --- src/sage/features/fes.py | 96 ------------------- .../polynomial/multi_polynomial_sequence.py | 8 +- 2 files changed, 1 insertion(+), 103 deletions(-) delete mode 100644 src/sage/features/fes.py diff --git a/src/sage/features/fes.py b/src/sage/features/fes.py deleted file mode 100644 index 50a2eba97c3..00000000000 --- a/src/sage/features/fes.py +++ /dev/null @@ -1,96 +0,0 @@ -# -*- coding: utf-8 -*- -r""" -Features for testing the presence of ``fes`` -""" - -from . import CythonFeature, PythonModule -from .join_feature import JoinFeature - - -TEST_CODE = """ -# distutils: libraries=fes - -from libc.stdint cimport uint64_t -cdef extern from "": - ctypedef int (*solution_callback_t)(void *, uint64_t) - void exhaustive_search_wrapper(int n, int n_eqs, int degree, int ***coeffs, solution_callback_t callback, void* callback_state, int verbose) - -solutions = 0 - -class InternalState: - verbose = False - sols = [] - max_sols = 0 - -cdef int report_solution(void *_state, uint64_t i): - global solutions - solutions += 1 - return 0 - -sig_on() -cdef int ***coeffs = sig_calloc(1, sizeof(int **)) -coeffs[0] = sig_calloc(3, sizeof(int *)) -coeffs[0][0] = sig_calloc(1, sizeof(int)) -coeffs[0][1] = sig_calloc(2, sizeof(int)) -coeffs[0][2] = sig_calloc(1, sizeof(int)) -coeffs[0][2][0] = 1 # x*y = 0 -internal_state = InternalState() - -exhaustive_search_wrapper(2, 1, 2, coeffs, report_solution, internal_state, 0) - -sig_free(coeffs[0][2]) -sig_free(coeffs[0][1]) -sig_free(coeffs[0][0]) -sig_free(coeffs[0]) -sig_free(coeffs) -sig_off() - -if solutions != 3: - raise AssertionError("libFES did not find three solutions for x*y = 0") -""" - - -class LibFESLibrary(CythonFeature): - r""" - A :class:`~sage.features.Feature` which describes whether the FES library - is present and functional. - - EXAMPLES:: - - sage: from sage.features.fes import LibFESLibrary - sage: LibFESLibrary().require() # optional - fes - """ - def __init__(self): - r""" - TESTS:: - - sage: from sage.features.fes import LibFESLibrary - sage: isinstance(LibFESLibrary(), LibFESLibrary) - True - """ - CythonFeature.__init__(self, "LibFES", test_code=TEST_CODE, spkg="fes", - url="http://www.lifl.fr/~bouillag/fes/") - - -class LibFES(JoinFeature): - r""" - A :class:`~sage.features.Feature` which describes whether the :mod:`sage.libs.fes` - module has been enabled for this build of Sage and is functional. - - EXAMPLES:: - - sage: from sage.features.fes import LibFES - sage: LibFES().require() # optional - fes - """ - def __init__(self): - r""" - TESTS:: - - sage: from sage.features.fes import LibFES - sage: isinstance(LibFES(), LibFES) - True - """ - JoinFeature.__init__(self, 'fes', - [PythonModule("sage.libs.fes")], - spkg="fes", - url="http://www.lifl.fr/~bouillag/fes/") diff --git a/src/sage/rings/polynomial/multi_polynomial_sequence.py b/src/sage/rings/polynomial/multi_polynomial_sequence.py index e7d6f7e0207..632704d46ad 100644 --- a/src/sage/rings/polynomial/multi_polynomial_sequence.py +++ b/src/sage/rings/polynomial/multi_polynomial_sequence.py @@ -1466,13 +1466,7 @@ def solve(self, algorithm='polybori', n=1, eliminate_linear_variables=True, ver S = PolynomialSequence( R_solving, [ R_solving(f) for f in T] ) if S != []: - if algorithm == "exhaustive_search": - from sage.features.fes import LibFES - LibFES().require() - from sage.libs.fes import exhaustive_search - solutions = exhaustive_search(S, max_sols=n, verbose=verbose, **kwds) - - elif algorithm == "polybori": + if algorithm == "polybori": I = S.ideal() if verbose: I.groebner_basis(full_prot=True, **kwds) From 1ecd04267fc16a0e6b7b9c8763cd9ac6bb37f5d8 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 19 Nov 2022 10:44:02 -0800 Subject: [PATCH 488/632] src/doc/en/reference/libs/index.rst: Remove fes --- src/doc/en/reference/libs/index.rst | 2 +- src/doc/en/reference/misc/index.rst | 1 - src/sage/features/__init__.py | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/doc/en/reference/libs/index.rst b/src/doc/en/reference/libs/index.rst index 43da10f063c..0a100e35da1 100644 --- a/src/doc/en/reference/libs/index.rst +++ b/src/doc/en/reference/libs/index.rst @@ -147,6 +147,6 @@ Symmetrica .. Cannot be imported independently of mpmath: sage/libs/mpmath/ext_main sage/libs/mpmath/ext_impl sage/libs/mpmath/ext_libmp -.. Modules depending on optional packages: sage/libs/coxeter3/coxeter sage/libs/coxeter3/coxeter_group sage/libs/fes sage/libs/homfly sage/libs/braiding +.. Modules depending on optional packages: sage/libs/coxeter3/coxeter sage/libs/coxeter3/coxeter_group sage/libs/homfly sage/libs/braiding .. include:: ../footer.txt diff --git a/src/doc/en/reference/misc/index.rst b/src/doc/en/reference/misc/index.rst index a78a7f2e729..8a26fcb9a00 100644 --- a/src/doc/en/reference/misc/index.rst +++ b/src/doc/en/reference/misc/index.rst @@ -156,7 +156,6 @@ Features sage/features/csdp sage/features/databases sage/features/dvipng - sage/features/fes sage/features/ffmpeg sage/features/four_ti_2 sage/features/gap diff --git a/src/sage/features/__init__.py b/src/sage/features/__init__.py index fd899aa4770..2ebb177069c 100644 --- a/src/sage/features/__init__.py +++ b/src/sage/features/__init__.py @@ -742,8 +742,8 @@ def __init__(self, name, test_code, **kwds): TESTS:: sage: from sage.features import CythonFeature - sage: from sage.features.fes import LibFESLibrary - sage: isinstance(LibFESLibrary(), CythonFeature) # indirect doctest + sage: from sage.features.bliss import BlissLibrary + sage: isinstance(BlissLibrary(), CythonFeature) # indirect doctest True """ Feature.__init__(self, name, **kwds) From 71cc4e960313234d61cd58ef57eec73f6b3e87b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 19 Nov 2022 20:05:27 +0100 Subject: [PATCH 489/632] adding some :meth: links --- src/sage/topology/filtered_simplicial_complex.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/topology/filtered_simplicial_complex.py b/src/sage/topology/filtered_simplicial_complex.py index 4d860357904..a8193680b29 100644 --- a/src/sage/topology/filtered_simplicial_complex.py +++ b/src/sage/topology/filtered_simplicial_complex.py @@ -322,7 +322,7 @@ def filtration(self, s, filtration_value=None): If ``filtration_value`` is set, this function inserts the simplex into the complex with the specified value. - See documentation of ``insert`` for more details. + See documentation of :meth:`insert` for more details. EXAMPLES:: @@ -492,7 +492,7 @@ def _add_interval(self, s, t, intervals): Add a new interval (i.e. homology element). This method should not be called by users, it is used in - the ``_persistent_homology`` method. The simplex of + the :meth:`_persistent_homology` method. The simplex of death may be ``None``, in which case the interval is infinite. INPUT: @@ -545,7 +545,7 @@ def _remove_pivot_rows(self, s, simplices): This method implements the subroutine of the same name in [ZC2005]_. This method should not be called by users, - it is used in the ``_persistent_homology`` method. + it is used in the :meth:`_persistent_homology` method. TESTS:: @@ -596,8 +596,8 @@ def _max_index(self, d): Return the maximal index of all simplices with nonzero coefficient in ``d``. - This method is called in ``_remove_pivot_rows`` and - ``_persistent_homology``. It should not be called by users + This method is called in :meth:`_remove_pivot_rows` and + :meth:`_persistent_homology`. It should not be called by users outside of those methods. TESTS:: @@ -665,7 +665,7 @@ def betti_number(self, k, a, b, field=2, strict=True, verbose=None): persistent homology computation; the default is the verbosity of ``self`` - The Betti number ``\beta_k^{a,a+b}`` counts the number of + The Betti number `\beta_k^{a,a+b}` counts the number of homology elements which are alive throughout the whole duration ``[a, a+b]``. From 89fdaf3944146a50f92bb12e60120b7d56313344 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Sat, 19 Nov 2022 11:55:25 -0800 Subject: [PATCH 490/632] trac 34762: fix doctest with random chain complexes --- src/sage/homology/tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/homology/tests.py b/src/sage/homology/tests.py index 89c86857d44..89dc727e20e 100644 --- a/src/sage/homology/tests.py +++ b/src/sage/homology/tests.py @@ -47,9 +47,9 @@ def random_chain_complex(level=1): sage: from sage.homology.tests import random_chain_complex sage: C = random_chain_complex() - sage: C + sage: C # random Chain complex with at most ... nonzero terms over Integer Ring - sage: len(C.nonzero_degrees()) in [1, 2] + sage: len(C.nonzero_degrees()) in [0, 1, 2] True sage: C.degree_of_differential() in [-1, 1] True From 51412f335ff90d77c65967c58d1de98476245680 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Sun, 20 Nov 2022 09:28:06 -0500 Subject: [PATCH 491/632] src/sage/modular/quasimodform/ring.py: delete unused variable assignment --- src/sage/modular/quasimodform/ring.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/modular/quasimodform/ring.py b/src/sage/modular/quasimodform/ring.py index d557568239c..5579aa621d6 100644 --- a/src/sage/modular/quasimodform/ring.py +++ b/src/sage/modular/quasimodform/ring.py @@ -673,7 +673,6 @@ def polynomial_ring(self, names=None): sage: QM.polynomial_ring(names="P, Q, R") Multivariate Polynomial Ring in P, Q, R over Rational Field """ - M = self.__modular_forms_subring gens = self.__modular_forms_subring.gen_forms() weights = [f.weight() for f in gens] gens = iter(gens) From ccc925fd1bd22721b9e478f13350231663c31334 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 20 Nov 2022 20:10:42 +0100 Subject: [PATCH 492/632] check whether we have a fraction field --- src/sage/rings/lazy_series.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 48af601a570..19507745273 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -3032,6 +3032,7 @@ def _div_(self, other): R = P._internal_poly_ring if (isinstance(left, Stream_exact) and isinstance(right, Stream_exact) + and hasattr(R, "fraction_field") and hasattr(R, "_gcd_univariate_polynomial")): z = R.gen() num = left._polynomial_part(R) * (1-z) + left._constant * z**left._degree @@ -3052,13 +3053,13 @@ def _div_(self, other): # dividing by z^k d = den[exponents[0]] v = num.valuation() - initial_coefficients = [num[i] / d for i in range(v, num.degree() + 1)] + initial_coefficients = [num[i] / d + for i in range(v, num.degree() + 1)] order = v - den.valuation() return P.element_class(P, Stream_exact(initial_coefficients, P._sparse, order=order, constant=0)) - if (len(exponents) == 2 and exponents[0] + 1 == exponents[1] and den[exponents[0]] == -den[exponents[1]]): From f5b5d990e343261ea3feec4afc17638419fbfa71 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 20 Nov 2022 14:35:59 -0800 Subject: [PATCH 493/632] build/pkgs/meson: Add spkg-configure.m4, distros/ --- build/pkgs/meson/distros/alpine.txt | 1 + build/pkgs/meson/distros/arch.txt | 1 + build/pkgs/meson/distros/debian.txt | 1 + build/pkgs/meson/distros/fedora.txt | 1 + build/pkgs/meson/distros/freebsd.txt | 1 + build/pkgs/meson/distros/gentoo.txt | 1 + build/pkgs/meson/distros/homebrew.txt | 1 + build/pkgs/meson/distros/nix.txt | 1 + build/pkgs/meson/distros/opensuse.txt | 1 + build/pkgs/meson/distros/repology.txt | 1 + build/pkgs/meson/distros/slackware.txt | 1 + build/pkgs/meson/spkg-configure.m4 | 15 +++++++++++++++ 12 files changed, 26 insertions(+) create mode 100644 build/pkgs/meson/distros/alpine.txt create mode 100644 build/pkgs/meson/distros/arch.txt create mode 100644 build/pkgs/meson/distros/debian.txt create mode 100644 build/pkgs/meson/distros/fedora.txt create mode 100644 build/pkgs/meson/distros/freebsd.txt create mode 100644 build/pkgs/meson/distros/gentoo.txt create mode 100644 build/pkgs/meson/distros/homebrew.txt create mode 100644 build/pkgs/meson/distros/nix.txt create mode 100644 build/pkgs/meson/distros/opensuse.txt create mode 100644 build/pkgs/meson/distros/repology.txt create mode 100644 build/pkgs/meson/distros/slackware.txt create mode 100644 build/pkgs/meson/spkg-configure.m4 diff --git a/build/pkgs/meson/distros/alpine.txt b/build/pkgs/meson/distros/alpine.txt new file mode 100644 index 00000000000..0b9952eaa15 --- /dev/null +++ b/build/pkgs/meson/distros/alpine.txt @@ -0,0 +1 @@ +meson diff --git a/build/pkgs/meson/distros/arch.txt b/build/pkgs/meson/distros/arch.txt new file mode 100644 index 00000000000..0b9952eaa15 --- /dev/null +++ b/build/pkgs/meson/distros/arch.txt @@ -0,0 +1 @@ +meson diff --git a/build/pkgs/meson/distros/debian.txt b/build/pkgs/meson/distros/debian.txt new file mode 100644 index 00000000000..0b9952eaa15 --- /dev/null +++ b/build/pkgs/meson/distros/debian.txt @@ -0,0 +1 @@ +meson diff --git a/build/pkgs/meson/distros/fedora.txt b/build/pkgs/meson/distros/fedora.txt new file mode 100644 index 00000000000..0b9952eaa15 --- /dev/null +++ b/build/pkgs/meson/distros/fedora.txt @@ -0,0 +1 @@ +meson diff --git a/build/pkgs/meson/distros/freebsd.txt b/build/pkgs/meson/distros/freebsd.txt new file mode 100644 index 00000000000..9dfdf90524a --- /dev/null +++ b/build/pkgs/meson/distros/freebsd.txt @@ -0,0 +1 @@ +devel/meson diff --git a/build/pkgs/meson/distros/gentoo.txt b/build/pkgs/meson/distros/gentoo.txt new file mode 100644 index 00000000000..b5c4183b217 --- /dev/null +++ b/build/pkgs/meson/distros/gentoo.txt @@ -0,0 +1 @@ +dev-util/meson diff --git a/build/pkgs/meson/distros/homebrew.txt b/build/pkgs/meson/distros/homebrew.txt new file mode 100644 index 00000000000..0b9952eaa15 --- /dev/null +++ b/build/pkgs/meson/distros/homebrew.txt @@ -0,0 +1 @@ +meson diff --git a/build/pkgs/meson/distros/nix.txt b/build/pkgs/meson/distros/nix.txt new file mode 100644 index 00000000000..0b9952eaa15 --- /dev/null +++ b/build/pkgs/meson/distros/nix.txt @@ -0,0 +1 @@ +meson diff --git a/build/pkgs/meson/distros/opensuse.txt b/build/pkgs/meson/distros/opensuse.txt new file mode 100644 index 00000000000..0b9952eaa15 --- /dev/null +++ b/build/pkgs/meson/distros/opensuse.txt @@ -0,0 +1 @@ +meson diff --git a/build/pkgs/meson/distros/repology.txt b/build/pkgs/meson/distros/repology.txt new file mode 100644 index 00000000000..0b9952eaa15 --- /dev/null +++ b/build/pkgs/meson/distros/repology.txt @@ -0,0 +1 @@ +meson diff --git a/build/pkgs/meson/distros/slackware.txt b/build/pkgs/meson/distros/slackware.txt new file mode 100644 index 00000000000..0b9952eaa15 --- /dev/null +++ b/build/pkgs/meson/distros/slackware.txt @@ -0,0 +1 @@ +meson diff --git a/build/pkgs/meson/spkg-configure.m4 b/build/pkgs/meson/spkg-configure.m4 new file mode 100644 index 00000000000..3fbcf288065 --- /dev/null +++ b/build/pkgs/meson/spkg-configure.m4 @@ -0,0 +1,15 @@ +SAGE_SPKG_CONFIGURE( + [meson], [ + AC_CACHE_CHECK([for meson >= 0.63.3], [ac_cv_path_MESON], [ + AC_PATH_PROGS_FEATURE_CHECK([MESON], [meson], [ + meson_version=`$ac_path_MESON --version 2>&1` + AS_IF([test -n "$meson_version"], [ + AX_COMPARE_VERSION([$meson_version], [ge], [0.63.3], [ + ac_cv_path_MESON="$ac_path_MESON" + ac_path_MESON_found=: + ]) + ]) + ]) + ]) + AS_IF([test -z "$ac_cv_path_MESON"], [sage_spkg_install_meson=yes]) +]) From 249ce49000a0beb73a5fe1ed51b43963adb1f01a Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 21 Nov 2022 08:23:45 +0100 Subject: [PATCH 494/632] fix latex for empty skew partitions --- src/sage/combinat/skew_partition.py | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/sage/combinat/skew_partition.py b/src/sage/combinat/skew_partition.py index ea6b3b52057..e61e75fd26f 100644 --- a/src/sage/combinat/skew_partition.py +++ b/src/sage/combinat/skew_partition.py @@ -267,8 +267,16 @@ def _latex_diagram(self): &\lr{\ast}&\lr{\ast}\\\cline{2-3} \end{array}$} } + + TESTS: + + Check that :trac:`34760` is fixed:: + + sage: print(SkewPartition([[],[]])._latex_diagram()) + {\emptyset} + """ - if len(self._list) == 0: + if not any(self._list): return "{\\emptyset}" char = self.parent().options.latex_diagram_str @@ -294,8 +302,15 @@ def _latex_young_diagram(self): &\lr{\phantom{x}}&\lr{\phantom{x}}\\\cline{2-3} \end{array}$} } + + TESTS: + + Check that :trac:`34760` is fixed:: + + sage: print(SkewPartition([[],[]])._latex_young_diagram()) + {\emptyset} """ - if len(self._list) == 0: + if not any(self._list): return "{\\emptyset}" from sage.combinat.output import tex_from_array @@ -319,8 +334,15 @@ def _latex_marked(self): \lr{X}&\lr{\phantom{x}}&\lr{\phantom{x}}\\\cline{1-3} \end{array}$} } + + TESTS: + + Check that :trac:`34760` is fixed:: + + sage: print(SkewPartition([[],[]])._latex_marked()) + {\emptyset} """ - if len(self._list) == 0: + if not any(self._list): return "{\\emptyset}" from sage.combinat.output import tex_from_array From 18671900baa942dd408cda82be6ebb4d995afed8 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Mon, 21 Nov 2022 13:50:46 +0000 Subject: [PATCH 495/632] use libgap rather than pexpect gap --- src/sage/topology/simplicial_complex.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/topology/simplicial_complex.py b/src/sage/topology/simplicial_complex.py index 736df96ef20..fe237895d83 100644 --- a/src/sage/topology/simplicial_complex.py +++ b/src/sage/topology/simplicial_complex.py @@ -4081,7 +4081,7 @@ def fundamental_group(self, base_point=None, simplify=True): return self.connected_component(Simplex([base_point])).fundamental_group(simplify=simplify) from sage.groups.free_group import FreeGroup - from sage.interfaces.gap import gap + from sage.libs.gap.libgap import libgap as gap G = self.graph() # If the vertices and edges of G are not sortable, e.g., a mix # of str and int, Sage+Python 3 may raise a TypeError when From e9590f5c24b32e2c69dcd29553ae522d37bbfdcb Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 21 Nov 2022 11:50:25 -0800 Subject: [PATCH 496/632] build/pkgs/meson_python: Update to 0.11.0 --- build/pkgs/meson_python/checksums.ini | 6 +++--- build/pkgs/meson_python/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/meson_python/checksums.ini b/build/pkgs/meson_python/checksums.ini index 6c2854e30a4..fa69cec7dca 100644 --- a/build/pkgs/meson_python/checksums.ini +++ b/build/pkgs/meson_python/checksums.ini @@ -1,5 +1,5 @@ tarball=meson_python-VERSION.tar.gz -sha1=6ff574d17feb98165a2678c78a429d3ff32aaa7b -md5=afd824bf61e79cd5eeab2a6dc7863eaf -cksum=3939516041 +sha1=09035196e1576073a7e4acac1f010e5e07e55f89 +md5=60856897b63bc91e1f953bf29f410be4 +cksum=3201302061 upstream_url=https://pypi.io/packages/source/m/meson_python/meson_python-VERSION.tar.gz diff --git a/build/pkgs/meson_python/package-version.txt b/build/pkgs/meson_python/package-version.txt index 78bc1abd14f..d9df1bbc0c7 100644 --- a/build/pkgs/meson_python/package-version.txt +++ b/build/pkgs/meson_python/package-version.txt @@ -1 +1 @@ -0.10.0 +0.11.0 From bbf5f95fc58782b544514959c9199ffb68060575 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 21 Nov 2022 11:53:16 -0800 Subject: [PATCH 497/632] build/pkgs/numpy: Update to 1.23.5 --- build/pkgs/numpy/checksums.ini | 6 +++--- build/pkgs/numpy/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/numpy/checksums.ini b/build/pkgs/numpy/checksums.ini index 74a09aeafe1..70cc0ddaefd 100644 --- a/build/pkgs/numpy/checksums.ini +++ b/build/pkgs/numpy/checksums.ini @@ -1,5 +1,5 @@ tarball=numpy-VERSION.tar.gz -sha1=8cb1e6b72cbb84e5892889bf7b09cb341d1708ec -md5=d9ffd2c189633486ec246e61d4b947a0 -cksum=1174837624 +sha1=6c7f2278b4ddd113b30821e7e4d5f246dc3ee735 +md5=8b2692a511a3795f3af8af2cd7566a15 +cksum=3950457778 upstream_url=https://pypi.io/packages/source/n/numpy/numpy-VERSION.tar.gz diff --git a/build/pkgs/numpy/package-version.txt b/build/pkgs/numpy/package-version.txt index 27ddcc14da0..ca8ec414e78 100644 --- a/build/pkgs/numpy/package-version.txt +++ b/build/pkgs/numpy/package-version.txt @@ -1 +1 @@ -1.23.4 +1.23.5 From 84f02afa5c85e98aeba55c13e09d414871e6e35e Mon Sep 17 00:00:00 2001 From: Release Manager Date: Mon, 21 Nov 2022 22:25:54 +0100 Subject: [PATCH 498/632] Updated SageMath version to 9.8.beta4 --- .zenodo.json | 8 ++++---- VERSION.txt | 2 +- build/pkgs/configure/checksums.ini | 6 +++--- build/pkgs/configure/package-version.txt | 2 +- build/pkgs/sage_conf/install-requires.txt | 2 +- build/pkgs/sage_docbuild/install-requires.txt | 2 +- build/pkgs/sage_setup/install-requires.txt | 2 +- build/pkgs/sage_sws2rst/install-requires.txt | 2 +- build/pkgs/sagelib/install-requires.txt | 2 +- build/pkgs/sagemath_categories/install-requires.txt | 2 +- build/pkgs/sagemath_environment/install-requires.txt | 2 +- build/pkgs/sagemath_objects/install-requires.txt | 2 +- build/pkgs/sagemath_repl/install-requires.txt | 2 +- pkgs/sage-conf/VERSION.txt | 2 +- pkgs/sage-conf_pypi/VERSION.txt | 2 +- pkgs/sage-docbuild/VERSION.txt | 2 +- pkgs/sage-setup/VERSION.txt | 2 +- pkgs/sage-sws2rst/VERSION.txt | 2 +- pkgs/sagemath-categories/VERSION.txt | 2 +- pkgs/sagemath-environment/VERSION.txt | 2 +- pkgs/sagemath-objects/VERSION.txt | 2 +- pkgs/sagemath-repl/VERSION.txt | 2 +- src/VERSION.txt | 2 +- src/bin/sage-version.sh | 6 +++--- src/sage/version.py | 6 +++--- 25 files changed, 34 insertions(+), 34 deletions(-) diff --git a/.zenodo.json b/.zenodo.json index 81ea9dde6d0..4e6641194eb 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.8.beta3", - "version": "9.8.beta3", + "title": "sagemath/sage: 9.8.beta4", + "version": "9.8.beta4", "upload_type": "software", - "publication_date": "2022-10-30", + "publication_date": "2022-11-21", "creators": [ { "affiliation": "SageMath.org", @@ -15,7 +15,7 @@ "related_identifiers": [ { "scheme": "url", - "identifier": "https://github.com/sagemath/sage/tree/9.8.beta3", + "identifier": "https://github.com/sagemath/sage/tree/9.8.beta4", "relation": "isSupplementTo" }, { diff --git a/VERSION.txt b/VERSION.txt index ff069aea672..ba4e46788bd 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 9.8.beta3, Release Date: 2022-10-30 +SageMath version 9.8.beta4, Release Date: 2022-11-21 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 5e03f886f84..9b6e528d13e 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=5c6eda541f708e7bbb272abbf285a492fa743128 -md5=b7fe25797f79a090262df39cc0a93c0c -cksum=1970835260 +sha1=013bfce4d1d0d0d68553c4025647445b7036f4db +md5=a429cd9c90d6bd90f7b2254b7fed8669 +cksum=1487098037 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 8b531a65419..3be6f9a021c 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -e063389aa995fb79f79f5e4cf2bb413f2a1e0b35 +03f68f4aa9602d246824f764e2ea328fa52577c7 diff --git a/build/pkgs/sage_conf/install-requires.txt b/build/pkgs/sage_conf/install-requires.txt index e1ce4a85f34..c890e648a67 100644 --- a/build/pkgs/sage_conf/install-requires.txt +++ b/build/pkgs/sage_conf/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-conf ~= 9.8b3 +sage-conf ~= 9.8b4 diff --git a/build/pkgs/sage_docbuild/install-requires.txt b/build/pkgs/sage_docbuild/install-requires.txt index 3db13eeb1f7..35da0e99bbb 100644 --- a/build/pkgs/sage_docbuild/install-requires.txt +++ b/build/pkgs/sage_docbuild/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-docbuild ~= 9.8b3 +sage-docbuild ~= 9.8b4 diff --git a/build/pkgs/sage_setup/install-requires.txt b/build/pkgs/sage_setup/install-requires.txt index d8a7c30eba0..c020dd4b7fc 100644 --- a/build/pkgs/sage_setup/install-requires.txt +++ b/build/pkgs/sage_setup/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-setup ~= 9.8b3 +sage-setup ~= 9.8b4 diff --git a/build/pkgs/sage_sws2rst/install-requires.txt b/build/pkgs/sage_sws2rst/install-requires.txt index 95d6d392aa5..e2197377b3f 100644 --- a/build/pkgs/sage_sws2rst/install-requires.txt +++ b/build/pkgs/sage_sws2rst/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-sws2rst ~= 9.8b3 +sage-sws2rst ~= 9.8b4 diff --git a/build/pkgs/sagelib/install-requires.txt b/build/pkgs/sagelib/install-requires.txt index 3016d9eebcc..c66a399a69c 100644 --- a/build/pkgs/sagelib/install-requires.txt +++ b/build/pkgs/sagelib/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagelib ~= 9.8b3 +sagelib ~= 9.8b4 diff --git a/build/pkgs/sagemath_categories/install-requires.txt b/build/pkgs/sagemath_categories/install-requires.txt index d1bce6cf96e..ba664945cd1 100644 --- a/build/pkgs/sagemath_categories/install-requires.txt +++ b/build/pkgs/sagemath_categories/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-categories ~= 9.8b3 +sagemath-categories ~= 9.8b4 diff --git a/build/pkgs/sagemath_environment/install-requires.txt b/build/pkgs/sagemath_environment/install-requires.txt index 99458e3a96c..aa6c86145f3 100644 --- a/build/pkgs/sagemath_environment/install-requires.txt +++ b/build/pkgs/sagemath_environment/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-environment ~= 9.8b3 +sagemath-environment ~= 9.8b4 diff --git a/build/pkgs/sagemath_objects/install-requires.txt b/build/pkgs/sagemath_objects/install-requires.txt index e0b880eaac0..14d20cf2459 100644 --- a/build/pkgs/sagemath_objects/install-requires.txt +++ b/build/pkgs/sagemath_objects/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-objects ~= 9.8b3 +sagemath-objects ~= 9.8b4 diff --git a/build/pkgs/sagemath_repl/install-requires.txt b/build/pkgs/sagemath_repl/install-requires.txt index 3cd52abc7d6..c03509f6f5c 100644 --- a/build/pkgs/sagemath_repl/install-requires.txt +++ b/build/pkgs/sagemath_repl/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-repl ~= 9.8b3 +sagemath-repl ~= 9.8b4 diff --git a/pkgs/sage-conf/VERSION.txt b/pkgs/sage-conf/VERSION.txt index b0789c4fde8..ad07d6bad83 100644 --- a/pkgs/sage-conf/VERSION.txt +++ b/pkgs/sage-conf/VERSION.txt @@ -1 +1 @@ -9.8.beta3 +9.8.beta4 diff --git a/pkgs/sage-conf_pypi/VERSION.txt b/pkgs/sage-conf_pypi/VERSION.txt index b0789c4fde8..ad07d6bad83 100644 --- a/pkgs/sage-conf_pypi/VERSION.txt +++ b/pkgs/sage-conf_pypi/VERSION.txt @@ -1 +1 @@ -9.8.beta3 +9.8.beta4 diff --git a/pkgs/sage-docbuild/VERSION.txt b/pkgs/sage-docbuild/VERSION.txt index b0789c4fde8..ad07d6bad83 100644 --- a/pkgs/sage-docbuild/VERSION.txt +++ b/pkgs/sage-docbuild/VERSION.txt @@ -1 +1 @@ -9.8.beta3 +9.8.beta4 diff --git a/pkgs/sage-setup/VERSION.txt b/pkgs/sage-setup/VERSION.txt index b0789c4fde8..ad07d6bad83 100644 --- a/pkgs/sage-setup/VERSION.txt +++ b/pkgs/sage-setup/VERSION.txt @@ -1 +1 @@ -9.8.beta3 +9.8.beta4 diff --git a/pkgs/sage-sws2rst/VERSION.txt b/pkgs/sage-sws2rst/VERSION.txt index b0789c4fde8..ad07d6bad83 100644 --- a/pkgs/sage-sws2rst/VERSION.txt +++ b/pkgs/sage-sws2rst/VERSION.txt @@ -1 +1 @@ -9.8.beta3 +9.8.beta4 diff --git a/pkgs/sagemath-categories/VERSION.txt b/pkgs/sagemath-categories/VERSION.txt index b0789c4fde8..ad07d6bad83 100644 --- a/pkgs/sagemath-categories/VERSION.txt +++ b/pkgs/sagemath-categories/VERSION.txt @@ -1 +1 @@ -9.8.beta3 +9.8.beta4 diff --git a/pkgs/sagemath-environment/VERSION.txt b/pkgs/sagemath-environment/VERSION.txt index b0789c4fde8..ad07d6bad83 100644 --- a/pkgs/sagemath-environment/VERSION.txt +++ b/pkgs/sagemath-environment/VERSION.txt @@ -1 +1 @@ -9.8.beta3 +9.8.beta4 diff --git a/pkgs/sagemath-objects/VERSION.txt b/pkgs/sagemath-objects/VERSION.txt index b0789c4fde8..ad07d6bad83 100644 --- a/pkgs/sagemath-objects/VERSION.txt +++ b/pkgs/sagemath-objects/VERSION.txt @@ -1 +1 @@ -9.8.beta3 +9.8.beta4 diff --git a/pkgs/sagemath-repl/VERSION.txt b/pkgs/sagemath-repl/VERSION.txt index b0789c4fde8..ad07d6bad83 100644 --- a/pkgs/sagemath-repl/VERSION.txt +++ b/pkgs/sagemath-repl/VERSION.txt @@ -1 +1 @@ -9.8.beta3 +9.8.beta4 diff --git a/src/VERSION.txt b/src/VERSION.txt index b0789c4fde8..ad07d6bad83 100644 --- a/src/VERSION.txt +++ b/src/VERSION.txt @@ -1 +1 @@ -9.8.beta3 +9.8.beta4 diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index b4ab677a7f3..a32acb79bcb 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -4,6 +4,6 @@ # which stops "setup.py develop" from rewriting it as a Python file. : # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='9.8.beta3' -SAGE_RELEASE_DATE='2022-10-30' -SAGE_VERSION_BANNER='SageMath version 9.8.beta3, Release Date: 2022-10-30' +SAGE_VERSION='9.8.beta4' +SAGE_RELEASE_DATE='2022-11-21' +SAGE_VERSION_BANNER='SageMath version 9.8.beta4, Release Date: 2022-11-21' diff --git a/src/sage/version.py b/src/sage/version.py index a82d4578e10..aeb6bb8f0f5 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.8.beta3' -date = '2022-10-30' -banner = 'SageMath version 9.8.beta3, Release Date: 2022-10-30' +version = '9.8.beta4' +date = '2022-11-21' +banner = 'SageMath version 9.8.beta4, Release Date: 2022-11-21' From bd193c4f4f1d1440559ed3aab8fd9189a094fd7e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 21 Nov 2022 17:17:33 -0800 Subject: [PATCH 499/632] .github/workflows: Update actions --- .github/workflows/build.yml | 4 ++-- .github/workflows/ci-conda.yml | 2 +- .github/workflows/ci-linux.yml | 4 ++-- .github/workflows/ci-macos.yml | 18 +++++++++--------- .github/workflows/ci-wsl.yml | 4 ++-- .github/workflows/cygwin.yml | 12 ++++++------ .github/workflows/dist.yml | 16 ++++++++-------- .github/workflows/doc-build.yml | 2 +- .github/workflows/docker.yml | 8 ++++---- .github/workflows/lint.yml | 12 ++++++------ 10 files changed, 41 insertions(+), 41 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eec448fed1f..10957615728 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ jobs: steps: - name: Checkout id: checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Prepare id: prepare @@ -55,7 +55,7 @@ jobs: - name: Set up node to install pyright if: always() && steps.prepare.outcome == 'success' - uses: actions/setup-node@v1 + uses: actions/setup-node@v3 with: node-version: '12' diff --git a/.github/workflows/ci-conda.yml b/.github/workflows/ci-conda.yml index 74e37b3a408..197e4730249 100644 --- a/.github/workflows/ci-conda.yml +++ b/.github/workflows/ci-conda.yml @@ -29,7 +29,7 @@ jobs: conda-env: [environment, environment-optional] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Check for Miniconda id: check_conda diff --git a/.github/workflows/ci-linux.yml b/.github/workflows/ci-linux.yml index 06ceaee1731..24327b6e61e 100644 --- a/.github/workflows/ci-linux.yml +++ b/.github/workflows/ci-linux.yml @@ -178,7 +178,7 @@ jobs: TOX_ENV: local-${{ matrix.tox_system_factor }}-${{ matrix.tox_packages_factor }} LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-tox-local-${{ matrix.tox_system_factor }}-${{ matrix.tox_packages_factor }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install test prerequisites run: | sudo DEBIAN_FRONTEND=noninteractive apt-get update @@ -192,7 +192,7 @@ jobs: run: | mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; cp -r .tox/*/log "artifacts/$LOGS_ARTIFACT_NAME" if: always() - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v3 with: path: artifacts name: ${{ env.LOGS_ARTIFACT_NAME }} diff --git a/.github/workflows/ci-macos.yml b/.github/workflows/ci-macos.yml index 1921fc0d066..a0a0f330f04 100644 --- a/.github/workflows/ci-macos.yml +++ b/.github/workflows/ci-macos.yml @@ -49,14 +49,14 @@ jobs: LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-tox-local-${{ matrix.tox_env }}-${{ matrix.os }}-xcode_${{ matrix.xcode_version_factor }} LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-tox-local-${{ matrix.tox_env }}--${{ matrix.os }}-xcode_${{ matrix.xcode_version_factor }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Select Xcode version run: | if [ ${{ matrix.xcode_version_factor }} != default ]; then sudo xcode-select -s /Applications/Xcode_${{ matrix.xcode_version_factor }}.app; fi - name: Install test prerequisites run: | brew install tox - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 with: path: sage-local-artifact name: ${{ env.LOCAL_ARTIFACT_NAME }} @@ -92,7 +92,7 @@ jobs: run: | mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; cp -r .tox/*/log "artifacts/$LOGS_ARTIFACT_NAME" if: always() - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v3 with: path: artifacts name: ${{ env.LOGS_ARTIFACT_NAME }} @@ -110,7 +110,7 @@ jobs: run: | mkdir -p sage-local-artifact && (cd .tox/$TOX_ENV && rm -f "local/lib64" && tar -cf - $(pwd)) > sage-local-artifact/sage-${{ env.TOX_ENV }}-${{ matrix.stage }}.tar if: contains(matrix.stage, '1') - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: sage-local-artifact/sage-${{ env.TOX_ENV }}-${{ matrix.stage }}.tar name: ${{ env.LOCAL_ARTIFACT_NAME }} @@ -120,7 +120,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 with: fetch-depth: 500 - name: fetch tags @@ -140,7 +140,7 @@ jobs: - name: make dist run: | ./configure --enable-download-from-upstream-url && make dist - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: "dist/*.tar.gz" name: dist @@ -162,9 +162,9 @@ jobs: TOX_ENV: local-${{ matrix.tox_system_factor }}-${{ matrix.tox_packages_factor }} LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-tox-local-${{ matrix.tox_system_factor }}-${{ matrix.tox_packages_factor }}-xcode_${{ matrix.xcode_version_factor }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 if: "!contains(matrix.tox_system_factor, 'nobootstrap')" - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 with: path: . name: dist @@ -191,7 +191,7 @@ jobs: run: | mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; cp -r .tox/*/log "artifacts/$LOGS_ARTIFACT_NAME" if: always() - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v3 with: path: artifacts name: ${{ env.LOGS_ARTIFACT_NAME }} diff --git a/.github/workflows/ci-wsl.yml b/.github/workflows/ci-wsl.yml index 222af0695a5..df75cbf1283 100644 --- a/.github/workflows/ci-wsl.yml +++ b/.github/workflows/ci-wsl.yml @@ -19,7 +19,7 @@ jobs: steps: - name: Configure git run: git config --global core.symlinks true - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Ubuntu 20.04 (in WSL) run: | (New-Object System.Net.WebClient).DownloadFile("https://aka.ms/wslubuntu2004", "Ubuntu.appx") @@ -44,7 +44,7 @@ jobs: run: mkdir -p "artifacts/logs"; cp -r .tox/*/log "artifacts/logs" shell: bash if: always() - - uses: actions/upload-artifact@v1 + - uses: actions/upload-artifact@v3 with: path: artifacts name: logs diff --git a/.github/workflows/cygwin.yml b/.github/workflows/cygwin.yml index e6f5cbb1391..0d57bd8440f 100644 --- a/.github/workflows/cygwin.yml +++ b/.github/workflows/cygwin.yml @@ -73,14 +73,14 @@ jobs: choco install git python39 python39-pip --source cygwin - name: Check out SageMath - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: ${{ inputs.sage_repo }} ref: ${{ inputs.sage_ref }} fetch-depth: 2000 - name: Check out git-trac-command - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: sagemath/git-trac-command path: git-trac-command @@ -95,7 +95,7 @@ jobs: if: inputs.sage_trac_git != '' - name: Download upstream artifact - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v3 with: path: upstream name: ${{ inputs.upstream_artifact }} @@ -106,7 +106,7 @@ jobs: if: inputs.upstream_artifact - name: Download sage-local artifact - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v3 with: name: ${{ env.LOCAL_ARTIFACT_NAME }} path: C:\\tools\\cygwin\\tmp @@ -124,7 +124,7 @@ jobs: run: | C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/"$(basename "$a")".tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME"' if: always() - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: artifacts name: ${{ env.LOGS_ARTIFACT_NAME }} @@ -142,7 +142,7 @@ jobs: run: | C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ inputs.stage }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' if: always() - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 # upload-artifact@v2 does not support whitespace in file names. # so we tar up the directory ourselves with: diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml index 5086ca60a7c..c64ae55bad7 100644 --- a/.github/workflows/dist.yml +++ b/.github/workflows/dist.yml @@ -31,7 +31,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install bootstrap prerequisites run: | sudo DEBIAN_FRONTEND=noninteractive apt-get update @@ -39,7 +39,7 @@ jobs: - name: make dist run: | ./bootstrap -D && ./configure && make dist - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: "dist/*.tar.gz" name: release_dist @@ -50,7 +50,7 @@ jobs: env: CAN_DEPLOY: ${{ secrets.SAGEMATH_PYPI_API_TOKEN != '' }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install bootstrap prerequisites run: | sudo DEBIAN_FRONTEND=noninteractive apt-get update @@ -62,7 +62,7 @@ jobs: make pypi-sdists V=0 (mkdir dist && mv upstream/sage*.tar.gz dist/) ls -l dist - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: "dist/*.tar.gz" name: dist @@ -105,9 +105,9 @@ jobs: # Use 'build', not 'pip wheel' CIBW_BUILD_FRONTEND: build steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 with: name: dist path: dist @@ -130,7 +130,7 @@ jobs: pipx run cibuildwheel==2.7.0 unpacked/$pkg* done - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: name: wheels path: ./wheelhouse/*.whl @@ -142,7 +142,7 @@ jobs: CAN_DEPLOY: ${{ secrets.SAGEMATH_PYPI_API_TOKEN != '' }} steps: - - uses: actions/download-artifact@v2 + - uses: actions/download-artifact@v3 with: name: wheels path: wheelhouse diff --git a/.github/workflows/doc-build.yml b/.github/workflows/doc-build.yml index 9d5934bd3af..73dd9b95d53 100644 --- a/.github/workflows/doc-build.yml +++ b/.github/workflows/doc-build.yml @@ -17,7 +17,7 @@ jobs: if: github.repository == 'sagemath/sagetrac-mirror' steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Prepare run: | diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index fd29075014b..a87d351b42f 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -138,7 +138,7 @@ jobs: EXTRA_SAGE_PACKAGES: ${{ inputs.extra_sage_packages }} steps: - name: Check out SageMath - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: ${{ inputs.sage_repo }} ref: ${{ inputs.sage_ref }} @@ -158,7 +158,7 @@ jobs: df -h if: inputs.free_disk_space - name: Check out git-trac-command - uses: actions/checkout@v2 + uses: actions/checkout@v3 with: repository: sagemath/git-trac-command path: git-trac-command @@ -173,7 +173,7 @@ jobs: if: inputs.sage_trac_git != '' - name: Download upstream artifact - uses: actions/download-artifact@v2 + uses: actions/download-artifact@v3 with: path: upstream name: ${{ inputs.upstream_artifact }} @@ -225,7 +225,7 @@ jobs: if [ -f .tox/$TOX_ENV/Dockertags ]; then CONTAINERS=$(docker create $(tail -1 .tox/$TOX_ENV/Dockertags) /bin/bash || true); fi if [ -n "$CONTAINERS" ]; then for CONTAINER in $CONTAINERS; do for ARTIFACT in /sage/logs; do docker cp $CONTAINER:$ARTIFACT artifacts/$LOGS_ARTIFACT_NAME && HAVE_LOG=1; done; if [ -n "$HAVE_LOG" ]; then break; fi; done; fi if: always() - - uses: actions/upload-artifact@v2 + - uses: actions/upload-artifact@v3 with: path: artifacts name: ${{ env.LOGS_ARTIFACT_NAME }} diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 811a61cc928..853104fe939 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -13,9 +13,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.8 - name: Install pycodestyle @@ -27,9 +27,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.8 - name: Install relint @@ -41,9 +41,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: 3.8 - name: Install tox From 21e38a73fa41698d02ec4c00597e35b5dbd0eda6 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Mon, 21 Nov 2022 23:51:59 +0000 Subject: [PATCH 500/632] fix coersion of libgap FFelements; switch to libgap With the ticket branch, this works: sage: F=GF(25) sage: F(libgap.Z(25)^3) 4*z2 + 3 --- .../rings/finite_rings/element_givaro.pyx | 10 +++++++--- .../rings/finite_rings/element_ntl_gf2e.pyx | 18 +++++++++++++---- .../rings/finite_rings/element_pari_ffelt.pyx | 20 ++++++++++++++----- .../rings/finite_rings/integer_mod_ring.py | 6 ++---- 4 files changed, 38 insertions(+), 16 deletions(-) diff --git a/src/sage/rings/finite_rings/element_givaro.pyx b/src/sage/rings/finite_rings/element_givaro.pyx index 920fdf273fc..ca3cda3b5f8 100644 --- a/src/sage/rings/finite_rings/element_givaro.pyx +++ b/src/sage/rings/finite_rings/element_givaro.pyx @@ -66,7 +66,6 @@ import operator import sage.arith.all import sage.rings.finite_rings.finite_field_constructor as finite_field -import sage.interfaces.gap from sage.libs.pari.all import pari from cypari2.gen cimport Gen from cypari2.stack cimport clear_stack @@ -431,9 +430,12 @@ cdef class Cache_givaro(Cache_base): # Reduce to pari e = e.__pari__() + elif isinstance(e, sage.libs.gap.element.GapElement_FiniteField): + return e.sage(ring=self.parent) + elif sage.interfaces.gap.is_GapElement(e): - from sage.interfaces.gap import gfq_gap_to_sage - return gfq_gap_to_sage(e, self.parent) + from sage.libs.gap.libgap import libgap + return libgap(e).sage(ring=self.parent) elif isinstance(e, list): if len(e) > self.exponent(): @@ -1637,6 +1639,8 @@ cdef class FiniteField_givaroElement(FinitePolyExtElement): 'Z(25)^3' sage: S(gap('Z(25)^3')) 4*b + 3 + sage: S(libgap.Z(25)^3) + 4*b + 3 """ cdef Cache_givaro cache = self._cache if self == 0: diff --git a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx index c36adac770f..ebe16dcdcd8 100644 --- a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx +++ b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx @@ -46,14 +46,16 @@ from sage.libs.pari.all import pari from cypari2.gen cimport Gen from cypari2.stack cimport clear_stack -from sage.interfaces.gap import is_GapElement - from sage.misc.randstate import current_randstate from sage.arith.long cimport pyobject_to_long from .element_pari_ffelt import FiniteFieldElement_pari_ffelt from .finite_field_ntl_gf2e import FiniteField_ntl_gf2e +from sage.libs.gap.element import GapElement_FiniteField + +from sage.interfaces.gap import is_GapElement + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing cdef object is_IntegerMod @@ -364,9 +366,13 @@ cdef class Cache_ntl_gf2e(Cache_base): # Reduce to pari e = e.__pari__() + elif isinstance(e, GapElement_FiniteField): + return e.sage(ring=self._parent) + elif is_GapElement(e): - from sage.interfaces.gap import gfq_gap_to_sage - return gfq_gap_to_sage(e, self._parent) + from sage.libs.gap.libgap import libgap + return libgap(e).sage(ring=self._parent) + else: raise TypeError("unable to coerce %r" % type(e)) @@ -1174,6 +1180,10 @@ cdef class FiniteField_ntl_gf2eElement(FinitePolyExtElement): sage: k. = GF(2^16) sage: b._gap_init_() 'Z(65536)^1' + sage: k(gap('Z(2^16)^3+Z(2^16)^5')) + b^5 + b^3 + sage: k(libgap.Z(2^16)^3+libgap.Z(2^16)^5) + b^5 + b^3 """ F = self._parent if not F.is_conway(): diff --git a/src/sage/rings/finite_rings/element_pari_ffelt.pyx b/src/sage/rings/finite_rings/element_pari_ffelt.pyx index 3bf5c62ff32..0ee972a7b61 100644 --- a/src/sage/rings/finite_rings/element_pari_ffelt.pyx +++ b/src/sage/rings/finite_rings/element_pari_ffelt.pyx @@ -505,9 +505,15 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): self.construct_from(self._parent.polynomial_ring()(x)) elif is_GapElement(x): - from sage.interfaces.gap import gfq_gap_to_sage try: - self.construct_from(gfq_gap_to_sage(x, self._parent)) + from sage.libs.gap.libgap import libgap + self.construct_from(libgap(x).sage(ring=self._parent)) + except (ValueError, IndexError, TypeError): + raise TypeError("no coercion defined") + + elif isinstance(x, sage.libs.gap.element.GapElement_FiniteField): + try: + self.construct_from(x.sage(ring=self._parent)) except (ValueError, IndexError, TypeError): raise TypeError("no coercion defined") @@ -1258,9 +1264,9 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): EXAMPLES:: - sage: F = FiniteField(2^3, 'a', impl='pari_ffelt') - sage: a = F.multiplicative_generator() - sage: gap(a) # indirect doctest + sage: F = FiniteField(2^3, 'aa', impl='pari_ffelt') + sage: aa = F.multiplicative_generator() + sage: gap(aa) # indirect doctest Z(2^3) sage: b = F.multiplicative_generator() sage: a = b^3 @@ -1268,6 +1274,10 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): Z(2^3)^3 sage: gap(a^3) Z(2^3)^2 + sage: F(gap('Z(8)^3')) + aa + 1 + sage: F(libgap.Z(8)^3) + aa + 1 You can specify the instance of the Gap interpreter that is used:: diff --git a/src/sage/rings/finite_rings/integer_mod_ring.py b/src/sage/rings/finite_rings/integer_mod_ring.py index 12c3c71c5ba..d1d3c30d2e6 100644 --- a/src/sage/rings/finite_rings/integer_mod_ring.py +++ b/src/sage/rings/finite_rings/integer_mod_ring.py @@ -72,7 +72,6 @@ from sage.libs.pari.all import pari, PariError -import sage.interfaces.all from sage.misc.cachefunc import cached_method from sage.structure.factory import UniqueFactory @@ -1187,9 +1186,8 @@ def _element_constructor_(self, x): raise TypeError("error coercing to finite field") except TypeError: if sage.interfaces.gap.is_GapElement(x): - from sage.interfaces.gap import intmod_gap_to_sage - y = intmod_gap_to_sage(x) - return integer_mod.IntegerMod(self, y) + from sage.libs.gap.libgap import libgap + return libgap(x).sage() raise # Continue up with the original TypeError def __iter__(self): From d4bde80f350975b30e5795aa5ffc7333f5c80911 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Tue, 22 Nov 2022 10:06:17 +0000 Subject: [PATCH 501/632] make imports of is_GapEelment uniform across files --- src/sage/rings/finite_rings/element_givaro.pyx | 4 +++- src/sage/rings/finite_rings/integer_mod_ring.py | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/finite_rings/element_givaro.pyx b/src/sage/rings/finite_rings/element_givaro.pyx index ca3cda3b5f8..42f93cceb75 100644 --- a/src/sage/rings/finite_rings/element_givaro.pyx +++ b/src/sage/rings/finite_rings/element_givaro.pyx @@ -72,6 +72,8 @@ from cypari2.stack cimport clear_stack from sage.structure.parent cimport Parent +from sage.interfaces.gap import is_GapElement + cdef object is_IntegerMod cdef object Integer cdef object Rational @@ -433,7 +435,7 @@ cdef class Cache_givaro(Cache_base): elif isinstance(e, sage.libs.gap.element.GapElement_FiniteField): return e.sage(ring=self.parent) - elif sage.interfaces.gap.is_GapElement(e): + elif is_GapElement(e): from sage.libs.gap.libgap import libgap return libgap(e).sage(ring=self.parent) diff --git a/src/sage/rings/finite_rings/integer_mod_ring.py b/src/sage/rings/finite_rings/integer_mod_ring.py index d1d3c30d2e6..7049f2d6b11 100644 --- a/src/sage/rings/finite_rings/integer_mod_ring.py +++ b/src/sage/rings/finite_rings/integer_mod_ring.py @@ -77,6 +77,7 @@ from sage.structure.factory import UniqueFactory from sage.structure.richcmp import richcmp, richcmp_method +from sage.interfaces.gap import is_GapElement class IntegerModFactory(UniqueFactory): r""" @@ -1185,7 +1186,7 @@ def _element_constructor_(self, x): except (NotImplementedError, PariError): raise TypeError("error coercing to finite field") except TypeError: - if sage.interfaces.gap.is_GapElement(x): + if is_GapElement(x): from sage.libs.gap.libgap import libgap return libgap(x).sage() raise # Continue up with the original TypeError From 76d41ebbfa432b45af0f17fb9d3151d90f769355 Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Wed, 26 Oct 2022 17:40:19 +0100 Subject: [PATCH 502/632] Add williamson type hadamard matrix construction --- src/sage/combinat/matrices/hadamard_matrix.py | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index f4911807d9e..adc121b166e 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -223,6 +223,68 @@ def hadamard_matrix_paleyII(n): return normalise_hadamard(H) +def hadamard_matrix_williamson_type(a, b, c, d, check=False): + """ + Construction of Williamson type Hadamard matrix. + + Given `n\times n` circulant matrices `A`, `B`, `C`, `D` with 1,-1 entries, + and satisfying `AA^\top + BB^\top + CC^\top + DD^\top = 4nI`, + one can construct a Hadamard matrix of order `4n`, cf. [Ha83]_. + + INPUT: + + - ``a`` -- 1,-1 list specifying the 1st row of `A` + + - ``b`` -- 1,-1 list specifying the 1st row of `B` + + - ``d`` -- 1,-1 list specifying the 1st row of `C` + + - ``c`` -- 1,-1 list specifying the 1st row of `D` + + - ``check`` (boolean) -- Whether to check that the input is correct and that the output is an hadamard matrix + + EXAMPLES:: + + sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_williamson_type + sage: a = [ 1, 1, 1] + sage: b = [ 1, -1, -1] + sage: c = [ 1, -1, -1] + sage: d = [ 1, -1, -1] + sage: M = hadamard_matrix_williamson_type(a,b,c,d,check=True) + + sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_williamson_type, is_hadamard_matrix + sage: a = [ 1, 1, 1] + sage: b = [ 1, -1, -1] + sage: c = [ 1, -1, -1] + sage: d = [ 1, -1, -1] + sage: is_hadamard_matrix(hadamard_matrix_williamson_type(a,b,c,d)) + True + sage: e = [1, 1, 1] + sage: hadamard_matrix_williamson_type(a,b,c,e, check=True) + Traceback (most recent call last): + ... + AssertionError + sage: f = [1, -1, 1, -1] + sage: hadamard_matrix_williamson_type(a,b,c,f, check=True) + Traceback (most recent call last): + ... + AssertionError + """ + A, B, C, D = map(matrix.circulant, [a, b, c, d]) + + if check: + n = len(a) + assert len(a) == len(b) == len(c) == len(d) + assert A*A.T+B*B.T+C*C.T+D*D.T==4*n*I(n) + + M = block_matrix([[ A, B, C, D], + [-B, A, -D, C], + [-C, D, A, -B], + [-D, -C, B, A]]) + if check: + assert is_hadamard_matrix(M, normalized=False, skew=False) + return M + def is_hadamard_matrix(M, normalized=False, skew=False, verbose=False): r""" Test if `M` is a hadamard matrix. From 063ebe84c8e5a64fb1eee87a5977047d8dc040d5 Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Sat, 29 Oct 2022 15:02:04 +0100 Subject: [PATCH 503/632] Add contruction of williamson type hadamard matrix for n=116 and n=172 --- src/sage/combinat/matrices/hadamard_matrix.py | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index adc121b166e..9fb3203a884 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -285,6 +285,58 @@ def hadamard_matrix_williamson_type(a, b, c, d, check=False): assert is_hadamard_matrix(M, normalized=False, skew=False) return M +def williamson_hadamard_matrix_smallcases(n, existence=False, check=True): + """ + Data for construction of Williamson type Hadamard matrices. + + This function contains the data needed for the williamson contruction. + Namely, it needs 4 circulant matrices with the properties described in + :func:`sage.combinat.matrices.hadamard_matrix.hadamard_matrix_williamson_type`. + The matrices for n=116 and n=172 are given in [Ha83]_. + + INPUT: + + - ``n`` -- the order of the matrix + + - ``existence`` -- if true, only check that we can do the construction (default false). + + - ``check`` -- if true (default), check the result. + + TESTS:: + + sage: from sage.combinat.matrices.hadamard_matrix import williamson_hadamard_matrix_smallcases + sage: williamson_hadamard_matrix_smallcases(116) + 116 x 116 dense matrix over Integer Ring... + sage: williamson_hadamard_matrix_smallcases(172) + 172 x 172 dense matrix over Integer Ring... + sage: williamson_hadamard_matrix_smallcases(100) + Traceback (most recent call last): + ... + ValueError: The Williamson type Hadamard matrix of order 100 is not yet implemented. + """ + if n not in [116, 172]: + if existence: + return False + raise ValueError("The Williamson type Hadamard matrix of order %s is not yet implemented." % n) + + if existence: + return True + + if n == 116: + a = [1, 1, 1,-1,-1,-1, 1, 1,-1,-1, 1,-1, 1,-1,-1,-1,-1, 1,-1, 1,-1,-1, 1, 1,-1,-1,-1, 1, 1] + b = [1,-1, 1,-1,-1,-1, 1, 1,-1,-1, 1,-1, 1, 1, 1, 1, 1, 1,-1, 1,-1,-1, 1, 1,-1,-1,-1, 1,-1] + c = [1, 1, 1, 1,-1, 1, 1,-1, 1,-1,-1,-1, 1, 1, 1, 1, 1, 1,-1,-1,-1, 1,-1, 1, 1,-1, 1, 1, 1] + d = [1, 1,-1,-1, 1,-1,-1, 1,-1, 1, 1, 1,-1, 1, 1, 1, 1,-1, 1, 1, 1,-1, 1,-1,-1, 1,-1,-1, 1] + return hadamard_matrix_williamson_type(a, b, c, d, check=check) + + if n == 172: + a = [1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1] + b = [1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, 1] + c = [1, 1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1] + d = [1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1] + return hadamard_matrix_williamson_type(a, b, c, d, check=check) + + def is_hadamard_matrix(M, normalized=False, skew=False, verbose=False): r""" Test if `M` is a hadamard matrix. From 11cc9e5a5f20eb5004f3ec140a5cf71ba148e0bc Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Sat, 29 Oct 2022 15:07:52 +0100 Subject: [PATCH 504/632] Always check williamson necessary conditions --- src/sage/combinat/matrices/hadamard_matrix.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 9fb3203a884..12e4a9bbfdc 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -223,7 +223,7 @@ def hadamard_matrix_paleyII(n): return normalise_hadamard(H) -def hadamard_matrix_williamson_type(a, b, c, d, check=False): +def hadamard_matrix_williamson_type(a, b, c, d, check=True): """ Construction of Williamson type Hadamard matrix. @@ -241,7 +241,7 @@ def hadamard_matrix_williamson_type(a, b, c, d, check=False): - ``c`` -- 1,-1 list specifying the 1st row of `D` - - ``check`` (boolean) -- Whether to check that the input is correct and that the output is an hadamard matrix + - ``check`` (boolean) -- Whether to check that the output is an hadamard matrix before returning it EXAMPLES:: @@ -272,10 +272,9 @@ def hadamard_matrix_williamson_type(a, b, c, d, check=False): """ A, B, C, D = map(matrix.circulant, [a, b, c, d]) - if check: - n = len(a) - assert len(a) == len(b) == len(c) == len(d) - assert A*A.T+B*B.T+C*C.T+D*D.T==4*n*I(n) + n = len(a) + assert len(a) == len(b) == len(c) == len(d) + assert A*A.T+B*B.T+C*C.T+D*D.T==4*n*I(n) M = block_matrix([[ A, B, C, D], [-B, A, -D, C], From 54b10a4dfe13cf54f8edd5e06245b77f8181671a Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Sat, 29 Oct 2022 15:20:34 +0100 Subject: [PATCH 505/632] Add williamson method to general hadamard matrix contruction --- src/sage/combinat/matrices/hadamard_matrix.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 12e4a9bbfdc..2b78cf0bb6c 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -518,7 +518,7 @@ def hadamard_matrix(n,existence=False, check=True): False sage: matrix.hadamard(12,existence=True) True - sage: matrix.hadamard(116,existence=True) + sage: matrix.hadamard(472,existence=True) Unknown sage: matrix.hadamard(10) Traceback (most recent call last): @@ -561,6 +561,10 @@ def hadamard_matrix(n,existence=False, check=True): if existence: return True M = hadamard_matrix_paleyI(n) + elif williamson_hadamard_matrix_smallcases(n, existence=True): + if existence: + return True + M = williamson_hadamard_matrix_smallcases(n, check=False) elif skew_hadamard_matrix(n, existence=True) is True: if existence: return True From 381fc7d15dac4f0fe200956531039994cd109ca0 Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Sat, 29 Oct 2022 17:08:47 +0100 Subject: [PATCH 506/632] Fix documentation for williamson contruction --- src/sage/combinat/matrices/hadamard_matrix.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 2b78cf0bb6c..34e4c25cd19 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -224,7 +224,7 @@ def hadamard_matrix_paleyII(n): return normalise_hadamard(H) def hadamard_matrix_williamson_type(a, b, c, d, check=True): - """ + r""" Construction of Williamson type Hadamard matrix. Given `n\times n` circulant matrices `A`, `B`, `C`, `D` with 1,-1 entries, @@ -285,7 +285,7 @@ def hadamard_matrix_williamson_type(a, b, c, d, check=True): return M def williamson_hadamard_matrix_smallcases(n, existence=False, check=True): - """ + r""" Data for construction of Williamson type Hadamard matrices. This function contains the data needed for the williamson contruction. From cb8da90078fe2f54e30fe49fde01aef70dcbdd48 Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Sun, 30 Oct 2022 18:09:26 +0000 Subject: [PATCH 507/632] Add construction for order 156 --- src/sage/combinat/matrices/hadamard_matrix.py | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 34e4c25cd19..d8fb50477be 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -336,6 +336,28 @@ def williamson_hadamard_matrix_smallcases(n, existence=False, check=True): return hadamard_matrix_williamson_type(a, b, c, d, check=check) +def hadamard_matrix_156(): + a = [1, 1,-1,-1, 1,-1, 1, 1,-1, 1,-1,-1, 1] + b = [1,-1,-1,-1, 1, 1, 1, 1, 1, 1,-1,-1,-1] + c = [1, 1, 1,-1, 1, 1,-1,-1, 1, 1,-1, 1, 1] + d = [1, 1,-1, 1,-1, 1, 1, 1, 1,-1, 1,-1, 1] + + A, B, C, D = map(matrix.circulant, [a, b, c, d]) + + return block_matrix([[ A, A, A, B,-B, C,-C,-D, B, C,-D,-D], + [ A,-A, B,-A,-B,-D, D,-C,-B,-D,-C,-C], + [ A,-B,-A, A,-D, D,-B, B,-C,-D, C,-C], + [ B, A,-A,-A, D, D, D, C, C,-B,-B,-C], + [ B,-D, D, D, A, A, A, C,-C, B,-C, B], + [ B, C,-D, D, A,-A, C,-A,-D, C, B,-B], + [ D,-C, B,-B, A,-C,-A, A, B, C, D,-D], + [-C,-D,-C,-D, C, A,-A,-A,-D, B,-B,-B], + [ D,-C,-B,-B,-B, C, C,-D, A, A, A, D], + [-D,-B, C, C, C, B, B,-D, A,-A, D,-A], + [ C,-B,-C, C, D,-B,-D,-B, A,-D,-A, A], + [-C,-D,-D, C,-C,-B, B, B, D, A,-A,-A]]) + + def is_hadamard_matrix(M, normalized=False, skew=False, verbose=False): r""" Test if `M` is a hadamard matrix. From 6f86d95cced80245fc17bf1e0352499851b498a6 Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Sun, 30 Oct 2022 18:37:02 +0000 Subject: [PATCH 508/632] Add documentation for hadamard_matrix_156 --- src/doc/en/reference/references/index.rst | 4 ++++ src/sage/combinat/matrices/hadamard_matrix.py | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index ae57b822aa0..6d95418f3d4 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -483,6 +483,10 @@ REFERENCES: "PHOTON-BeetleAuthenticated Encryption and Hash Family" https://csrc.nist.gov/CSRC/media/Projects/Lightweight-Cryptography/documents/round-1/spec-doc/PHOTON-Beetle-spec.pdf +.. [BH1965] \L. D. Baumert, M. Hall Jr. + "A new construction for Hadamard matrices". + Bulletin of the American Mathematical Society 71, no. 1 (1965): 169-170. + .. [BH2012] \A. Brouwer and W. Haemers, Spectra of graphs, Springer, 2012, diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index d8fb50477be..2cc6b629625 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -337,6 +337,21 @@ def williamson_hadamard_matrix_smallcases(n, existence=False, check=True): def hadamard_matrix_156(): + r""" + Construct an hadamard matrix of order 156. + + The matrix is created using the construction detailed in [BH1965]_. + It is constructed from four circulant matrices of size `13\times 13`, + which are composed into a `156\times 156` block matrix. + + TESTS:: + + sage: from sage.combinat.matrices.hadamard_matrix import is_hadamard_matrix, hadamard_matrix_156 + sage: is_hadamard_matrix(hadamard_matrix_156()) + True + sage: hadamard_matrix_156() + 156 x 156 dense matrix over Integer Ring... + """ a = [1, 1,-1,-1, 1,-1, 1, 1,-1, 1,-1,-1, 1] b = [1,-1,-1,-1, 1, 1, 1, 1, 1, 1,-1,-1,-1] c = [1, 1, 1,-1, 1, 1,-1,-1, 1, 1,-1, 1, 1] From 1536825610738e6cd8dc5dfbf6f90439dedf468c Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Sun, 30 Oct 2022 18:48:58 +0000 Subject: [PATCH 509/632] Add matrix of order 156 to general hadamard matrix construction --- src/sage/combinat/matrices/hadamard_matrix.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 2cc6b629625..e12b095eeb7 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -345,7 +345,7 @@ def hadamard_matrix_156(): which are composed into a `156\times 156` block matrix. TESTS:: - + sage: from sage.combinat.matrices.hadamard_matrix import is_hadamard_matrix, hadamard_matrix_156 sage: is_hadamard_matrix(hadamard_matrix_156()) True @@ -602,6 +602,10 @@ def hadamard_matrix(n,existence=False, check=True): if existence: return True M = williamson_hadamard_matrix_smallcases(n, check=False) + elif n == 156: + if existence: + return True + M = hadamard_matrix_156() elif skew_hadamard_matrix(n, existence=True) is True: if existence: return True From 4036d0f9cc48dbbc7efb581b508cfa20239270b7 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Mon, 31 Oct 2022 17:44:39 +0000 Subject: [PATCH 510/632] slight fix of the reference format --- src/doc/en/reference/references/index.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 6d95418f3d4..daa1bfddde1 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -484,8 +484,8 @@ REFERENCES: https://csrc.nist.gov/CSRC/media/Projects/Lightweight-Cryptography/documents/round-1/spec-doc/PHOTON-Beetle-spec.pdf .. [BH1965] \L. D. Baumert, M. Hall Jr. - "A new construction for Hadamard matrices". - Bulletin of the American Mathematical Society 71, no. 1 (1965): 169-170. + *A new construction for Hadamard matrices*. + Bulletin of the American Mathematical Society 71(1):169-170, 1965. .. [BH2012] \A. Brouwer and W. Haemers, Spectra of graphs, From 759f67411867134d81a04ba4ccfd9fb74c1be9fd Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Mon, 7 Nov 2022 14:17:26 +0000 Subject: [PATCH 511/632] Fix doctests in hadamard_matrix_williamson_type --- src/sage/combinat/matrices/hadamard_matrix.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index e12b095eeb7..a42cd52d26c 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -252,7 +252,9 @@ def hadamard_matrix_williamson_type(a, b, c, d, check=True): sage: d = [ 1, -1, -1] sage: M = hadamard_matrix_williamson_type(a,b,c,d,check=True) - sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_williamson_type, is_hadamard_matrix + TESTS:: + + sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_williamson_type, is_hadamard_matrix sage: a = [ 1, 1, 1] sage: b = [ 1, -1, -1] sage: c = [ 1, -1, -1] From beb392cba91bebf5d6f774bfbd8a1cbef5b320da Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Tue, 8 Nov 2022 16:42:10 +0000 Subject: [PATCH 512/632] Move williamson quadruples to external function --- src/sage/combinat/matrices/hadamard_matrix.py | 92 +++++++++++++++---- 1 file changed, 72 insertions(+), 20 deletions(-) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index a42cd52d26c..0288b6d173f 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -64,6 +64,7 @@ from sage.matrix.constructor import ones_matrix as J from sage.misc.unknown import Unknown from sage.cpython.string import bytes_to_str +from sage.modules.free_module_element import vector def normalise_hadamard(H): @@ -286,14 +287,75 @@ def hadamard_matrix_williamson_type(a, b, c, d, check=True): assert is_hadamard_matrix(M, normalized=False, skew=False) return M +def williamson_type_quadruples_smallcases(n, existence=False): + r""" + Quadruples of matrices that can be used to construct Williamson Type hadamard Matrices. + + This function contains for some values of n, four `n\times n` matrices used in the + Williamson construction of Hadamard matrices. Namely, the function returns the first row of + 4 `n\times n` circulant matrices with the properties described in + :func:`sage.combinat.matrices.hadamard_matrix.hadamard_matrix_williamson_type`. + The matrices for n=29 and n=43 are given in [Ha83]_. + + INPUT: + + - ``n`` -- the order of the matrices to be returned + + - ``existence`` -- if true, only check that we have the quadruple (default false). + + OUTPUT: + + If ``existence`` is false, returns a tuple containing four vectors, each being the first line + of one of the four matrices. It raises an error if no such matrices are available. + If ``existence`` is true, returns a boolean representing whether the matrices are available or not. + + EXAMPLES:: + + sage: from sage.combinat.matrices.hadamard_matrix import williamson_type_quadruples_smallcases + sage: williamson_type_quadruples_smallcases(29) + ((1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, 1), + (1, -1, 1, -1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, 1, -1, -1, -1, 1, -1), + (1, 1, 1, 1, -1, 1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, 1, 1, 1), + (1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1)) + sage: williamson_type_quadruples_smallcases(43, existence=True) + True + + TESTS:: + + sage: williamson_type_quadruples_smallcases(123, existence=True) + False + sage: williamson_type_quadruples_smallcases(123) + Traceback (most recent call last): + ... + ValueError: The Williamson type quadruple of order 123 is not yet implemented. + """ + db = { + 29: ([1, 1, 1,-1,-1,-1, 1, 1,-1,-1, 1,-1, 1,-1,-1,-1,-1, 1,-1, 1,-1,-1, 1, 1,-1,-1,-1, 1, 1], + [1,-1, 1,-1,-1,-1, 1, 1,-1,-1, 1,-1, 1, 1, 1, 1, 1, 1,-1, 1,-1,-1, 1, 1,-1,-1,-1, 1,-1], + [1, 1, 1, 1,-1, 1, 1,-1, 1,-1,-1,-1, 1, 1, 1, 1, 1, 1,-1,-1,-1, 1,-1, 1, 1,-1, 1, 1, 1], + [1, 1,-1,-1, 1,-1,-1, 1,-1, 1, 1, 1,-1, 1, 1, 1, 1,-1, 1, 1, 1,-1, 1,-1,-1, 1,-1,-1, 1]), + 43: ([1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1], + [1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, 1], + [1, 1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1], + [1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1]), + } + + if existence: + return n in db + + if n not in db: + raise ValueError("The Williamson type quadruple of order %s is not yet implemented." % n) + a, b, c, d = map(vector, db[n]) + return a, b, c, d + def williamson_hadamard_matrix_smallcases(n, existence=False, check=True): r""" - Data for construction of Williamson type Hadamard matrices. + Construction of Williamson type Hadamard matrices for some small values of n. - This function contains the data needed for the williamson contruction. - Namely, it needs 4 circulant matrices with the properties described in + This function uses the data contained in + :func:`sage.combinat.matrices.hadamard_matrix.williamson_type_quadruples_smallcases` + to create Hadamard matrices of the Williamson type, using the construction from :func:`sage.combinat.matrices.hadamard_matrix.hadamard_matrix_williamson_type`. - The matrices for n=116 and n=172 are given in [Ha83]_. INPUT: @@ -315,27 +377,18 @@ def williamson_hadamard_matrix_smallcases(n, existence=False, check=True): ... ValueError: The Williamson type Hadamard matrix of order 100 is not yet implemented. """ - if n not in [116, 172]: + assert n%4 == 0 + + if not williamson_type_quadruples_smallcases(n//4, existence=True): if existence: return False raise ValueError("The Williamson type Hadamard matrix of order %s is not yet implemented." % n) - + if existence: return True - if n == 116: - a = [1, 1, 1,-1,-1,-1, 1, 1,-1,-1, 1,-1, 1,-1,-1,-1,-1, 1,-1, 1,-1,-1, 1, 1,-1,-1,-1, 1, 1] - b = [1,-1, 1,-1,-1,-1, 1, 1,-1,-1, 1,-1, 1, 1, 1, 1, 1, 1,-1, 1,-1,-1, 1, 1,-1,-1,-1, 1,-1] - c = [1, 1, 1, 1,-1, 1, 1,-1, 1,-1,-1,-1, 1, 1, 1, 1, 1, 1,-1,-1,-1, 1,-1, 1, 1,-1, 1, 1, 1] - d = [1, 1,-1,-1, 1,-1,-1, 1,-1, 1, 1, 1,-1, 1, 1, 1, 1,-1, 1, 1, 1,-1, 1,-1,-1, 1,-1,-1, 1] - return hadamard_matrix_williamson_type(a, b, c, d, check=check) - - if n == 172: - a = [1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1] - b = [1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, 1] - c = [1, 1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1] - d = [1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1] - return hadamard_matrix_williamson_type(a, b, c, d, check=check) + a, b, c, d = williamson_type_quadruples_smallcases(n//4) + return hadamard_matrix_williamson_type(a, b, c, d, check=check) def hadamard_matrix_156(): @@ -1320,7 +1373,6 @@ def true(): if check: assert is_hadamard_matrix(M, normalized=False, skew=True) if skew_normalize: - from sage.modules.free_module_element import vector assert M[0]==vector([1]*n) _skew_had_cache[n]=True return M From dc40b74bc3644be27d9c86e30b7106c3d1d82957 Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Wed, 9 Nov 2022 12:16:57 +0000 Subject: [PATCH 513/632] Add constructions of 4-symbol delta codes --- src/doc/en/reference/references/index.rst | 5 + src/sage/combinat/matrices/hadamard_matrix.py | 209 ++++++++++++++++++ 2 files changed, 214 insertions(+) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index daa1bfddde1..dae5c526596 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -5831,6 +5831,11 @@ REFERENCES: .. [TTWL2009] Trebst, Troyer, Wang and Ludwig, A short introduction to Fibonacci anyon models, :arxiv:`0902.3275`. +.. [Tur1974] \R. J. Turyn *Hadamard matrices, Baumert-Hall units, + four-symbol sequences, pulse compression, and surface wave encodings*. + Journal of Combinatorial Theory, Series A 16.3 (1974), pp 313–333. + :doi:`10.1016/0097-3165(74)90056-9` + .. [TW1980] \A.D. Thomas and G.V. Wood, Group Tables (Exeter: Shiva Publishing, 1980) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 0288b6d173f..d7b6cc96322 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -427,6 +427,215 @@ def hadamard_matrix_156(): [ C,-B,-C, C, D,-B,-D,-B, A,-D,-A, A], [-C,-D,-D, C,-C,-B, B, B, D, A,-A,-A]]) +def construction_four_symbol_delta_code_I(X, Y, Z, W): + r""" + Construct 4-symbol delta code of length 2*n+1. + + The 4-symbol delta code is constructed from sequences X, Y, Z, W of + length n+1, n+1, n, n satisfying for all `s > 0`: + + .. MATH:: + + N_X(s) + N_Y(s) + N_Z(s) + N_W(s) = 0 + + where `N_A(s)` is the nonperiodic correlation function: + + .. MATH:: + + N_A(s) = \sum_{i=1}^{n-s}a_ia_{i+s} + + The construction (detailed in [Tur1974]_) is as follows: + + .. MATH:: + + T_1 = X;Z + T_2 = X;-Z + T_3 = Y;W + T_4 = Y;-W + + INPUT: + + - ``X`` -- a list, representing the first sequence (length n+1) + + - ``Y`` -- a list, representing the second sequence (length n+1) + + - ``Z`` -- a list, representing the third sequence (length n) + + - ``W`` -- a list, representing the fourth sequence (length n) + + OUTPUT: + A tuple containing the 4-symbol delta code of length 2*n+1 + + EXAMPLE:: + + sage: from sage.combinat.matrices.hadamard_matrix import construction_four_symbol_delta_code_I + sage: construction_four_symbol_delta_code_I([1, 1], [1, -1], [1], [1]) + ([1, 1, 1], [1, 1, -1], [1, -1, 1], [1, -1, -1]) + + TESTS:: + + sage: construction_four_symbol_delta_code_I([1, 1], [1, -1], [1, 1], [1]) + Traceback (most recent call last): + ... + AssertionError + sage: construction_four_symbol_delta_code_I([1, 1], [1, 1], [-1], [1]) + Traceback (most recent call last): + ... + AssertionError + """ + n = len(X) + assert len(Y) == n and len(Z) == n-1 and len(W) == n-1 + + autocorrelation = lambda seq, j: sum([seq[i]*seq[i+j] for i in range(len(seq)-j)]) + for j in range(1, n): + assert sum(map(lambda seq: autocorrelation(seq, j), [X, Y, Z, W])) == 0 + + T1 = X + Z + T2 = X + [-z for z in Z] + T3 = Y + W + T4 = Y + [-w for w in W] + return T1, T2, T3, T4 + + +def construction_four_symbol_delta_code_II(X, Y, Z, W): + r""" + Construct 4-symbol delta code of length 4*n+3. + + The 4-symbol delta code is constructed from sequences X, Y, Z, W of + length n+1, n+1, n, n satisfying for all `s > 0`: + + .. MATH:: + N_X(s) + N_Y(s) + N_Z(s) + N_W(s) = 0 + + where `N_A(s)` is the nonperiodic correlation function: + + .. MATH:: + + N_A(s) = \sum_{i=1}^{n-s}a_ia_{i+s} + + The construction (detailed in [Tur1974]_) is as follows (writing + A/B to mean A alternated with B): + + .. MATH:: + + T_1 = X/Z;Y/W;1 + T_2 = X/Z;Y/-W;-1 + T_3 = X/Z;-Y/-W;1 + T_4 = X/Z;-Y/W;-1 + + INPUT: + + - ``X`` -- a list, representing the first sequence (length n+1) + + - ``Y`` -- a list, representing the second sequence (length n+1) + + - ``Z`` -- a list, representing the third sequence (length n) + + - ``W`` -- a list, representing the fourth sequence (length n) + + OUTPUT: + A tuple containing the four 4-symbol delta code of length 2*n+1 + + EXAMPLE:: + + sage: from sage.combinat.matrices.hadamard_matrix import construction_four_symbol_delta_code_II + sage: construction_four_symbol_delta_code_II([1, 1], [1, -1], [1], [1]) + ([1, 1, 1, 1, 1, -1, 1], + [1, 1, 1, 1, -1, -1, -1], + [1, 1, 1, -1, -1, 1, 1], + [1, 1, 1, -1, 1, 1, -1]) + + TESTS:: + + sage: construction_four_symbol_delta_code_II([1, 1], [1, -1], [1, 1], [1]) + Traceback (most recent call last): + ... + AssertionError + sage: construction_four_symbol_delta_code_II([1, 1], [1, 1], [-1], [1, 1]) + Traceback (most recent call last): + ... + AssertionError + + """ + + n = len(Z) + assert len(X) == n+1 and len(Y) == n+1 and len(W) == n + + autocorrelation = lambda seq, j: sum([seq[i]*seq[i+j] for i in range(len(seq)-j)]) + for j in range(1, n): + assert sum(map(lambda seq: autocorrelation(seq, j), [X, Y, Z, W])) == 0 + + def alternate(seq1, seq2): + return [seq1[i//2] if i%2 == 0 else seq2[(i-1)//2] for i in range(len(seq1)+len(seq2))] + + XaltZ = alternate(X, Z) + Wneg = [-w for w in W] + Yneg = [-y for y in Y] + + T1 = XaltZ + alternate(Y, W) + [1] + T2 = XaltZ + alternate(Y, Wneg) + [-1] + T3 = XaltZ + alternate(Yneg, Wneg) + [1] + T4 = XaltZ + alternate(Yneg, W) + [-1] + return T1, T2, T3, T4 + +def four_symbol_delta_code_smallcases(n, existence=False): + """ + Return the 4-symobl delta code of length n if available. + + The 4-symbol delta codes are constructed using :func:`construction_four_symbol_delta_code_I` + or :func:`construction_four_symbol_delta_code_II`. + The base sequences used are taken from [Tur1974]_. + + INPUT: + + - ``n`` -- integer, the length of the desired 4-symbol delta code + + - ``existence`` -- boolean, if true only check if the sequences are available + EXAMPLES:: + + sage: from sage.combinat.matrices.hadamard_matrix import four_symbol_delta_code_smallcases + sage: four_symbol_delta_code_smallcases(4) + ([1, -1, 1], [1, -1, -1], [1, 1, 1], [1, 1, -1]) + sage: four_symbol_delta_code_smallcases(3, existence=True) + True + + TESTS:: + + sage: four_symbol_delta_code_smallcases(17) + Traceback (most recent call last): + ... + ValueError: The four-symbol delta code of length 17 have not yet been implemented + sage: four_symbol_delta_code_smallcases(17, existence=True) + False + """ + db = { + 1: ([1, -1], [1, 1], [1], [1]), + 14: ([1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1], + [1, 1, 1,-1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1], + [1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1], + [1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1]) + } + + T1, T2, T3, T4 = None, None, None, None + if (n-1)//2 in db: + if existence: + return True + X, Y, Z, W = db[(n-1)//2] + T1, T2, T3, T4 = construction_four_symbol_delta_code_I(X, Y, Z, W) + elif (n-3)//4 in db: + if existence: + return True + X, Y, Z, W = db[(n-3)//4] + T1, T2, T3, T4 = construction_four_symbol_delta_code_II(X, Y, Z, W) + + if existence: + return False + + if T1 is None: + raise ValueError("The four-symbol delta code of length %s have not yet been implemented" % n) + + return T1, T2, T3, T4 + def is_hadamard_matrix(M, normalized=False, skew=False, verbose=False): r""" From 3cd5592f5b21d72864278e8dfb3babeaf3edbdbc Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Wed, 9 Nov 2022 12:44:45 +0000 Subject: [PATCH 514/632] Add construction of Baumert-Hall units --- src/sage/combinat/matrices/hadamard_matrix.py | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index d7b6cc96322..538cf7c337f 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -636,6 +636,86 @@ def four_symbol_delta_code_smallcases(n, existence=False): return T1, T2, T3, T4 +def _get_baumert_hall_units(n, existence=False): + r""" + Construct Baumert-Hall units of size n from available 4-symbol delta codes. + + The construction is detailed in Theroem 2 from [Tur1974]_, and is based on the + Goethals-Seidel construction of hadamard matrices. + We need 4-symbol delta codes to detail the first row of circulant matrices M1, M2, M3, M4 + used in the construction. + + INPUT: + + - ``n`` -- integer, the size of the Baumert-Hall units + + - ``existence`` -- boolean (default False): if true only check whether the units can be contructed + + OUTPUT: + + If ``existence`` is true, return a boolean representing whether the Baumert-Hall units can + be constructed. Otherwise, return a tuple containing the four Baumer-Hall units. + + EXAMPLES:: + + sage: from sage.combinat.matrices.hadamard_matrix import _get_baumert_hall_units + sage: _get_baumert_hall_units(236) + (236 x 236 dense matrix over Integer Ring, + 236 x 236 dense matrix over Integer Ring, + 236 x 236 dense matrix over Integer Ring, + 236 x 236 dense matrix over Integer Ring) + + TESTS:: + + sage: _get_baumert_hall_units(236, existence=True) + True + sage: _get_baumert_hall_units(200, existence=True) + False + sage: _get_baumert_hall_units(15) + Traceback (most recent call last): + ... + AssertionError + sage: _get_baumert_hall_units(200) + Traceback (most recent call last): + ... + ValueError: The Baumert-Hall units of size 200 have not yet been implemented + """ + assert n%4 == 0 and n > 0 + + def construct_baumert_hall_unit(A, B ,C, D): + n = len(A[0]) + R = matrix([[1 if i+j==(n)-1 else 0 for j in range(n)] for i in range(n)]) + return block_matrix([[ A, B*R, C*R, D*R], + [-B*R, A, -D.T*R, C.T*R], + [-C*R, D.T*R, A, -B.T*R], + [-D*R, -C.T*R, B.T*R, A]]) + + delta_codes_len = n//4 + if not four_symbol_delta_code_smallcases(delta_codes_len, existence=True): + if existence: + return False + raise ValueError("The Baumert-Hall units of size %s have not yet been implemented" % n) + + if existence: + return True + + T1, T2, T3, T4 = four_symbol_delta_code_smallcases(delta_codes_len) + M1 = matrix.circulant(T1) + M2 = matrix.circulant(T2) + M3 = matrix.circulant(T3) + M4 = matrix.circulant(T4) + + M1hat = matrix(ZZ, 0.25*(M1+M2+M3+M4)) + M2hat = matrix(ZZ, 0.25*(M1-M2-M3+M4)) + M3hat = matrix(ZZ, 0.25*(M1+M2-M3-M4)) + M4hat = matrix(ZZ, 0.25*(M1-M2+M3-M4)) + + e1 = construct_baumert_hall_unit(M1hat, -M2hat, -M3hat, -M4hat) + e2 = construct_baumert_hall_unit(M2hat, M1hat, M4hat, -M3hat) + e3 = construct_baumert_hall_unit(M3hat, -M4hat, M1hat, M2hat) + e4 = construct_baumert_hall_unit(M4hat, M3hat, -M2hat, M1hat) + return e1, e2, e3, e4 + def is_hadamard_matrix(M, normalized=False, skew=False, verbose=False): r""" From 58cc895bcfecb3476d3785fe88630f43af3c336c Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Wed, 9 Nov 2022 13:29:54 +0000 Subject: [PATCH 515/632] Add Turyn construction of hadamard matrices --- src/sage/combinat/matrices/hadamard_matrix.py | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 538cf7c337f..4bd92da56fb 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -716,6 +716,84 @@ def construct_baumert_hall_unit(A, B ,C, D): e4 = construct_baumert_hall_unit(M4hat, M3hat, -M2hat, M1hat) return e1, e2, e3, e4 +def hadamard_matrix_turyn_type(a, b, c, d, e1, e2, e3, e4, check=True): + r""" + Construction of Turyn type Hadamard matrix. + + Given `n\times n` circulant matrices `A`, `B`, `C`, `D` with 1,-1 entries, + satisfying `AA^\top + BB^\top + CC^\top + DD^\top = 4nI`, and a set of + Baumert-Hall units of order 4t, one can construct a Hadamard matrix of order + `4tn` as detailed by Turyn in [Tur1974]_. + + INPUT: + + - ``a`` -- 1,-1 list specifying the 1st row of `A` + + - ``b`` -- 1,-1 list specifying the 1st row of `B` + + - ``d`` -- 1,-1 list specifying the 1st row of `C` + + - ``c`` -- 1,-1 list specifying the 1st row of `D` + + - ``e1`` -- Matrix representing the first Baumert-Hall unit + + - ``e2`` -- Matrix representing the second Baumert-Hall unit + + - ``e3`` -- Matrix representing the third Baumert-Hall unit + + - ``e4`` -- Matrix representing the fourth Baumert-Hall unit + + - ``check`` -- Whether to check that the output is an hadamard matrix before returning it + + EXAMPLES:: + + sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_turyn_type, _get_baumert_hall_units + sage: A, B, C, D = _get_baumert_hall_units(236) + sage: hadamard_matrix_turyn_type([1], [1], [1], [1], A, B, C, D) + 236 x 236 dense matrix over Integer Ring... + + TESTS:: + + sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_turyn_type, _get_baumert_hall_units, is_hadamard_matrix + sage: A, B, C, D = _get_baumert_hall_units(236) + sage: is_hadamard_matrix(hadamard_matrix_turyn_type([1], [1], [1], [1], A, B, C, D)) + True + sage: hadamard_matrix_turyn_type([1, -1], [1], [1], [1], A, B, C, D) + Traceback (most recent call last): + ... + AssertionError + sage: hadamard_matrix_turyn_type([1, -1], [1, 1], [1, 1], [1, 1], A, B, C, D) + Traceback (most recent call last): + ... + AssertionError + """ + A, B, C, D = map(matrix.circulant, [a, b, c, d]) + + n = len(a) + assert len(a) == len(b) == len(c) == len(d) + assert A*A.T+B*B.T+C*C.T+D*D.T==4*n*I(n) + + t4 = len(e1[0]) + assert t4 %4 == 0 + t = t4//4 + + # Check that e1, e2, e3, e4 are valid Baumert-Hall units + for i in range(t4): + for j in range(t4): + assert abs(e1[i, j]) + abs(e2[i, j]) + abs(e3[i, j]) + abs(e4[i, j]) == 1 + + assert e1*e1.T == t*I(t4) and e2*e2.T == t*I(t4) and e3*e3.T == t*I(t4) and e4*e4.T == t*I(t4) + + units = [e1, e2, e3, e4] + for i in range(len(units)): + for j in range(i+1, len(units)): + assert units[i]*units[j].T + units[j]*units[i].T == 0*I(t4) + + + H = e1.tensor_product(A) + e2.tensor_product(B) + e3.tensor_product(C) + e4.tensor_product(D) + if check: + assert is_hadamard_matrix(H) + return H def is_hadamard_matrix(M, normalized=False, skew=False, verbose=False): r""" From cfbaa8572dbe5ec4e87130f46b81bd3a8aebfd50 Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Wed, 9 Nov 2022 13:57:41 +0000 Subject: [PATCH 516/632] Add Turyn construction with available data --- src/sage/combinat/matrices/hadamard_matrix.py | 61 ++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 4bd92da56fb..3a5c9b9ac82 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -330,6 +330,7 @@ def williamson_type_quadruples_smallcases(n, existence=False): ValueError: The Williamson type quadruple of order 123 is not yet implemented. """ db = { + 1: ([1], [1], [1], [1]), 29: ([1, 1, 1,-1,-1,-1, 1, 1,-1,-1, 1,-1, 1,-1,-1,-1,-1, 1,-1, 1,-1,-1, 1, 1,-1,-1,-1, 1, 1], [1,-1, 1,-1,-1,-1, 1, 1,-1,-1, 1,-1, 1, 1, 1, 1, 1, 1,-1, 1,-1,-1, 1, 1,-1,-1,-1, 1,-1], [1, 1, 1, 1,-1, 1, 1,-1, 1,-1,-1,-1, 1, 1, 1, 1, 1, 1,-1,-1,-1, 1,-1, 1, 1,-1, 1, 1, 1], @@ -795,6 +796,60 @@ def hadamard_matrix_turyn_type(a, b, c, d, e1, e2, e3, e4, check=True): assert is_hadamard_matrix(H) return H +def turyn_type_hadamard_matrix_smallcases(n, existence=False, check=True): + r""" + Use available 4-symbol delta codes and Williamson quadruples to construct an hadamard matrix of order `n`. + + The function looks for Baumert-Hall units and Williamson type matrices from + :func:`four_symbol_delta_code_smallcases` and :func:`williamson_type_quadruples_smallcases` + and use them to construct an Hadamard matrix with the Turyn construction + defined in :func:`hadamard_matrix_turyn_type`. + + INPUT: + + - ``n`` -- integer, the order of the matrix to be constructed + + - ``existence`` -- boolean (default False): if True, only check if matrix exists + + - ``check`` -- bolean: if True (default), check the the matrix is an hadamard matrix before returning + + EXAMPLES:: + + sage: from sage.combinat.matrices.hadamard_matrix import turyn_type_hadamard_matrix_smallcases + sage: turyn_type_hadamard_matrix_smallcases(40, existence=True) + True + sage: turyn_type_hadamard_matrix_smallcases(236) + 236 x 236 dense matrix over Integer Ring... + + TESTS:: + + sage: from sage.combinat.matrices.hadamard_matrix import turyn_type_hadamard_matrix_smallcases, is_hadamard_matrix + sage: is_hadamard_matrix(turyn_type_hadamard_matrix_smallcases(40)) + True + sage: turyn_type_hadamard_matrix_smallcases(64, existence=True) + False + sage: turyn_type_hadamard_matrix_smallcases(64) + Traceback (most recent call last): + ... + ValueError: The Turyn type construction for hadamard matrices of order 64 is not yet implemented. + """ + assert n%4 == 0 and n > 0 + + for delta_code_len in divisors(n//4): + units_size = delta_code_len*4 + will_size = n//units_size + if _get_baumert_hall_units(units_size, existence=True) and williamson_type_quadruples_smallcases(will_size, existence=True): + if existence: + return True + + e1, e2, e3, e4 = _get_baumert_hall_units(units_size) + a, b, c, d = williamson_type_quadruples_smallcases(will_size) + return hadamard_matrix_turyn_type(a, b, c, d, e1, e2, e3, e4, check=check) + + if existence: + return False + raise ValueError("The Turyn type construction for hadamard matrices of order %s is not yet implemented." % n) + def is_hadamard_matrix(M, normalized=False, skew=False, verbose=False): r""" Test if `M` is a hadamard matrix. @@ -977,7 +1032,7 @@ def hadamard_matrix(n,existence=False, check=True): False sage: matrix.hadamard(12,existence=True) True - sage: matrix.hadamard(472,existence=True) + sage: matrix.hadamard(476,existence=True) Unknown sage: matrix.hadamard(10) Traceback (most recent call last): @@ -1028,6 +1083,10 @@ def hadamard_matrix(n,existence=False, check=True): if existence: return True M = hadamard_matrix_156() + elif turyn_type_hadamard_matrix_smallcases(n, existence=True): + if existence: + return True + M = turyn_type_hadamard_matrix_smallcases(n, check=False) elif skew_hadamard_matrix(n, existence=True) is True: if existence: return True From 0d3333d86c099447f7c5338b220cd96da8c62847 Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Thu, 10 Nov 2022 11:01:23 +0000 Subject: [PATCH 517/632] Fix baumert-hall units construction and related tests --- src/sage/combinat/matrices/hadamard_matrix.py | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 3a5c9b9ac82..bbeb828d4a3 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -595,7 +595,7 @@ def four_symbol_delta_code_smallcases(n, existence=False): EXAMPLES:: sage: from sage.combinat.matrices.hadamard_matrix import four_symbol_delta_code_smallcases - sage: four_symbol_delta_code_smallcases(4) + sage: four_symbol_delta_code_smallcases(3) ([1, -1, 1], [1, -1, -1], [1, 1, 1], [1, 1, -1]) sage: four_symbol_delta_code_smallcases(3, existence=True) True @@ -618,12 +618,12 @@ def four_symbol_delta_code_smallcases(n, existence=False): } T1, T2, T3, T4 = None, None, None, None - if (n-1)//2 in db: + if n%2 == 1 and (n-1)//2 in db: if existence: return True X, Y, Z, W = db[(n-1)//2] T1, T2, T3, T4 = construction_four_symbol_delta_code_I(X, Y, Z, W) - elif (n-3)//4 in db: + elif n%4 == 3 and (n-3)//4 in db: if existence: return True X, Y, Z, W = db[(n-3)//4] @@ -660,15 +660,15 @@ def _get_baumert_hall_units(n, existence=False): EXAMPLES:: sage: from sage.combinat.matrices.hadamard_matrix import _get_baumert_hall_units - sage: _get_baumert_hall_units(236) - (236 x 236 dense matrix over Integer Ring, - 236 x 236 dense matrix over Integer Ring, - 236 x 236 dense matrix over Integer Ring, - 236 x 236 dense matrix over Integer Ring) + sage: _get_baumert_hall_units(28) + (28 x 28 dense matrix over Integer Ring, + 28 x 28 dense matrix over Integer Ring, + 28 x 28 dense matrix over Integer Ring, + 28 x 28 dense matrix over Integer Ring) TESTS:: - sage: _get_baumert_hall_units(236, existence=True) + sage: _get_baumert_hall_units(116, existence=True) True sage: _get_baumert_hall_units(200, existence=True) False @@ -749,14 +749,14 @@ def hadamard_matrix_turyn_type(a, b, c, d, e1, e2, e3, e4, check=True): EXAMPLES:: sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_turyn_type, _get_baumert_hall_units - sage: A, B, C, D = _get_baumert_hall_units(236) + sage: A, B, C, D = _get_baumert_hall_units(28) sage: hadamard_matrix_turyn_type([1], [1], [1], [1], A, B, C, D) - 236 x 236 dense matrix over Integer Ring... + 28 x 28 dense matrix over Integer Ring... TESTS:: sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_turyn_type, _get_baumert_hall_units, is_hadamard_matrix - sage: A, B, C, D = _get_baumert_hall_units(236) + sage: A, B, C, D = _get_baumert_hall_units(12) sage: is_hadamard_matrix(hadamard_matrix_turyn_type([1], [1], [1], [1], A, B, C, D)) True sage: hadamard_matrix_turyn_type([1, -1], [1], [1], [1], A, B, C, D) @@ -816,15 +816,15 @@ def turyn_type_hadamard_matrix_smallcases(n, existence=False, check=True): EXAMPLES:: sage: from sage.combinat.matrices.hadamard_matrix import turyn_type_hadamard_matrix_smallcases - sage: turyn_type_hadamard_matrix_smallcases(40, existence=True) + sage: turyn_type_hadamard_matrix_smallcases(28, existence=True) True - sage: turyn_type_hadamard_matrix_smallcases(236) - 236 x 236 dense matrix over Integer Ring... + sage: turyn_type_hadamard_matrix_smallcases(28) + 28 x 28 dense matrix over Integer Ring... TESTS:: sage: from sage.combinat.matrices.hadamard_matrix import turyn_type_hadamard_matrix_smallcases, is_hadamard_matrix - sage: is_hadamard_matrix(turyn_type_hadamard_matrix_smallcases(40)) + sage: is_hadamard_matrix(turyn_type_hadamard_matrix_smallcases(236)) # long time True sage: turyn_type_hadamard_matrix_smallcases(64, existence=True) False From 26a37c2a5ca19d25f14aa661bf5676f658e0256b Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Thu, 10 Nov 2022 16:57:53 +0000 Subject: [PATCH 518/632] Fix documentation --- src/sage/combinat/matrices/hadamard_matrix.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index bbeb828d4a3..02d28e5284f 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -467,7 +467,7 @@ def construction_four_symbol_delta_code_I(X, Y, Z, W): OUTPUT: A tuple containing the 4-symbol delta code of length 2*n+1 - EXAMPLE:: + EXAMPLES:: sage: from sage.combinat.matrices.hadamard_matrix import construction_four_symbol_delta_code_I sage: construction_four_symbol_delta_code_I([1, 1], [1, -1], [1], [1]) @@ -537,7 +537,7 @@ def construction_four_symbol_delta_code_II(X, Y, Z, W): OUTPUT: A tuple containing the four 4-symbol delta code of length 2*n+1 - EXAMPLE:: + EXAMPLES:: sage: from sage.combinat.matrices.hadamard_matrix import construction_four_symbol_delta_code_II sage: construction_four_symbol_delta_code_II([1, 1], [1, -1], [1], [1]) @@ -592,6 +592,7 @@ def four_symbol_delta_code_smallcases(n, existence=False): - ``n`` -- integer, the length of the desired 4-symbol delta code - ``existence`` -- boolean, if true only check if the sequences are available + EXAMPLES:: sage: from sage.combinat.matrices.hadamard_matrix import four_symbol_delta_code_smallcases From 7d0d7b7bff00cda079cda46c6f39051266005575 Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Tue, 15 Nov 2022 10:12:43 +0000 Subject: [PATCH 519/632] Fix format of INPUT blocks in documentation --- src/sage/combinat/matrices/hadamard_matrix.py | 54 ++++++++++--------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 02d28e5284f..d0610e373d2 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -450,19 +450,22 @@ def construction_four_symbol_delta_code_I(X, Y, Z, W): .. MATH:: T_1 = X;Z + T_2 = X;-Z + T_3 = Y;W + T_4 = Y;-W INPUT: - - ``X`` -- a list, representing the first sequence (length n+1) + - ``X`` -- a list, representing the first sequence (length n+1) - - ``Y`` -- a list, representing the second sequence (length n+1) + - ``Y`` -- a list, representing the second sequence (length n+1) - - ``Z`` -- a list, representing the third sequence (length n) + - ``Z`` -- a list, representing the third sequence (length n) - - ``W`` -- a list, representing the fourth sequence (length n) + - ``W`` -- a list, representing the fourth sequence (length n) OUTPUT: A tuple containing the 4-symbol delta code of length 2*n+1 @@ -520,19 +523,22 @@ def construction_four_symbol_delta_code_II(X, Y, Z, W): .. MATH:: T_1 = X/Z;Y/W;1 + T_2 = X/Z;Y/-W;-1 + T_3 = X/Z;-Y/-W;1 + T_4 = X/Z;-Y/W;-1 INPUT: - - ``X`` -- a list, representing the first sequence (length n+1) + - ``X`` -- a list, representing the first sequence (length n+1) - - ``Y`` -- a list, representing the second sequence (length n+1) + - ``Y`` -- a list, representing the second sequence (length n+1) - - ``Z`` -- a list, representing the third sequence (length n) + - ``Z`` -- a list, representing the third sequence (length n) - - ``W`` -- a list, representing the fourth sequence (length n) + - ``W`` -- a list, representing the fourth sequence (length n) OUTPUT: A tuple containing the four 4-symbol delta code of length 2*n+1 @@ -589,9 +595,9 @@ def four_symbol_delta_code_smallcases(n, existence=False): INPUT: - - ``n`` -- integer, the length of the desired 4-symbol delta code + - ``n`` -- integer, the length of the desired 4-symbol delta code - - ``existence`` -- boolean, if true only check if the sequences are available + - ``existence`` -- boolean, if true only check if the sequences are available EXAMPLES:: @@ -649,9 +655,9 @@ def _get_baumert_hall_units(n, existence=False): INPUT: - - ``n`` -- integer, the size of the Baumert-Hall units + - ``n`` -- integer, the size of the Baumert-Hall units - - ``existence`` -- boolean (default False): if true only check whether the units can be contructed + - ``existence`` -- boolean (default False): if true only check whether the units can be contructed OUTPUT: @@ -729,23 +735,23 @@ def hadamard_matrix_turyn_type(a, b, c, d, e1, e2, e3, e4, check=True): INPUT: - - ``a`` -- 1,-1 list specifying the 1st row of `A` + - ``a`` -- 1,-1 list specifying the 1st row of `A` - - ``b`` -- 1,-1 list specifying the 1st row of `B` + - ``b`` -- 1,-1 list specifying the 1st row of `B` - - ``d`` -- 1,-1 list specifying the 1st row of `C` + - ``d`` -- 1,-1 list specifying the 1st row of `C` - - ``c`` -- 1,-1 list specifying the 1st row of `D` + - ``c`` -- 1,-1 list specifying the 1st row of `D` - - ``e1`` -- Matrix representing the first Baumert-Hall unit + - ``e1`` -- Matrix representing the first Baumert-Hall unit - - ``e2`` -- Matrix representing the second Baumert-Hall unit + - ``e2`` -- Matrix representing the second Baumert-Hall unit - - ``e3`` -- Matrix representing the third Baumert-Hall unit + - ``e3`` -- Matrix representing the third Baumert-Hall unit - - ``e4`` -- Matrix representing the fourth Baumert-Hall unit + - ``e4`` -- Matrix representing the fourth Baumert-Hall unit - - ``check`` -- Whether to check that the output is an hadamard matrix before returning it + - ``check`` -- Whether to check that the output is an hadamard matrix before returning it EXAMPLES:: @@ -808,11 +814,11 @@ def turyn_type_hadamard_matrix_smallcases(n, existence=False, check=True): INPUT: - - ``n`` -- integer, the order of the matrix to be constructed + - ``n`` -- integer, the order of the matrix to be constructed - - ``existence`` -- boolean (default False): if True, only check if matrix exists + - ``existence`` -- boolean (default False): if True, only check if matrix exists - - ``check`` -- bolean: if True (default), check the the matrix is an hadamard matrix before returning + - ``check`` -- bolean: if True (default), check the the matrix is an hadamard matrix before returning EXAMPLES:: From 991e6eb88a15e005f3dcbedd2ecf638e0fee6e1d Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Fri, 18 Nov 2022 18:02:07 +0000 Subject: [PATCH 520/632] Add file with T-sequences constructions --- src/doc/en/reference/references/index.rst | 9 + src/sage/combinat/T_sequences.py | 530 ++++++++++++++++++++++ 2 files changed, 539 insertions(+) create mode 100644 src/sage/combinat/T_sequences.py diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index dae5c526596..c207ac755f2 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1769,6 +1769,11 @@ REFERENCES: graphs from orthogonal groups* `O^+(6,2)` *and* `O^-(6,2)`. :arxiv:`1609.07133` +.. [CRSKKY1989] \G. Cohen, D. Rubie, J. Seberry, C. Koukouvinos, S. Kounias, M. Yamada, + *A survey of base sequences, disjoint complementary sequences and OD(4t;t,t,t,t)*. + JCMCC. The Journal of Combinatorial Mathematics and Combinatorial Computing, + vol 5, 1989. + .. [CRST2006] \M. Chudnovsky, N. Robertson, P. Seymour, R. Thomas. *The strong perfect graph theorem*. Annals of Mathematics, vol 164, number 1, pages 51--230, 2006. @@ -5261,6 +5266,10 @@ REFERENCES: *Wide-open encryption design offers flexible implementations*; in Cryptologia, (1985), pp. 75-91. +.. [Seb2017] \J. Seberry, + *Orthogonal designs: Hadamard matrices, quadratic forms and algebras*. + (2017). :doi:`10.1007/978-3-319-59032-5`` + .. [SE1962] \N. E. Steenrod and D. B. A. Epstein, Cohomology operations, Ann. of Math. Stud. 50 (Princeton University Press, 1962). diff --git a/src/sage/combinat/T_sequences.py b/src/sage/combinat/T_sequences.py new file mode 100644 index 00000000000..c262ef1a94d --- /dev/null +++ b/src/sage/combinat/T_sequences.py @@ -0,0 +1,530 @@ +r""" +T-sequences + +T-sequences are sets of four (-1, 0, 1) sequences of length `t` where +for every `i` exactly one sequence has a nonzero entry at index `i` +and for which the nonperiodic autocorrelation function is equal to zero +(i.e. they are complementary). See Definition 7.5 of [Seb2017]_. + +These can be constructed from Turyn sequences. In particular, +if Turyn sequences of length `l` exists, there will be T-sequences +of length `4l-1` and `2l-1`. + +Turyn sequences are sets of four (-1, +1) sequences `X, U, Y, V` of length +`l`, `l`, `l-1`, `l-1` with nonperiodic autocorrelation equal to zero and +the additional contstraints that: +- the first element of `X` is 1 +- the last element of `X` is -1 +- the last element of `U` is 1 + +The nonperiodic autocorrelation of a familiy of sequences `X=\{A_1, A_2, ..., A_n\}` is defined as +(see Definition 7.2 of [Seb2017]_): + +.. MATH:: + + N_X(j) = \sum_{i=1}^{n-j}(a_{1,i}a_{1,i+j} + a_{2,i}a_{2,i+j} + ... + a_{n,i}a_{n,i+j}) + +AUTHORS: + +- Matteo Cati (2022-11-16): initial version + +""" + +# **************************************************************************** +# Copyright (C) 2022 Matteo Cati matteo.cati@keble.ox.ac.uk +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.structure.sequence import Sequence + + +def _nonperiodic_autocorrelation(sequences, j): + r""" + Compute the nonperiodic autocorrelation of a familiy of sequences. + + Namely, given a family of sequences `X` it computes: + + .. MATH:: + + N_X(j) = \sum_{i=1}^{n-j}(a_{1,i}a_{1,i+j} + a_{2,i}a_{2,i+j} + ... + a_{n,i}a_{n,i+j}) + + INPUT: + + - ``sequences`` - either a single Sequence or a list of sequences for which we want + to compute the nonperiodic autocorrelation + + - ``j`` - integer, the parameter j used when calculating the nonperiodic autocorrelation + """ + if not isinstance(sequences[0], list): + sequences = [sequences] + + t = len(sequences[0]) + result = 0 + for i in range(t-j): + for seq in sequences: + result += seq[i]*seq[i+j] + return result + +def is_skew(seq, verbose=False): + r""" + Check if the given sequence is skew. + + A sequence `X=\{x_1, x_2, ...,x_n\} is defined skew (according to Definition + 7.4 of [Seb2017]_) if `n` is even and `x_i = -x_{n-i+1}`. + + INPUT: + + - ``seq`` -- the sequence that should be checked + + - ``verbose`` -- a boolean (default false). If true the function will be verbose + when the sequences do not satisfy the contraints. + + EXAMPLES:: + + sage: from sage.combinat.T_sequences import is_skew + sage: is_skew([1, -1, 1, -1, 1, -1]) + True + sage: is_skew([1, -1, -1, -1], verbose=True) + Constraint not satisfied at index 1 + False + + TESTS:: + + sage: is_skew([1, -1, -1]) + False + sage: is_skew([1, -1, -1, 1, -1], verbose=True) + Sequence should be of even length + False + """ + + n = len(seq) + + if n%2 == 1: + if verbose: + print('Sequence should be of even length') + return False + + for i in range(n): + if seq[i] != -seq[n-i-1]: + if verbose: + print(f'Constraint not satisfied at index {i}') + return False + return True + +def is_symmetric(seq, verbose=False): + r""" + Check if the given sequence is symmetric. + + A sequence `X=\{x_1, x_2, ...,x_n\} is defined symmetric (according to Definition + 7.4 of [Seb2017]_) if `n` is odd and `x_i = x_{n-i+1}`. + + INPUT: + + - ``seq`` -- the sequence that should be checked + + - ``verbose`` -- a boolean (default false). If true the function will be verbose + when the sequences do not satisfy the contraints. + + EXAMPLES:: + + sage: from sage.combinat.T_sequences import is_symmetric + sage: is_symmetric([1, -1, 1, -1, 1]) + True + sage: is_symmetric([1, -1, 1, 1, 1], verbose=True) + Constraint not satisfied at index 1 + False + + TESTS:: + + sage: is_symmetric([1, -1, -1, 1]) + False + sage: is_symmetric([1, -1, -1, 1], verbose=True) + Sequence should be of odd length + False + """ + + n = len(seq) + + if n%2 == 0: + if verbose: + print('Sequence should be of odd length') + return False + + for i in range(n): + if seq[i] != seq[n-i-1]: + if verbose: + print(f'Constraint not satisfied at index {i}') + return False + return True + + +def is_T_sequences_set(sequences, verbose=False): + """ + Check if a family of sequences is composed of T-sequences. + + Given 4 (-1, 0, +1) sequences, they will be T-sequences if + (Definition 7.4 of [Seb2017]_): + - they have all the same length `t` + - for each index `i`, exactly one sequence is nonzero at `i` + - the nonperiodic autocorrelation is equal to `0` + + INPUT: + + - ``sequences`` -- a list of four sequences. + + - ``verbose`` -- a boolean( (default false). If true the function will be verbose + when the sequences do not satisfy the contraints. + + EXAMPLES:: + + sage: from sage.combinat.T_sequences import is_T_sequences_set + sage: seqs = [[1, 1, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, 1, -1], [0, 0, 0, 0, 0]] + sage: is_T_sequences_set(seqs) + True + sage: seqs = [[1, 1, 0, 1, 0], [0, 0, 1, 0, 0], [0, 0, 0, 1, -1], [0, 0, 0, 0, 0]] + sage: is_T_sequences_set(seqs, verbose=True) + There should be exactly a nonzero element at every index, found 2 such elemnents at index 3 + False + + + TESTS:: + + sage: seqs = [[1, 1, 0, 1, 0], [0, 0, 1, 0, 0], [0, 0, 0, 1, -1], [0, 0, 0, 0, 0]] + sage: is_T_sequences_set(seqs) + False + sage: seqs = [[1, 1, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, -1, -1], [0, 0, 0, 0, 0]] + sage: is_T_sequences_set(seqs, verbose=True) + Nonperiodic autocorrelation should always be zero, found 2 for parameter 1 + False + sage: is_T_sequences_set([[1, 0, ], [0, -1, 0], [0, 0, 1]]) + False + sage: seqs = [[1, 2, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, -1, -1], [0, 0, 0, 0, 0]] + sage: is_T_sequences_set(seqs, verbose=True) + Elements should be in (-1, 0, +1), but 2 was found at index 1 + False + """ + if len(sequences) != 4: + if verbose: + print(f"T-Sequence should contain 4 sequences, found {len(sequences)} instead") + return False + + + t = len(sequences[0]) + + for i in range(t): + tot = 0 + for seq in sequences: + if seq[i] not in [-1, 0, 1]: + if verbose: + print(f"Elements should be in (-1, 0, +1), but {seq[i]} was found at index {i}") + return False + tot += abs(seq[i]) + if tot != 1: + if verbose: + print(f"There should be exactly a nonzero element at every index, found {tot} such elemnents at index {i}") + return False + + for j in range(1, t): + autocorr = _nonperiodic_autocorrelation(sequences, j) + if autocorr != 0: + if verbose: + print(f"Nonperiodic autocorrelation should always be zero, found {autocorr} for parameter {j}") + return False + + return True + +def turyn_sequences_smallcases(l, existence=False): + r""" + Construction of Turyn sequences for small values of `l`. + + The data is taken from [Seb2017]_ and [CRSKKY1989]_. + + INPUT: + + - ``l`` -- integer, the length of the Turyn sequences + + - ``existence`` -- boolean (default False). If true, only return whether the + Turyn sequences are available for the given length. + + EXAMPLES: + + By default, this method returns the four Turyn sequences :: + + sage: from sage.combinat.T_sequences import turyn_sequences_smallcases + sage: turyn_sequences_smallcases(4) + [[1, 1, -1, -1], [1, 1, -1, 1], [1, 1, 1], [1, -1, 1]] + + If we pass the ``existence`` flag, the method will return a boolean :: + + sage: turyn_sequences_smallcases(4, existence=True) + True + + TESTS:: + + sage: turyn_sequences_smallcases(17) + Traceback (most recent call last): + ... + ValueError: Turyn sequence of length 17 is not implemented yet. + sage: turyn_sequences_smallcases(17, existence=True) + False + """ + db = { + 2: [[1, -1], [1, 1], [1], [1]], + 3: [[1, 1, 1], [1, 1, -1], [1, -1], [1, -1]], + 4: [[1, 1, -1, -1], [1, 1, -1, 1], [1, 1, 1], [1, -1, 1]], + 5: [[1, 1, -1, 1, 1], [1, 1, 1, 1, -1], [1, 1, -1, -1], [1, -1, 1, -1]], + 6: [[1, 1, 1, -1, -1, -1], [1, 1, -1, 1, -1, 1], [1, 1, -1, 1, 1], [1, 1, -1, 1, 1]], + 7: [[1, 1, 1, -1, 1, 1, 1], [1, 1, -1, -1, -1, 1, -1], [1, 1, -1, 1, -1, -1], [1, 1, -1, 1, -1, -1]], + 8: [[1, 1, -1, 1, -1, 1, -1, -1], [1, 1, 1, 1, -1, -1, -1, 1], [1, 1, 1, -1, 1, 1, 1], [1, -1, -1, 1, -1, -1, 1]], + 13: [[1, 1, 1, 1, -1, 1, -1, 1, -1, 1, 1, 1, 1], [1, 1, 1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1], + [1, 1, 1, -1, 1, 1, -1, -1, 1, -1, -1, -1], [1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1]], + 15: [[1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1], [1, 1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1], + [1, 1, 1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1], [1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1]], + } + + if existence: + return l in db + + if l not in db: + raise ValueError(f"Turyn sequence of length {l} is not implemented yet.") + + return list(map(Sequence, db[l])) + +def T_sequences_construction_I(turyn_sequences, check=True): + """ + Construct T-sequences of length `2l-1` from turyn sequences of length `l`. + + Given Turyn sequences `X, U, Y, V`, the T-sequences are constructed as described in + theorem 7.7 of [Seb2017]_: + + .. MATH:: + + T_1 = \frac{1}{2}(X+U); 0_{l-1} + + T_2 = \frac{1}{2}(X-U); 0_{l-1} + + T_3 = 0_{l} + \frac{1}{2}(Y+V) + + T_4 = 0_{l} + \frac{1}{2}(Y-V) + + INPUT: + + - ``turyn_sequences`` -- the turyn sequences that should be used to construct the T-sequences + + - ``check`` -- boolean, if true (deafult) checks that the sequences created are T-sequences before returning them + + EXAMPLES:: + + sage: from sage.combinat.T_sequences import turyn_sequences_smallcases, T_sequences_construction_I, is_T_sequences_set + sage: seqs = turyn_sequences_smallcases(4) + sage: T_sequences_construction_I(seqs) + [[1, 1, -1, 0, 0, 0, 0], + [0, 0, 0, -1, 0, 0, 0], + [0, 0, 0, 0, 1, 0, 1], + [0, 0, 0, 0, 0, 1, 0]] + + TESTS:: + sage: seqs = turyn_sequences_smallcases(4) + sage: is_T_sequences_set(T_sequences_construction_I(seqs)) + True + sage: T_sequences_construction_I([[1, -1], [-1, 1], [1]]) + Traceback (most recent call last): + ... + AssertionError + """ + + assert len(turyn_sequences) == 4 + + X, U, Y, V = turyn_sequences + l = len(X) + + assert len(X) == len(U) == len(Y)+1 == len(V)+1 + + def seq_sum(seq1, seq2): + return [(a+b)//2 for (a, b) in zip(seq1, seq2)] + + def seq_subtract(seq1, seq2): + return [(a-b)//2 for (a, b) in zip(seq1, seq2)] + + def zero_seq(n): + return [0 for _ in range(n)] + + X1 = Sequence(seq_sum(X, U) + zero_seq(l-1)) + X2 = Sequence(seq_subtract(X, U) + zero_seq(l-1)) + X3 = Sequence(zero_seq(l) + seq_sum(Y, V)) + X4 = Sequence(zero_seq(l) + seq_subtract(Y, V)) + + res = [X1, X2, X3, X4] + if check: + assert is_T_sequences_set(res) + return res + +def T_sequences_construction_II(turyn_sequences, check=True): + """ + Construct T-sequences of length `4l-1` from turyn sequences of length `l`. + + Given Turyn sequences `X, U, Y, V`, the T-sequences are constructed as described in + theorem 7.7 of [Seb2017]_: + + .. MATH:: + + T_1 = 1; 0_{4l-2} + + T_2 = 0; X/Y; 0_{2l-1} + + T_3 = 0_{2l}; U/0_{l-2} + + T_4 = 0_{2l} + 0_{l}/V + + INPUT: + + - ``turyn_sequences`` -- the turyn sequences that should be used to construct the T-sequences + + - ``check`` -- boolean, if true (deafult) checks that the sequences created are T-sequences before returning them + + EXAMPLES:: + + sage: from sage.combinat.T_sequences import turyn_sequences_smallcases, T_sequences_construction_II, is_T_sequences_set + sage: seqs = turyn_sequences_smallcases(4) + sage: T_sequences_construction_II(seqs) + [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 1, 1, 1, -1, 1, -1, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, -1, 0, 1], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, -1, 0, 1, 0]] + + TESTS:: + sage: seqs = turyn_sequences_smallcases(4) + sage: is_T_sequences_set(T_sequences_construction_II(seqs)) + True + sage: T_sequences_construction_II([[1, -1], [-1, 1], [1]]) + Traceback (most recent call last): + ... + AssertionError + + """ + + assert len(turyn_sequences) == 4 + + X, U, Y, V = turyn_sequences + l = len(X) + + assert len(X) == len(U) == len(Y)+1 == len(V)+1 + + def zero_seq(n): + return [0 for _ in range(n)] + + def interleave(seq1, seq2): + res = [] + for i in range(len(seq1) + len(seq2)): + if i%2 == 0: + res.append(seq1[i//2]) + else: + res.append(seq2[i//2]) + return res + + X1 = Sequence([1]+ zero_seq(4*l-2)) + X2 = Sequence([0] + interleave(X, Y) + zero_seq(2*l-1)) + X3 = Sequence(zero_seq(2*l) + interleave(U, zero_seq(l-1))) + X4 = Sequence(zero_seq(2*l) + interleave(zero_seq(l), V)) + + res = [X1, X2, X3, X4] + if check: + assert is_T_sequences_set(res) + return res + +def T_sequences_smallcases(t, existence=False, check=True): + r""" + Construct T-sequences for some small values of `t`. + + This method will try to use the constructions defined in + :func:`sage.combinat.T_sequences.T_sequences_construction_I` and + :func:`sage.combinat.T_sequences.T_sequences_construction_II` + together with the Turyn sequences stored in :func:`sage.combinat.T_sequences.turyn_sequences_smallcases`, + or some T-sequences taken directly from [CRSKKY1989]_. + + INPUT: + + - ``t`` -- integer, the length of the T-sequences to construct + + - ``existence`` -- boolean (default false). If true, thsi method only returns whether a T-sequences of + the given size can be constructed. + + - ``check`` -- boolean, if true (default) check that the sequences are T-sequences before returning them. + + EXAMPLES: + + By default, this method returns the four T-sequences :: + + sage: from sage.combinat.T_sequences import T_sequences_smallcases, is_T_sequences_set + sage: T_sequences_smallcases(9) + [[1, 1, 0, 1, 0, 0, 0, 0, 0], + [0, 0, -1, 0, 1, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 1, 0, 0, -1], + [0, 0, 0, 0, 0, 0, 1, -1, 0]] + + If the existence flag is passed, the method retunrs a boolean :: + + sage: T_sequences_smallcases(9, existence=True) + True + + TESTS:: + + sage: T_sequences_smallcases(66) + Traceback (most recent call last): + ... + ValueError: T Sequences of length 66 not yet implemented. + sage: is_T_sequences_set(T_sequences_smallcases(47)) + True + sage: is_T_sequences_set(T_sequences_smallcases(11)) + True + sage: T_sequences_smallcases(69, existence=True) + False + """ + db = { + 47: [ + [1,-1,-1,0,0,-1,1,-1]+[0]*8+[1,-1,-1,0,0,-1,-1]+[0]*24, + [0,0,0,-1,1,0,0,0,-1,-1,-1,1,1,1,1,1,0,0,0,1,-1,0,0,1]+[0]*23, + [0]*26+[-1,0,1,0,0,0,0,1,-1,1,1,1,0,0,0,0,1,0,-1,0,0], + [0]*24+ [1,1,0,-1,0,-1,1,1,-1,0,0,0,0,0,-1,1,-1,-1,0,-1,0,-1,1] + ], + 65: [ + [0]*33+[1,1,1,1,1,-1,-1,1,1,-1,1,-1,1,1,-1,-1,1,1,1,1,1,-1,-1,1,-1,1,-1,1,-1,-1,1,1], + [0]*32+[1]+[0]*32, + [1]*5+[-1,-1,1,1,-1,1,-1,1,1]+[-1]*7+[1,1,-1,1,-1,1,-1,1,1,-1,-1]+[0]*33, + [0]*65 + ], + 93: [ + [0,-1,0,0,-1,1,0,-1,1,0,1,1,0,0,1,1,1,0,0,-1,0,-1,1,1,1,-1,0,1,0,0,1]+[0]*33+[1,1,0,0,1,0,0,-1,0,0,-1,1,0,1]+[0]*15, + [-1,0,-1,1,0,0,1,0,0,-1,0,0,-1,-1,0,0,0,-1,1,0,1]+[0]*5+[-1,0,1,1]+[0]*32+[1,1,0,0,1,1,0,1,-1,0,1,-1,0,0,-1]+[0]*16, + [0]*32+[1,0,0,1,-1,0,1,-1,0,-1,-1,0,0,-1,-1,1,0,0,-1,0,-1,1,1,1,-1,0,1,0,0,1]+[0]*17+[1,1,0,-1]+[0]*5+[1,0,1,-1,0], + [0]*31+[1,0,1,-1,0,0,-1,0,0,1,0,0,1,1,0,0,0,-1,1,0,1]+[0]*5+[-1,0,1,1]+[0]*17+[-1,0,0,-1,0,1,-1,-1,-1,1,0,1,0,0,-1] + ] + } + + if t in db: + if existence: + return True + sequences = list(map(Sequence, db[t])) + if check: + assert is_T_sequences_set(sequences) + return sequences + if (t+1) %2 == 0 and turyn_sequences_smallcases((t+1)//2, existence=True): + if existence: + return True + turyn_seqs = turyn_sequences_smallcases((t+1)//2) + return T_sequences_construction_I(turyn_seqs, check=check) + + if (t+1)%4 == 0 and turyn_sequences_smallcases((t+1)//4, existence=True): + if existence: + return True + turyn_seqs = turyn_sequences_smallcases((t+1)//4) + return T_sequences_construction_II(turyn_seqs, check=check) + + if existence: + return False + raise ValueError(f'T Sequences of length {t} not yet implemented.') \ No newline at end of file From bb6587dd02bc840f9d032e16397ec8ffc2a40dae Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Sat, 19 Nov 2022 13:44:38 +0000 Subject: [PATCH 521/632] Create separate function for Goethals Seidel matrix --- src/sage/combinat/matrices/hadamard_matrix.py | 79 +++++++++++++++---- 1 file changed, 62 insertions(+), 17 deletions(-) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index d0610e373d2..a886b15edc4 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -527,7 +527,7 @@ def construction_four_symbol_delta_code_II(X, Y, Z, W): T_2 = X/Z;Y/-W;-1 T_3 = X/Z;-Y/-W;1 - + T_4 = X/Z;-Y/W;-1 INPUT: @@ -644,6 +644,62 @@ def four_symbol_delta_code_smallcases(n, existence=False): return T1, T2, T3, T4 +def _construction_goethals_seidel_matrix(A, B ,C, D): + r""" + Construct the Goethals Seidel matrix. + + The matrix is described in [GS70s]_. Given matrices `A`, `B`, `C`, `D` + the construction is: + + .. MATH:: + + \left(\begin{array}{rrrr} + A & BR & CR & DR \\ + -BR & A & -D\top R & C\top R \\ + -CR & D\top R & A & -B\top R \\ + -DR & -C\top R & B\top R & A + \end{array}\right) + + Where `R` is the anti-diagonal matrix with all nonzero entries + equal to one. + + INPUT: + + - ``A`` -- The first matrix used in the construction + + - ``B`` -- The second matrix used in the construction + + - ``C`` -- The third matrix used in the construction + + - ``D`` -- The fourth matrix used in the construction + + TESTS:: + + sage: from sage.combinat.matrices.hadamard_matrix import _construction_goethals_seidel_matrix + sage: A = matrix([[1, -1], [1, 1]]) + sage: B = matrix([[-1, -1], [-1, 1]]) + sage: C = matrix([[1, 1], [1, -1]]) + sage: D = matrix([[-1, 1], [-1, 1]]) + sage: _construction_goethals_seidel_matrix(A, B, C, D) + [ 1 -1|-1 -1| 1 1| 1 -1] + [ 1 1| 1 -1|-1 1| 1 -1] + [-----+-----+-----+-----] + [ 1 1| 1 -1| 1 1| 1 1] + [-1 1| 1 1|-1 -1|-1 1] + [-----+-----+-----+-----] + [-1 -1|-1 -1| 1 -1| 1 1] + [ 1 -1| 1 1| 1 1|-1 1] + [-----+-----+-----+-----] + [-1 1|-1 -1|-1 -1| 1 -1] + [-1 1| 1 -1| 1 -1| 1 1] + """ + n = len(A[0]) + R = matrix(ZZ, n, n, lambda i,j: 1 if i+j==n-1 else 0) + return block_matrix([[ A, B*R, C*R, D*R], + [-B*R, A, -D.T*R, C.T*R], + [-C*R, D.T*R, A, -B.T*R], + [-D*R, -C.T*R, B.T*R, A]]) + def _get_baumert_hall_units(n, existence=False): r""" Construct Baumert-Hall units of size n from available 4-symbol delta codes. @@ -690,13 +746,6 @@ def _get_baumert_hall_units(n, existence=False): """ assert n%4 == 0 and n > 0 - def construct_baumert_hall_unit(A, B ,C, D): - n = len(A[0]) - R = matrix([[1 if i+j==(n)-1 else 0 for j in range(n)] for i in range(n)]) - return block_matrix([[ A, B*R, C*R, D*R], - [-B*R, A, -D.T*R, C.T*R], - [-C*R, D.T*R, A, -B.T*R], - [-D*R, -C.T*R, B.T*R, A]]) delta_codes_len = n//4 if not four_symbol_delta_code_smallcases(delta_codes_len, existence=True): @@ -718,10 +767,10 @@ def construct_baumert_hall_unit(A, B ,C, D): M3hat = matrix(ZZ, 0.25*(M1+M2-M3-M4)) M4hat = matrix(ZZ, 0.25*(M1-M2+M3-M4)) - e1 = construct_baumert_hall_unit(M1hat, -M2hat, -M3hat, -M4hat) - e2 = construct_baumert_hall_unit(M2hat, M1hat, M4hat, -M3hat) - e3 = construct_baumert_hall_unit(M3hat, -M4hat, M1hat, M2hat) - e4 = construct_baumert_hall_unit(M4hat, M3hat, -M2hat, M1hat) + e1 = _construction_goethals_seidel_matrix(M1hat, -M2hat, -M3hat, -M4hat) + e2 = _construction_goethals_seidel_matrix(M2hat, M1hat, M4hat, -M3hat) + e3 = _construction_goethals_seidel_matrix(M3hat, -M4hat, M1hat, M2hat) + e4 = _construction_goethals_seidel_matrix(M4hat, M3hat, -M2hat, M1hat) return e1, e2, e3, e4 def hadamard_matrix_turyn_type(a, b, c, d, e1, e2, e3, e4, check=True): @@ -1580,16 +1629,12 @@ def williamson_goethals_seidel_skew_hadamard_matrix(a, b, c, d, check=True): Discrete Math. 308(2008) 2723-2731 """ n = len(a) - R = matrix(ZZ, n, n, lambda i,j: 1 if i+j==n-1 else 0) A,B,C,D=map(matrix.circulant, [a,b,c,d]) if check: assert A*A.T+B*B.T+C*C.T+D*D.T==4*n*I(n) assert A+A.T==2*I(n) - M = block_matrix([[ A, B*R, C*R, D*R], - [-B*R, A, -D.T*R, C.T*R], - [-C*R, D.T*R, A, -B.T*R], - [-D*R, -C.T*R, B.T*R, A]]) + M = _construction_goethals_seidel_matrix(A, B, C, D) if check: assert is_hadamard_matrix(M, normalized=False, skew=True) return M From ec6384b6a544d91de3042fe20a47683525c1936b Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Sat, 19 Nov 2022 16:06:34 +0000 Subject: [PATCH 522/632] Add Cooper Wallis construction --- src/doc/en/reference/references/index.rst | 5 ++ src/sage/combinat/matrices/hadamard_matrix.py | 89 ++++++++++++++++++- 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index c207ac755f2..4d181afd7d5 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1842,6 +1842,11 @@ REFERENCES: *Signature-based algorithms for Gröbner bases over Tate algebras*, :arxiv:`2002.04491` (2020) +.. [CW1972] \J. Cooper and J. Wallis. + *A construction for Hadamard arrays*, + Bulletin of the Australian Mathematical Society 7(2) (1972): 269-277. + :doi:`10.1017/S0004972700045019` + .. [CW2005] \J. E. Cremona and M. Watkins. Computing isogenies of elliptic curves. preprint, 2005. diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index a886b15edc4..fa0b913ea5f 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -62,6 +62,7 @@ from math import sqrt from sage.matrix.constructor import identity_matrix as I from sage.matrix.constructor import ones_matrix as J +from sage.matrix.constructor import zero_matrix from sage.misc.unknown import Unknown from sage.cpython.string import bytes_to_str from sage.modules.free_module_element import vector @@ -698,7 +699,93 @@ def _construction_goethals_seidel_matrix(A, B ,C, D): return block_matrix([[ A, B*R, C*R, D*R], [-B*R, A, -D.T*R, C.T*R], [-C*R, D.T*R, A, -B.T*R], - [-D*R, -C.T*R, B.T*R, A]]) + [-D*R, -C.T*R, B.T*R, A]]) + +def hadamard_matrix_cooper_wallis_construction(x1, x2, x3, x4, A, B, C, D, check=True): + r""" + Create an hadamard matrix using the contruction detailed in [CW1972]_. + + Given four circulant matrices X1, X2, X3, X4 of order `n` with entries (0, 1, -1) such that the elementwise + product of two distinct matrices is always equal to 0, and that `\sum_{i=1}^{4}X_iX_i\top = nI_n`, + and four matrices A, B, C, D of order `m` with elements (1, -1) such that `MN\top = NM\top` for all distinct `M`, `N` + and `AA\top + BB\top + CC\top + DD\top = 4mI_n`, we construct an hadamard matrix of order `4nm`. + + INPUT: + + - ``x1`` -- a list or vector, representing the first row of the circulant matrix `X_1` + + - ``x2`` -- a list or vector, representing the first row of the circulant matrix `X_2` + + - ``x3`` -- a list or vector, representing the first row of the circulant matrix `X_3` + + - ``x4`` -- a list or vector, representing the first row of the circulant matrix `X_4` + + - ``A`` -- the matrix described above + + - ``B`` -- the matrix described above + + - ``C`` -- the matrix described above + + - ``D`` -- the matrix described above + + - ``check`` -- a boolean, if true (default) check that the resulting matrix is hadamard + before returing it. + + EXAMPLES:: + + sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_cooper_wallis_construction + sage: from sage.combinat.T_sequences import T_sequences_smallcases + sage: seqs = T_sequences_smallcases(19) + sage: hadamard_matrix_cooper_wallis_construction(seqs[0], seqs[1], seqs[2], seqs[3], matrix([1]), matrix([1]), matrix([1]), matrix([1])) + 76 x 76 dense matrix over Integer Ring... + + TESTS:: + + sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_cooper_wallis_construction, is_hadamard_matrix + sage: seqs = T_sequences_smallcases(13) + sage: H = hadamard_matrix_cooper_wallis_construction(seqs[0], seqs[1], seqs[2], seqs[3], matrix([1]), matrix([1]), matrix([1]), matrix([1])) + sage: is_hadamard_matrix(H) + True + sage: len(H[0]) == 13*4*1 + True + sage: hadamard_matrix_cooper_wallis_construction(seqs[0], seqs[1], seqs[2], seqs[3], matrix([1]), matrix([1, -1]), matrix([1]), matrix([1])) + Traceback (most recent call last): + ... + AssertionError + sage: hadamard_matrix_cooper_wallis_construction([1,-1], [1, 1], [1,1], [1,1], matrix([1]), matrix([1]), matrix([1]), matrix([1])) + Traceback (most recent call last): + ... + AssertionError + """ + + n = len(x1) + assert n == len(x2) == len(x3) == len(x4) + + X1, X2, X3, X4 = map(matrix.circulant, [x1, x2, x3, x4]) + + matrices = [X1, X2, X3, X4] + for i in range(4): + for j in range(i+1, 4): + assert matrices[i].elementwise_product(matrices[j]) == zero_matrix(n) + assert X1*X1.T + X2*X2.T + X3*X3.T + X4*X4.T == n*I(n) + + m = len(A[0]) + assert m == len(B[0]) == len(C[0]) == len(D[0]) + will_matrices = [A, B, C, D] + for i in range(4): + for j in range(i+1, 4): + assert will_matrices[i]*will_matrices[j].T == will_matrices[j]*will_matrices[i].T + assert A*A.T + B*B.T + C*C.T + D*D.T == 4*m*I(m) + + e1 = _construction_goethals_seidel_matrix(X1, X2, X3, X4) + e2 = _construction_goethals_seidel_matrix(X2, -X1, X4, -X3) + e3 = _construction_goethals_seidel_matrix(X3, -X4, -X1, X2) + e4 = _construction_goethals_seidel_matrix(X4, X3, -X2, -X1) + + H = e1.tensor_product(A) + e2.tensor_product(B) + e3.tensor_product(C) + e4.tensor_product(D) + if check: + assert is_hadamard_matrix(H) + return H def _get_baumert_hall_units(n, existence=False): r""" From 1f955fcdc0fd7e72bd4725b13d91804b4ee512d3 Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Sat, 19 Nov 2022 16:38:10 +0000 Subject: [PATCH 523/632] Add smallcases function for Cooper Wallis construction --- src/sage/combinat/matrices/hadamard_matrix.py | 80 ++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index fa0b913ea5f..214b7df299f 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -66,6 +66,7 @@ from sage.misc.unknown import Unknown from sage.cpython.string import bytes_to_str from sage.modules.free_module_element import vector +from sage.combinat.T_sequences import T_sequences_smallcases def normalise_hadamard(H): @@ -732,7 +733,7 @@ def hadamard_matrix_cooper_wallis_construction(x1, x2, x3, x4, A, B, C, D, check before returing it. EXAMPLES:: - + sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_cooper_wallis_construction sage: from sage.combinat.T_sequences import T_sequences_smallcases sage: seqs = T_sequences_smallcases(19) @@ -787,6 +788,83 @@ def hadamard_matrix_cooper_wallis_construction(x1, x2, x3, x4, A, B, C, D, check assert is_hadamard_matrix(H) return H +def hadamard_matrix_cooper_wallis_smallcases(n, check=True, existence=False): + r""" + Construct hadamard matrices using the Cooper-Wallis construction for some small values of `n`. + + This functions uses the data from :func:`sage.combinat.T_sequences.T_sequences_smallcases` + and :func:`williamson_type_quadruples_smallcases` + to construct an hadamard matrix of order `n` using the Cooper-Wallis construction + detailed at :func:`hadamard_matrix_cooper_wallis_construction`. + + INPUT: + + - ``n`` -- integer, the order of the matrix to be constructed + + - ``check`` -- bolean: if True (default), check the the matrix is an hadamard matrix before returning + + - ``existence`` -- boolean (default False): if True, only check if matrix exists + + OUTPUT: + + If ``existence`` is false, returns the hadamard matrix of order `n`. It raises an error if no data + is available to construct the matrix of the given order. + If ``existence`` is true, returns a boolean representing whether the matrix can be constructed or not. + + .. SEEALSO:: + + :func:`hadamard_matrix_cooper_wallis_construction` + + EXAMPLES: + + By default The function returns the hadamard matrix :: + + sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_cooper_wallis_smallcases + sage: hadamard_matrix_cooper_wallis_smallcases(28) + 28 x 28 dense matrix over Integer Ring... + + If ``existence`` is set to True, the function returns a boolean :: + + sage: hadamard_matrix_cooper_wallis_smallcases(20, existence=True) + True + + TESTS:: + + sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_cooper_wallis_smallcases, is_hadamard_matrix + sage: is_hadamard_matrix(hadamard_matrix_cooper_wallis_smallcases(188)) + True + sage: hadamard_matrix_cooper_wallis_smallcases(64, existence=True) + False + sage: hadamard_matrix_cooper_wallis_smallcases(64) + Traceback (most recent call last): + ... + ValueError: The Cooper-Wallis construction for hadamard matrices of order 64 is not yet implemented. + sage: hadamard_matrix_cooper_wallis_smallcases(14) + Traceback (most recent call last): + ... + AssertionError + """ + assert n%4 == 0 and n > 0 + + for T_seq_len in divisors(n//4): + will_size = n//(4* T_seq_len) + if T_sequences_smallcases(T_seq_len, existence=True) and williamson_type_quadruples_smallcases(will_size, existence=True): + if existence: + return True + + e1, e2, e3, e4 = T_sequences_smallcases(T_seq_len, check=False) + will_matrices = williamson_type_quadruples_smallcases(will_size) + A, B, C, D = map(matrix.circulant, will_matrices) + M = hadamard_matrix_cooper_wallis_construction(e1, e2, e3, e4, A, B, C, D, check=False) + + if check: + assert is_hadamard_matrix(M) + return M + + if existence: + return False + raise ValueError("The Cooper-Wallis construction for hadamard matrices of order %s is not yet implemented." % n) + def _get_baumert_hall_units(n, existence=False): r""" Construct Baumert-Hall units of size n from available 4-symbol delta codes. From 41a56fc56f87eefa375412707357444bb779a924 Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Sat, 19 Nov 2022 16:45:07 +0000 Subject: [PATCH 524/632] Add Cooper Wallis construction to general hadamard_matrix method --- src/sage/combinat/matrices/hadamard_matrix.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 214b7df299f..4da868791a1 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -1304,6 +1304,10 @@ def hadamard_matrix(n,existence=False, check=True): if existence: return True M = hadamard_matrix_156() + elif hadamard_matrix_cooper_wallis_smallcases(n, existence=True): + if existence: + return True + M = hadamard_matrix_cooper_wallis_smallcases(n, check=False) elif turyn_type_hadamard_matrix_smallcases(n, existence=True): if existence: return True From 59f103bd88cdc8ffd3a66adeebf0f58dcc7581b5 Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Sat, 19 Nov 2022 18:07:03 +0000 Subject: [PATCH 525/632] Add T-matrices of order 67 in Cooper Wallis construction --- src/doc/en/reference/references/index.rst | 5 +++ src/sage/combinat/matrices/hadamard_matrix.py | 31 ++++++++++++++----- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 4d181afd7d5..8dd552086ed 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -5225,6 +5225,11 @@ REFERENCES: .. [Sam2012] \P. Samanta: *Antipodal Graphs* :doi:`10.13140/RG.2.2.28238.46409` +.. [Saw1985] \K. Sawade. + *A Hadamard matrix of order 268*, + Graphs and Combinatorics 1(1) (1985): 185-187. + :doi:`10.1007/BF02582942` + .. [Sch1961] Craige Schensted. *Longest increasing and decreasing subsequences*, Canadian Journal of Mathematics, Vol 13 (1961), pp. 179--191. diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 4da868791a1..a6f4283ad86 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -792,10 +792,13 @@ def hadamard_matrix_cooper_wallis_smallcases(n, check=True, existence=False): r""" Construct hadamard matrices using the Cooper-Wallis construction for some small values of `n`. - This functions uses the data from :func:`sage.combinat.T_sequences.T_sequences_smallcases` - and :func:`williamson_type_quadruples_smallcases` - to construct an hadamard matrix of order `n` using the Cooper-Wallis construction - detailed at :func:`hadamard_matrix_cooper_wallis_construction`. + This function calls the function :func:`hadamard_matrix_cooper_wallis_construction` + with the appropriate arguments. + It constructs the matrices `X_1`, `X_2`, `X_3`, `X_4` using either + T-matrices or the T-sequences from :func:`sage.combinat.T_sequences.T_sequences_smallcases`. + The matrices `A`, `B`, `C`, `D` are taken from :func:`williamson_type_quadruples_smallcases`. + + Data for T-matrices of order 67 is taken from [Saw1985]_. INPUT: @@ -846,14 +849,28 @@ def hadamard_matrix_cooper_wallis_smallcases(n, check=True, existence=False): """ assert n%4 == 0 and n > 0 + db = { + 67: ( + [1, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, -1, -1, -1, 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, -1, 0, -1, 0, 1, -1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], + [0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 1, 1, 0, 0, -1, -1, -1, 0, 0, 0, 0, 0, -1, 1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0], + [0, 0, 0, 0, 0, -1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, -1, 0, 0, 1, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, -1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 1, -1, -1, 0, 1, 0, 0, 0, 0, 0, 0], + [0, 0, -1, -1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, -1, 1, -1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, -1, 0, 0, -1, 0, 0, 0, 0, -1, 0, -1, 1, 1, 0, 0, 0] + ) + } + for T_seq_len in divisors(n//4): will_size = n//(4* T_seq_len) - if T_sequences_smallcases(T_seq_len, existence=True) and williamson_type_quadruples_smallcases(will_size, existence=True): + if (T_seq_len in db or T_sequences_smallcases(T_seq_len, existence=True)) and williamson_type_quadruples_smallcases(will_size, existence=True): if existence: return True - e1, e2, e3, e4 = T_sequences_smallcases(T_seq_len, check=False) - will_matrices = williamson_type_quadruples_smallcases(will_size) + e1, e2, e3, e4 = None, None, None, None + if T_seq_len in db: + e1, e2, e3, e4 = db[T_seq_len] + else: + e1, e2, e3, e4 = T_sequences_smallcases(T_seq_len, check=False) + + will_matrices = williamson_type_quadruples_smallcases(will_size) A, B, C, D = map(matrix.circulant, will_matrices) M = hadamard_matrix_cooper_wallis_construction(e1, e2, e3, e4, A, B, C, D, check=False) From 0bcdf4242948f9197d90421c06935213ac7692d3 Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Sat, 19 Nov 2022 18:21:15 +0000 Subject: [PATCH 526/632] Fix T-sequences documentation --- src/sage/combinat/T_sequences.py | 12 ++++++------ src/sage/combinat/matrices/hadamard_matrix.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/combinat/T_sequences.py b/src/sage/combinat/T_sequences.py index c262ef1a94d..399dbdbf246 100644 --- a/src/sage/combinat/T_sequences.py +++ b/src/sage/combinat/T_sequences.py @@ -56,7 +56,7 @@ def _nonperiodic_autocorrelation(sequences, j): INPUT: - ``sequences`` - either a single Sequence or a list of sequences for which we want - to compute the nonperiodic autocorrelation + to compute the nonperiodic autocorrelation - ``j`` - integer, the parameter j used when calculating the nonperiodic autocorrelation """ @@ -82,7 +82,7 @@ def is_skew(seq, verbose=False): - ``seq`` -- the sequence that should be checked - ``verbose`` -- a boolean (default false). If true the function will be verbose - when the sequences do not satisfy the contraints. + when the sequences do not satisfy the contraints. EXAMPLES:: @@ -128,7 +128,7 @@ def is_symmetric(seq, verbose=False): - ``seq`` -- the sequence that should be checked - ``verbose`` -- a boolean (default false). If true the function will be verbose - when the sequences do not satisfy the contraints. + when the sequences do not satisfy the contraints. EXAMPLES:: @@ -178,7 +178,7 @@ def is_T_sequences_set(sequences, verbose=False): - ``sequences`` -- a list of four sequences. - ``verbose`` -- a boolean( (default false). If true the function will be verbose - when the sequences do not satisfy the contraints. + when the sequences do not satisfy the contraints. EXAMPLES:: @@ -249,7 +249,7 @@ def turyn_sequences_smallcases(l, existence=False): - ``l`` -- integer, the length of the Turyn sequences - ``existence`` -- boolean (default False). If true, only return whether the - Turyn sequences are available for the given length. + Turyn sequences are available for the given length. EXAMPLES: @@ -452,7 +452,7 @@ def T_sequences_smallcases(t, existence=False, check=True): - ``t`` -- integer, the length of the T-sequences to construct - ``existence`` -- boolean (default false). If true, thsi method only returns whether a T-sequences of - the given size can be constructed. + the given size can be constructed. - ``check`` -- boolean, if true (default) check that the sequences are T-sequences before returning them. diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index a6f4283ad86..2cb045541fc 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -730,7 +730,7 @@ def hadamard_matrix_cooper_wallis_construction(x1, x2, x3, x4, A, B, C, D, check - ``D`` -- the matrix described above - ``check`` -- a boolean, if true (default) check that the resulting matrix is hadamard - before returing it. + before returing it. EXAMPLES:: From 26440afe56b8cba5c1d8b5456c8806bf7e6ce465 Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Sun, 20 Nov 2022 15:28:30 +0000 Subject: [PATCH 527/632] Rename T_sequecences.py to lowercase --- src/sage/combinat/matrices/hadamard_matrix.py | 6 +++--- .../{T_sequences.py => t_sequences.py} | 20 +++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) rename src/sage/combinat/{T_sequences.py => t_sequences.py} (96%) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 2cb045541fc..6024cfc6dbe 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -66,7 +66,7 @@ from sage.misc.unknown import Unknown from sage.cpython.string import bytes_to_str from sage.modules.free_module_element import vector -from sage.combinat.T_sequences import T_sequences_smallcases +from sage.combinat.t_sequences import T_sequences_smallcases def normalise_hadamard(H): @@ -735,7 +735,7 @@ def hadamard_matrix_cooper_wallis_construction(x1, x2, x3, x4, A, B, C, D, check EXAMPLES:: sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_cooper_wallis_construction - sage: from sage.combinat.T_sequences import T_sequences_smallcases + sage: from sage.combinat.t_sequences import T_sequences_smallcases sage: seqs = T_sequences_smallcases(19) sage: hadamard_matrix_cooper_wallis_construction(seqs[0], seqs[1], seqs[2], seqs[3], matrix([1]), matrix([1]), matrix([1]), matrix([1])) 76 x 76 dense matrix over Integer Ring... @@ -795,7 +795,7 @@ def hadamard_matrix_cooper_wallis_smallcases(n, check=True, existence=False): This function calls the function :func:`hadamard_matrix_cooper_wallis_construction` with the appropriate arguments. It constructs the matrices `X_1`, `X_2`, `X_3`, `X_4` using either - T-matrices or the T-sequences from :func:`sage.combinat.T_sequences.T_sequences_smallcases`. + T-matrices or the T-sequences from :func:`sage.combinat.t_sequences.t_sequences_smallcases`. The matrices `A`, `B`, `C`, `D` are taken from :func:`williamson_type_quadruples_smallcases`. Data for T-matrices of order 67 is taken from [Saw1985]_. diff --git a/src/sage/combinat/T_sequences.py b/src/sage/combinat/t_sequences.py similarity index 96% rename from src/sage/combinat/T_sequences.py rename to src/sage/combinat/t_sequences.py index 399dbdbf246..a6405494c4d 100644 --- a/src/sage/combinat/T_sequences.py +++ b/src/sage/combinat/t_sequences.py @@ -86,7 +86,7 @@ def is_skew(seq, verbose=False): EXAMPLES:: - sage: from sage.combinat.T_sequences import is_skew + sage: from sage.combinat.t_sequences import is_skew sage: is_skew([1, -1, 1, -1, 1, -1]) True sage: is_skew([1, -1, -1, -1], verbose=True) @@ -132,7 +132,7 @@ def is_symmetric(seq, verbose=False): EXAMPLES:: - sage: from sage.combinat.T_sequences import is_symmetric + sage: from sage.combinat.t_sequences import is_symmetric sage: is_symmetric([1, -1, 1, -1, 1]) True sage: is_symmetric([1, -1, 1, 1, 1], verbose=True) @@ -182,7 +182,7 @@ def is_T_sequences_set(sequences, verbose=False): EXAMPLES:: - sage: from sage.combinat.T_sequences import is_T_sequences_set + sage: from sage.combinat.t_sequences import is_T_sequences_set sage: seqs = [[1, 1, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, 1, -1], [0, 0, 0, 0, 0]] sage: is_T_sequences_set(seqs) True @@ -255,7 +255,7 @@ def turyn_sequences_smallcases(l, existence=False): By default, this method returns the four Turyn sequences :: - sage: from sage.combinat.T_sequences import turyn_sequences_smallcases + sage: from sage.combinat.t_sequences import turyn_sequences_smallcases sage: turyn_sequences_smallcases(4) [[1, 1, -1, -1], [1, 1, -1, 1], [1, 1, 1], [1, -1, 1]] @@ -320,7 +320,7 @@ def T_sequences_construction_I(turyn_sequences, check=True): EXAMPLES:: - sage: from sage.combinat.T_sequences import turyn_sequences_smallcases, T_sequences_construction_I, is_T_sequences_set + sage: from sage.combinat.t_sequences import turyn_sequences_smallcases, T_sequences_construction_I, is_T_sequences_set sage: seqs = turyn_sequences_smallcases(4) sage: T_sequences_construction_I(seqs) [[1, 1, -1, 0, 0, 0, 0], @@ -389,7 +389,7 @@ def T_sequences_construction_II(turyn_sequences, check=True): EXAMPLES:: - sage: from sage.combinat.T_sequences import turyn_sequences_smallcases, T_sequences_construction_II, is_T_sequences_set + sage: from sage.combinat.t_sequences import turyn_sequences_smallcases, T_sequences_construction_II, is_T_sequences_set sage: seqs = turyn_sequences_smallcases(4) sage: T_sequences_construction_II(seqs) [[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], @@ -442,9 +442,9 @@ def T_sequences_smallcases(t, existence=False, check=True): Construct T-sequences for some small values of `t`. This method will try to use the constructions defined in - :func:`sage.combinat.T_sequences.T_sequences_construction_I` and - :func:`sage.combinat.T_sequences.T_sequences_construction_II` - together with the Turyn sequences stored in :func:`sage.combinat.T_sequences.turyn_sequences_smallcases`, + :func:`sage.combinat.t_sequences.T_sequences_construction_I` and + :func:`sage.combinat.t_sequences.T_sequences_construction_II` + together with the Turyn sequences stored in :func:`sage.combinat.t_sequences.turyn_sequences_smallcases`, or some T-sequences taken directly from [CRSKKY1989]_. INPUT: @@ -460,7 +460,7 @@ def T_sequences_smallcases(t, existence=False, check=True): By default, this method returns the four T-sequences :: - sage: from sage.combinat.T_sequences import T_sequences_smallcases, is_T_sequences_set + sage: from sage.combinat.t_sequences import T_sequences_smallcases, is_T_sequences_set sage: T_sequences_smallcases(9) [[1, 1, 0, 1, 0, 0, 0, 0, 0], [0, 0, -1, 0, 1, 0, 0, 0, 0], From 122a947d2d9282e3dc74b9fc9584622ee4c85033 Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Sun, 20 Nov 2022 16:25:29 +0000 Subject: [PATCH 528/632] Add T-sequences file to manual --- src/doc/en/reference/combinat/module_list.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/src/doc/en/reference/combinat/module_list.rst b/src/doc/en/reference/combinat/module_list.rst index 3226ea182d8..cdb81324b4c 100644 --- a/src/doc/en/reference/combinat/module_list.rst +++ b/src/doc/en/reference/combinat/module_list.rst @@ -353,6 +353,7 @@ Comprehensive Module List sage/combinat/superpartition sage/combinat/symmetric_group_algebra sage/combinat/symmetric_group_representations + sage/combinat/t_sequences sage/combinat/tableau sage/combinat/tableau_residues sage/combinat/tableau_tuple From 53c98c894132a882e3503241c452350b0fee50e3 Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Tue, 22 Nov 2022 15:06:31 +0000 Subject: [PATCH 529/632] Fix docstrings format --- src/sage/combinat/matrices/hadamard_matrix.py | 6 +++--- src/sage/combinat/t_sequences.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index 6024cfc6dbe..e28573f6320 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -70,7 +70,7 @@ def normalise_hadamard(H): - """ + r""" Return the normalised Hadamard matrix corresponding to ``H``. The normalised Hadamard matrix corresponding to a Hadamard matrix `H` is a @@ -588,7 +588,7 @@ def alternate(seq1, seq2): return T1, T2, T3, T4 def four_symbol_delta_code_smallcases(n, existence=False): - """ + r""" Return the 4-symobl delta code of length n if available. The 4-symbol delta codes are constructed using :func:`construction_four_symbol_delta_code_I` @@ -1349,7 +1349,7 @@ def hadamard_matrix(n,existence=False, check=True): def hadamard_matrix_www(url_file, comments=False): - """ + r""" Pull file from Sloane's database and return the corresponding Hadamard matrix as a Sage matrix. diff --git a/src/sage/combinat/t_sequences.py b/src/sage/combinat/t_sequences.py index a6405494c4d..7ac9393c81a 100644 --- a/src/sage/combinat/t_sequences.py +++ b/src/sage/combinat/t_sequences.py @@ -164,7 +164,7 @@ def is_symmetric(seq, verbose=False): def is_T_sequences_set(sequences, verbose=False): - """ + r""" Check if a family of sequences is composed of T-sequences. Given 4 (-1, 0, +1) sequences, they will be T-sequences if @@ -296,7 +296,7 @@ def turyn_sequences_smallcases(l, existence=False): return list(map(Sequence, db[l])) def T_sequences_construction_I(turyn_sequences, check=True): - """ + r""" Construct T-sequences of length `2l-1` from turyn sequences of length `l`. Given Turyn sequences `X, U, Y, V`, the T-sequences are constructed as described in @@ -365,7 +365,7 @@ def zero_seq(n): return res def T_sequences_construction_II(turyn_sequences, check=True): - """ + r""" Construct T-sequences of length `4l-1` from turyn sequences of length `l`. Given Turyn sequences `X, U, Y, V`, the T-sequences are constructed as described in From 3e0e1dccde3d58bbc070bea40ec55731c3525ea5 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 22 Nov 2022 22:56:59 -0800 Subject: [PATCH 530/632] configure.ac: Make --enable-download-from-upstream-url the default --- configure.ac | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 1070ac424ab..0edf830c05d 100644 --- a/configure.ac +++ b/configure.ac @@ -441,8 +441,9 @@ AC_ARG_ENABLE([experimental-packages], [AS_HELP_STRING([--enable-experimental-packages], [allow installing experimental packages (default: no = ask for user confirmation for each package)])]) AC_ARG_ENABLE([download-from-upstream-url], - [AS_HELP_STRING([--enable-download-from-upstream-url], - [allow downloading packages from their upstream URL if they cannot be found on the Sage mirrors])]) + [AS_HELP_STRING([--disable-download-from-upstream-url], + [disallow downloading packages from their upstream URL if they cannot be found on the Sage mirrors])], [], + [AS_VAR_SET([enable_download_from_upstream_url], [yes])]) SAGE_SPKG_OPTIONS="" AS_IF([test "x$enable_experimental_packages" = "xyes"], [ From 362f4540b958c8346cb85824c408c4d301bcdbd7 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Tue, 22 Nov 2022 23:04:10 -0800 Subject: [PATCH 531/632] .github/workflows/dist.yml (release_dist): Use --disable-download-from-upstream-url --- .github/workflows/dist.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dist.yml b/.github/workflows/dist.yml index 5086ca60a7c..d3d9b808dee 100644 --- a/.github/workflows/dist.yml +++ b/.github/workflows/dist.yml @@ -18,8 +18,9 @@ jobs: release_dist: - # This job, in contrast to "dist" in tox.yml, - # does not use "configure --enable-download-from-upstream-url". + # This job, in contrast to "dist" in ci-macos.yml, + # does not use "configure --enable-download-from-upstream-url" + # (the default since #32390). # # In this way, we check that all necessary package tarballs # have already been uploaded to the Sage server at the time @@ -38,7 +39,7 @@ jobs: sudo DEBIAN_FRONTEND=noninteractive apt-get install $(build/bin/sage-get-system-packages debian _bootstrap) - name: make dist run: | - ./bootstrap -D && ./configure && make dist + ./bootstrap -D && ./configure --disable-download-from-upstream-url && make dist - uses: actions/upload-artifact@v2 with: path: "dist/*.tar.gz" From 83aa1b2f9c2e3d05f0e15d0046369084a527d050 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 23 Nov 2022 12:09:26 +0100 Subject: [PATCH 532/632] adapt an error message --- src/sage/databases/findstat.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/databases/findstat.py b/src/sage/databases/findstat.py index 0eff62eba25..5401cffb508 100644 --- a/src/sage/databases/findstat.py +++ b/src/sage/databases/findstat.py @@ -1007,7 +1007,9 @@ def findstat(query=None, values=None, distribution=None, domain=None, sage: findstat((PM, [1 for m in PM])) # optional -- internet Traceback (most recent call last): ... - ValueError: E016: You passed too few elements (0 < 3) to FindStat! + ValueError: E016: The statistic finder was unable to perform a search on your data. The following errors have occured: + + You passed too few elements (0 < 3) to FindStat! Finally, we can also retrieve all statistics with a given domain:: From 163d780eb758e40b47624922708e71e94578c247 Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Wed, 23 Nov 2022 11:55:32 +0000 Subject: [PATCH 533/632] Fix typos in docstrings --- src/sage/combinat/matrices/hadamard_matrix.py | 186 +++++++++--------- src/sage/combinat/t_sequences.py | 76 +++---- 2 files changed, 130 insertions(+), 132 deletions(-) diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index e28573f6320..c257c0783a1 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -236,15 +236,15 @@ def hadamard_matrix_williamson_type(a, b, c, d, check=True): INPUT: - - ``a`` -- 1,-1 list specifying the 1st row of `A` + - ``a`` -- (1,-1) list specifying the 1st row of `A`. - - ``b`` -- 1,-1 list specifying the 1st row of `B` + - ``b`` -- (1,-1) list specifying the 1st row of `B`. - - ``d`` -- 1,-1 list specifying the 1st row of `C` + - ``d`` -- (1,-1) list specifying the 1st row of `C`. - - ``c`` -- 1,-1 list specifying the 1st row of `D` + - ``c`` -- (1,-1) list specifying the 1st row of `D`. - - ``check`` (boolean) -- Whether to check that the output is an hadamard matrix before returning it + - ``check`` (boolean) -- Whether to check that the output is an Hadamard matrix before returning it. EXAMPLES:: @@ -291,7 +291,7 @@ def hadamard_matrix_williamson_type(a, b, c, d, check=True): def williamson_type_quadruples_smallcases(n, existence=False): r""" - Quadruples of matrices that can be used to construct Williamson Type hadamard Matrices. + Quadruples of matrices that can be used to construct Williamson type Hadamard matrices. This function contains for some values of n, four `n\times n` matrices used in the Williamson construction of Hadamard matrices. Namely, the function returns the first row of @@ -353,7 +353,7 @@ def williamson_type_quadruples_smallcases(n, existence=False): def williamson_hadamard_matrix_smallcases(n, existence=False, check=True): r""" - Construction of Williamson type Hadamard matrices for some small values of n. + Construct Williamson type Hadamard matrices for some small values of n. This function uses the data contained in :func:`sage.combinat.matrices.hadamard_matrix.williamson_type_quadruples_smallcases` @@ -362,7 +362,7 @@ def williamson_hadamard_matrix_smallcases(n, existence=False, check=True): INPUT: - - ``n`` -- the order of the matrix + - ``n`` -- the order of the matrix. - ``existence`` -- if true, only check that we can do the construction (default false). @@ -396,10 +396,10 @@ def williamson_hadamard_matrix_smallcases(n, existence=False, check=True): def hadamard_matrix_156(): r""" - Construct an hadamard matrix of order 156. + Construct an Hadamard matrix of order 156. The matrix is created using the construction detailed in [BH1965]_. - It is constructed from four circulant matrices of size `13\times 13`, + This uses four circulant matrices of size `13\times 13`, which are composed into a `156\times 156` block matrix. TESTS:: @@ -432,10 +432,10 @@ def hadamard_matrix_156(): def construction_four_symbol_delta_code_I(X, Y, Z, W): r""" - Construct 4-symbol delta code of length 2*n+1. + Construct 4-symbol `\delta` code of length `2n+1`. - The 4-symbol delta code is constructed from sequences X, Y, Z, W of - length n+1, n+1, n, n satisfying for all `s > 0`: + The 4-symbol `\delta` code is constructed from sequences `X, Y, Z, W` of + length `n+1`, `n+1`, `n`, `n` satisfying for all `s > 0`: .. MATH:: @@ -451,26 +451,25 @@ def construction_four_symbol_delta_code_I(X, Y, Z, W): .. MATH:: - T_1 = X;Z - - T_2 = X;-Z - - T_3 = Y;W - - T_4 = Y;-W + \begin{aligned} + T_1 &= X;Z \\ + T_2 &= X;-Z \\ + T_3 &= Y;W \\ + T_4 &= Y;-W + \end{aligned} INPUT: - - ``X`` -- a list, representing the first sequence (length n+1) + - ``X`` -- a list, representing the first sequence (length `n+1`). - - ``Y`` -- a list, representing the second sequence (length n+1) + - ``Y`` -- a list, representing the second sequence (length `n+1`). - - ``Z`` -- a list, representing the third sequence (length n) + - ``Z`` -- a list, representing the third sequence (length `n`). - - ``W`` -- a list, representing the fourth sequence (length n) + - ``W`` -- a list, representing the fourth sequence (length `n`). OUTPUT: - A tuple containing the 4-symbol delta code of length 2*n+1 + A tuple containing the 4-symbol `\delta` code of length `2n+1`. EXAMPLES:: @@ -505,10 +504,10 @@ def construction_four_symbol_delta_code_I(X, Y, Z, W): def construction_four_symbol_delta_code_II(X, Y, Z, W): r""" - Construct 4-symbol delta code of length 4*n+3. + Construct 4-symbol `\delta` code of length `4n+3`. - The 4-symbol delta code is constructed from sequences X, Y, Z, W of - length n+1, n+1, n, n satisfying for all `s > 0`: + The 4-symbol `\delta` code is constructed from sequences `X, Y, Z, W` of + length `n+1`, `n+1`, `n`, `n` satisfying for all `s > 0`: .. MATH:: N_X(s) + N_Y(s) + N_Z(s) + N_W(s) = 0 @@ -520,30 +519,29 @@ def construction_four_symbol_delta_code_II(X, Y, Z, W): N_A(s) = \sum_{i=1}^{n-s}a_ia_{i+s} The construction (detailed in [Tur1974]_) is as follows (writing - A/B to mean A alternated with B): + `A/B` to mean `A` alternated with `B`): .. MATH:: - T_1 = X/Z;Y/W;1 - - T_2 = X/Z;Y/-W;-1 - - T_3 = X/Z;-Y/-W;1 - - T_4 = X/Z;-Y/W;-1 + \begin{aligned} + T_1 &= X/Z;Y/W;1 \\ + T_2 &= X/Z;Y/-W;-1 \\ + T_3 &= X/Z;-Y/-W;1 \\ + T_4 &= X/Z;-Y/W;-1 + \end{aligned} INPUT: - - ``X`` -- a list, representing the first sequence (length n+1) + - ``X`` -- a list, representing the first sequence (length `n+1`). - - ``Y`` -- a list, representing the second sequence (length n+1) + - ``Y`` -- a list, representing the second sequence (length `n+1`). - - ``Z`` -- a list, representing the third sequence (length n) + - ``Z`` -- a list, representing the third sequence (length `n`). - - ``W`` -- a list, representing the fourth sequence (length n) + - ``W`` -- a list, representing the fourth sequence (length `n`). OUTPUT: - A tuple containing the four 4-symbol delta code of length 2*n+1 + A tuple containing the four 4-symbol `\delta` code of length `4n+3`. EXAMPLES:: @@ -589,17 +587,17 @@ def alternate(seq1, seq2): def four_symbol_delta_code_smallcases(n, existence=False): r""" - Return the 4-symobl delta code of length n if available. + Return the 4-symobl `\delta` code of length `n` if available. - The 4-symbol delta codes are constructed using :func:`construction_four_symbol_delta_code_I` + The 4-symbol `\delta` codes are constructed using :func:`construction_four_symbol_delta_code_I` or :func:`construction_four_symbol_delta_code_II`. The base sequences used are taken from [Tur1974]_. INPUT: - - ``n`` -- integer, the length of the desired 4-symbol delta code + - ``n`` -- integer, the length of the desired 4-symbol `\delta` code. - - ``existence`` -- boolean, if true only check if the sequences are available + - ``existence`` -- boolean, if true only check if the sequences are available. EXAMPLES:: @@ -667,13 +665,13 @@ def _construction_goethals_seidel_matrix(A, B ,C, D): INPUT: - - ``A`` -- The first matrix used in the construction + - ``A`` -- The first matrix used in the construction. - - ``B`` -- The second matrix used in the construction + - ``B`` -- The second matrix used in the construction. - - ``C`` -- The third matrix used in the construction + - ``C`` -- The third matrix used in the construction. - - ``D`` -- The fourth matrix used in the construction + - ``D`` -- The fourth matrix used in the construction. TESTS:: @@ -704,32 +702,32 @@ def _construction_goethals_seidel_matrix(A, B ,C, D): def hadamard_matrix_cooper_wallis_construction(x1, x2, x3, x4, A, B, C, D, check=True): r""" - Create an hadamard matrix using the contruction detailed in [CW1972]_. + Create an Hadamard matrix using the contruction detailed in [CW1972]_. - Given four circulant matrices X1, X2, X3, X4 of order `n` with entries (0, 1, -1) such that the elementwise - product of two distinct matrices is always equal to 0, and that `\sum_{i=1}^{4}X_iX_i\top = nI_n`, - and four matrices A, B, C, D of order `m` with elements (1, -1) such that `MN\top = NM\top` for all distinct `M`, `N` - and `AA\top + BB\top + CC\top + DD\top = 4mI_n`, we construct an hadamard matrix of order `4nm`. + Given four circulant matrices `X_1`, X_2, X_3, X_4` of order `n` with entries (0, 1, -1) such that the elementwise + product of two distinct matrices is always equal to `0` and that `\sum_{i=1}^{4}X_iX_i^\top = nI_n`, + and four matrices `A, B, C, D` of order `m` with elements (1, -1) such that `MN^\top = NM^\top` for all distinct `M`, `N` + and `AA^\top + BB^\top + CC^\top + DD^\top = 4mI_n`, we construct an Hadamard matrix of order `4nm`. INPUT: - - ``x1`` -- a list or vector, representing the first row of the circulant matrix `X_1` + - ``x1`` -- a list or vector, representing the first row of the circulant matrix `X_1`. - - ``x2`` -- a list or vector, representing the first row of the circulant matrix `X_2` + - ``x2`` -- a list or vector, representing the first row of the circulant matrix `X_2`. - - ``x3`` -- a list or vector, representing the first row of the circulant matrix `X_3` + - ``x3`` -- a list or vector, representing the first row of the circulant matrix `X_3`. - - ``x4`` -- a list or vector, representing the first row of the circulant matrix `X_4` + - ``x4`` -- a list or vector, representing the first row of the circulant matrix `X_4`. - - ``A`` -- the matrix described above + - ``A`` -- the matrix described above. - - ``B`` -- the matrix described above + - ``B`` -- the matrix described above. - - ``C`` -- the matrix described above + - ``C`` -- the matrix described above. - - ``D`` -- the matrix described above + - ``D`` -- the matrix described above. - - ``check`` -- a boolean, if true (default) check that the resulting matrix is hadamard + - ``check`` -- a boolean, if true (default) check that the resulting matrix is Hadamard before returing it. EXAMPLES:: @@ -790,7 +788,7 @@ def hadamard_matrix_cooper_wallis_construction(x1, x2, x3, x4, A, B, C, D, check def hadamard_matrix_cooper_wallis_smallcases(n, check=True, existence=False): r""" - Construct hadamard matrices using the Cooper-Wallis construction for some small values of `n`. + Construct Hadamard matrices using the Cooper-Wallis construction for some small values of `n`. This function calls the function :func:`hadamard_matrix_cooper_wallis_construction` with the appropriate arguments. @@ -802,15 +800,15 @@ def hadamard_matrix_cooper_wallis_smallcases(n, check=True, existence=False): INPUT: - - ``n`` -- integer, the order of the matrix to be constructed + - ``n`` -- integer, the order of the matrix to be constructed. - - ``check`` -- bolean: if True (default), check the the matrix is an hadamard matrix before returning + - ``check`` -- boolean: if True (default), check the the matrix is an Hadamard matrix before returning. - - ``existence`` -- boolean (default False): if True, only check if matrix exists + - ``existence`` -- boolean (default False): if True, only check if matrix exists. OUTPUT: - If ``existence`` is false, returns the hadamard matrix of order `n`. It raises an error if no data + If ``existence`` is false, returns the Hadamard matrix of order `n`. It raises an error if no data is available to construct the matrix of the given order. If ``existence`` is true, returns a boolean representing whether the matrix can be constructed or not. @@ -820,7 +818,7 @@ def hadamard_matrix_cooper_wallis_smallcases(n, check=True, existence=False): EXAMPLES: - By default The function returns the hadamard matrix :: + By default The function returns the Hadamard matrix :: sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_cooper_wallis_smallcases sage: hadamard_matrix_cooper_wallis_smallcases(28) @@ -841,7 +839,7 @@ def hadamard_matrix_cooper_wallis_smallcases(n, check=True, existence=False): sage: hadamard_matrix_cooper_wallis_smallcases(64) Traceback (most recent call last): ... - ValueError: The Cooper-Wallis construction for hadamard matrices of order 64 is not yet implemented. + ValueError: The Cooper-Wallis construction for Hadamard matrices of order 64 is not yet implemented. sage: hadamard_matrix_cooper_wallis_smallcases(14) Traceback (most recent call last): ... @@ -880,27 +878,27 @@ def hadamard_matrix_cooper_wallis_smallcases(n, check=True, existence=False): if existence: return False - raise ValueError("The Cooper-Wallis construction for hadamard matrices of order %s is not yet implemented." % n) + raise ValueError("The Cooper-Wallis construction for Hadamard matrices of order %s is not yet implemented." % n) def _get_baumert_hall_units(n, existence=False): r""" - Construct Baumert-Hall units of size n from available 4-symbol delta codes. + Construct Baumert-Hall units of size `n` from available 4-symbol `\delta` codes. The construction is detailed in Theroem 2 from [Tur1974]_, and is based on the - Goethals-Seidel construction of hadamard matrices. - We need 4-symbol delta codes to detail the first row of circulant matrices M1, M2, M3, M4 + Goethals-Seidel construction of Hadamard matrices. + We need a 4-symbol `\delta` code to detail the first row of circulant matrices M1, M2, M3, M4 used in the construction. INPUT: - - ``n`` -- integer, the size of the Baumert-Hall units + - ``n`` -- integer, the size of the Baumert-Hall units. - - ``existence`` -- boolean (default False): if true only check whether the units can be contructed + - ``existence`` -- boolean (default False): if true only check whether the units can be contructed. OUTPUT: If ``existence`` is true, return a boolean representing whether the Baumert-Hall units can - be constructed. Otherwise, return a tuple containing the four Baumer-Hall units. + be constructed. Otherwise, return a tuple containing the four Baumert-Hall units. EXAMPLES:: @@ -961,28 +959,28 @@ def hadamard_matrix_turyn_type(a, b, c, d, e1, e2, e3, e4, check=True): Given `n\times n` circulant matrices `A`, `B`, `C`, `D` with 1,-1 entries, satisfying `AA^\top + BB^\top + CC^\top + DD^\top = 4nI`, and a set of - Baumert-Hall units of order 4t, one can construct a Hadamard matrix of order + Baumert-Hall units of order `4t`, one can construct a Hadamard matrix of order `4tn` as detailed by Turyn in [Tur1974]_. INPUT: - - ``a`` -- 1,-1 list specifying the 1st row of `A` + - ``a`` -- 1,-1 list specifying the 1st row of `A`. - - ``b`` -- 1,-1 list specifying the 1st row of `B` + - ``b`` -- 1,-1 list specifying the 1st row of `B`. - - ``d`` -- 1,-1 list specifying the 1st row of `C` + - ``d`` -- 1,-1 list specifying the 1st row of `C`. - - ``c`` -- 1,-1 list specifying the 1st row of `D` + - ``c`` -- 1,-1 list specifying the 1st row of `D`. - - ``e1`` -- Matrix representing the first Baumert-Hall unit + - ``e1`` -- Matrix representing the first Baumert-Hall unit. - - ``e2`` -- Matrix representing the second Baumert-Hall unit + - ``e2`` -- Matrix representing the second Baumert-Hall unit. - - ``e3`` -- Matrix representing the third Baumert-Hall unit + - ``e3`` -- Matrix representing the third Baumert-Hall unit. - - ``e4`` -- Matrix representing the fourth Baumert-Hall unit + - ``e4`` -- Matrix representing the fourth Baumert-Hall unit. - - ``check`` -- Whether to check that the output is an hadamard matrix before returning it + - ``check`` -- Whether to check that the output is an Hadamard matrix before returning it. EXAMPLES:: @@ -1036,7 +1034,7 @@ def hadamard_matrix_turyn_type(a, b, c, d, e1, e2, e3, e4, check=True): def turyn_type_hadamard_matrix_smallcases(n, existence=False, check=True): r""" - Use available 4-symbol delta codes and Williamson quadruples to construct an hadamard matrix of order `n`. + Construct an Hadamard matrix of order `n` from available 4-symbol `\delta` codes and Williamson quadruples. The function looks for Baumert-Hall units and Williamson type matrices from :func:`four_symbol_delta_code_smallcases` and :func:`williamson_type_quadruples_smallcases` @@ -1045,11 +1043,11 @@ def turyn_type_hadamard_matrix_smallcases(n, existence=False, check=True): INPUT: - - ``n`` -- integer, the order of the matrix to be constructed + - ``n`` -- integer, the order of the matrix to be constructed. - - ``existence`` -- boolean (default False): if True, only check if matrix exists + - ``existence`` -- boolean (default False): if True, only check if matrix exists. - - ``check`` -- bolean: if True (default), check the the matrix is an hadamard matrix before returning + - ``check`` -- bolean: if True (default), check the the matrix is an Hadamard matrix before returning. EXAMPLES:: @@ -1069,7 +1067,7 @@ def turyn_type_hadamard_matrix_smallcases(n, existence=False, check=True): sage: turyn_type_hadamard_matrix_smallcases(64) Traceback (most recent call last): ... - ValueError: The Turyn type construction for hadamard matrices of order 64 is not yet implemented. + ValueError: The Turyn type construction for Hadamard matrices of order 64 is not yet implemented. """ assert n%4 == 0 and n > 0 @@ -1086,11 +1084,11 @@ def turyn_type_hadamard_matrix_smallcases(n, existence=False, check=True): if existence: return False - raise ValueError("The Turyn type construction for hadamard matrices of order %s is not yet implemented." % n) + raise ValueError("The Turyn type construction for Hadamard matrices of order %s is not yet implemented." % n) def is_hadamard_matrix(M, normalized=False, skew=False, verbose=False): r""" - Test if `M` is a hadamard matrix. + Test if `M` is a Hadamard matrix. INPUT: diff --git a/src/sage/combinat/t_sequences.py b/src/sage/combinat/t_sequences.py index 7ac9393c81a..44dc73bcd89 100644 --- a/src/sage/combinat/t_sequences.py +++ b/src/sage/combinat/t_sequences.py @@ -12,10 +12,11 @@ Turyn sequences are sets of four (-1, +1) sequences `X, U, Y, V` of length `l`, `l`, `l-1`, `l-1` with nonperiodic autocorrelation equal to zero and -the additional contstraints that: -- the first element of `X` is 1 -- the last element of `X` is -1 -- the last element of `U` is 1 +the additional constraints that: + +* the first element of `X` is 1 +* the last element of `X` is -1 +* the last element of `U` is 1 The nonperiodic autocorrelation of a familiy of sequences `X=\{A_1, A_2, ..., A_n\}` is defined as (see Definition 7.2 of [Seb2017]_): @@ -55,10 +56,10 @@ def _nonperiodic_autocorrelation(sequences, j): INPUT: - - ``sequences`` - either a single Sequence or a list of sequences for which we want - to compute the nonperiodic autocorrelation + - ``sequences`` -- either a single sequence or a list of sequences for which we want + to compute the nonperiodic autocorrelation. - - ``j`` - integer, the parameter j used when calculating the nonperiodic autocorrelation + - ``j`` -- integer, the parameter `j` used when calculating the nonperiodic autocorrelation. """ if not isinstance(sequences[0], list): sequences = [sequences] @@ -74,12 +75,12 @@ def is_skew(seq, verbose=False): r""" Check if the given sequence is skew. - A sequence `X=\{x_1, x_2, ...,x_n\} is defined skew (according to Definition + A sequence `X=\{x_1, x_2, ...,x_n\}` is defined skew (according to Definition 7.4 of [Seb2017]_) if `n` is even and `x_i = -x_{n-i+1}`. INPUT: - - ``seq`` -- the sequence that should be checked + - ``seq`` -- the sequence that should be checked. - ``verbose`` -- a boolean (default false). If true the function will be verbose when the sequences do not satisfy the contraints. @@ -120,12 +121,12 @@ def is_symmetric(seq, verbose=False): r""" Check if the given sequence is symmetric. - A sequence `X=\{x_1, x_2, ...,x_n\} is defined symmetric (according to Definition + A sequence `X=\{x_1, x_2, ...,x_n\}` is defined symmetric (according to Definition 7.4 of [Seb2017]_) if `n` is odd and `x_i = x_{n-i+1}`. INPUT: - - ``seq`` -- the sequence that should be checked + - ``seq`` -- the sequence that should be checked. - ``verbose`` -- a boolean (default false). If true the function will be verbose when the sequences do not satisfy the contraints. @@ -169,15 +170,16 @@ def is_T_sequences_set(sequences, verbose=False): Given 4 (-1, 0, +1) sequences, they will be T-sequences if (Definition 7.4 of [Seb2017]_): - - they have all the same length `t` - - for each index `i`, exactly one sequence is nonzero at `i` - - the nonperiodic autocorrelation is equal to `0` + + * they have all the same length `t` + * for each index `i`, exactly one sequence is nonzero at `i` + * the nonperiodic autocorrelation is equal to `0` INPUT: - ``sequences`` -- a list of four sequences. - - ``verbose`` -- a boolean( (default false). If true the function will be verbose + - ``verbose`` -- a boolean (default false). If true the function will be verbose when the sequences do not satisfy the contraints. EXAMPLES:: @@ -246,7 +248,7 @@ def turyn_sequences_smallcases(l, existence=False): INPUT: - - ``l`` -- integer, the length of the Turyn sequences + - ``l`` -- integer, the length of the Turyn sequences. - ``existence`` -- boolean (default False). If true, only return whether the Turyn sequences are available for the given length. @@ -297,26 +299,25 @@ def turyn_sequences_smallcases(l, existence=False): def T_sequences_construction_I(turyn_sequences, check=True): r""" - Construct T-sequences of length `2l-1` from turyn sequences of length `l`. + Construct T-sequences of length `2l-1` from Turyn sequences of length `l`. Given Turyn sequences `X, U, Y, V`, the T-sequences are constructed as described in theorem 7.7 of [Seb2017]_: .. MATH:: - T_1 = \frac{1}{2}(X+U); 0_{l-1} - - T_2 = \frac{1}{2}(X-U); 0_{l-1} - - T_3 = 0_{l} + \frac{1}{2}(Y+V) - - T_4 = 0_{l} + \frac{1}{2}(Y-V) + \begin{aligned} + T_1 &= \frac{1}{2}(X+U); 0_{l-1} \\ + T_2 &= \frac{1}{2}(X-U); 0_{l-1} \\ + T_3 &= 0_{l} + \frac{1}{2}(Y+V) \\ + T_4 &= 0_{l} + \frac{1}{2}(Y-V) + \end{aligned} INPUT: - - ``turyn_sequences`` -- the turyn sequences that should be used to construct the T-sequences + - ``turyn_sequences`` -- the Turyn sequences that should be used to construct the T-sequences. - - ``check`` -- boolean, if true (deafult) checks that the sequences created are T-sequences before returning them + - ``check`` -- boolean, if true (default) checks that the sequences created are T-sequences before returning them. EXAMPLES:: @@ -366,26 +367,25 @@ def zero_seq(n): def T_sequences_construction_II(turyn_sequences, check=True): r""" - Construct T-sequences of length `4l-1` from turyn sequences of length `l`. + Construct T-sequences of length `4l-1` from Turyn sequences of length `l`. Given Turyn sequences `X, U, Y, V`, the T-sequences are constructed as described in theorem 7.7 of [Seb2017]_: .. MATH:: - T_1 = 1; 0_{4l-2} - - T_2 = 0; X/Y; 0_{2l-1} - - T_3 = 0_{2l}; U/0_{l-2} - - T_4 = 0_{2l} + 0_{l}/V + \begin{aligned} + T_1 &= 1; 0_{4l-2} \\ + T_2 &= 0; X/Y; 0_{2l-1} \\ + T_3 &= 0_{2l}; U/0_{l-2} \\ + T_4 &= 0_{2l} + 0_{l}/V + \end{aligned} INPUT: - - ``turyn_sequences`` -- the turyn sequences that should be used to construct the T-sequences + - ``turyn_sequences`` -- the Turyn sequences that should be used to construct the T-sequences . - - ``check`` -- boolean, if true (deafult) checks that the sequences created are T-sequences before returning them + - ``check`` -- boolean, if true (default) checks that the sequences created are T-sequences before returning them. EXAMPLES:: @@ -449,7 +449,7 @@ def T_sequences_smallcases(t, existence=False, check=True): INPUT: - - ``t`` -- integer, the length of the T-sequences to construct + - ``t`` -- integer, the length of the T-sequences to construct. - ``existence`` -- boolean (default false). If true, thsi method only returns whether a T-sequences of the given size can be constructed. @@ -467,7 +467,7 @@ def T_sequences_smallcases(t, existence=False, check=True): [0, 0, 0, 0, 0, 1, 0, 0, -1], [0, 0, 0, 0, 0, 0, 1, -1, 0]] - If the existence flag is passed, the method retunrs a boolean :: + If the existence flag is passed, the method returns a boolean :: sage: T_sequences_smallcases(9, existence=True) True From 5e5d65919878e996288a3622f089561a257bea0a Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 23 Nov 2022 16:19:39 +0100 Subject: [PATCH 534/632] raise EmptySetError when there is no map or statistic with the required parameters --- src/sage/databases/findstat.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/sage/databases/findstat.py b/src/sage/databases/findstat.py index 4bd9de84563..510ab2daf1c 100644 --- a/src/sage/databases/findstat.py +++ b/src/sage/databases/findstat.py @@ -2530,7 +2530,11 @@ def _an_element_(self): sage: findstat(domain="Permutations").an_element() # optional -- internet St000001: The number of reduced words for a permutation. """ - return next(iter(self)) + try: + return next(iter(self)) + except StopIteration: + from sage.categories.sets_cat import EmptySetError + raise EmptySetError Element = FindStatStatistic @@ -3474,7 +3478,11 @@ def _an_element_(self): sage: findmap(domain="Dyck paths", codomain="Posets").an_element() # optional -- internet Mp00232: parallelogram poset """ - return next(iter(self)) + try: + return next(iter(self)) + except StopIteration: + from sage.categories.sets_cat import EmptySetError + raise EmptySetError Element = FindStatMap From 927684bd04f2f9fa9ccde20174cb4c683dc31dde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 23 Nov 2022 19:13:53 +0100 Subject: [PATCH 535/632] activate pycodestyle E275 in the minimal linter --- src/tox.ini | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/tox.ini b/src/tox.ini index 1fdf5230aae..07d33b1bf7a 100644 --- a/src/tox.ini +++ b/src/tox.ini @@ -96,6 +96,7 @@ description = check against Sage's minimal style conventions # Check for the following issues: # E111: indentation is not a multiple of four + # E275: Missing whitespace after keyword # E306: expected 1 blank line before a nested definition, found 0 # E401: multiple imports on one line # E701: multiple statements on one line (colon) @@ -110,8 +111,8 @@ description = # W605: invalid escape sequence ‘x’ # See https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes deps = pycodestyle -commands = pycodestyle --select E111,E306,E401,E701,E702,E703,W391,W605,E711,E712,E713,E721,E722 {posargs:{toxinidir}/sage/} - pycodestyle --select E111,E306,E401,E703,W605,E712,E713,E721,E722 --filename *.pyx {posargs:{toxinidir}/sage/} +commands = pycodestyle --select E111,E275,E306,E401,E701,E702,E703,W391,W605,E711,E712,E713,E721,E722 {posargs:{toxinidir}/sage/} + pycodestyle --select E111,E275,E306,E401,E703,W605,E712,E713,E721,E722 --filename *.pyx {posargs:{toxinidir}/sage/} [pycodestyle] max-line-length = 160 From c729c7be2d426cd2ca54d47ba2e823eb25469efa Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Wed, 23 Nov 2022 11:38:27 -0800 Subject: [PATCH 536/632] trac 34593: document the variable TMPDIR. Also, move the discussion of runtime environment variables from "Building from source" to "Launching" in the installation guide. --- src/doc/en/installation/launching.rst | 30 +++++++++++++++++++++++++++ src/doc/en/installation/source.rst | 14 ------------- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/doc/en/installation/launching.rst b/src/doc/en/installation/launching.rst index c7b4e1b35ec..24a42cb5457 100644 --- a/src/doc/en/installation/launching.rst +++ b/src/doc/en/installation/launching.rst @@ -29,6 +29,36 @@ To start a Jupyter Notebook instead of a Sage console, run the command instead of just ``sage``. To quit the Jupyter Notebook press `` + `` twice in the console where you launched the command. +Environment variables +--------------------- + +Sage uses the following environment variables when it runs: + +- :envvar:`DOT_SAGE` - this is the directory, to which the user has read and + write access, where Sage stores a number of files. + The default location is :file:`$HOME/.sage/`. + +- :envvar:`SAGE_STARTUP_FILE` - a file including commands to be executed every + time Sage starts. + The default value is :file:`$DOT_SAGE/init.sage`. + +- :envvar:`BROWSER` - on most platforms, Sage will detect the command to + run a web browser, but if this doesn't seem to work on your machine, set this + variable to the appropriate command. + +- :envvar:`TMPDIR` - this variable is used by Python, and hence by + Sage; it gives the directory in which temporary files should be + stored. This includes files used by the notebook. Some browsers have + security settings which restrict the locations of files that they + will access, and users may need to set this variable to handle this + situation. + +- See + https://docs.python.org/3/using/cmdline.html#environment-variables + for more variables used by Python (not an exhaustive list). With + Python 3.11 or later, a brief summary can also be obtained by + running `python3 --help-env`. + Using a Jupyter Notebook remotely --------------------------------- diff --git a/src/doc/en/installation/source.rst b/src/doc/en/installation/source.rst index 879c15f4847..2840b839973 100644 --- a/src/doc/en/installation/source.rst +++ b/src/doc/en/installation/source.rst @@ -1086,20 +1086,6 @@ Some standard environment variables which are used by Sage: - :envvar:`OPENBLAS_CONFIGURE` - adds additional configuration flags for the OpenBLAS package that gets added to the make command. (see :trac:`23272`) -Sage uses the following environment variables when it runs: - -- :envvar:`DOT_SAGE` - this is the directory, to which the user has read and - write access, where Sage stores a number of files. - The default location is :file:`$HOME/.sage/`. - -- :envvar:`SAGE_STARTUP_FILE` - a file including commands to be executed every - time Sage starts. - The default value is :file:`$DOT_SAGE/init.sage`. - -- :envvar:`BROWSER` - on most platforms, Sage will detect the command to - run a web browser, but if this doesn't seem to work on your machine, set this - variable to the appropriate command. - Variables dealing with doctesting: - :envvar:`SAGE_TIMEOUT` - used for Sage's doctesting: the number of seconds From f7c983e03922ca9b996e9572baa41bd7a43758db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 24 Nov 2022 08:56:22 +0100 Subject: [PATCH 537/632] fix linter and activate E275 only for pyx files --- src/sage/algebras/all.py | 1 - src/sage/algebras/fusion_rings/all.py | 2 -- src/sage/algebras/fusion_rings/f_matrix.py | 1 - src/sage/algebras/fusion_rings/fusion_ring.py | 1 - src/sage/combinat/diagram.py | 3 +-- src/sage/combinat/skew_partition.py | 1 - src/sage/rings/lazy_series_ring.py | 1 + src/tox.ini | 2 +- 8 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/sage/algebras/all.py b/src/sage/algebras/all.py index cd71ece02d8..ac06fa76ab8 100644 --- a/src/sage/algebras/all.py +++ b/src/sage/algebras/all.py @@ -65,4 +65,3 @@ lazy_import('sage.algebras.cluster_algebra', 'ClusterAlgebra') lazy_import('sage.algebras.yangian', 'Yangian') - diff --git a/src/sage/algebras/fusion_rings/all.py b/src/sage/algebras/fusion_rings/all.py index 4a7b2196938..9c375f15440 100644 --- a/src/sage/algebras/fusion_rings/all.py +++ b/src/sage/algebras/fusion_rings/all.py @@ -1,7 +1,6 @@ """ Fusion Rings """ - # **************************************************************************** # Copyright (C) 2022 Guillermo Aboumrad # @@ -17,4 +16,3 @@ lazy_import('sage.algebras.fusion_rings.fusion_ring', ['FusionRing']) del lazy_import - diff --git a/src/sage/algebras/fusion_rings/f_matrix.py b/src/sage/algebras/fusion_rings/f_matrix.py index 47f04a5e125..82b5d764fa6 100644 --- a/src/sage/algebras/fusion_rings/f_matrix.py +++ b/src/sage/algebras/fusion_rings/f_matrix.py @@ -2457,4 +2457,3 @@ def certify_pentagons(self, use_mp=True, verbose=False): print("Something went wrong. Pentagons remain.") self._fvars = fvars_copy return pe - diff --git a/src/sage/algebras/fusion_rings/fusion_ring.py b/src/sage/algebras/fusion_rings/fusion_ring.py index d96afeaaee3..da46343b91f 100644 --- a/src/sage/algebras/fusion_rings/fusion_ring.py +++ b/src/sage/algebras/fusion_rings/fusion_ring.py @@ -1572,4 +1572,3 @@ def q_dimension(self, base_coercion=True): if (not base_coercion) or (self.parent()._basecoer is None): return ret return self.parent()._basecoer(ret) - diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index a4a4169848a..747a4800519 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -1465,7 +1465,6 @@ def RotheDiagram(w): winv = w.inverse() from sage.misc.mrange import cartesian_product_iterator cells = [c for c in cartesian_product_iterator((range(N), range(N))) - if c[0]+1 < winv(c[1]+1) and c[1]+1 < w(c[0]+1)] + if c[0] + 1 < winv(c[1] + 1) and c[1] + 1 < w(c[0] + 1)] return NorthwestDiagram(cells, n_rows=N, n_cols=N, check=False) - diff --git a/src/sage/combinat/skew_partition.py b/src/sage/combinat/skew_partition.py index 91644f47462..d6153f4b2a4 100644 --- a/src/sage/combinat/skew_partition.py +++ b/src/sage/combinat/skew_partition.py @@ -1967,4 +1967,3 @@ def __iter__(self): from sage.misc.persist import register_unpickle_override register_unpickle_override('sage.combinat.skew_partition', 'SkewPartition_class', SkewPartition) - diff --git a/src/sage/rings/lazy_series_ring.py b/src/sage/rings/lazy_series_ring.py index 974cced9341..b8613ba2b9c 100644 --- a/src/sage/rings/lazy_series_ring.py +++ b/src/sage/rings/lazy_series_ring.py @@ -1543,6 +1543,7 @@ def q_pochhammer(self, q=None): from sage.arith.misc import binomial qP = q.parent() one = qP.one() + def coeff(n): return (-1)**n * q**binomial(n, 2) / qP.prod(one - q**i for i in range(1, n+1)) return self(coefficients=coeff, valuation=0) diff --git a/src/tox.ini b/src/tox.ini index 07d33b1bf7a..974b7f7c3e7 100644 --- a/src/tox.ini +++ b/src/tox.ini @@ -111,7 +111,7 @@ description = # W605: invalid escape sequence ‘x’ # See https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes deps = pycodestyle -commands = pycodestyle --select E111,E275,E306,E401,E701,E702,E703,W391,W605,E711,E712,E713,E721,E722 {posargs:{toxinidir}/sage/} +commands = pycodestyle --select E111,E306,E401,E701,E702,E703,W391,W605,E711,E712,E713,E721,E722 {posargs:{toxinidir}/sage/} pycodestyle --select E111,E275,E306,E401,E703,W605,E712,E713,E721,E722 --filename *.pyx {posargs:{toxinidir}/sage/} [pycodestyle] From 40c82e88708fdb5040632b4a903a2face5166efe Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 24 Nov 2022 09:11:22 +0100 Subject: [PATCH 538/632] polish categories --- src/sage/combinat/sf/sfa.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index a4abf9181fd..23512a9338c 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -375,15 +375,12 @@ def super_categories(self): # KeyError when doing the C3 algorithm!!! R = self.base().base_ring() cat = HopfAlgebras(R) + categories = [self.base().Realizations(), + cat.Commutative().WithBasis(), + cat.Graded().Realizations()] if R in PrincipalIdealDomains: - return [self.base().Realizations(), - cat.Commutative().WithBasis(), - cat.Graded().Realizations(), - UniqueFactorizationDomains()] - - return [self.base().Realizations(), - cat.Commutative().WithBasis(), - cat.Graded().Realizations()] + categories.append(UniqueFactorizationDomains()) + return categories class ParentMethods: From 9eca08f6d8237e6b6615cc5d64aa6e6c577b57c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 24 Nov 2022 09:26:13 +0100 Subject: [PATCH 539/632] fix indentation E111 in pyx files and disactivate E275 --- .../fast_parallel_fmats_methods.pyx | 44 ++++++++----------- .../algebras/fusion_rings/poly_tup_engine.pyx | 20 ++++----- .../algebras/fusion_rings/shm_managers.pyx | 2 +- src/tox.ini | 3 +- 4 files changed, 30 insertions(+), 39 deletions(-) diff --git a/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx b/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx index 64b8254f13a..bf6bb29c3d7 100644 --- a/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx +++ b/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx @@ -169,29 +169,21 @@ cpdef _backward_subs(factory, bint flatten=True): res = _flatten_coeffs(res) fvars[sextuple] = res + cdef _fmat(fvars, _Nk_ij, id_anyon, a, b, c, d, x, y): - """ - Cython version of fmat class method. Using cdef for fastest dispatch - """ - if _Nk_ij(a, b, x) == 0 or _Nk_ij(x, c, d) == 0 or _Nk_ij(b, c, y) == 0 or _Nk_ij(a, y, d) == 0: - return 0 - # Some known F-symbols - if a == id_anyon: - if x == b and y == d: - return 1 - else: - return 0 - if b == id_anyon: - if x == a and y == c: - return 1 - else: - return 0 - if c == id_anyon: - if x == d and y == b: - return 1 - else: - return 0 - return fvars[a, b, c, d, x, y] + """ + Cython version of fmat class method. Using cdef for fastest dispatch + """ + if _Nk_ij(a, b, x) == 0 or _Nk_ij(x, c, d) == 0 or _Nk_ij(b, c, y) == 0 or _Nk_ij(a, y, d) == 0: + return 0 + # Some known F-symbols + if a == id_anyon: + return 1 if x == b and y == d else 0 + if b == id_anyon: + return 1 if x == a and y == c else 0 + if c == id_anyon: + return 1 if x == d and y == b else 0 + return fvars[a, b, c, d, x, y] ###################################### ### Fast fusion coefficients cache ### @@ -232,7 +224,7 @@ cdef req_cy(tuple basis, r_matrix, dict fvars, Nk_ij, id_anyon, tuple sextuple): lhs = r_matrix(a, c, e, base_coercion=False) * _fmat(fvars, Nk_ij, id_anyon, a, c, b, d, e, g) * r_matrix(b, c, g, base_coercion=False) rhs = 0 for f in basis: - rhs += _fmat(fvars, Nk_ij, id_anyon, c, a, b, d, e, f) * r_matrix(f, c, d, base_coercion=False) * _fmat(fvars, Nk_ij, id_anyon, a, b, c, d, f, g) + rhs += _fmat(fvars, Nk_ij, id_anyon, c, a, b, d, e, f) * r_matrix(f, c, d, base_coercion=False) * _fmat(fvars, Nk_ij, id_anyon, a, b, c, d, f, g) return lhs-rhs @cython.wraparound(False) @@ -299,7 +291,7 @@ cdef MPolynomial_libsingular feq_cy(tuple basis, fvars, Nk_ij, id_anyon, zero, t return zero rhs = zero for h in basis: - rhs += _fmat(fvars, Nk_ij, id_anyon, a, b, c, g, f, h)*_fmat(fvars, Nk_ij, id_anyon, a, h, d, e, g, k)*_fmat(fvars, Nk_ij, id_anyon, b, c, d, k, h, l) + rhs += _fmat(fvars, Nk_ij, id_anyon, a, b, c, g, f, h)*_fmat(fvars, Nk_ij, id_anyon, a, h, d, e, g, k)*_fmat(fvars, Nk_ij, id_anyon, b, c, d, k, h, l) return lhs - rhs @cython.wraparound(False) @@ -511,10 +503,10 @@ cdef feq_verif(factory, worker_results, fvars, Nk_ij, id_anyon, tuple nonuple, f lhs = _fmat(fvars, Nk_ij, id_anyon, f, c, d, e, g, l)*_fmat(fvars, Nk_ij, id_anyon, a, b, l, e, f, k) rhs = 0.0 for h in factory._FR.basis(): - rhs += _fmat(fvars, Nk_ij, id_anyon, a, b, c, g, f, h)*_fmat(fvars, Nk_ij, id_anyon, a, h, d, e, g, k)*_fmat(fvars, Nk_ij, id_anyon, b, c, d, k, h, l) + rhs += _fmat(fvars, Nk_ij, id_anyon, a, b, c, g, f, h)*_fmat(fvars, Nk_ij, id_anyon, a, h, d, e, g, k)*_fmat(fvars, Nk_ij, id_anyon, b, c, d, k, h, l) diff = lhs - rhs if diff > tol or diff < -tol: - worker_results.append(diff) + worker_results.append(diff) @cython.wraparound(False) @cython.nonecheck(False) diff --git a/src/sage/algebras/fusion_rings/poly_tup_engine.pyx b/src/sage/algebras/fusion_rings/poly_tup_engine.pyx index 47e403c6d2c..ac465e14a77 100644 --- a/src/sage/algebras/fusion_rings/poly_tup_engine.pyx +++ b/src/sage/algebras/fusion_rings/poly_tup_engine.pyx @@ -316,7 +316,7 @@ cpdef tuple apply_coeff_map(tuple eq_tup, coeff_map): cdef ETuple exp cdef list new_tup = [] for exp, coeff in eq_tup: - new_tup.append((exp, coeff_map(coeff))) + new_tup.append((exp, coeff_map(coeff))) return tuple(new_tup) # cpdef inline bint tup_fixes_sq(tuple eq_tup): @@ -368,7 +368,7 @@ cdef dict subs_squares(dict eq_dict, KSHandler known_sq): coeff *= pow(known_sq.get(idx), power // 2) # New power is 1 if power is odd if power & True: - new_e[idx] = 1 + new_e[idx] = 1 else: new_e[idx] = power exp = ETuple(new_e, len(exp)) @@ -567,13 +567,13 @@ cpdef tuple poly_tup_sortkey(tuple eq_tup): cdef int i, l, nnz cdef list key = [] for exp, c in eq_tup: - # Compare by term degree - key.append(exp.unweighted_degree()) - # Next compare by term w.r.t. lex order - for i in range(exp._nonzero): - # key.append(exp._length-1-exp._data[2*(nnz-i-1)]) - # key.append(-exp._data[2*(nnz-i-1)+1]) - key.append(-exp._data[2*i]) - key.append(exp._data[2*i+1]) + # Compare by term degree + key.append(exp.unweighted_degree()) + # Next compare by term w.r.t. lex order + for i in range(exp._nonzero): + # key.append(exp._length-1-exp._data[2*(nnz-i-1)]) + # key.append(-exp._data[2*(nnz-i-1)+1]) + key.append(-exp._data[2*i]) + key.append(exp._data[2*i+1]) return tuple(key) diff --git a/src/sage/algebras/fusion_rings/shm_managers.pyx b/src/sage/algebras/fusion_rings/shm_managers.pyx index 5d7ec652613..91aba7ba59f 100644 --- a/src/sage/algebras/fusion_rings/shm_managers.pyx +++ b/src/sage/algebras/fusion_rings/shm_managers.pyx @@ -559,7 +559,7 @@ cdef class FvarsHandler: cache, so each process must update its cache before retrieving a modified entry, tagged via its ``modified`` property. """ - if not sextuple in self.sext_to_idx: + if sextuple not in self.sext_to_idx: raise KeyError('invalid sextuple {}'.format(sextuple)) cdef Py_ssize_t idx = self.sext_to_idx[sextuple] # Each process builds its own cache, so each process must know diff --git a/src/tox.ini b/src/tox.ini index 974b7f7c3e7..1fdf5230aae 100644 --- a/src/tox.ini +++ b/src/tox.ini @@ -96,7 +96,6 @@ description = check against Sage's minimal style conventions # Check for the following issues: # E111: indentation is not a multiple of four - # E275: Missing whitespace after keyword # E306: expected 1 blank line before a nested definition, found 0 # E401: multiple imports on one line # E701: multiple statements on one line (colon) @@ -112,7 +111,7 @@ description = # See https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes deps = pycodestyle commands = pycodestyle --select E111,E306,E401,E701,E702,E703,W391,W605,E711,E712,E713,E721,E722 {posargs:{toxinidir}/sage/} - pycodestyle --select E111,E275,E306,E401,E703,W605,E712,E713,E721,E722 --filename *.pyx {posargs:{toxinidir}/sage/} + pycodestyle --select E111,E306,E401,E703,W605,E712,E713,E721,E722 --filename *.pyx {posargs:{toxinidir}/sage/} [pycodestyle] max-line-length = 160 From f58b8f58eb95956219237225340348bbafb83cb4 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 24 Nov 2022 17:16:13 +0100 Subject: [PATCH 540/632] factor out conversion to and from polynomials --- src/sage/combinat/sf/sfa.py | 173 +++++++++++++++++++++++------------- 1 file changed, 113 insertions(+), 60 deletions(-) diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index 23512a9338c..793ca21ecd3 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -228,7 +228,7 @@ from sage.combinat.free_module import CombinatorialFreeModule from sage.matrix.constructor import matrix from sage.structure.factorization import Factorization -from sage.structure.element import parent +from sage.structure.element import parent, coerce_binop from sage.misc.misc_c import prod from sage.data_structures.blas_dict import convert_remove_zeroes, linear_combination from copy import copy @@ -3029,7 +3029,7 @@ class SymmetricFunctionAlgebra_generic_Element(CombinatorialFreeModule.Element): m[1, 1, 1] + m[2, 1] + m[3] sage: m.set_print_style('lex') """ - def factor(self, proof=None): + def factor(self): """ Return the factorization of this symmetric function. @@ -3064,6 +3064,15 @@ def factor(self, proof=None): sage: f.factor() (1/(t^2 + 4*t + 4)) * ((-t-2)*JackP[1, 1, 1] + (-t^2-2*t)*JackP[2, 1])^2 + Some corner cases:: + + sage: s = SymmetricFunctions(ZZ).s() + sage: factor(s(6)) + 2 * 3 + + sage: factor(6*s[1]) + 2*s[] * 3*s[] * s[1] + """ from sage.combinat.sf.multiplicative import SymmetricFunctionAlgebra_multiplicative L = self.parent() @@ -3073,15 +3082,12 @@ def factor(self, proof=None): M = L.realization_of().h() self = M(self) - n = max((part[0] for part in self.support() if part), default=0) - R = PolynomialRing(self.base_ring(), - ["v%s" % a for a in range(1, n + 1)]) - poly = R({tuple(part.to_exp(n)): c for part, c in self}) - factors = poly.factor(proof=proof) + poly = _to_polynomials([self], self.base_ring())[0] + factors = poly.factor() unit = factors.unit() - factors = [(M.element_class(M, {_Partitions.from_exp(e): c - for e, c in factor.iterator_exp_coeff(False)}), - exponent) + if factors.universe() == self.base_ring(): + return Factorization(factors, unit=unit) + factors = [(_from_polynomial(factor, M), exponent) for factor, exponent in factors] if not isinstance(L, SymmetricFunctionAlgebra_multiplicative): @@ -3100,41 +3106,35 @@ def _floordiv_(self, other): EXAMPLES:: sage: e = SymmetricFunctions(ZZ).e() - sage: e[3,2,1] // e[2] - e[3, 1] + sage: h = SymmetricFunctions(ZZ).h() + sage: e[3,2,1] // h[2] + -e[3, 1] + + TESTS:: + + sage: s = SymmetricFunctions(ZZ).s() + sage: s(0) // s[1] + 0 + + sage: s(6) // s(2) + 3*s[] """ from sage.combinat.sf.multiplicative import SymmetricFunctionAlgebra_multiplicative - from sage.structure.element import get_coercion_model - cm = get_coercion_model() - L = cm.common_parent(self.parent(), parent(other)) + # we can assume that the parents of self and other are the same + L = self.parent() if isinstance(L, SymmetricFunctionAlgebra_multiplicative): M = L else: M = L.realization_of().h() - self = M(self) - other = M(other) - - n1 = max((part[0] for part in self.support() if part), default=0) - n2 = max((part[0] for part in other.support() if part), default=0) - n = max(n1, n2, 1) - R = PolynomialRing(M.base_ring(), - ["v%s" % a for a in range(1, n + 1)]) - if n == 1: - p1 = R({part.to_exp(n)[0]: c for part, c in self}) - p2 = R({part.to_exp(n)[0]: c for part, c in other}) - else: - p1 = R({tuple(part.to_exp(n)): c for part, c in self}) - p2 = R({tuple(part.to_exp(n)): c for part, c in other}) + self = M(self) + other = M(other) + + p1, p2 = _to_polynomials([self, other], self.base_ring()) g = p1 // p2 - if n == 1: - g = {_Partitions.from_exp([e]): c - for e, c in g.dict().items()} - else: - g = {_Partitions.from_exp(e): c - for e, c in g.iterator_exp_coeff(False)} - return L(M.element_class(M, g)) + return L(_from_polynomial(g, M)) + @coerce_binop def gcd(self, other): """ Return the greatest common divisor with ``other``. @@ -3172,35 +3172,17 @@ def gcd(self, other): """ from sage.combinat.sf.multiplicative import SymmetricFunctionAlgebra_multiplicative - from sage.structure.element import get_coercion_model - cm = get_coercion_model() - L = cm.common_parent(self.parent(), parent(other)) + L = self.parent() if isinstance(L, SymmetricFunctionAlgebra_multiplicative): M = L else: M = L.realization_of().h() - self = M(self) - other = M(other) - - n1 = max((part[0] for part in self.support() if part), default=0) - n2 = max((part[0] for part in other.support() if part), default=0) - n = max(n1, n2, 1) - R = PolynomialRing(M.base_ring(), - ["v%s" % a for a in range(1, n + 1)]) - if n == 1: - p1 = R({part.to_exp(n)[0]: c for part, c in self}) - p2 = R({part.to_exp(n)[0]: c for part, c in other}) - else: - p1 = R({tuple(part.to_exp(n)): c for part, c in self}) - p2 = R({tuple(part.to_exp(n)): c for part, c in other}) + self = M(self) + other = M(other) + + p1, p2 = _to_polynomials([self, other], self.base_ring()) g = p1.gcd(p2) - if n == 1: - g = {_Partitions.from_exp([e]): c - for e, c in g.dict().items()} - else: - g = {_Partitions.from_exp(e): c - for e, c in g.iterator_exp_coeff(False)} - return L(M.element_class(M, g)) + return L(_from_polynomial(g, M)) def plethysm(self, x, include=None, exclude=None): r""" @@ -6471,3 +6453,74 @@ def _raise_variables(c, n, variables): """ return c.subs(**{str(g): g ** n for g in variables}) + + +def _to_polynomials(lf, R): + """ + Return the symmetric functions as polynomials, where each + part of a partition corresponds to a variable. + + The result makes sense only if the symmetric functions are all + given in the same basis, which is multiplicative, but we do not + check this. + + INPUT: + + - ``lf`` -- a list of symmetric functions + - ``R`` -- the base ring + + .. SEEALSO:: + + :func:`_from_polynomials` + + EXAMPLES:: + + sage: from sage.combinat.sf.sfa import _to_polynomials + sage: e = SymmetricFunctions(QQ).e() + sage: _to_polynomials([5*e[3] + e[2,1] + e[1]], QQ) + [v1*v2 + v1 + 5*v3] + """ + n = max(max((part[0] for part in f.support() if part), default=0) + for f in lf) + # the polynomial ring with no variables is not well supported, + # eg., gcd does not work + n = max(n, 1) + P = PolynomialRing(R, ["v%s" % a for a in range(1, n + 1)]) + if n == 1: + return [P({part.to_exp(n)[0]: c for part, c in f}) + for f in lf] + return [P({tuple(part.to_exp(n)): c for part, c in f}) + for f in lf] + +def _from_polynomial(p, f): + """ + Return the polynomial as a symmetric function in the given + basis , where the `n`th variable corresponds to the symmetric + function`f[n]`. + + INPUT: + + - ``p`` -- a polynomial + - ``f`` -- a basis of the ring of symmetric functions + + .. SEEALSO:: + + :func:`_to_polynomials` + + EXAMPLES:: + + sage: from sage.combinat.sf.sfa import _to_polynomials, _from_polynomial + sage: e = SymmetricFunctions(QQ).e() + sage: p = _to_polynomials([5*e[3] + e[2,1] + e[1]], ZZ)[0]; p + v1*v2 + v1 + 5*v3 + sage: _from_polynomial(p, e) + e[1] + e[2, 1] + 5*e[3] + """ + n = p.parent().ngens() + if n == 1: + d = {_Partitions.from_exp([e]): c + for e, c in p.dict().items()} + else: + d = {_Partitions.from_exp(e): c + for e, c in p.iterator_exp_coeff(False)} + return f.element_class(f, d) From 0493593a2cc0d992b80496442aa4937d58d44d00 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 24 Nov 2022 17:24:11 +0100 Subject: [PATCH 541/632] fix thinko --- src/sage/rings/lazy_series.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/lazy_series.py b/src/sage/rings/lazy_series.py index 19507745273..b4c5556f9ef 100644 --- a/src/sage/rings/lazy_series.py +++ b/src/sage/rings/lazy_series.py @@ -3032,7 +3032,7 @@ def _div_(self, other): R = P._internal_poly_ring if (isinstance(left, Stream_exact) and isinstance(right, Stream_exact) - and hasattr(R, "fraction_field") + and hasattr(R.base_ring(), "fraction_field") and hasattr(R, "_gcd_univariate_polynomial")): z = R.gen() num = left._polynomial_part(R) * (1-z) + left._constant * z**left._degree From 5417e7bc571f7ba919dd2055e12301444ce01477 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 24 Nov 2022 20:48:34 +0100 Subject: [PATCH 542/632] some care for E275 (algebras, dynamics, matrix, schemes, categories) --- .../algebras/affine_nil_temperley_lieb.py | 9 +++--- .../finite_dimensional_algebra.py | 4 +-- .../lie_conformal_algebra_element.py | 12 +++---- .../algebras/steenrod/steenrod_algebra.py | 2 +- src/sage/categories/category.py | 9 +++--- .../covariant_functorial_construction.py | 11 ++++--- src/sage/categories/examples/sets_cat.py | 2 +- .../categories/examples/with_realizations.py | 4 +-- src/sage/categories/function_fields.py | 2 +- src/sage/categories/magmas.py | 4 +-- src/sage/categories/modules_with_basis.py | 4 +-- src/sage/categories/pushout.py | 2 +- .../arithmetic_dynamics/projective_ds.py | 2 +- .../matrix/matrix_integer_dense_saturation.py | 4 +-- src/sage/matrix/matrix_space.py | 32 +++++++++---------- src/sage/matrix/operation_table.py | 4 +-- .../schemes/berkovich/berkovich_cp_element.py | 4 +-- src/sage/schemes/berkovich/berkovich_space.py | 2 +- src/sage/schemes/curves/projective_curve.py | 14 ++++---- src/sage/schemes/generic/divisor.py | 4 +-- .../plane_quartics/quartic_constructor.py | 8 ++--- .../product_projective/rational_point.py | 6 ++-- src/sage/schemes/product_projective/space.py | 10 +++--- .../projective/projective_subscheme.py | 10 +++--- src/sage/schemes/toric/divisor.py | 2 +- src/sage/schemes/toric/library.py | 4 +-- 26 files changed, 85 insertions(+), 86 deletions(-) diff --git a/src/sage/algebras/affine_nil_temperley_lieb.py b/src/sage/algebras/affine_nil_temperley_lieb.py index 492043ddf7d..8aff4321abb 100644 --- a/src/sage/algebras/affine_nil_temperley_lieb.py +++ b/src/sage/algebras/affine_nil_temperley_lieb.py @@ -85,12 +85,11 @@ def _element_constructor_(self, w): a2*a1 """ W = self.weyl_group() - assert(w in W) + assert w in W word = w.reduced_word() - if all( self.has_no_braid_relation(W.from_reduced_word(word[:i]), word[i]) for i in range(len(word)) ): + if all(self.has_no_braid_relation(W.from_reduced_word(word[:i]), word[i]) for i in range(len(word))): return self.monomial(w) - else: - return self.zero() + return self.zero() @cached_method def one_basis(self): @@ -203,7 +202,7 @@ def product_on_basis(self, w, w1): ... AssertionError """ - assert(self(w) != self.zero()) + assert self(w) != self.zero() for i in w1.reduced_word(): if self.has_no_braid_relation(w, i): w = w.apply_simple_reflection(i) diff --git a/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra.py b/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra.py index 1b466d157b7..9c8090c8929 100644 --- a/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra.py +++ b/src/sage/algebras/finite_dimensional_algebras/finite_dimensional_algebra.py @@ -756,8 +756,8 @@ def maximal_ideal(self): """ if self.degree() == 0: raise ValueError("the zero algebra is not local") - if not(self.is_unitary() and self.is_commutative() - and (self._assume_associative or self.is_associative())): + if not (self.is_unitary() and self.is_commutative() + and (self._assume_associative or self.is_associative())): raise TypeError("algebra must be unitary, commutative and associative") gens = [] for x in self.gens(): diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py index b11c912f36d..912b5e1d677 100644 --- a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py @@ -174,15 +174,15 @@ def _repr_(self): if p._names: terms = [("T^({0}){1}".format(k[1], p._names[p._index_to_pos[k[0]]]),v) if k[1] > 1 \ - else("T{}".format(p._names[p._index_to_pos[k[0]]]),v) \ + else ("T{}".format(p._names[p._index_to_pos[k[0]]]),v) \ if k[1] == 1 \ else ("{}".format(p._names[p._index_to_pos[k[0]]]),v)\ for k,v in self.monomial_coefficients().items()] else: terms = [("T^({0}){1}".format(k[1], p._repr_generator(k[0])),v)\ - if k[1] > 1 else("T{}".format(p._repr_generator(k[0])),v)\ - if k[1] == 1 else ("{}".format(p._repr_generator(k[0])), - v) for k,v in self.monomial_coefficients().items()] + if k[1] > 1 else ("T{}".format(p._repr_generator(k[0])),v)\ + if k[1] == 1 else ("{}".format(p._repr_generator(k[0])),v) + for k, v in self.monomial_coefficients().items()] return repr_lincomb(terms, strip_one=True) @@ -224,13 +224,13 @@ def _latex_(self): if names: terms = [("T^{{({0})}}{1}".format(k[1], names[p._index_to_pos[k[0]]]),v) if k[1] > 1 \ - else("T{}".format(names[p._index_to_pos[k[0]]]),v)\ + else ("T{}".format(names[p._index_to_pos[k[0]]]),v)\ if k[1] == 1\ else ("{}".format(names[p._index_to_pos[k[0]]]),v)\ for k, v in self.monomial_coefficients().items()] else: terms = [("T^{{({0})}}{1}".format(k[1], latex(k[0])),v) if k[1] > 1 \ - else("T{}".format(latex(k[0])),v) if k[1] == 1 \ + else ("T{}".format(latex(k[0])),v) if k[1] == 1 \ else ("{}".format(latex(k[0])),v)\ for k, v in self.monomial_coefficients().items()] diff --git a/src/sage/algebras/steenrod/steenrod_algebra.py b/src/sage/algebras/steenrod/steenrod_algebra.py index ea2fb868c52..49b281d9a49 100644 --- a/src/sage/algebras/steenrod/steenrod_algebra.py +++ b/src/sage/algebras/steenrod/steenrod_algebra.py @@ -505,7 +505,7 @@ def __classcall__(self, p=2, basis='milnor', **kwds): std_generic = generic if p != 2: std_generic = True - if not(std_generic is True or std_generic is False): + if not (std_generic is True or std_generic is False): raise ValueError("option 'generic' is not a boolean") std_basis = get_basis_name(basis, p, generic=std_generic) diff --git a/src/sage/categories/category.py b/src/sage/categories/category.py index 02f4b292f42..f70455cf1a0 100644 --- a/src/sage/categories/category.py +++ b/src/sage/categories/category.py @@ -1886,11 +1886,10 @@ def _is_subclass(self, c): sage: Algebras(QQ)._is_subclass(ModulesWithBasis) False """ - assert( isinstance(c, Category) or (issubclass(c.__class__, type) and issubclass(c, Category)) ) + assert (isinstance(c, Category) or (issubclass(c.__class__, type) and issubclass(c, Category))) if isinstance(c, Category): return self.is_subcategory(c) - else: - return any(isinstance(cat, c) for cat in self._all_super_categories) + return any(isinstance(cat, c) for cat in self._all_super_categories) @cached_method def _meet_(self, other): @@ -2954,8 +2953,8 @@ def __init__(self, super_categories, **kwds): sage: TestSuite(C).run() """ - assert(len(super_categories) >= 2) - assert(all(not isinstance(category, JoinCategory) for category in super_categories)) + assert len(super_categories) >= 2 + assert all(not isinstance(category, JoinCategory) for category in super_categories) # Use __super_categories to not overwrite the lazy attribute Category._super_categories # Maybe this would not be needed if the flattening/sorting is does consistently? self.__super_categories = list(super_categories) diff --git a/src/sage/categories/covariant_functorial_construction.py b/src/sage/categories/covariant_functorial_construction.py index 813b66e8afd..40d661945e8 100644 --- a/src/sage/categories/covariant_functorial_construction.py +++ b/src/sage/categories/covariant_functorial_construction.py @@ -139,7 +139,7 @@ def category_from_parents(self, parents): Category of tensor products of finite dimensional vector spaces with basis over Rational Field """ from sage.structure.parent import Parent - assert(all(isinstance(parent, Parent) for parent in parents)) + assert all(isinstance(parent, Parent) for parent in parents) # Should we pass a set of categories to reduce the cache size? # But then this would impose that, for any constructor, the # category of the result does not depend on the order/repetition @@ -169,7 +169,7 @@ def category_from_categories(self, categories): sage: cartesian_product.category_from_categories((Cat1, Cat2)) Category of Cartesian products of monoids """ - assert(len(categories) > 0) + assert len(categories) > 0 return self.category_from_category(Category.meet(categories)) def category_from_category(self, category): @@ -217,11 +217,12 @@ def __call__(self, args, **kwargs): sage: tensor((E, E, E)) E # E # E """ - args = tuple(args) # a bit brute force; let's see if this becomes a bottleneck later - assert(all( hasattr(arg, self._functor_name) for arg in args)) - assert(len(args) > 0) + args = tuple(args) # a bit brute force; let's see if this becomes a bottleneck later + assert all(hasattr(arg, self._functor_name) for arg in args) + assert len(args) > 0 return getattr(args[0], self._functor_name)(*args[1:], **kwargs) + class FunctorialConstructionCategory(Category): # Should this be CategoryWithBase? """ Abstract class for categories `F_{Cat}` obtained through a diff --git a/src/sage/categories/examples/sets_cat.py b/src/sage/categories/examples/sets_cat.py index eee40b1bfbe..93cc264580d 100644 --- a/src/sage/categories/examples/sets_cat.py +++ b/src/sage/categories/examples/sets_cat.py @@ -249,7 +249,7 @@ def next(self, i): sage: x.parent() Set of prime numbers """ - assert(i in self) + assert i in self return self._from_integer_((Integer(i) + 1).next_prime()) def some_elements(self): diff --git a/src/sage/categories/examples/with_realizations.py b/src/sage/categories/examples/with_realizations.py index e72b2f5b3d0..06e060f8925 100644 --- a/src/sage/categories/examples/with_realizations.py +++ b/src/sage/categories/examples/with_realizations.py @@ -166,8 +166,8 @@ def __init__(self, R, S): From: The subset algebra of {1, 2, 3} over Rational Field in the Fundamental basis To: The subset algebra of {1, 2, 3} over Rational Field in the Out basis """ - assert(R in Rings()) - self._base = R # Won't be needed when CategoryObject won't override anymore base_ring + assert R in Rings() + self._base = R # Won't be needed when CategoryObject won't override anymore base_ring self._S = S Parent.__init__(self, category=Algebras(R).Commutative().WithRealizations()) diff --git a/src/sage/categories/function_fields.py b/src/sage/categories/function_fields.py index 3141500a511..6c30067e53f 100644 --- a/src/sage/categories/function_fields.py +++ b/src/sage/categories/function_fields.py @@ -42,7 +42,7 @@ def super_categories(self): sage: FunctionFields().super_categories() [Category of fields] """ - return[Fields()] + return [Fields()] def _call_(self, x): r""" diff --git a/src/sage/categories/magmas.py b/src/sage/categories/magmas.py index 936ada2a40e..975ea0491b3 100644 --- a/src/sage/categories/magmas.py +++ b/src/sage/categories/magmas.py @@ -1128,8 +1128,8 @@ def product(self, x, y): sage: B[3] * B[2] 4*B[2] + 6*B[3] + 5*B[6] """ - assert(x in self) - assert(y in self) + assert x in self + assert y in self return self.retract(self.lift(x) * self.lift(y)) class Realizations(RealizationsCategory): diff --git a/src/sage/categories/modules_with_basis.py b/src/sage/categories/modules_with_basis.py index 26b51ef4b87..99b57ff8c8e 100644 --- a/src/sage/categories/modules_with_basis.py +++ b/src/sage/categories/modules_with_basis.py @@ -2126,9 +2126,9 @@ def tensor(*elements): FIXME: is this a policy that we want to enforce on all parents? """ - assert(all(isinstance(element, Element) for element in elements)) + assert all(isinstance(element, Element) for element in elements) parents = [parent(element) for element in elements] - return tensor(parents)._tensor_of_elements(elements) # good name??? + return tensor(parents)._tensor_of_elements(elements) # good name ? class Homsets(HomsetsCategory): class ParentMethods: diff --git a/src/sage/categories/pushout.py b/src/sage/categories/pushout.py index 760f97a3c4f..b0945a4642b 100644 --- a/src/sage/categories/pushout.py +++ b/src/sage/categories/pushout.py @@ -1349,7 +1349,7 @@ def __ne__(self, other): sage: F != sage.categories.pushout.InfinitePolynomialFunctor(['a','b','x'],'deglex','sparse') True """ - return not(self == other) + return not (self == other) __hash__ = ConstructionFunctor.__hash__ diff --git a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py index 79a4b06ebd6..0e045aa323b 100644 --- a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py @@ -5192,7 +5192,7 @@ def multiplier_spectra(self, n, formal=False, type='point', use_algebraic_closur number_field = True if K in FiniteFields(): finite_field = True - if not(number_field or finite_field): + if not (number_field or finite_field): raise NotImplementedError('Only implemented for number fields, QQbar, finite fields, and algebraic closures of finite fields') Kbar = K.algebraic_closure() if Kbar.has_coerce_map_from(K): diff --git a/src/sage/matrix/matrix_integer_dense_saturation.py b/src/sage/matrix/matrix_integer_dense_saturation.py index 01621f6844f..a7ce81fc86a 100644 --- a/src/sage/matrix/matrix_integer_dense_saturation.py +++ b/src/sage/matrix/matrix_integer_dense_saturation.py @@ -103,12 +103,12 @@ def random_sublist_of_size(k, n): w = random_sublist_of_size(k, k - n) m = set(w) w = [z for z in range(k) if z not in m] - assert(len(w) == n) + assert len(w) == n return w randrange = current_randstate().python_random().randrange - w = set([]) + w = set() while len(w) < n: z = randrange(k) if z not in w: diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index cfb8ce85931..e889cee7905 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -2467,39 +2467,39 @@ def _test_trivial_matrices_inverse(ring, sparse=True, implementation=None, check # Check that the empty 0x0 matrix is it's own inverse with det=1. ms00 = MatrixSpace(ring, 0, 0, sparse=sparse) m00 = ms00(0) - assert(m00.determinant() == ring(1)) - assert(m00.is_invertible()) - assert(m00.inverse() == m00) + assert m00.determinant() == ring(1) + assert m00.is_invertible() + assert m00.inverse() == m00 if checkrank: - assert(m00.rank() == 0) + assert m00.rank() == 0 # Check that the empty 0x3 and 3x0 matrices are not invertible and that # computing the determinant raise the proper exception. for ms0 in [MatrixSpace(ring, 0, 3, sparse=sparse), MatrixSpace(ring, 3, 0, sparse=sparse)]: mn0 = ms0(0) - assert(not mn0.is_invertible()) + assert not mn0.is_invertible() try: d = mn0.determinant() print(d) res = False except ValueError: res = True - assert(res) + assert res try: mn0.inverse() res = False except ArithmeticError: res = True - assert(res) + assert res if checkrank: - assert(mn0.rank() == 0) + assert mn0.rank() == 0 # Check that the null 1x1 matrix is not invertible and that det=0 ms1 = MatrixSpace(ring, 1, 1, sparse=sparse) m0 = ms1(0) - assert(not m0.is_invertible()) - assert(m0.determinant() == ring(0)) + assert not m0.is_invertible() + assert m0.determinant() == ring(0) try: m0.inverse() res = False @@ -2507,18 +2507,18 @@ def _test_trivial_matrices_inverse(ring, sparse=True, implementation=None, check # FIXME: Make pynac throw a ZeroDivisionError on division by # zero instead of a runtime Error res = True - assert(res) + assert res if checkrank: - assert(m0.rank() == 0) + assert m0.rank() == 0 # Check that the identity 1x1 matrix is its own inverse with det=1 m1 = ms1(1) - assert(m1.is_invertible()) - assert(m1.determinant() == ring(1)) + assert m1.is_invertible() + assert m1.determinant() == ring(1) inv = m1.inverse() - assert(inv == m1) + assert inv == m1 if checkrank: - assert(m1.rank() == 1) + assert m1.rank() == 1 test_trivial_matrices_inverse = deprecated_function_alias(33612, _test_trivial_matrices_inverse) diff --git a/src/sage/matrix/operation_table.py b/src/sage/matrix/operation_table.py index 13f0a88822a..ea2c3aec203 100644 --- a/src/sage/matrix/operation_table.py +++ b/src/sage/matrix/operation_table.py @@ -397,7 +397,7 @@ def __init__(self, S, operation, names='letters', elements=None): # Note: there exist listable infinite objects (like ZZ) if (elements is None): if hasattr(S, 'is_finite'): - if not(S.is_finite()): + if not S.is_finite(): raise ValueError('%s is infinite' % S) try: try: @@ -411,7 +411,7 @@ def __init__(self, S, operation, names='letters', elements=None): try: for e in elements: coerced = S(e) - if not(coerced in elems): + if coerced not in elems: elems.append(coerced) except Exception: raise TypeError('unable to coerce %s into %s' % (e, S)) diff --git a/src/sage/schemes/berkovich/berkovich_cp_element.py b/src/sage/schemes/berkovich/berkovich_cp_element.py index 9587901c476..b8cc1887957 100644 --- a/src/sage/schemes/berkovich/berkovich_cp_element.py +++ b/src/sage/schemes/berkovich/berkovich_cp_element.py @@ -314,7 +314,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= center = (self._base_space)(center) except (TypeError, ValueError): raise ValueError('could not convert %s to %s' % (center, self._base_space)) - if not(center.scheme().ambient_space() is center.scheme()): + if center.scheme().ambient_space() is not center.scheme(): raise ValueError("the center of a point of projective Berkovich space cannot be " + "a point of %s" % (center.scheme())) # since we are over a field, we normalize coordinates @@ -334,7 +334,7 @@ def __init__(self, parent, center, radius=None, power=None, prec=20, space_type= raise ValueError("center in %s, should be in %s") % (center.parent(), self._base_space) else: # make sure the center is in the appropriate number field - if not(center.parent() == self._base_space): + if not (center.parent() == self._base_space): try: center = (self._base_space)(center) except (TypeError, ValueError): diff --git a/src/sage/schemes/berkovich/berkovich_space.py b/src/sage/schemes/berkovich/berkovich_space.py index 73369fda8d4..e1e6d87cea5 100644 --- a/src/sage/schemes/berkovich/berkovich_space.py +++ b/src/sage/schemes/berkovich/berkovich_space.py @@ -258,7 +258,7 @@ def __ne__(self,right): sage: B != C False """ - return not(self == right) + return not (self == right) def __hash__(self): """ diff --git a/src/sage/schemes/curves/projective_curve.py b/src/sage/schemes/curves/projective_curve.py index f9ff5944a90..e5a6ba01be1 100644 --- a/src/sage/schemes/curves/projective_curve.py +++ b/src/sage/schemes/curves/projective_curve.py @@ -434,9 +434,9 @@ def projection(self, P=None, PS=None): l = list(PP.gens()) for i in range(n + 1): l[i] = 0 - while(F(l) == 0): - l[i] = l[i] + 1 - Q = PP(l) # will be a point not on the curve + while F(l) == 0: + l[i] += 1 + Q = PP(l) # will be a point not on the curve else: # if the base ring is a finite field, iterate over all points in the ambient space and check which # are on this curve @@ -1893,20 +1893,20 @@ def rational_points_iterator(self): g10 = R(g(X,one,zero)) if g10.is_zero(): for x in K: - yield(self.point([x,one,zero])) + yield self.point([x, one, zero]) else: for x in g10.roots(multiplicities=False): - yield(self.point([x,one,zero])) + yield self.point([x, one, zero]) # points with Z = 1 for y in K: gy1 = R(g(X,y,one)) if gy1.is_zero(): for x in K: - yield(self.point([x,y,one])) + yield self.point([x, y, one]) else: for x in gy1.roots(multiplicities=False): - yield(self.point([x,y,one])) + yield self.point([x, y, one]) def _points_via_singular(self, sort=True): r""" diff --git a/src/sage/schemes/generic/divisor.py b/src/sage/schemes/generic/divisor.py index 0d0079dbc28..336fee8304f 100644 --- a/src/sage/schemes/generic/divisor.py +++ b/src/sage/schemes/generic/divisor.py @@ -433,11 +433,11 @@ def coefficient(self, P): -1 """ P = self.parent().scheme()(P) - if not(P in self.support()): + if P not in self.support(): return self.base_ring().zero() t, i = search(self.support(), P) assert t try: return self._points[i][0] except AttributeError: - raise NotImplementedError + raise NotImplementedError diff --git a/src/sage/schemes/plane_quartics/quartic_constructor.py b/src/sage/schemes/plane_quartics/quartic_constructor.py index 4b459b3c718..a0b5ce3c93a 100644 --- a/src/sage/schemes/plane_quartics/quartic_constructor.py +++ b/src/sage/schemes/plane_quartics/quartic_constructor.py @@ -51,12 +51,12 @@ def QuarticCurve(F, PP=None, check=False): """ if not is_MPolynomial(F): - raise ValueError("Argument F (=%s) must be a multivariate polynomial"%F) + raise ValueError(f"Argument F (={F}) must be a multivariate polynomial") P = F.parent() if not P.ngens() == 3: - raise ValueError("Argument F (=%s) must be a polynomial in 3 variables"%F) - if not(F.is_homogeneous() and F.degree()==4): - raise ValueError("Argument F (=%s) must be a homogeneous polynomial of degree 4"%F) + raise ValueError("Argument F (=%s) must be a polynomial in 3 variables" % F) + if not (F.is_homogeneous() and F.degree() == 4): + raise ValueError("Argument F (=%s) must be a homogeneous polynomial of degree 4" % F) if PP is not None: if not is_ProjectiveSpace(PP) and PP.dimension == 2: diff --git a/src/sage/schemes/product_projective/rational_point.py b/src/sage/schemes/product_projective/rational_point.py index 3bd717ee39e..53e08bcc171 100644 --- a/src/sage/schemes/product_projective/rational_point.py +++ b/src/sage/schemes/product_projective/rational_point.py @@ -122,7 +122,7 @@ def enum_product_projective_rational_field(X, B): (0 : 0 : 1 , 0 : 1), (0 : 0 : 1 , 1 : 1), (0 : 1 : 0 , 0 : 1), (0 : 1 : 0 , 1 : 1), (1 : -1/2 : 1 , 0 : 1), (1 : -1/2 : 1 , 1 : 1)] """ - if(is_Scheme(X)): + if is_Scheme(X): if (not is_ProductProjectiveSpaces(X.ambient_space())): raise TypeError("ambient space must be product of projective space over the rational field") X = X(X.base_ring()) @@ -225,7 +225,7 @@ def enum_product_projective_number_field(X, **kwds): tol = kwds.pop('tolerance', 1e-2) prec = kwds.pop('precision', 53) - if(is_Scheme(X)): + if is_Scheme(X): if (not is_ProductProjectiveSpaces(X.ambient_space())): raise TypeError("ambient space must be product of projective space over the rational field") X = X(X.base_ring()) @@ -281,7 +281,7 @@ def enum_product_projective_finite_field(X): sage: len(enum_product_projective_finite_field(X)) 36 """ - if(is_Scheme(X)): + if is_Scheme(X): if (not is_ProductProjectiveSpaces(X.ambient_space())): raise TypeError("ambient space must be product of projective space over the rational field") X = X(X.base_ring()) diff --git a/src/sage/schemes/product_projective/space.py b/src/sage/schemes/product_projective/space.py index 7bd6ea88ec1..07ea141dc0b 100644 --- a/src/sage/schemes/product_projective/space.py +++ b/src/sage/schemes/product_projective/space.py @@ -1259,19 +1259,19 @@ def __iter__(self): (1 : 0 : 0 , 1 : 0)] """ iters = [iter(T) for T in self._components] - L=[] + L = [] for x in iters: - L.append(next(x)) # put at zero - yield(self(L)) + L.append(next(x)) # put at zero + yield self(L) j = 0 while j < self.num_components(): try: L[j] = next(iters[j]) - yield(self(L)) + yield self(L) j = 0 except StopIteration: iters[j] = iter(self[j]) # reset - L[j] = next(iters[j]) # put at zero + L[j] = next(iters[j]) # put at zero j += 1 def rational_points(self, F=None): diff --git a/src/sage/schemes/projective/projective_subscheme.py b/src/sage/schemes/projective/projective_subscheme.py index 3c0faa498a6..c491edebea4 100644 --- a/src/sage/schemes/projective/projective_subscheme.py +++ b/src/sage/schemes/projective/projective_subscheme.py @@ -982,7 +982,7 @@ def dual(self): from sage.libs.singular.function_factory import ff K = self.base_ring() - if not(is_RationalField(K) or is_FiniteField(K)): + if not (is_RationalField(K) or is_FiniteField(K)): raise NotImplementedError("base ring must be QQ or a finite field") I = self.defining_ideal() m = I.ngens() @@ -1163,19 +1163,19 @@ def multiplicity(self, P): sage: C.multiplicity(Q) 8 """ - if not self.base_ring() in Fields(): + if self.base_ring() not in Fields(): raise TypeError("subscheme must be defined over a field") # check whether P is a point on this subscheme try: P = self(P) except TypeError: - raise TypeError("(=%s) is not a point on (=%s)"%(P,self)) + raise TypeError("(=%s) is not a point on (=%s)" % (P,self)) # find an affine chart of the ambient space of self that contains P i = 0 - while(P[i] == 0): - i = i + 1 + while P[i] == 0: + i += 1 X = self.affine_patch(i) return X.multiplicity(X(P.dehomogenize(i))) diff --git a/src/sage/schemes/toric/divisor.py b/src/sage/schemes/toric/divisor.py index f028b1d10af..8647225c77b 100644 --- a/src/sage/schemes/toric/divisor.py +++ b/src/sage/schemes/toric/divisor.py @@ -1402,7 +1402,7 @@ def _sheaf_cohomology(self, cplx): degree = h[0] + 1 cohomology_dim = h[1].dimension() if degree > d or degree < 0: - assert(cohomology_dim == 0) + assert cohomology_dim == 0 continue HH_list[degree] = cohomology_dim diff --git a/src/sage/schemes/toric/library.py b/src/sage/schemes/toric/library.py index 984243c8934..c8ecbf6a587 100644 --- a/src/sage/schemes/toric/library.py +++ b/src/sage/schemes/toric/library.py @@ -1391,7 +1391,7 @@ def WP(self, *q, **kw): Closed subscheme of 2-d toric variety covered by 3 affine patches defined by: -x^6 + z^6 + y^2 """ - if len(q)==1: + if len(q) == 1: # tuples and lists of weights are acceptable input if isinstance(q[0], (list, tuple)): q = q[0] @@ -1403,7 +1403,7 @@ def WP(self, *q, **kw): for i in range(m): try: q[i] = ZZ(q[i]) - except(TypeError): + except TypeError: raise TypeError("the weights (=%s) must be integers" % q) if q[i] <= 0: raise ValueError("the weights (=%s) must be positive integers" % q) From 68cef160ba45dc79054e1a3723f05c202a5f8da4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 24 Nov 2022 22:01:24 +0100 Subject: [PATCH 543/632] fix and activate W291 (trailing whitespace) in linter --- .vscode/settings.json | 2 +- src/conftest_inputtest.py | 2 +- src/sage/combinat/diagram.py | 2 +- src/sage/combinat/matrices/hadamard_matrix.py | 2 +- .../geometry/hyperplane_arrangement/plot.py | 4 +- src/sage/geometry/ribbon_graph.py | 42 +++++++++---------- .../triangulation/point_configuration.py | 2 +- src/sage/libs/eclib/interface.py | 35 ++++++++-------- src/sage/libs/pari/tests.py | 2 +- src/sage/logic/logic.py | 2 +- src/sage/manifolds/catalog.py | 3 +- src/sage/manifolds/differentiable/manifold.py | 2 +- .../differentiable/symplectic_form.py | 2 +- src/sage/modular/abvar/homspace.py | 2 +- src/sage/monoids/free_monoid_element.py | 8 ++-- .../numerical/backends/cvxopt_backend_test.py | 4 +- .../numerical/backends/glpk_backend_test.py | 4 +- .../numerical/backends/ppl_backend_test.py | 4 +- .../numerical/linear_tensor_constraints.py | 12 +++--- .../asymptotic_expansion_generators.py | 2 +- .../padics/eisenstein_extension_generic.py | 2 +- .../padics/unramified_extension_generic.py | 2 +- src/sage/rings/tate_algebra.py | 3 +- src/sage/schemes/affine/affine_point.py | 2 +- .../elliptic_curves/ell_modular_symbols.py | 2 +- src/sage/schemes/elliptic_curves/ell_point.py | 2 +- src/sage/schemes/product_projective/homset.py | 2 +- .../judson-abstract-algebra/boolean-sage.py | 2 +- src/sage/tests/cmdline.py | 2 +- src/sage_setup/autogen/giacpy-mkkeywords.py | 10 ++--- src/tox.ini | 5 ++- 31 files changed, 89 insertions(+), 83 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index e414fb65d7f..58c9bc7af2b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -27,7 +27,7 @@ "python.linting.enabled": true, // The following pycodestyle arguments are the same as the pycodestyle-minimal // tox environnment, see the file SAGE_ROOT/src/tox.ini - "python.linting.pycodestyleArgs": ["--select=E111,E306,E401,E701,E702,E703,W391,W605,E711,E712,E713,E721,E722"], + "python.linting.pycodestyleArgs": ["--select=E111,E306,E401,E701,E702,E703,W291,W391,W605,E711,E712,E713,E721,E722"], "cSpell.words": [ "furo", "Conda", diff --git a/src/conftest_inputtest.py b/src/conftest_inputtest.py index d3f5d5cdfb2..aeca6276691 100644 --- a/src/conftest_inputtest.py +++ b/src/conftest_inputtest.py @@ -1,6 +1,6 @@ def something(): """ a doctest in a docstring - + EXAMPLES:: sage: something() diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index a4a4169848a..c86cc4c45f4 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -856,7 +856,7 @@ def peelable_tableaux(self): For a fixed northwest diagram `D`, we say that a Young tableau `T` is `D`-peelable if: - + 1. the row indices of the cells in the first column of `D` are the entries in an initial segment in the first column of `T` and 2. the tableau `Q` obtained by removing those cells from `T` and playing diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index f4911807d9e..1fff63878e3 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -453,7 +453,7 @@ def hadamard_matrix(n,existence=False, check=True): return True M = skew_hadamard_matrix(n, check=False) elif regular_symmetric_hadamard_matrix_with_constant_diagonal(n, 1, existence=True) is True: - if existence: + if existence: return True M = regular_symmetric_hadamard_matrix_with_constant_diagonal(n, 1) else: diff --git a/src/sage/geometry/hyperplane_arrangement/plot.py b/src/sage/geometry/hyperplane_arrangement/plot.py index 9f89585578e..dc407a40ad3 100644 --- a/src/sage/geometry/hyperplane_arrangement/plot.py +++ b/src/sage/geometry/hyperplane_arrangement/plot.py @@ -103,9 +103,9 @@ sage: a.plot(hyperplane_labels=True,label_colors=['red','green','black']) # optional - sage.plot Graphics3d Object """ - from copy import copy from colorsys import hsv_to_rgb + from sage.misc.lazy_import import lazy_import lazy_import("sage.plot.plot3d.parametric_plot3d", "parametric_plot3d") lazy_import("sage.plot.plot3d.shapes2", "text3d") @@ -119,7 +119,7 @@ def plot(hyperplane_arrangement, **kwds): r""" - Return a plot of the hyperplane arrangement. + Return a plot of the hyperplane arrangement. If the arrangement is in 4 dimensions but inessential, a plot of the essentialization is returned. diff --git a/src/sage/geometry/ribbon_graph.py b/src/sage/geometry/ribbon_graph.py index d2a8d7de63a..6cc269c6b99 100644 --- a/src/sage/geometry/ribbon_graph.py +++ b/src/sage/geometry/ribbon_graph.py @@ -1,9 +1,9 @@ r""" Ribbon Graphs -This file implements objects called *ribbon graphs*. These are graphs -together with a cyclic ordering of the darts adjacent to each -vertex. This data allows us to unambiguously "thicken" the ribbon +This file implements objects called *ribbon graphs*. These are graphs +together with a cyclic ordering of the darts adjacent to each +vertex. This data allows us to unambiguously "thicken" the ribbon graph to an orientable surface with boundary. Also, every orientable surface with non-empty boundary is the thickening of a ribbon graph. @@ -103,10 +103,10 @@ class RibbonGraph(SageObject, UniqueRepresentation): **Brief introduction** - Let `\Sigma` be an orientable surface with non-empty boundary and let - `\Gamma` be the topological realization of a graph that is embedded in + Let `\Sigma` be an orientable surface with non-empty boundary and let + `\Gamma` be the topological realization of a graph that is embedded in `\Sigma` in such a way that the graph is a strong deformation retract of - the surface. + the surface. Let `v(\Gamma)` be the set of vertices of `\Gamma`, suppose that these are white vertices. Now we mark black vertices in an interior point @@ -121,7 +121,7 @@ class RibbonGraph(SageObject, UniqueRepresentation): `\Gamma` and suppose that we enumerate the set `D(\Gamma)` and that it has `n` elements. - With the orientation of the surface and the embedding of the graph in + With the orientation of the surface and the embedding of the graph in the surface we can produce two permutations: - A permutation that we denote by `\sigma`. This permutation is a @@ -139,13 +139,13 @@ class RibbonGraph(SageObject, UniqueRepresentation): .. RUBRIC:: Abstract definition - Consider a graph `\Gamma` (not a priori embedded in any surface). - Now we can again consider one vertex in the interior of each edge + Consider a graph `\Gamma` (not a priori embedded in any surface). + Now we can again consider one vertex in the interior of each edge splitting each edge in two darts. We label the darts with numbers. - We say that a ribbon structure on `\Gamma` is a set of two + We say that a ribbon structure on `\Gamma` is a set of two permutations `(\sigma, \rho)`. Where `\sigma` is formed by as many - disjoint cycles as vertices had `\Gamma`. And each cycle is a + disjoint cycles as vertices had `\Gamma`. And each cycle is a cyclic ordering of the darts adjacent to a vertex. The permutation `\rho` just tell us which two darts belong to the same edge. @@ -210,7 +210,7 @@ class RibbonGraph(SageObject, UniqueRepresentation): sage: R2.sigma() (1,3,5,8)(2,4,6) - This example is constructed by taking the bipartite graph of + This example is constructed by taking the bipartite graph of type `(3,3)`:: sage: s3 = PermutationGroupElement('(1,2,3)(4,5,6)(7,8,9)(10,11,12)(13,14,15)(16,17,18)') @@ -384,7 +384,7 @@ def number_boundaries(self): sage: R1.number_boundaries() 1 - This example is constructed by taking the bipartite graph of + This example is constructed by taking the bipartite graph of type `(3,3)`:: sage: s2 = PermutationGroupElement('(1,2,3)(4,5,6)(7,8,9)(10,11,12)(13,14,15)(16,17,18)') @@ -409,7 +409,7 @@ def contract_edge(self, k): INPUT: - - ``k`` -- non-negative integer; the position in `\rho` of the + - ``k`` -- non-negative integer; the position in `\rho` of the transposition that is going to be contracted OUTPUT: @@ -441,7 +441,7 @@ def contract_edge(self, k): ValueError: the edge is a loop and cannot be contracted In this example, we consider a graph that has one edge ``(19,20)`` - such that one of its ends is a vertex of valency 1. This is + such that one of its ends is a vertex of valency 1. This is the vertex ``(20)`` that is not specified when defining `\sigma`. We contract precisely this edge and get a ribbon graph with no vertices of valency 1:: @@ -514,7 +514,7 @@ def extrude_edge(self, vertex, dart1, dart2): OUTPUT: - A ribbon graph resulting from extruding a new edge that + A ribbon graph resulting from extruding a new edge that pulls from ``vertex`` a new vertex that is, now, adjacent to all the darts from ``dart1``to ``dart2`` (not including ``dart2``) in the cyclic ordering given by the cycle corresponding @@ -553,7 +553,7 @@ def extrude_edge(self, vertex, dart1, dart2): (1,2)(3,4)(5,6)(7,8) In the following example we first extrude one edge from a vertex - of valency 3 generating a new vertex of valency 2. Then we + of valency 3 generating a new vertex of valency 2. Then we extrude a new edge from this vertex of valency 2:: sage: s1 = PermutationGroupElement('(1,3,5)(2,4,6)') @@ -674,7 +674,7 @@ def boundary(self): OUTPUT: - A list of lists. The number of inner lists is the number of + A list of lists. The number of inner lists is the number of boundary components of the surface. Each list in the list consists of an ordered tuple of numbers, each number comes from the number assigned to the corresponding dart before @@ -878,7 +878,7 @@ def make_generic(self): def homology_basis(self): r""" - Return an oriented basis of the first homology group of the + Return an oriented basis of the first homology group of the graph. OUTPUT: @@ -1098,8 +1098,8 @@ def make_ribbon(g, r): OUTPUT: - - a ribbon graph that has 2 vertices (two non-trivial cycles - in its sigma permutation) of valency `2g + r` and it has + - a ribbon graph that has 2 vertices (two non-trivial cycles + in its sigma permutation) of valency `2g + r` and it has `2g + r` edges (and hence `4g + 2r` darts) EXAMPLES:: diff --git a/src/sage/geometry/triangulation/point_configuration.py b/src/sage/geometry/triangulation/point_configuration.py index 1bf933700c9..f970a5faf4a 100644 --- a/src/sage/geometry/triangulation/point_configuration.py +++ b/src/sage/geometry/triangulation/point_configuration.py @@ -1865,7 +1865,7 @@ def contained_simplex(self, large=True, initial_point=None, point_order=None): sage: pc.contained_simplex() (P(-1, -1), P(1, 1), P(0, 1)) sage: pc.contained_simplex(point_order = [pc[1],pc[3],pc[4],pc[2],pc[0]]) - (P(0, 1), P(1, 1), P(-1, -1)) + (P(0, 1), P(1, 1), P(-1, -1)) Lower-dimensional example:: diff --git a/src/sage/libs/eclib/interface.py b/src/sage/libs/eclib/interface.py index 119a68ba34d..0faee97661e 100644 --- a/src/sage/libs/eclib/interface.py +++ b/src/sage/libs/eclib/interface.py @@ -731,9 +731,9 @@ class mwrank_MordellWeil(SageObject): Reducing saturation bound from given value 20 to computed index bound 3 Tamagawa index primes are [ 2 ] Checking saturation at [ 2 3 ] - Checking 2-saturation + Checking 2-saturation Points were proved 2-saturated (max q used = 7) - Checking 3-saturation + Checking 3-saturation Points were proved 3-saturated (max q used = 7) done P2 = [-2:3:1] is generator number 2 @@ -741,10 +741,10 @@ class mwrank_MordellWeil(SageObject): Reducing saturation bound from given value 20 to computed index bound 4 Tamagawa index primes are [ 2 ] Checking saturation at [ 2 3 ] - Checking 2-saturation + Checking 2-saturation possible kernel vector = [1,1] This point may be in 2E(Q): [14:-52:1] - ...and it is! + ...and it is! Replacing old generator #1 with new generator [1:-1:1] Reducing index bound from 4 to 2 Points have successfully been 2-saturated (max q used = 7) @@ -756,9 +756,9 @@ class mwrank_MordellWeil(SageObject): Reducing saturation bound from given value 20 to computed index bound 3 Tamagawa index primes are [ 2 ] Checking saturation at [ 2 3 ] - Checking 2-saturation + Checking 2-saturation Points were proved 2-saturated (max q used = 11) - Checking 3-saturation + Checking 3-saturation Points were proved 3-saturated (max q used = 13) done, index = 1. P4 = [-1:3:1] = -1*P1 + -1*P2 + -1*P3 (mod torsion) @@ -911,10 +911,10 @@ def process(self, v, saturation_bound=0): The resulting points may not be p-saturated for p between this and the computed index bound 93 Tamagawa index primes are [ 2 ] Checking saturation at [ 2 ] - Checking 2-saturation + Checking 2-saturation possible kernel vector = [1,0,0] This point may be in 2E(Q): [1547:-2967:343] - ...and it is! + ...and it is! Replacing old generator #1 with new generator [-2:3:1] Reducing index bound from 93 to 46 Points have successfully been 2-saturated (max q used = 11) @@ -933,12 +933,12 @@ def process(self, v, saturation_bound=0): The resulting points may not be p-saturated for p between this and the computed index bound 46 Tamagawa index primes are [ 2 ] Checking saturation at [ 2 3 ] - Checking 2-saturation + Checking 2-saturation Points were proved 2-saturated (max q used = 11) - Checking 3-saturation + Checking 3-saturation possible kernel vector = [0,1,0] This point may be in 3E(Q): [2707496766203306:864581029138191:2969715140223272] - ...and it is! + ...and it is! Replacing old generator #2 with new generator [-14:25:8] Reducing index bound from 46 to 15 Points have successfully been 3-saturated (max q used = 13) @@ -957,14 +957,14 @@ def process(self, v, saturation_bound=0): The resulting points may not be p-saturated for p between this and the computed index bound 15 Tamagawa index primes are [ 2 ] Checking saturation at [ 2 3 5 ] - Checking 2-saturation + Checking 2-saturation Points were proved 2-saturated (max q used = 11) - Checking 3-saturation + Checking 3-saturation Points were proved 3-saturated (max q used = 13) - Checking 5-saturation + Checking 5-saturation possible kernel vector = [0,0,1] This point may be in 5E(Q): [-13422227300:-49322830557:12167000000] - ...and it is! + ...and it is! Replacing old generator #3 with new generator [1:-1:1] Reducing index bound from 15 to 3 Points have successfully been 5-saturated (max q used = 71) @@ -981,9 +981,9 @@ def process(self, v, saturation_bound=0): saturating basis...Saturation index bound (for points of good reduction) = 3 Tamagawa index primes are [ 2 ] Checking saturation at [ 2 3 ] - Checking 2-saturation + Checking 2-saturation Points were proved 2-saturated (max q used = 11) - Checking 3-saturation + Checking 3-saturation Points were proved 3-saturated (max q used = 13) done (True, 1, '[ ]') @@ -1329,6 +1329,5 @@ def points(self): P4 = [12:35:27] = 1*P1 + -1*P2 + -1*P3 (mod torsion) sage: EQ.points() [[1, -1, 1], [-2, 3, 1], [-14, 25, 8]] - """ return self.__mw.getbasis() diff --git a/src/sage/libs/pari/tests.py b/src/sage/libs/pari/tests.py index e5a2aa25172..472b1c2f66e 100644 --- a/src/sage/libs/pari/tests.py +++ b/src/sage/libs/pari/tests.py @@ -1767,7 +1767,7 @@ sage: eta1 = e.elleta(precision=150)[0] sage: eta1.sage() 3.605463601432652085915820564207726774810268996598024745444380641429820491740 # 64-bit - 3.60546360143265208591582056420772677481026899659802474544 # 32-bit + 3.60546360143265208591582056420772677481026899659802474544 # 32-bit sage: from cypari2 import Pari sage: pari = Pari() diff --git a/src/sage/logic/logic.py b/src/sage/logic/logic.py index 769fae29289..13a45b337ff 100644 --- a/src/sage/logic/logic.py +++ b/src/sage/logic/logic.py @@ -318,7 +318,7 @@ def combine(self, statement1, statement2): 'CPAREN', 'CPAREN'], {'a': 'False', 'b': 'False'}, - ['a', 'b', 'b']] + ['a', 'b', 'b']] """ toks = ['OPAREN'] + statement1[0] + ['OR'] + statement2[0] + ['CPAREN'] variables = dict(statement1[1]) diff --git a/src/sage/manifolds/catalog.py b/src/sage/manifolds/catalog.py index e71faca3072..bb09ed8885d 100644 --- a/src/sage/manifolds/catalog.py +++ b/src/sage/manifolds/catalog.py @@ -262,11 +262,12 @@ def Torus(R=2, r=1, names=None): M.induced_metric() return M + def RealProjectiveSpace(dim=2): r""" Generate projective space of dimension ``dim`` over the reals. - This is the topological space of lines through the origin in + This is the topological space of lines through the origin in `\RR^{d+1}`. The standard atlas consists of `d+2` charts, which sends the set `U_i = \{[x_1, x_2, \ldots, x_{d+1}] : x_i \neq 0 \}` to `k^{d}` by dividing by `x_i` and omitting the `i`th coordinate diff --git a/src/sage/manifolds/differentiable/manifold.py b/src/sage/manifolds/differentiable/manifold.py index d650d4eb712..e674ad8db05 100644 --- a/src/sage/manifolds/differentiable/manifold.py +++ b/src/sage/manifolds/differentiable/manifold.py @@ -3957,7 +3957,7 @@ def affine_connection(self, name, latex_name=None): AffineConnection return AffineConnection(self, name, latex_name) - def metric(self, name: str, signature: Optional[int] = None, + def metric(self, name: str, signature: Optional[int] = None, latex_name: Optional[str] = None, dest_map: Optional[DiffMap] = None) -> PseudoRiemannianMetric: r""" diff --git a/src/sage/manifolds/differentiable/symplectic_form.py b/src/sage/manifolds/differentiable/symplectic_form.py index 482f9527ba0..c72f50b70fa 100644 --- a/src/sage/manifolds/differentiable/symplectic_form.py +++ b/src/sage/manifolds/differentiable/symplectic_form.py @@ -498,7 +498,7 @@ def poisson_bracket( def volume_form(self, contra: int = 0) -> TensorField: r""" - Liouville volume form `\frac{1}{n!}\omega^n` associated with the + Liouville volume form `\frac{1}{n!}\omega^n` associated with the symplectic form `\omega`, where `2n` is the dimension of the manifold. INPUT: diff --git a/src/sage/modular/abvar/homspace.py b/src/sage/modular/abvar/homspace.py index 4698e509f86..adf9889bebe 100644 --- a/src/sage/modular/abvar/homspace.py +++ b/src/sage/modular/abvar/homspace.py @@ -350,7 +350,7 @@ def __call__(self, M, **kwds): elif M.domain() == self.domain() and M.codomain() == self.codomain(): M = M.matrix() else: - raise ValueError("cannot convert %s into %s" % (M, self)) + raise ValueError("cannot convert %s into %s" % (M, self)) elif is_Matrix(M): if M.base_ring() != ZZ: M = M.change_ring(ZZ) diff --git a/src/sage/monoids/free_monoid_element.py b/src/sage/monoids/free_monoid_element.py index f23dba93b45..23d2a8abc66 100644 --- a/src/sage/monoids/free_monoid_element.py +++ b/src/sage/monoids/free_monoid_element.py @@ -149,7 +149,7 @@ def _latex_(self): \alpha b sage: latex(b*alpha) b \alpha - sage: "%s"%latex(alpha*b) + sage: "%s" % latex(alpha*b) '\\alpha b' """ s = "" @@ -159,10 +159,10 @@ def _latex_(self): g = x[int(v[i][0])] e = v[i][1] if e == 1: - s += "%s "%(g,) + s += "%s " % (g,) else: - s += "%s^{%s}"%(g,e) - s = s.rstrip(" ") # strip the trailing whitespace caused by adding a space after each element name + s += "%s^{%s}" % (g, e) + s = s.rstrip(" ") # strip the trailing whitespace caused by adding a space after each element name if len(s) == 0: s = "1" return s diff --git a/src/sage/numerical/backends/cvxopt_backend_test.py b/src/sage/numerical/backends/cvxopt_backend_test.py index 0e7227940c1..6029221fda2 100644 --- a/src/sage/numerical/backends/cvxopt_backend_test.py +++ b/src/sage/numerical/backends/cvxopt_backend_test.py @@ -1,14 +1,16 @@ import pytest + from sage.structure.sage_object import SageObject from sage.numerical.backends.generic_backend_test import GenericBackendTests from sage.numerical.backends.generic_backend import GenericBackend from sage.numerical.mip import MixedIntegerLinearProgram + class TestCVXOPTBackend(GenericBackendTests): @pytest.fixture def backend(self) -> GenericBackend: - return MixedIntegerLinearProgram(solver="CVXOPT").get_backend() + return MixedIntegerLinearProgram(solver="CVXOPT").get_backend() def test_sage_unittest_testsuite(self, sage_object: SageObject): # TODO: Remove this test as soon as all old test methods are migrated diff --git a/src/sage/numerical/backends/glpk_backend_test.py b/src/sage/numerical/backends/glpk_backend_test.py index ec37cd25f36..b41b1ae80c8 100644 --- a/src/sage/numerical/backends/glpk_backend_test.py +++ b/src/sage/numerical/backends/glpk_backend_test.py @@ -1,10 +1,12 @@ import pytest + from sage.numerical.backends.generic_backend_test import GenericBackendTests from sage.numerical.backends.generic_backend import GenericBackend from sage.numerical.mip import MixedIntegerLinearProgram + class TestGLPKBackend(GenericBackendTests): @pytest.fixture def backend(self) -> GenericBackend: - return MixedIntegerLinearProgram(solver="GLPK").get_backend() + return MixedIntegerLinearProgram(solver="GLPK").get_backend() diff --git a/src/sage/numerical/backends/ppl_backend_test.py b/src/sage/numerical/backends/ppl_backend_test.py index ac241275ee2..852c3be82c1 100644 --- a/src/sage/numerical/backends/ppl_backend_test.py +++ b/src/sage/numerical/backends/ppl_backend_test.py @@ -1,10 +1,12 @@ import pytest + from sage.numerical.backends.generic_backend_test import GenericBackendTests from sage.numerical.backends.generic_backend import GenericBackend from sage.numerical.mip import MixedIntegerLinearProgram + class TestPPLBackend(GenericBackendTests): @pytest.fixture def backend(self) -> GenericBackend: - return MixedIntegerLinearProgram(solver="PPL").get_backend() + return MixedIntegerLinearProgram(solver="PPL").get_backend() diff --git a/src/sage/numerical/linear_tensor_constraints.py b/src/sage/numerical/linear_tensor_constraints.py index 6535867f8c8..24f94f3b3d6 100644 --- a/src/sage/numerical/linear_tensor_constraints.py +++ b/src/sage/numerical/linear_tensor_constraints.py @@ -97,7 +97,7 @@ def LinearTensorConstraintsParent(linear_functions_parent): sage: LF = LinearFunctionsParent(QQ) sage: LT = LinearTensorParent(QQ^2, LF) sage: LinearTensorConstraintsParent(LT) - Linear constraints in the tensor product of Vector space of dimension 2 + Linear constraints in the tensor product of Vector space of dimension 2 over Rational Field and Linear functions over Rational Field """ return LinearTensorConstraintsParent_class(linear_functions_parent) @@ -215,7 +215,7 @@ def lhs(self): sage: mip. = MixedIntegerLinearProgram() sage: (x[0] * vector([1,2]) == 0).lhs() - (1.0, 2.0)*x_0 + (1.0, 2.0)*x_0 """ return self._lhs @@ -250,7 +250,7 @@ def _ascii_art_(self): sage: mip. = MixedIntegerLinearProgram() sage: ascii_art(x[0] * vector([1,2]) >= 0) (0.0, 0.0) <= (1.0, 2.0)*x_0 - sage: ascii_art(x[0] * matrix([[1,2],[3,4]]) >= 0) + sage: ascii_art(x[0] * matrix([[1,2],[3,4]]) >= 0) [0 0] <= [x_0 2*x_0] [0 0] [3*x_0 4*x_0] """ @@ -313,7 +313,7 @@ class LinearTensorConstraintsParent_class(Parent): sage: p = MixedIntegerLinearProgram() sage: LT = p.linear_functions_parent().tensor(RDF^2); LT - Tensor product of Vector space of dimension 2 over Real Double + Tensor product of Vector space of dimension 2 over Real Double Field and Linear functions over Real Double Field sage: from sage.numerical.linear_tensor_constraints import LinearTensorConstraintsParent sage: LTC = LinearTensorConstraintsParent(LT); LTC @@ -342,7 +342,7 @@ def __init__(self, linear_tensor_parent): sage: LT = LinearTensorParent(RDF^2, LF) sage: from sage.numerical.linear_tensor_constraints import LinearTensorConstraintsParent sage: LinearTensorConstraintsParent(LT) - Linear constraints in the tensor product of Vector space of + Linear constraints in the tensor product of Vector space of dimension 2 over Real Double Field and Linear functions over Real Double Field """ @@ -399,7 +399,7 @@ def _repr_(self): sage: ieq = (x[0] * vector([1,2]) >= 0) sage: ieq.parent() # indirect doctests Linear constraints in the tensor product of Vector space of - dimension 2 over Real Double Field and Linear functions over + dimension 2 over Real Double Field and Linear functions over Real Double Field """ return 'Linear constraints in the tensor product of {0} and {1}'.format( diff --git a/src/sage/rings/asymptotic/asymptotic_expansion_generators.py b/src/sage/rings/asymptotic/asymptotic_expansion_generators.py index 3100ed5356f..4f4c506bc40 100644 --- a/src/sage/rings/asymptotic/asymptotic_expansion_generators.py +++ b/src/sage/rings/asymptotic/asymptotic_expansion_generators.py @@ -173,7 +173,7 @@ def Stirling(var, precision=None, skip_constant_factor=False): sage: set_series_precision(5) sage: asymptotic_expansions.Stirling('n') - sqrt(2)*sqrt(pi)*e^(n*log(n))*(e^n)^(-1)*n^(1/2) + + sqrt(2)*sqrt(pi)*e^(n*log(n))*(e^n)^(-1)*n^(1/2) + ... + O(e^(n*log(n))*(e^n)^(-1)*n^(-5/2)) sage: set_series_precision(20) # restore series precision default """ diff --git a/src/sage/rings/padics/eisenstein_extension_generic.py b/src/sage/rings/padics/eisenstein_extension_generic.py index 6473ba4d2ca..4f68e63ffae 100644 --- a/src/sage/rings/padics/eisenstein_extension_generic.py +++ b/src/sage/rings/padics/eisenstein_extension_generic.py @@ -39,7 +39,7 @@ def __init__(self, poly, prec, print_mode, names, element_class): def _extension_type(self): """ - Return the type (``Unramified``, ``Eisenstein``) of this + Return the type (``Unramified``, ``Eisenstein``) of this extension as a string, if any. Used for printing. diff --git a/src/sage/rings/padics/unramified_extension_generic.py b/src/sage/rings/padics/unramified_extension_generic.py index d8eecc36f02..7973ed814c3 100644 --- a/src/sage/rings/padics/unramified_extension_generic.py +++ b/src/sage/rings/padics/unramified_extension_generic.py @@ -56,7 +56,7 @@ def __init__(self, poly, prec, print_mode, names, element_class): def _extension_type(self): """ - Return the type (``Unramified``, ``Eisenstein``) of this + Return the type (``Unramified``, ``Eisenstein``) of this extension as a string, if any. Used for printing. diff --git a/src/sage/rings/tate_algebra.py b/src/sage/rings/tate_algebra.py index 701278b2eb1..1b601e4992a 100644 --- a/src/sage/rings/tate_algebra.py +++ b/src/sage/rings/tate_algebra.py @@ -239,7 +239,6 @@ class TateAlgebraFactory(UniqueFactory): AUTHORS: - Xavier Caruso, Thibaut Verron (2018-09) - """ def create_key(self, base, prec=None, log_radii=ZZ(0), names=None, order='degrevlex'): """ @@ -251,7 +250,7 @@ def create_key(self, base, prec=None, log_radii=ZZ(0), names=None, order='degrev - ``prec`` -- an integer or ``None`` (default: ``None``) - - ``log_radii`` -- an integer or a list or a tuple of integers + - ``log_radii`` -- an integer or a list or a tuple of integers (default: ``0``) - ``names`` -- names of the indeterminates diff --git a/src/sage/schemes/affine/affine_point.py b/src/sage/schemes/affine/affine_point.py index bf86bc225c6..72feecf97f8 100644 --- a/src/sage/schemes/affine/affine_point.py +++ b/src/sage/schemes/affine/affine_point.py @@ -105,7 +105,7 @@ def _matrix_times_point_(self, mat, dom): - ``mat`` -- a matrix - - ``dom`` -- (unused) needed for consistent function call with projective + - ``dom`` -- (unused) needed for consistent function call with projective OUTPUT: a scheme point given by ``mat*self`` diff --git a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py index 72489b121f9..2b23f6c7555 100644 --- a/src/sage/schemes/elliptic_curves/ell_modular_symbols.py +++ b/src/sage/schemes/elliptic_curves/ell_modular_symbols.py @@ -8,7 +8,7 @@ subspace on which complex conjugation acts as multiplication by `+1` and one on which it acts by `-1`. -There are three implementations of modular symbols, two within +There are three implementations of modular symbols, two within ``Sage`` and one in Cremona's ``eclib`` library. One can choose here which one is used. diff --git a/src/sage/schemes/elliptic_curves/ell_point.py b/src/sage/schemes/elliptic_curves/ell_point.py index 4587756b06d..9b870b5df9f 100644 --- a/src/sage/schemes/elliptic_curves/ell_point.py +++ b/src/sage/schemes/elliptic_curves/ell_point.py @@ -3559,7 +3559,7 @@ def discrete_log(self, Q, ord=None): - Otherwise (if this test is inconclusive), check that the Weil pairing of `P` and `Q` is trivial. - For anomalous curves with `\#E = p`, the + For anomalous curves with `\#E = p`, the :meth:`padic_elliptic_logarithm` function is called. INPUT: diff --git a/src/sage/schemes/product_projective/homset.py b/src/sage/schemes/product_projective/homset.py index 7f4e1de0773..28aa566bb73 100644 --- a/src/sage/schemes/product_projective/homset.py +++ b/src/sage/schemes/product_projective/homset.py @@ -146,7 +146,7 @@ def points(self, **kwds): (1 : 0 : 0 , 0 : 1), (1 : 0 : 0 , 1 : 0), (1 : 0 : 0 , 1 : 1), (1 : 0 : 0 , 2 : 1), (1 : 0 : 1 , 0 : 1), (1 : 0 : 1 , 1 : 0), (1 : 0 : 1 , 1 : 1), (1 : 0 : 1 , 2 : 1), (1 : 1 : 0 , 0 : 1), (1 : 1 : 0 , 1 : 0), (1 : 1 : 0 , 1 : 1), (1 : 1 : 0 , 2 : 1), - (1 : 1 : 1 , 0 : 1), (1 : 1 : 1 , 1 : 0), (1 : 1 : 1 , 1 : 1), (1 : 1 : 1 , 2 : 1), + (1 : 1 : 1 , 0 : 1), (1 : 1 : 1 , 1 : 0), (1 : 1 : 1 , 1 : 1), (1 : 1 : 1 , 2 : 1), (1 : 2 : 1 , 0 : 1), (1 : 2 : 1 , 1 : 0), (1 : 2 : 1 , 1 : 1), (1 : 2 : 1 , 2 : 1), (2 : 0 : 1 , 0 : 1), (2 : 0 : 1 , 1 : 0), (2 : 0 : 1 , 1 : 1), (2 : 0 : 1 , 2 : 1), (2 : 1 : 0 , 0 : 1), (2 : 1 : 0 , 1 : 0), (2 : 1 : 0 , 1 : 1), (2 : 1 : 0 , 2 : 1), diff --git a/src/sage/tests/books/judson-abstract-algebra/boolean-sage.py b/src/sage/tests/books/judson-abstract-algebra/boolean-sage.py index 460403b3efb..2d7b13c00f7 100644 --- a/src/sage/tests/books/judson-abstract-algebra/boolean-sage.py +++ b/src/sage/tests/books/judson-abstract-algebra/boolean-sage.py @@ -152,7 +152,7 @@ ~~~~~~~~~~~~~~~~~~~~~~ :: sage: linear = P.linear_extension(); linear - [18, 9, 11, 6, 10, 0, 2, 1, 8, 3, 15, + [18, 9, 11, 6, 10, 0, 2, 1, 8, 3, 15, 7, 4, 14, 5, 19, 16, 13, 17, 12] ~~~~~~~~~~~~~~~~~~~~~~ :: diff --git a/src/sage/tests/cmdline.py b/src/sage/tests/cmdline.py index 1570b9c9b6a..12e73393206 100644 --- a/src/sage/tests/cmdline.py +++ b/src/sage/tests/cmdline.py @@ -725,7 +725,7 @@ def test_executable(args, input="", timeout=100.0, pydebug_ignore_warnings=False except KeyError: pass - __with_pydebug = hasattr(sys, 'gettotalrefcount') # This is a Python debug build (--with-pydebug) + __with_pydebug = hasattr(sys, 'gettotalrefcount') # This is a Python debug build (--with-pydebug) if __with_pydebug and pydebug_ignore_warnings: pexpect_env['PYTHONWARNINGS'] = ','.join([ 'ignore::DeprecationWarning', diff --git a/src/sage_setup/autogen/giacpy-mkkeywords.py b/src/sage_setup/autogen/giacpy-mkkeywords.py index 3a918b20aeb..8c5c1653b88 100644 --- a/src/sage_setup/autogen/giacpy-mkkeywords.py +++ b/src/sage_setup/autogen/giacpy-mkkeywords.py @@ -1,6 +1,6 @@ -#see the discussion in trac #29171 about where to store this file. +#see the discussion in trac #29171 about where to store this file. """ -This file is to help the maintainer to upgrade the giac keywords in +This file is to help the maintainer to upgrade the giac keywords in giacpy or giacpy_sage or sage.libs.giac @@ -12,14 +12,14 @@ It should not be used on the fly for an automatic installation script, because auto-methods.pxi is -quite long to be built and adding new giac keywords often break the built of giacpy +quite long to be built and adding new giac keywords often break the built of giacpy while not implementing a new giac keyword doesn't break the giacpy built. newkeywords.pxi is just created to check things manually but is not used in iacpy or sage.libs.giac AUTHORS: -- Frederic Han (2020-07) +- Frederic Han (2020-07) """ from subprocess import PIPE, Popen @@ -89,7 +89,7 @@ Mi.write(s) Mi.close() - + # building keywords.pxi with open("keywords.pxi", "w") as Fi: Fi.write("# file auto generated by mkkeywords.py\n") diff --git a/src/tox.ini b/src/tox.ini index 1fdf5230aae..216d2376511 100644 --- a/src/tox.ini +++ b/src/tox.ini @@ -103,14 +103,15 @@ description = # E703: statement ends with a semicolon # E711: comparison to None should be ‘if cond is None:’ # E712: comparison to True should be ‘if cond is True:’ or ‘if cond:’ - # E713 test for membership should be 'not in' + # E713 test for membership should be ’not in’ # E721: do not compare types, use isinstance() # E722: do not use bare except, specify exception instead + # W291: trailing whitespace # W391: blank line at end of file # W605: invalid escape sequence ‘x’ # See https://pycodestyle.pycqa.org/en/latest/intro.html#error-codes deps = pycodestyle -commands = pycodestyle --select E111,E306,E401,E701,E702,E703,W391,W605,E711,E712,E713,E721,E722 {posargs:{toxinidir}/sage/} +commands = pycodestyle --select E111,E306,E401,E701,E702,E703,W291,W391,W605,E711,E712,E713,E721,E722 {posargs:{toxinidir}/sage/} pycodestyle --select E111,E306,E401,E703,W605,E712,E713,E721,E722 --filename *.pyx {posargs:{toxinidir}/sage/} [pycodestyle] From f9135f002b3a08e55c0ec74c349f7ba1721e20a6 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Fri, 25 Nov 2022 15:58:11 +0800 Subject: [PATCH 544/632] streamline .set_order() for elliptic curves and points --- .../elliptic_curves/ell_finite_field.py | 46 ++++++++++--------- src/sage/schemes/elliptic_curves/ell_point.py | 36 +++++++++------ 2 files changed, 46 insertions(+), 36 deletions(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index a8235585b17..f489f51b5a4 100644 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -958,25 +958,25 @@ def abelian_group(self): if len(gens) == 2: P, Q = gens - n = self.cardinality() # cached - n1 = P.order() # cached + n = self.cardinality() # cached + n1 = P.order() # cached n2 = n//n1 - assert not n1 * Q # PARI should guarantee this + assert not n1 * Q # PARI should guarantee this k = n1.prime_to_m_part(n2) - Q *= k # don't need; kill that part + Q *= k # don't need; kill that part nQ = n2 * generic.order_from_multiple(n2*Q, n1//k//n2) S = n//nQ * P T = n2 * Q - S.set_order(nQ//n2) # for .discrete_log() + S.set_order(nQ//n2, check=False) # for .discrete_log() x = S.discrete_log(T) Q -= x * n1//nQ * P - Q.set_order(n2) # verifies n2*Q == 0 + Q.set_order(n2, check=False) # verifies n2*Q == 0 gens = P, Q - orders = [T.order() for T in gens] # cached + orders = [T.order() for T in gens] # cached self.gens.set_cache(gens) return AdditiveAbelianGroupWrapper(self.point_homset(), gens, orders) @@ -1158,7 +1158,7 @@ def is_ordinary(self, proof=True): """ return not is_j_supersingular(self.j_invariant(), proof=proof) - def set_order(self, value, num_checks=8): + def set_order(self, value, *, check=True, num_checks=8): r""" Set the value of self._order to value. @@ -1170,9 +1170,12 @@ def set_order(self, value, num_checks=8): - ``value`` -- integer in the Hasse-Weil range for this curve. - - ``num_checks`` (integer, default: 8) -- number of times to - check whether value*(a random point on this curve) is - equal to the identity. + - ``check`` (boolean, default: ``True``) -- whether or + not to run sanity checks on the input. + + - ``num_checks`` (integer, default: 8) -- if ``check`` is + ``True``, the number of times to check whether ``value`` + times a random point on this curve equals the identity. OUTPUT: @@ -1270,16 +1273,17 @@ def set_order(self, value, num_checks=8): """ value = Integer(value) - # Is value in the Hasse range? - q = self.base_field().order() - a,b = Hasse_bounds(q,1) - if not a <= value <= b: - raise ValueError('Value %s illegal (not an integer in the Hasse range)' % value) - # Is value*random == identity? - for i in range(num_checks): - G = self.random_point() - if value * G != self(0): - raise ValueError('Value %s illegal (multiple of random point not the identity)' % value) + if check: + # Is value in the Hasse range? + q = self.base_field().order() + a,b = Hasse_bounds(q,1) + if not a <= value <= b: + raise ValueError('Value %s illegal (not an integer in the Hasse range)' % value) + # Is value*random == identity? + for i in range(num_checks): + G = self.random_point() + if value * G != self(0): + raise ValueError('Value %s illegal (multiple of random point not the identity)' % value) # TODO: It might help some of PARI's algorithms if we # could copy this over to the .pari_curve() as well. diff --git a/src/sage/schemes/elliptic_curves/ell_point.py b/src/sage/schemes/elliptic_curves/ell_point.py index 4587756b06d..e09f557a94b 100644 --- a/src/sage/schemes/elliptic_curves/ell_point.py +++ b/src/sage/schemes/elliptic_curves/ell_point.py @@ -486,8 +486,13 @@ def order(self): sage: E(0).order() == 1 True """ + try: + return self._order + except AttributeError: + pass if self.is_zero(): - return Integer(1) + self._order = Integer(1) + return self._order raise NotImplementedError("Computation of order of a point " "not implemented over general fields.") @@ -1172,7 +1177,7 @@ def _divide_out(self, p): pts = Q.division_points(p) return (Q, k) - def set_order(self, value): + def set_order(self, value, *, check=True): r""" Set the value of self._order to value. @@ -1193,7 +1198,7 @@ def set_order(self, value): :: - sage: E = EllipticCurve(GF(7), [0, 1]) # This curve has order 6 + sage: E = EllipticCurve(GF(7), [0, 1]) # This curve has order 12 sage: G = E(5, 0) sage: G.set_order(2) sage: 2*G @@ -1221,7 +1226,7 @@ def set_order(self, value): It is an error to pass a `value` equal to `0`:: - sage: E = EllipticCurve(GF(7), [0, 1]) # This curve has order 6 + sage: E = EllipticCurve(GF(7), [0, 1]) # This curve has order 12 sage: G = E.random_point() sage: G.set_order(0) Traceback (most recent call last): @@ -1236,7 +1241,7 @@ def set_order(self, value): order of this point. How unlikely is determined by the factorization of the actual order, and the actual group structure:: - sage: E = EllipticCurve(GF(7), [0, 1]) # This curve has order 6 + sage: E = EllipticCurve(GF(7), [0, 1]) # This curve has order 12 sage: G = E(5, 0) # G has order 2 sage: G.set_order(11) Traceback (most recent call last): @@ -1247,7 +1252,7 @@ def set_order(self, value): of interest". For instance, the order can be set to a multiple the actual order:: - sage: E = EllipticCurve(GF(7), [0, 1]) # This curve has order 6 + sage: E = EllipticCurve(GF(7), [0, 1]) # This curve has order 12 sage: G = E(5, 0) # G has order 2 sage: G.set_order(8) sage: G.order() @@ -1259,15 +1264,16 @@ def set_order(self, value): """ value = Integer(value) - E = self.curve() - q = E.base_ring().order() - if value <= 0: - raise ValueError('Value %s illegal for point order' % value) - low, hi = Hasse_bounds(q) - if value > hi: - raise ValueError('Value %s illegal: outside max Hasse bound' % value) - if value * self != E(0): - raise ValueError('Value %s illegal: %s * %s is not the identity' % (value, value, self)) + if check: + if value <= 0: + raise ValueError('Value %s illegal for point order' % value) + E = self.curve() + q = E.base_ring().cardinality() + low, hi = Hasse_bounds(q) + if value > hi: + raise ValueError('Value %s illegal: outside max Hasse bound' % value) + if value * self != E(0): + raise ValueError('Value %s illegal: %s * %s is not the identity' % (value, value, self)) self._order = value # ############################# end ################################ From f4f2cd31a3fc118ac7b6278dac8b8bb8acb09d02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 25 Nov 2022 09:34:09 +0100 Subject: [PATCH 545/632] preserve sparsity through change_ring --- src/sage/modules/free_module.py | 23 ++++++++++++++++------- src/sage/modules/free_module_element.pyx | 10 ++++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/sage/modules/free_module.py b/src/sage/modules/free_module.py index 47fb713d522..7ad14bc1c8b 100644 --- a/src/sage/modules/free_module.py +++ b/src/sage/modules/free_module.py @@ -5681,7 +5681,9 @@ def echelonized_basis(self): def change_ring(self, R): """ - Return the ambient free module over R of the same rank as self. + Return the ambient free module over ``R`` of the same rank as ``self``. + + This also preserves the sparsity. EXAMPLES:: @@ -5690,20 +5692,27 @@ def change_ring(self, R): sage: A = ZZ^3; A.change_ring(GF(5)) Vector space of dimension 3 over Finite Field of size 5 - For ambient modules any change of rings is defined. - - :: + For ambient modules any change of rings is defined:: sage: A = GF(5)**3; A.change_ring(QQ) Vector space of dimension 3 over Rational Field + + TESTS: + + Check for :trac:`29630`:: + + sage: V = VectorSpace(QQ, 2, sparse=True) + sage: V.change_ring(RR).is_sparse() + True """ if self.base_ring() is R: return self from .free_quadratic_module import is_FreeQuadraticModule if is_FreeQuadraticModule(self): - return FreeModule(R, self.rank(), inner_product_matrix=self.inner_product_matrix()) - else: - return FreeModule(R, self.rank()) + return FreeModule(R, self.rank(), + inner_product_matrix=self.inner_product_matrix(), + sparse=self.is_sparse()) + return FreeModule(R, self.rank(), sparse=self.is_sparse()) def linear_combination_of_basis(self, v): """ diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index c1e1424bfe8..0b8685c28e2 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -1536,10 +1536,20 @@ cdef class FreeModuleElement(Vector): # abstract base class sage: v = vector(QQ['x,y'], [1..5]); v.change_ring(GF(3)) (1, 2, 0, 1, 2) + + TESTS: + + Check for :trac:`29630`:: + + sage: v = vector(QQ, 4, {0:1}, sparse=True) + sage: v.change_ring(AA).is_sparse() + True """ if self.base_ring() is R: return self M = self._parent.change_ring(R) + if M.is_sparse(): + return M(self.dict(), sparse=True, coerce=True) return M(self.list(), coerce=True) def coordinate_ring(self): From 37067d08b700548ddd06b7a8ccd7f3d1452d08e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 25 Nov 2022 09:45:56 +0100 Subject: [PATCH 546/632] more tweaks (unrelated) --- .../lie_conformal_algebra_element.py | 45 +++++++++---------- 1 file changed, 20 insertions(+), 25 deletions(-) diff --git a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py index 912b5e1d677..c40e654e157 100644 --- a/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py +++ b/src/sage/algebras/lie_conformal_algebras/lie_conformal_algebra_element.py @@ -14,7 +14,6 @@ # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** - from sage.arith.all import factorial from sage.misc.misc_c import prod from sage.misc.repr import repr_lincomb @@ -27,7 +26,7 @@ class LCAWithGeneratorsElement(IndexedFreeModuleElement): The element class of a Lie conformal algebra with a preferred set of generators. """ - def T(self,n=1): + def T(self, n=1): r""" The n-th derivative of this element. @@ -135,7 +134,7 @@ def _bracket_(self, right): /factorial(m+k+j-l)/factorial(l-k-j)/factorial(j)*\ mbr[j].T(m+k+j-l) for j in mbr if j >= l-m-k and\ j <= l-k) for l in range(m+k+pole+1)} - return {k:v for k,v in ret.items() if v} + return {k: v for k, v in ret.items() if v} diclist = [i._bracket_(j) for i in self.terms() for j in right.terms()] @@ -143,8 +142,8 @@ def _bracket_(self, right): pz = p.zero() for d in diclist: for k in d: - ret[k] = ret.get(k,pz) + d[k] - return {k:v for k,v in ret.items() if v} + ret[k] = ret.get(k, pz) + d[k] + return {k: v for k, v in ret.items() if v} def _repr_(self): r""" @@ -172,17 +171,15 @@ def _repr_(self): return "0" p = self.parent() if p._names: - terms = [("T^({0}){1}".format(k[1], - p._names[p._index_to_pos[k[0]]]),v) if k[1] > 1 \ - else ("T{}".format(p._names[p._index_to_pos[k[0]]]),v) \ - if k[1] == 1 \ - else ("{}".format(p._names[p._index_to_pos[k[0]]]),v)\ - for k,v in self.monomial_coefficients().items()] + terms = [("T^({}){}".format(k1, p._names[p._index_to_pos[k0]]), v) if k1 > 1 + else ("T{}".format(p._names[p._index_to_pos[k0]]), v) if k1 == 1 + else ("{}".format(p._names[p._index_to_pos[k0]]), v) + for (k0, k1), v in self.monomial_coefficients().items()] else: - terms = [("T^({0}){1}".format(k[1], p._repr_generator(k[0])),v)\ - if k[1] > 1 else ("T{}".format(p._repr_generator(k[0])),v)\ - if k[1] == 1 else ("{}".format(p._repr_generator(k[0])),v) - for k, v in self.monomial_coefficients().items()] + terms = [("T^({}){}".format(k1, p._repr_generator(k0)), v) if k1 > 1 + else ("T{}".format(p._repr_generator(k0)), v) if k1 == 1 + else ("{}".format(p._repr_generator(k0)), v) + for (k0, k1), v in self.monomial_coefficients().items()] return repr_lincomb(terms, strip_one=True) @@ -222,16 +219,14 @@ def _latex_(self): except ValueError: names = None if names: - terms = [("T^{{({0})}}{1}".format(k[1], - names[p._index_to_pos[k[0]]]),v) if k[1] > 1 \ - else ("T{}".format(names[p._index_to_pos[k[0]]]),v)\ - if k[1] == 1\ - else ("{}".format(names[p._index_to_pos[k[0]]]),v)\ - for k, v in self.monomial_coefficients().items()] + terms = [("T^{{({0})}}{1}".format(k1, names[p._index_to_pos[k0]]), v) if k1 > 1 + else ("T{}".format(names[p._index_to_pos[k0]]), v) if k1 == 1 + else ("{}".format(names[p._index_to_pos[k0]]), v) + for (k0, k1), v in self.monomial_coefficients().items()] else: - terms = [("T^{{({0})}}{1}".format(k[1], latex(k[0])),v) if k[1] > 1 \ - else ("T{}".format(latex(k[0])),v) if k[1] == 1 \ - else ("{}".format(latex(k[0])),v)\ - for k, v in self.monomial_coefficients().items()] + terms = [("T^{{({0})}}{1}".format(k1, latex(k0)), v) if k1 > 1 + else ("T{}".format(latex(k0)), v) if k1 == 1 + else ("{}".format(latex(k0)), v) + for (k0, k1), v in self.monomial_coefficients().items()] return repr_lincomb(terms, is_latex=True, strip_one=True) From 12411a06646cfc571b03b197f440c78382c086d7 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Fri, 25 Nov 2022 18:09:31 +0800 Subject: [PATCH 547/632] deprecate .fetch_int() and .integer_representation() --- src/sage/crypto/mq/rijndael_gf.py | 4 +-- src/sage/crypto/mq/sr.py | 30 +++++++++---------- src/sage/libs/ntl/ntl_GF2X.pyx | 2 +- src/sage/libs/ntl/ntl_mat_GF2E.pyx | 2 +- src/sage/matrix/matrix_gf2e_dense.pyx | 2 +- src/sage/matrix/matrix_gfpn_dense.pyx | 12 ++++---- src/sage/rings/finite_rings/element_base.pyx | 10 +++---- .../rings/finite_rings/element_givaro.pyx | 12 ++++---- .../rings/finite_rings/element_ntl_gf2e.pyx | 14 ++++----- .../rings/finite_rings/finite_field_givaro.py | 9 ++++-- .../finite_rings/finite_field_ntl_gf2e.py | 9 ++++-- 11 files changed, 55 insertions(+), 51 deletions(-) diff --git a/src/sage/crypto/mq/rijndael_gf.py b/src/sage/crypto/mq/rijndael_gf.py index 619026bfe96..154c84d146c 100644 --- a/src/sage/crypto/mq/rijndael_gf.py +++ b/src/sage/crypto/mq/rijndael_gf.py @@ -785,7 +785,7 @@ def _GF_to_hex(self, GF): not GF.parent().order() == 2**8: msg = "keyword 'GF' must be in" raise TypeError(msg.format(self._F)) - return hex(GF.integer_representation())[2:].zfill(2) + return hex(GF.to_integer())[2:].zfill(2) def _bin_to_GF(self, B, matrix=True): r""" @@ -911,7 +911,7 @@ def _GF_to_bin(self, GF): not GF.parent().order() == 2**8: msg = "keyword 'GF' must be in" raise TypeError(msg.format(self._F)) - return bin(GF.integer_representation())[2:].zfill(8) + return bin(GF.to_integer())[2:].zfill(8) def encrypt(self, plain, key, format='hex'): r""" diff --git a/src/sage/crypto/mq/sr.py b/src/sage/crypto/mq/sr.py index 1b4a94824dc..594c09030b9 100644 --- a/src/sage/crypto/mq/sr.py +++ b/src/sage/crypto/mq/sr.py @@ -762,9 +762,9 @@ def sub_byte(self, b): # constant addition if e == 4: - b = b + k.fetch_int(6) + b = b + k.from_integer(6) elif e == 8: - b = b + k.fetch_int(99) + b = b + k.from_integer(99) return b @@ -782,9 +782,9 @@ def sbox_constant(self): """ k = self.k if self.e == 4: - return k.fetch_int(6) + return k.from_integer(6) elif self.e == 8: - return k.fetch_int(99) + return k.from_integer(99) else: raise TypeError("sbox constant only defined for e in (4, 8)") @@ -1006,7 +1006,7 @@ def state_array(self, d=None): sage: sr = mq.SR(2, 2, 2, 4) sage: k = sr.base_ring() - sage: e1 = [k.fetch_int(e) for e in range(2*2)]; e1 + sage: e1 = [k.from_integer(e) for e in range(2*2)]; e1 [0, 1, a, a + 1] sage: e2 = sr.phi( Matrix(k, 2*2, 1, e1) ) sage: sr.state_array(e1) # note the column major ordering @@ -1229,8 +1229,8 @@ def __call__(self, P, K): sage: sr = mq.SR(10, 4, 4, 8, star=True, allow_zero_inversions=True) sage: k = sr.base_ring() - sage: plaintext = sr.state_array([k.fetch_int(e) for e in range(16)]) - sage: key = sr.state_array([k.fetch_int(e) for e in range(16)]) + sage: plaintext = sr.state_array([k.from_integer(e) for e in range(16)]) + sage: key = sr.state_array([k.from_integer(e) for e in range(16)]) sage: print(sr.hex_str_matrix( sr(plaintext, key) )) 0A 41 F1 C6 94 6E C3 53 @@ -1301,9 +1301,9 @@ def __call__(self, P, K): F = self.base_ring() if isinstance(P, str): - P = self.state_array([F.fetch_int(ZZ(P[i:i+2], 16)) for i in range(0, len(P), 2)]) + P = self.state_array([F.from_integer(ZZ(P[i:i+2], 16)) for i in range(0, len(P), 2)]) if isinstance(K, str): - K = self.state_array([F.fetch_int(ZZ(K[i:i+2], 16)) for i in range(0, len(K), 2)]) + K = self.state_array([F.from_integer(ZZ(K[i:i+2], 16)) for i in range(0, len(K), 2)]) if self.is_state_array(P) and self.is_state_array(K): _type = self.state_array @@ -1433,9 +1433,9 @@ def hex_str_matrix(self, M): for x in range(M.nrows()): for y in range(M.ncols()): if e == 8: - st.append("%02X" % M[x, y].integer_representation()) + st.append("%02X" % M[x, y].to_integer()) else: - st.append("%X" % M[x, y].integer_representation()) + st.append("%X" % M[x, y].to_integer()) st.append("\n") return " ".join(st) @@ -1464,9 +1464,9 @@ def hex_str_vector(self, M): for y in range(M.ncols()): for x in range(M.nrows()): if e == 8: - st.append("%02X" % M[x, y].integer_representation()) + st.append("%02X" % M[x, y].to_integer()) else: - st.append("%X" % M[x, y].integer_representation()) + st.append("%X" % M[x, y].to_integer()) #st.append("\n") return "".join(st) @@ -2321,13 +2321,13 @@ def lin_matrix(self, length = None): lin = Matrix(self.base_ring(), length*e, length*e) if e == 4: - l = [ k.fetch_int(x) for x in (5, 1, 12, 5) ] + l = [ k.from_integer(x) for x in (5, 1, 12, 5) ] for k in range( 0, length ): for i in range(0, 4): for j in range(0, 4): lin[k*4+j, k*4+i] = l[(i-j)%4] ** (2**j) elif e == 8: - l = [ k.fetch_int(x) for x in (5, 9, 249, 37, 244, 1, 181, 143) ] + l = [ k.from_integer(x) for x in (5, 9, 249, 37, 244, 1, 181, 143) ] for k in range( 0, length ): for i in range(0, 8): for j in range(0, 8): diff --git a/src/sage/libs/ntl/ntl_GF2X.pyx b/src/sage/libs/ntl/ntl_GF2X.pyx index aea1fcf144e..8b809ebbb63 100644 --- a/src/sage/libs/ntl/ntl_GF2X.pyx +++ b/src/sage/libs/ntl/ntl_GF2X.pyx @@ -160,7 +160,7 @@ cdef class ntl_GF2X(): if x.characteristic() == 2: x = list(x.modulus()) elif isinstance(x, FiniteField_givaroElement): - x = "0x"+hex(x.integer_representation())[2:][::-1] + x = "0x"+hex(x.to_integer())[2:][::-1] elif isinstance(x, FiniteField_ntl_gf2eElement): x = x.polynomial().list() s = str(x).replace(","," ") diff --git a/src/sage/libs/ntl/ntl_mat_GF2E.pyx b/src/sage/libs/ntl/ntl_mat_GF2E.pyx index 574ff43c213..f167418ee0a 100644 --- a/src/sage/libs/ntl/ntl_mat_GF2E.pyx +++ b/src/sage/libs/ntl/ntl_mat_GF2E.pyx @@ -78,7 +78,7 @@ cdef class ntl_mat_GF2E(): [0x0 0x0 0x0 0x0 0x0] [0x0 0x0 0x0 0x0 0x0] ] - sage: A= matrix(k,5,5,[k.fetch_int(_%(2^4)) for _ in range(25)]) + sage: A = matrix(k, 5,5, [k.from_integer(i % 2^4) for i in range(25)]) sage: ntl.mat_GF2E(ctx, A) [[0x0 0x1 0x2 0x3 0x4] [0x5 0x6 0x7 0x8 0x9] diff --git a/src/sage/matrix/matrix_gf2e_dense.pyx b/src/sage/matrix/matrix_gf2e_dense.pyx index a35a149db56..9d51a80f993 100644 --- a/src/sage/matrix/matrix_gf2e_dense.pyx +++ b/src/sage/matrix/matrix_gf2e_dense.pyx @@ -129,7 +129,7 @@ cdef class M4RIE_finite_field: gf2e_free(self.ff) cdef m4ri_word poly_to_word(f): - return f.integer_representation() + return f.to_integer() cdef class Matrix_gf2e_dense(matrix_dense.Matrix_dense): diff --git a/src/sage/matrix/matrix_gfpn_dense.pyx b/src/sage/matrix/matrix_gfpn_dense.pyx index b3a61ae417e..1f9a3ecf725 100644 --- a/src/sage/matrix/matrix_gfpn_dense.pyx +++ b/src/sage/matrix/matrix_gfpn_dense.pyx @@ -105,11 +105,11 @@ cdef class FieldConverter_class: sage: C = M._converter sage: C.fel_to_field(15) 3*y - sage: F.fetch_int(15) + sage: F.from_integer(15) 3*y sage: C.field_to_fel(y) 5 - sage: y.integer_representation() + sage: y.to_integer() 5 """ def __init__(self, field): @@ -139,7 +139,7 @@ cdef class FieldConverter_class: sage: C = FieldConverter_class(F) sage: C.fel_to_field(15) 3*y - sage: F.fetch_int(15) + sage: F.from_integer(15) 3*y """ return self.field(FfToInt(x)) @@ -155,7 +155,7 @@ cdef class FieldConverter_class: sage: C = FieldConverter_class(F) sage: C.field_to_fel(y) 5 - sage: y.integer_representation() + sage: y.to_integer() 5 TESTS: @@ -165,9 +165,9 @@ cdef class FieldConverter_class: sage: C.field_to_fel('foo') Traceback (most recent call last): ... - AttributeError: 'str' object has no attribute 'integer_representation' + AttributeError: 'str' object has no attribute 'to_integer' """ - return FfFromInt(x.integer_representation()) + return FfFromInt(x.to_integer()) cdef class PrimeFieldConverter_class(FieldConverter_class): diff --git a/src/sage/rings/finite_rings/element_base.pyx b/src/sage/rings/finite_rings/element_base.pyx index 5ac383f9ea4..7aef260b807 100755 --- a/src/sage/rings/finite_rings/element_base.pyx +++ b/src/sage/rings/finite_rings/element_base.pyx @@ -21,6 +21,7 @@ from sage.structure.element cimport Element from sage.structure.parent cimport Parent from sage.rings.integer_ring import ZZ from sage.rings.integer import Integer +from sage.misc.superseded import deprecated_function_alias def is_FiniteFieldElement(x): """ @@ -993,11 +994,6 @@ cdef class FinitePolyExtElement(FiniteRingElement): Inverse of :meth:`sage.rings.finite_rings.finite_field_base.FiniteField.from_integer`. - .. SEEALSO:: - - - :meth:`sage.rings.finite_rings.element_givaro.FiniteField_givaroElement.integer_representation` - - :meth:`sage.rings.finite_rings.element_ntl_gf2e.FiniteField_ntl_gf2eElement.integer_representation` - EXAMPLES:: sage: F. = GF(7^5) @@ -1038,7 +1034,7 @@ cdef class FinitePolyExtElement(FiniteRingElement): """ if not reverse: try: - return self.integer_representation() + return self._integer_representation() except AttributeError: pass p = self.parent().characteristic() @@ -1047,6 +1043,8 @@ cdef class FinitePolyExtElement(FiniteRingElement): f = f.reverse(self.parent().degree()-1) return f(p) + integer_representation = deprecated_function_alias(33941, to_integer) + cdef class Cache_base(SageObject): cpdef FinitePolyExtElement fetch_int(self, number): r""" diff --git a/src/sage/rings/finite_rings/element_givaro.pyx b/src/sage/rings/finite_rings/element_givaro.pyx index 6461f59e49f..e296dfbe4f4 100644 --- a/src/sage/rings/finite_rings/element_givaro.pyx +++ b/src/sage/rings/finite_rings/element_givaro.pyx @@ -643,7 +643,7 @@ cdef class Cache_givaro(Cache_base): sage: k._cache._element_int_repr(a^20) '74' """ - return str(e.integer_representation()) + return str(e._integer_representation()) def _element_poly_repr(self, FiniteField_givaroElement e, varname=None): """ @@ -1350,7 +1350,7 @@ cdef class FiniteField_givaroElement(FinitePolyExtElement): raise TypeError("Cannot coerce element to an integer.") return self_int - def integer_representation(FiniteField_givaroElement self): + def _integer_representation(FiniteField_givaroElement self): r""" Return the integer representation of ``self``. When ``self`` is in the prime subfield, the integer returned is equal to ``self``. @@ -1370,11 +1370,11 @@ cdef class FiniteField_givaroElement(FinitePolyExtElement): sage: k. = GF(5^2); k Finite Field in b of size 5^2 - sage: k(4).integer_representation() + sage: k(4)._integer_representation() 4 - sage: b.integer_representation() + sage: b._integer_representation() 5 - sage: type(b.integer_representation()) + sage: type(b._integer_representation()) <... 'int'> """ return self._cache.log_to_int(self.element) @@ -1673,7 +1673,7 @@ cdef class FiniteField_givaroElement(FinitePolyExtElement): sage: hash(a) 5 """ - return hash(self.integer_representation()) + return hash(self._integer_representation()) def _vector_(FiniteField_givaroElement self, reverse=False): """ diff --git a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx index fd44e97aa7d..b5d27ea9fb5 100644 --- a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx +++ b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx @@ -872,8 +872,8 @@ cdef class FiniteField_ntl_gf2eElement(FinitePolyExtElement): rx = GF2X_deg(GF2E_rep((right).x)) if lx != rx: return richcmp_not_equal(lx, rx, op) - li = left.integer_representation() - ri = right.integer_representation() + li = left._integer_representation() + ri = right._integer_representation() return richcmp(li, ri, op) def _integer_(FiniteField_ntl_gf2eElement self, Integer): @@ -930,7 +930,7 @@ cdef class FiniteField_ntl_gf2eElement(FinitePolyExtElement): else: raise TypeError("Cannot coerce element to an integer.") - def integer_representation(FiniteField_ntl_gf2eElement self): + def _integer_representation(FiniteField_ntl_gf2eElement self): r""" Return the int representation of ``self``. When ``self`` is in the prime subfield, the integer returned is equal to ``self`` and not @@ -947,12 +947,12 @@ cdef class FiniteField_ntl_gf2eElement(FinitePolyExtElement): EXAMPLES:: sage: k. = GF(2^20) - sage: a.integer_representation() + sage: a._integer_representation() 2 - sage: (a^2 + 1).integer_representation() + sage: (a^2 + 1)._integer_representation() 5 sage: k. = GF(2^70) - sage: (a^65 + a^64 + 1).integer_representation() + sage: (a^65 + a^64 + 1)._integer_representation() 55340232221128654849 """ cdef unsigned int i = 0 @@ -1203,7 +1203,7 @@ cdef class FiniteField_ntl_gf2eElement(FinitePolyExtElement): sage: {a:1,a:0} # indirect doctest {a: 0} """ - return hash(self.integer_representation()) # todo, come up with a faster version + return hash(self._integer_representation()) # todo, come up with a faster version def _vector_(FiniteField_ntl_gf2eElement self, reverse=False): r""" diff --git a/src/sage/rings/finite_rings/finite_field_givaro.py b/src/sage/rings/finite_rings/finite_field_givaro.py index a70cfc0c717..8d88c1c059f 100644 --- a/src/sage/rings/finite_rings/finite_field_givaro.py +++ b/src/sage/rings/finite_rings/finite_field_givaro.py @@ -23,6 +23,7 @@ from sage.rings.integer import Integer from sage.rings.finite_rings.element_givaro import Cache_givaro from sage.libs.pari.all import pari +from sage.misc.superseded import deprecated_function_alias class FiniteField_givaro(FiniteField): @@ -479,7 +480,7 @@ def int_to_log(self, n): """ return self._cache.int_to_log(n) - def fetch_int(self, n): + def from_integer(self, n): r""" Given an integer `n` return a finite field element in ``self`` which equals `n` under the condition that :meth:`gen()` is set to @@ -488,15 +489,17 @@ def fetch_int(self, n): EXAMPLES:: sage: k. = GF(2^8) - sage: k.fetch_int(8) + sage: k.from_integer(8) a^3 - sage: e = k.fetch_int(151); e + sage: e = k.from_integer(151); e a^7 + a^4 + a^2 + a + 1 sage: 2^7 + 2^4 + 2^2 + 2 + 1 151 """ return self._cache.fetch_int(n) + fetch_int = deprecated_function_alias(33941, from_integer) + def _pari_modulus(self): """ Return the modulus of ``self`` in a format for PARI. diff --git a/src/sage/rings/finite_rings/finite_field_ntl_gf2e.py b/src/sage/rings/finite_rings/finite_field_ntl_gf2e.py index d2dd7f49613..82004502674 100644 --- a/src/sage/rings/finite_rings/finite_field_ntl_gf2e.py +++ b/src/sage/rings/finite_rings/finite_field_ntl_gf2e.py @@ -18,6 +18,7 @@ from sage.rings.finite_rings.finite_field_base import FiniteField from sage.libs.pari.all import pari from sage.rings.integer import Integer +from sage.misc.superseded import deprecated_function_alias def late_import(): @@ -276,7 +277,7 @@ def prime_subfield(self): """ return GF2 - def fetch_int(self, number): + def from_integer(self, number): r""" Given an integer `n` less than :meth:`cardinality` with base `2` representation `a_0 + 2 \cdot a_1 + \cdots + 2^k a_k`, returns @@ -290,15 +291,17 @@ def fetch_int(self, number): EXAMPLES:: sage: k. = GF(2^48) - sage: k.fetch_int(2^43 + 2^15 + 1) + sage: k.from_integer(2^43 + 2^15 + 1) a^43 + a^15 + 1 - sage: k.fetch_int(33793) + sage: k.from_integer(33793) a^15 + a^10 + 1 sage: 33793.digits(2) # little endian [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] """ return self._cache.fetch_int(number) + fetch_int = deprecated_function_alias(33941, from_integer) + def _pari_modulus(self): """ Return PARI object which is equivalent to the From dafcbff50b50377b1942583fbe3375bd65ebf6f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 25 Nov 2022 11:18:41 +0100 Subject: [PATCH 548/632] some fixes about normal_basis --- .../polynomial/multi_polynomial_ideal.py | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 8202930a1de..4d70e01c8a7 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -3093,12 +3093,21 @@ def _normal_basis_libsingular(self, degree, weights=None): sage: I = R.ideal(x^2-2*x*z+5, x*y^2+y*z+1, 3*y^2-8*x*z) sage: I._normal_basis_libsingular(5) [] + + Check what happens with weights but no degree (:trac:`34789`):: + + sage: TO = TermOrder('wdegrevlex', (2,)) + sage: R = PolynomialRing(QQ, 1, ['k'], order=TO) + sage: k = R.gen() + sage: I = R.ideal([k**2]) + sage: I.normal_basis() + [k, 1] """ from sage.rings.polynomial.multi_polynomial_ideal_libsingular import kbase_libsingular from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence gb = self._groebner_basis_libsingular() J = self.ring().ideal(gb) - if weights is None: + if weights is None or degree is None: res = kbase_libsingular(J, degree) else: from sage.libs.singular.function_factory import ff @@ -3180,21 +3189,8 @@ def normal_basis(self, degree=None, algorithm='libsingular', sage: S. = PolynomialRing(GF(2), order=T) sage: S.ideal(x^6 + y^3 + z^2).normal_basis(6, algorithm='singular') [x^4*y, x^2*y^2, y^3, x^3*z, x*y*z, z^2] - - Check the deprecation:: - - sage: R. = PolynomialRing(QQ) - sage: _ = R.ideal(x^2+y^2, x*y+2*y).normal_basis('singular') - doctest:...: DeprecationWarning: "algorithm" should be used as keyword argument - See https://trac.sagemath.org/29543 for details. """ from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence - if isinstance(degree, str): - from sage.misc.superseded import deprecation - deprecation(29543, - '"algorithm" should be used as keyword argument') - algorithm = degree - degree = None weights = tuple(x.degree() for x in self.ring().gens()) if all(w == 1 for w in weights): From 9e2e6eeaac8c22225d95dfc3ae794ec340b8cca7 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Fri, 25 Nov 2022 23:13:08 +0800 Subject: [PATCH 549/632] remove hyphens per reviewer request --- src/sage/rings/finite_rings/element_base.pyx | 10 +++++----- src/sage/rings/finite_rings/element_ntl_gf2e.pyx | 2 +- src/sage/rings/finite_rings/finite_field_base.pyx | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/rings/finite_rings/element_base.pyx b/src/sage/rings/finite_rings/element_base.pyx index 7aef260b807..c8af112ab9a 100755 --- a/src/sage/rings/finite_rings/element_base.pyx +++ b/src/sage/rings/finite_rings/element_base.pyx @@ -215,7 +215,7 @@ cdef class FinitePolyExtElement(FiniteRingElement): def __getitem__(self, n): r""" - Return the `n`\th coefficient of this finite-field element when + Return the `n`\th coefficient of this finite field element when written as a polynomial in the generator. EXAMPLES:: @@ -252,7 +252,7 @@ cdef class FinitePolyExtElement(FiniteRingElement): def list(self): r""" Return the list of coefficients (in little-endian) of this - finite-field element when written as a polynomial in the + finite field element when written as a polynomial in the generator. Equivalent to calling ``list()`` on this element. @@ -268,7 +268,7 @@ cdef class FinitePolyExtElement(FiniteRingElement): True The coefficients returned are those of a fully reduced - representative of the finite-field element:: + representative of the finite field element:: sage: b = u^777 sage: b.list() @@ -290,7 +290,7 @@ cdef class FinitePolyExtElement(FiniteRingElement): def __iter__(self): r""" - Return an iterator over the coefficients of this finite-field + Return an iterator over the coefficients of this finite field element, in the same order as :meth:`list`. EXAMPLES:: @@ -985,7 +985,7 @@ cdef class FinitePolyExtElement(FiniteRingElement): def to_integer(self, reverse=False): r""" - Return an integer representation of this finite-field element + Return an integer representation of this finite field element obtained by lifting its representative polynomial to `\ZZ` and evaluating it at the characteristic `p`. diff --git a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx index b5d27ea9fb5..145a4da11a5 100644 --- a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx +++ b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx @@ -1260,7 +1260,7 @@ cdef class FiniteField_ntl_gf2eElement(FinitePolyExtElement): INPUT: - - ``base`` -- finite-field element. + - ``base`` -- finite field element. OUTPUT: diff --git a/src/sage/rings/finite_rings/finite_field_base.pyx b/src/sage/rings/finite_rings/finite_field_base.pyx index c64bf52efb9..4c4f3cdb2e6 100644 --- a/src/sage/rings/finite_rings/finite_field_base.pyx +++ b/src/sage/rings/finite_rings/finite_field_base.pyx @@ -408,7 +408,7 @@ cdef class FiniteField(Field): def from_integer(self, n, reverse=False): r""" - Return the finite-field element obtained by reinterpreting the base-`p` + Return the finite field element obtained by reinterpreting the base-`p` expansion of `n` as a polynomial and evaluating it at the generator of this finite field. From fbfb3e2eaa79e480a7d9bf50f11caa6aee20655a Mon Sep 17 00:00:00 2001 From: Eric Gourgoulhon Date: Fri, 25 Nov 2022 21:57:23 +0100 Subject: [PATCH 550/632] Update documentation for modules built upon a FiniteRankFreeModule (#34792) --- src/sage/tensor/modules/ext_pow_free_module.py | 4 ++-- src/sage/tensor/modules/tensor_free_module.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/tensor/modules/ext_pow_free_module.py b/src/sage/tensor/modules/ext_pow_free_module.py index d9db1b5a05b..ec6858a1b47 100644 --- a/src/sage/tensor/modules/ext_pow_free_module.py +++ b/src/sage/tensor/modules/ext_pow_free_module.py @@ -85,7 +85,7 @@ class ExtPowerFreeModule(FiniteRankFreeModule_abstract): `R`, where `n` is the rank of `M`. Accordingly, the class :class:`ExtPowerFreeModule` inherits from the class - :class:`~sage.tensor.modules.finite_rank_free_module.FiniteRankFreeModule`. + :class:`~sage.tensor.modules.finite_rank_free_module.FiniteRankFreeModule_abstract`. This is a Sage *parent* class, whose *element* class is :class:`~sage.tensor.modules.alternating_contr_tensor.AlternatingContrTensor` @@ -465,7 +465,7 @@ class ExtPowerDualFreeModule(FiniteRankFreeModule_abstract): `R`, where `n` is the rank of `M`. Accordingly, the class :class:`ExtPowerDualFreeModule` inherits from the class - :class:`~sage.tensor.modules.finite_rank_free_module.FiniteRankFreeModule`. + :class:`~sage.tensor.modules.finite_rank_free_module.FiniteRankFreeModule_abstract`. This is a Sage *parent* class, whose *element* class is :class:`~sage.tensor.modules.free_module_alt_form.FreeModuleAltForm`. diff --git a/src/sage/tensor/modules/tensor_free_module.py b/src/sage/tensor/modules/tensor_free_module.py index 3dd3a573c89..8e56dbc0b76 100644 --- a/src/sage/tensor/modules/tensor_free_module.py +++ b/src/sage/tensor/modules/tensor_free_module.py @@ -29,7 +29,7 @@ `T^{(k,l)}(M)` is itself a free module over `R`, of rank `n^{k+l}`, `n` being the rank of `M`. Accordingly the class :class:`TensorFreeModule` inherits from the class -:class:`~sage.tensor.modules.finite_rank_free_module.FiniteRankFreeModule`. +:class:`~sage.tensor.modules.finite_rank_free_module.FiniteRankFreeModule_abstract`. .. TODO:: From 5e928939561e32d36a536fc518c5c1ecfb76b639 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Sat, 26 Nov 2022 10:29:28 +0900 Subject: [PATCH 551/632] Minor edits on spaces --- src/sage/crypto/mq/sr.py | 4 ++-- src/sage/libs/ntl/ntl_GF2X.pyx | 2 +- src/sage/libs/ntl/ntl_mat_GF2E.pyx | 2 +- src/sage/rings/finite_rings/element_base.pyx | 2 +- src/sage/rings/finite_rings/element_ntl_gf2e.pyx | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/crypto/mq/sr.py b/src/sage/crypto/mq/sr.py index 594c09030b9..b5cc95877f3 100644 --- a/src/sage/crypto/mq/sr.py +++ b/src/sage/crypto/mq/sr.py @@ -1301,9 +1301,9 @@ def __call__(self, P, K): F = self.base_ring() if isinstance(P, str): - P = self.state_array([F.from_integer(ZZ(P[i:i+2], 16)) for i in range(0, len(P), 2)]) + P = self.state_array([F.from_integer(ZZ(P[i: i + 2], 16)) for i in range(0, len(P), 2)]) if isinstance(K, str): - K = self.state_array([F.from_integer(ZZ(K[i:i+2], 16)) for i in range(0, len(K), 2)]) + K = self.state_array([F.from_integer(ZZ(K[i: i + 2], 16)) for i in range(0, len(K), 2)]) if self.is_state_array(P) and self.is_state_array(K): _type = self.state_array diff --git a/src/sage/libs/ntl/ntl_GF2X.pyx b/src/sage/libs/ntl/ntl_GF2X.pyx index 8b809ebbb63..d4581467952 100644 --- a/src/sage/libs/ntl/ntl_GF2X.pyx +++ b/src/sage/libs/ntl/ntl_GF2X.pyx @@ -160,7 +160,7 @@ cdef class ntl_GF2X(): if x.characteristic() == 2: x = list(x.modulus()) elif isinstance(x, FiniteField_givaroElement): - x = "0x"+hex(x.to_integer())[2:][::-1] + x = "0x" + hex(x.to_integer())[2:][::-1] elif isinstance(x, FiniteField_ntl_gf2eElement): x = x.polynomial().list() s = str(x).replace(","," ") diff --git a/src/sage/libs/ntl/ntl_mat_GF2E.pyx b/src/sage/libs/ntl/ntl_mat_GF2E.pyx index f167418ee0a..655ead78700 100644 --- a/src/sage/libs/ntl/ntl_mat_GF2E.pyx +++ b/src/sage/libs/ntl/ntl_mat_GF2E.pyx @@ -78,7 +78,7 @@ cdef class ntl_mat_GF2E(): [0x0 0x0 0x0 0x0 0x0] [0x0 0x0 0x0 0x0 0x0] ] - sage: A = matrix(k, 5,5, [k.from_integer(i % 2^4) for i in range(25)]) + sage: A = matrix(k, 5, 5, [k.from_integer(i % 2^4) for i in range(25)]) sage: ntl.mat_GF2E(ctx, A) [[0x0 0x1 0x2 0x3 0x4] [0x5 0x6 0x7 0x8 0x9] diff --git a/src/sage/rings/finite_rings/element_base.pyx b/src/sage/rings/finite_rings/element_base.pyx index c8af112ab9a..29ab05dd8ba 100755 --- a/src/sage/rings/finite_rings/element_base.pyx +++ b/src/sage/rings/finite_rings/element_base.pyx @@ -1040,7 +1040,7 @@ cdef class FinitePolyExtElement(FiniteRingElement): p = self.parent().characteristic() f = self.polynomial().change_ring(ZZ) if reverse: - f = f.reverse(self.parent().degree()-1) + f = f.reverse(self.parent().degree() - 1) return f(p) integer_representation = deprecated_function_alias(33941, to_integer) diff --git a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx index 145a4da11a5..477c830f867 100644 --- a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx +++ b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx @@ -1203,7 +1203,7 @@ cdef class FiniteField_ntl_gf2eElement(FinitePolyExtElement): sage: {a:1,a:0} # indirect doctest {a: 0} """ - return hash(self._integer_representation()) # todo, come up with a faster version + return hash(self._integer_representation()) # todo, come up with a faster version def _vector_(FiniteField_ntl_gf2eElement self, reverse=False): r""" @@ -1260,7 +1260,7 @@ cdef class FiniteField_ntl_gf2eElement(FinitePolyExtElement): INPUT: - - ``base`` -- finite field element. + - ``base`` -- finite field element OUTPUT: From 0eabc46980d3ae9194cdb36d03308212481268e0 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Sat, 26 Nov 2022 10:50:35 +0900 Subject: [PATCH 552/632] One more edit on spaces --- src/sage/crypto/mq/sr.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/crypto/mq/sr.py b/src/sage/crypto/mq/sr.py index b5cc95877f3..81ae146e204 100644 --- a/src/sage/crypto/mq/sr.py +++ b/src/sage/crypto/mq/sr.py @@ -2321,13 +2321,13 @@ def lin_matrix(self, length = None): lin = Matrix(self.base_ring(), length*e, length*e) if e == 4: - l = [ k.from_integer(x) for x in (5, 1, 12, 5) ] + l = [k.from_integer(x) for x in (5, 1, 12, 5)] for k in range( 0, length ): for i in range(0, 4): for j in range(0, 4): lin[k*4+j, k*4+i] = l[(i-j)%4] ** (2**j) elif e == 8: - l = [ k.from_integer(x) for x in (5, 9, 249, 37, 244, 1, 181, 143) ] + l = [k.from_integer(x) for x in (5, 9, 249, 37, 244, 1, 181, 143)] for k in range( 0, length ): for i in range(0, 8): for j in range(0, 8): From fdd4f7f9f0b50a6718eb682d905557f2766f4b4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 26 Nov 2022 08:18:16 +0100 Subject: [PATCH 553/632] little tweak --- .../algebras/fusion_rings/fast_parallel_fmats_methods.pyx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx b/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx index bf6bb29c3d7..a482c0c4fc1 100644 --- a/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx +++ b/src/sage/algebras/fusion_rings/fast_parallel_fmats_methods.pyx @@ -178,15 +178,15 @@ cdef _fmat(fvars, _Nk_ij, id_anyon, a, b, c, d, x, y): return 0 # Some known F-symbols if a == id_anyon: - return 1 if x == b and y == d else 0 + return int(x == b and y == d) if b == id_anyon: - return 1 if x == a and y == c else 0 + return int(x == a and y == c) if c == id_anyon: - return 1 if x == d and y == b else 0 + return int(x == d and y == b) return fvars[a, b, c, d, x, y] ###################################### -### Fast fusion coefficients cache ### +# Fast fusion coefficients cache # ###################################### # from sage.misc.cachefunc import cached_function From 5c13b55294b0afdfe3fa24f17d11c0735e1d5405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sat, 26 Nov 2022 09:43:37 +0100 Subject: [PATCH 554/632] fix --- src/sage/modules/free_module_element.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/modules/free_module_element.pyx b/src/sage/modules/free_module_element.pyx index 0b8685c28e2..54783c509bb 100644 --- a/src/sage/modules/free_module_element.pyx +++ b/src/sage/modules/free_module_element.pyx @@ -1549,7 +1549,7 @@ cdef class FreeModuleElement(Vector): # abstract base class return self M = self._parent.change_ring(R) if M.is_sparse(): - return M(self.dict(), sparse=True, coerce=True) + return M(self.dict(), coerce=True) return M(self.list(), coerce=True) def coordinate_ring(self): From ba35b103f8bbb3d4f82fd327b57d7ccd16699ec2 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Sat, 26 Nov 2022 09:09:56 -0500 Subject: [PATCH 555/632] src/sage/modular/modform/ring.py: fix bad spacing in dictionaries --- src/sage/modular/modform/ring.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/modular/modform/ring.py b/src/sage/modular/modform/ring.py index e122c73814e..3a56864da97 100644 --- a/src/sage/modular/modform/ring.py +++ b/src/sage/modular/modform/ring.py @@ -397,7 +397,7 @@ def _generators_variables_dictionnary(self, poly_parent, gens): nb_gens = self.ngens() if nb_var != nb_gens: raise ValueError('the number of variables (%s) must be equal to the number of generators of the modular forms ring (%s)'%(nb_var, self.ngens())) - return {poly_parent.gen(i) : self(gens[i]) for i in range(0, nb_var)} + return {poly_parent.gen(i): self(gens[i]) for i in range(0, nb_var)} def from_polynomial(self, polynomial, gens=None): r""" @@ -518,7 +518,7 @@ def _element_constructor_(self, forms_datum): forms_dictionary = forms_datum._forms_dictionary elif is_ModularFormElement(forms_datum): if self.group().is_subgroup(forms_datum.group()) and self.base_ring().has_coerce_map_from(forms_datum.base_ring()): - forms_dictionary = {forms_datum.weight() : forms_datum} + forms_dictionary = {forms_datum.weight(): forms_datum} else: raise ValueError('the group (%s) and/or the base ring (%s) of the given modular form is not consistant with the base space: %s'%(forms_datum.group(), forms_datum.base_ring(), self)) elif forms_datum in self.base_ring(): From a18ca9c74ccfcb56a5275ebb548dec0100bc7c5c Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 26 Nov 2022 17:28:37 -0800 Subject: [PATCH 556/632] build/pkgs/pybind11: Update to 2.10.1 --- build/pkgs/pybind11/checksums.ini | 6 +++--- build/pkgs/pybind11/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/pybind11/checksums.ini b/build/pkgs/pybind11/checksums.ini index 1a6fb10c749..1cc6d36c174 100644 --- a/build/pkgs/pybind11/checksums.ini +++ b/build/pkgs/pybind11/checksums.ini @@ -1,5 +1,5 @@ tarball=pybind11-VERSION.tar.gz -sha1=cee2f3c7879783bb5f021c7db332f8a290fe3763 -md5=c232928479f398693bface2731387691 -cksum=3770559962 +sha1=d0e6e22c2ce36fad4bb60dbac4d6d498ceb464df +md5=0b181dbb44c3cc632e724cef5081cae1 +cksum=4287838207 upstream_url=https://pypi.io/packages/source/p/pybind11/pybind11-VERSION.tar.gz diff --git a/build/pkgs/pybind11/package-version.txt b/build/pkgs/pybind11/package-version.txt index 5d9ade10c60..8bbb6e406a7 100644 --- a/build/pkgs/pybind11/package-version.txt +++ b/build/pkgs/pybind11/package-version.txt @@ -1 +1 @@ -2.9.2 +2.10.1 From 560d6ef226dc7fdd28535fbc09f2b5f2d3ec2585 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 26 Nov 2022 17:34:51 -0800 Subject: [PATCH 557/632] build/pkgs/wheel: Update to 0.38.4 --- build/pkgs/wheel/checksums.ini | 6 +++--- build/pkgs/wheel/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/wheel/checksums.ini b/build/pkgs/wheel/checksums.ini index 5872bf109ab..b01f275c477 100644 --- a/build/pkgs/wheel/checksums.ini +++ b/build/pkgs/wheel/checksums.ini @@ -1,5 +1,5 @@ tarball=wheel-VERSION.tar.gz -sha1=f9f3d980579b88baaacdb6c4a3cc4466b9353030 -md5=f490f1399e5903706cb1d4fbed9ecb28 -cksum=984523375 +sha1=ff9a5efeabf8e73e8b1a8646eaa829154f834726 +md5=83bb4e7bd4d687d398733f341a64ab91 +cksum=518943238 upstream_url=https://pypi.io/packages/source/w/wheel/wheel-VERSION.tar.gz diff --git a/build/pkgs/wheel/package-version.txt b/build/pkgs/wheel/package-version.txt index 9b1bb851239..87f0b954e35 100644 --- a/build/pkgs/wheel/package-version.txt +++ b/build/pkgs/wheel/package-version.txt @@ -1 +1 @@ -0.37.1 +0.38.4 From aaa3a9121f1f4b31f09ee8af5f3a36b5a1aba75e Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 26 Nov 2022 17:35:40 -0800 Subject: [PATCH 558/632] build/pkgs/zipp: Update to 3.11.0 --- build/pkgs/zipp/checksums.ini | 6 +++--- build/pkgs/zipp/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/zipp/checksums.ini b/build/pkgs/zipp/checksums.ini index 5876b93b924..53d559563f8 100644 --- a/build/pkgs/zipp/checksums.ini +++ b/build/pkgs/zipp/checksums.ini @@ -1,5 +1,5 @@ tarball=zipp-VERSION.tar.gz -sha1=017268ef95e7da55ca11e695b63cda797d7a64be -md5=f75b65d022528a44877626641f0f95c3 -cksum=1239244009 +sha1=3f6c57b68f3b9165586ea7cce96fc2540b0078ec +md5=1fbff3bca7294a3a7f09fa3f0652c3da +cksum=1128680850 upstream_url=https://pypi.io/packages/source/z/zipp/zipp-VERSION.tar.gz diff --git a/build/pkgs/zipp/package-version.txt b/build/pkgs/zipp/package-version.txt index 30291cba223..afad818663d 100644 --- a/build/pkgs/zipp/package-version.txt +++ b/build/pkgs/zipp/package-version.txt @@ -1 +1 @@ -3.10.0 +3.11.0 From 93da9327185081f1e591b3039c4328be12f0b4bd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 26 Nov 2022 17:38:50 -0800 Subject: [PATCH 559/632] build/pkgs/setuptools_wheel: Update to 65.6.3 --- build/pkgs/setuptools_wheel/checksums.ini | 6 +++--- build/pkgs/setuptools_wheel/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/setuptools_wheel/checksums.ini b/build/pkgs/setuptools_wheel/checksums.ini index 2da328e1726..8db0d8c3016 100644 --- a/build/pkgs/setuptools_wheel/checksums.ini +++ b/build/pkgs/setuptools_wheel/checksums.ini @@ -1,5 +1,5 @@ tarball=setuptools-VERSION.tar.gz -sha1=e5f9797d85db9bb2ce39b401c1b7aca38de616b5 -md5=ec88a6545351e72ca73fcec7a6bff6ad -cksum=1981709060 +sha1=2875e8f9a12d8a971461b36e2d1bd64a3497e0f2 +md5=1fd8bd04b0bed95ad6c81f03b1c080bc +cksum=1462078737 upstream_url=https://pypi.io/packages/source/s/setuptools/setuptools-VERSION.tar.gz diff --git a/build/pkgs/setuptools_wheel/package-version.txt b/build/pkgs/setuptools_wheel/package-version.txt index ba0d20023a1..c1a8c8394ad 100644 --- a/build/pkgs/setuptools_wheel/package-version.txt +++ b/build/pkgs/setuptools_wheel/package-version.txt @@ -1 +1 @@ -65.4.0 +65.6.3 From 6e90aa5193d63c9c6b2532f3b5c19fd9a7b3aa4b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 26 Nov 2022 17:39:20 -0800 Subject: [PATCH 560/632] build/pkgs/pyzmq: Update to 24.0.1 --- build/pkgs/pyzmq/checksums.ini | 6 +++--- build/pkgs/pyzmq/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/pyzmq/checksums.ini b/build/pkgs/pyzmq/checksums.ini index cd83e8c1a2d..d9abd25d464 100644 --- a/build/pkgs/pyzmq/checksums.ini +++ b/build/pkgs/pyzmq/checksums.ini @@ -1,5 +1,5 @@ tarball=pyzmq-VERSION.tar.gz -sha1=b31a94f504ef3104a28e64d88b7c1feafb124982 -md5=7de9c7bb05cc89d21e4105ec1ac7c270 -cksum=936855776 +sha1=1a2e7220d7d1b6167c14ae2cc001dfc5d9a28dde +md5=f10b7c3dee2c03557e2c5d00b73dfc7f +cksum=1163982926 upstream_url=https://pypi.io/packages/source/p/pyzmq/pyzmq-VERSION.tar.gz diff --git a/build/pkgs/pyzmq/package-version.txt b/build/pkgs/pyzmq/package-version.txt index 11ebcd0e4f9..1b3e74f84e7 100644 --- a/build/pkgs/pyzmq/package-version.txt +++ b/build/pkgs/pyzmq/package-version.txt @@ -1 +1 @@ -23.2.0 +24.0.1 From 8879d04fac3b426aa931e3f53aaca14250eca52b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 26 Nov 2022 17:40:28 -0800 Subject: [PATCH 561/632] build/pkgs/pyrsistent: Update to 0.19.2 --- build/pkgs/pyrsistent/checksums.ini | 6 +++--- build/pkgs/pyrsistent/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/pyrsistent/checksums.ini b/build/pkgs/pyrsistent/checksums.ini index 7ea885d3db7..7d537e43e37 100644 --- a/build/pkgs/pyrsistent/checksums.ini +++ b/build/pkgs/pyrsistent/checksums.ini @@ -1,5 +1,5 @@ tarball=pyrsistent-VERSION.tar.gz -sha1=58f9efc4800acb6f7a083688e988187cccee2266 -md5=cef3da08455664bf917dcf8cd00d49a4 -cksum=350737156 +sha1=a2c5cc517a33dcfd3918d3eabf4859b8901d3913 +md5=23da81256b8817e123568a858bf78997 +cksum=1165148669 upstream_url=https://pypi.io/packages/source/p/pyrsistent/pyrsistent-VERSION.tar.gz diff --git a/build/pkgs/pyrsistent/package-version.txt b/build/pkgs/pyrsistent/package-version.txt index 249afd517d9..61e6e92d914 100644 --- a/build/pkgs/pyrsistent/package-version.txt +++ b/build/pkgs/pyrsistent/package-version.txt @@ -1 +1 @@ -0.18.1 +0.19.2 From 4909c83ff3692c80cebc489c83c19c1795910d4f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 26 Nov 2022 17:41:12 -0800 Subject: [PATCH 562/632] build/pkgs/platformdirs: Update to 2.5.4 --- build/pkgs/platformdirs/checksums.ini | 6 +++--- build/pkgs/platformdirs/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/platformdirs/checksums.ini b/build/pkgs/platformdirs/checksums.ini index 8dc4a85a66d..604aa7f6c99 100644 --- a/build/pkgs/platformdirs/checksums.ini +++ b/build/pkgs/platformdirs/checksums.ini @@ -1,5 +1,5 @@ tarball=platformdirs-VERSION.tar.gz -sha1=344841a3cd4eb5b1a1b8adb8a57e845e5a06b236 -md5=2301a8a29c3082a49ee293073d893887 -cksum=1100125935 +sha1=082974f7d3ea03adfa147f4ab5be76079c2a116f +md5=f449b7f3767577fa2a57465a4523e92e +cksum=2966639810 upstream_url=https://pypi.io/packages/source/p/platformdirs/platformdirs-VERSION.tar.gz diff --git a/build/pkgs/platformdirs/package-version.txt b/build/pkgs/platformdirs/package-version.txt index f225a78adf0..fe16b348d97 100644 --- a/build/pkgs/platformdirs/package-version.txt +++ b/build/pkgs/platformdirs/package-version.txt @@ -1 +1 @@ -2.5.2 +2.5.4 From 192a393780113a77eec279ba6504626b988d0c5f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 26 Nov 2022 17:41:24 -0800 Subject: [PATCH 563/632] build/pkgs/pint: Update to 0.20.1 --- build/pkgs/pint/checksums.ini | 6 +++--- build/pkgs/pint/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/pint/checksums.ini b/build/pkgs/pint/checksums.ini index 9be8323d338..b852d25efe6 100644 --- a/build/pkgs/pint/checksums.ini +++ b/build/pkgs/pint/checksums.ini @@ -1,5 +1,5 @@ tarball=Pint-VERSION.tar.gz -sha1=98496b31efc63adfbf640953351aafc51cb46584 -md5=b14aa24f4a4bcbc2c2fae797b25d0b3e -cksum=3392069525 +sha1=c14ac08ca2d5a68d79ea7cd2252dc7e2a572c56a +md5=522a8e633e487e92ba54ccbec395947a +cksum=2139030399 upstream_url=https://pypi.io/packages/source/p/pint/Pint-VERSION.tar.gz diff --git a/build/pkgs/pint/package-version.txt b/build/pkgs/pint/package-version.txt index 50653ad0a6e..847e9aef6d1 100644 --- a/build/pkgs/pint/package-version.txt +++ b/build/pkgs/pint/package-version.txt @@ -1 +1 @@ -0.17 +0.20.1 From a5037f35a99a8c16887d650b81689e297d1fc7a6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 26 Nov 2022 17:41:33 -0800 Subject: [PATCH 564/632] build/pkgs/pathspec: Update to 0.10.2 --- build/pkgs/pathspec/checksums.ini | 6 +++--- build/pkgs/pathspec/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/pathspec/checksums.ini b/build/pkgs/pathspec/checksums.ini index 2db40ded285..a98c270d6e1 100644 --- a/build/pkgs/pathspec/checksums.ini +++ b/build/pkgs/pathspec/checksums.ini @@ -1,5 +1,5 @@ tarball=pathspec-VERSION.tar.gz -sha1=ef0f4b07097506575ca8052256b56f137a7b170d -md5=6f4fde5e701d328a2846d206edb63aa9 -cksum=2376511942 +sha1=418ae4112af18af995c0f20a22e5a903b8ce50ae +md5=28c87c3581b10152c4581d10fe33f765 +cksum=2161527634 upstream_url=https://pypi.io/packages/source/p/pathspec/pathspec-VERSION.tar.gz diff --git a/build/pkgs/pathspec/package-version.txt b/build/pkgs/pathspec/package-version.txt index 571215736a6..5eef0f10e8c 100644 --- a/build/pkgs/pathspec/package-version.txt +++ b/build/pkgs/pathspec/package-version.txt @@ -1 +1 @@ -0.10.1 +0.10.2 From 10d03522fe1041808a8286251ac10fa9a2113463 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 26 Nov 2022 17:42:51 -0800 Subject: [PATCH 565/632] build/pkgs/jsonschema: Update to 4.17.1 --- build/pkgs/jsonschema/checksums.ini | 6 +++--- build/pkgs/jsonschema/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/jsonschema/checksums.ini b/build/pkgs/jsonschema/checksums.ini index b25e78e04fa..5a214ae38fc 100644 --- a/build/pkgs/jsonschema/checksums.ini +++ b/build/pkgs/jsonschema/checksums.ini @@ -1,5 +1,5 @@ tarball=jsonschema-VERSION.tar.gz -sha1=912d562c1394408dca582e14843e3245df2f3827 -md5=3bc1f63a74fc61bf2847f22cadb0dfff -cksum=2200261176 +sha1=ccea159a8a0c453e6fbbcfd7bb681b1bc766500e +md5=527bc4d51d31e8d0b8a0d833b6a50002 +cksum=1885537635 upstream_url=https://pypi.io/packages/source/j/jsonschema/jsonschema-VERSION.tar.gz diff --git a/build/pkgs/jsonschema/package-version.txt b/build/pkgs/jsonschema/package-version.txt index ecbc3b03079..1b0a87fdfc9 100644 --- a/build/pkgs/jsonschema/package-version.txt +++ b/build/pkgs/jsonschema/package-version.txt @@ -1 +1 @@ -4.16.0 +4.17.1 From 929838645671ea85294a3933e9ef14e0d538a375 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 26 Nov 2022 17:43:08 -0800 Subject: [PATCH 566/632] build/pkgs/hatch_nodejs_version: Update to 0.3.1 --- build/pkgs/hatch_nodejs_version/checksums.ini | 6 +++--- build/pkgs/hatch_nodejs_version/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/hatch_nodejs_version/checksums.ini b/build/pkgs/hatch_nodejs_version/checksums.ini index d5210b7eab0..bb5f28d0e24 100644 --- a/build/pkgs/hatch_nodejs_version/checksums.ini +++ b/build/pkgs/hatch_nodejs_version/checksums.ini @@ -1,5 +1,5 @@ tarball=hatch_nodejs_version-VERSION.tar.gz -sha1=27420a3bae3f787640b2c8ad522d75fa32526294 -md5=644c2aea7558de91bbd66217fec6a1b9 -cksum=4058013444 +sha1=ce77992d461d5108c481e985250cfb401b4ee5df +md5=6e5f9d5cfa442572637478cacaa8ea81 +cksum=1271494344 upstream_url=https://pypi.io/packages/source/h/hatch_nodejs_version/hatch_nodejs_version-VERSION.tar.gz diff --git a/build/pkgs/hatch_nodejs_version/package-version.txt b/build/pkgs/hatch_nodejs_version/package-version.txt index 0d91a54c7d4..9e11b32fcaa 100644 --- a/build/pkgs/hatch_nodejs_version/package-version.txt +++ b/build/pkgs/hatch_nodejs_version/package-version.txt @@ -1 +1 @@ -0.3.0 +0.3.1 From 6814586e72a856a0d1178e5ebf2b317495bd76c9 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 26 Nov 2022 17:43:17 -0800 Subject: [PATCH 567/632] build/pkgs/importlib_metadata: Update to 5.1.0 --- build/pkgs/importlib_metadata/checksums.ini | 6 +++--- build/pkgs/importlib_metadata/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/importlib_metadata/checksums.ini b/build/pkgs/importlib_metadata/checksums.ini index c6d66d9ee23..4d326297be6 100644 --- a/build/pkgs/importlib_metadata/checksums.ini +++ b/build/pkgs/importlib_metadata/checksums.ini @@ -1,5 +1,5 @@ tarball=importlib_metadata-VERSION.tar.gz -sha1=38794db2afb90ed0be04bd8b5996a5b5fd45acc2 -md5=ccd58a387cc2bab6cf72fdf21e403749 -cksum=758044022 +sha1=4a49e8c6d8e2eb02e9ea821444b5ba153d8c34a6 +md5=56d34f2e854bb0f318baa9e47aba3439 +cksum=3438247256 upstream_url=https://pypi.io/packages/source/i/importlib_metadata/importlib_metadata-VERSION.tar.gz diff --git a/build/pkgs/importlib_metadata/package-version.txt b/build/pkgs/importlib_metadata/package-version.txt index 0062ac97180..831446cbd27 100644 --- a/build/pkgs/importlib_metadata/package-version.txt +++ b/build/pkgs/importlib_metadata/package-version.txt @@ -1 +1 @@ -5.0.0 +5.1.0 From 83330f928be369326db9d68a7696ce10902ad2fa Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 26 Nov 2022 17:43:25 -0800 Subject: [PATCH 568/632] build/pkgs/gmpy2: Update to 2.1.2 --- build/pkgs/gmpy2/checksums.ini | 6 +++--- build/pkgs/gmpy2/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/gmpy2/checksums.ini b/build/pkgs/gmpy2/checksums.ini index d17df675312..70ecbe7244d 100644 --- a/build/pkgs/gmpy2/checksums.ini +++ b/build/pkgs/gmpy2/checksums.ini @@ -1,5 +1,5 @@ tarball=gmpy2-VERSION.tar.gz -sha1=fcb929ab9a44d96bfb47b7ed411cc2f048b484b2 -md5=877d324e676b162053772affda5c0de7 -cksum=2408333571 +sha1=8280f6c68c57dd4a2fce149162c080c1dac86eb0 +md5=bb21846e99800e04d5b330b76c23ba10 +cksum=3835762323 upstream_url=https://pypi.io/packages/source/g/gmpy2/gmpy2-VERSION.tar.gz diff --git a/build/pkgs/gmpy2/package-version.txt b/build/pkgs/gmpy2/package-version.txt index 3e3c2f1e5ed..eca07e4c1a8 100644 --- a/build/pkgs/gmpy2/package-version.txt +++ b/build/pkgs/gmpy2/package-version.txt @@ -1 +1 @@ -2.1.1 +2.1.2 From ecaa5ea96274bbb3daa6364c651486b501f10658 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 26 Nov 2022 17:44:05 -0800 Subject: [PATCH 569/632] build/pkgs/docutils: Update to 0.19 --- build/pkgs/docutils/checksums.ini | 6 +++--- build/pkgs/docutils/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/docutils/checksums.ini b/build/pkgs/docutils/checksums.ini index 7eb275b5d93..7ba721d2c06 100644 --- a/build/pkgs/docutils/checksums.ini +++ b/build/pkgs/docutils/checksums.ini @@ -1,5 +1,5 @@ tarball=docutils-VERSION.tar.gz -sha1=f423535c12fcd2a68d4fc52525fbe36020a58ab5 -md5=ed810564c25063e9dac10dd0893ead47 -cksum=3160620183 +sha1=c38c6ccd1547b4d651e39b64dd6be676be5f14d5 +md5=0afa992a6e93db892107c3f087d0d9df +cksum=658477137 upstream_url=https://pypi.io/packages/source/d/docutils/docutils-VERSION.tar.gz diff --git a/build/pkgs/docutils/package-version.txt b/build/pkgs/docutils/package-version.txt index 7cca7711a0d..caa4836d8e0 100644 --- a/build/pkgs/docutils/package-version.txt +++ b/build/pkgs/docutils/package-version.txt @@ -1 +1 @@ -0.17.1 +0.19 From 8a16149424d22414007157dc9da4944de4e89794 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 26 Nov 2022 17:44:28 -0800 Subject: [PATCH 570/632] build/pkgs/cppy: Update to 1.2.1 --- build/pkgs/cppy/checksums.ini | 6 +++--- build/pkgs/cppy/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/cppy/checksums.ini b/build/pkgs/cppy/checksums.ini index 5c781671d43..613b3b3f5b8 100644 --- a/build/pkgs/cppy/checksums.ini +++ b/build/pkgs/cppy/checksums.ini @@ -1,5 +1,5 @@ tarball=cppy-VERSION.tar.gz -sha1=3af4f5f14ef1f9b49d7457e2fa5c241c721db29c -md5=2110891d75aa12551deebba1603428c6 -cksum=793876648 +sha1=c82ee7a4f38e302bfe4de2a695d2bdfefb69951f +md5=7c1f825c43dd66454440932a35b9969c +cksum=1879136901 upstream_url=https://files.pythonhosted.org/packages/source/c/cppy/cppy-VERSION.tar.gz diff --git a/build/pkgs/cppy/package-version.txt b/build/pkgs/cppy/package-version.txt index 9084fa2f716..6085e946503 100644 --- a/build/pkgs/cppy/package-version.txt +++ b/build/pkgs/cppy/package-version.txt @@ -1 +1 @@ -1.1.0 +1.2.1 From ff22d08b9449e2b064b83ac296f11fa8b747b953 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 26 Nov 2022 17:53:32 -0800 Subject: [PATCH 571/632] build/pkgs/argon2_cffi: Update to 21.3.0 --- build/pkgs/argon2_cffi/checksums.ini | 6 +++--- build/pkgs/argon2_cffi/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/argon2_cffi/checksums.ini b/build/pkgs/argon2_cffi/checksums.ini index 11705963d15..fa87877c2e6 100644 --- a/build/pkgs/argon2_cffi/checksums.ini +++ b/build/pkgs/argon2_cffi/checksums.ini @@ -1,5 +1,5 @@ tarball=argon2-cffi-VERSION.tar.gz -sha1=c79943104960db3024346731d5153392c187c0d7 -md5=e49ccb29351387fd853f31bf19b67f59 -cksum=3765128778 +sha1=c16c1506de0211bdfa23d4d51e780fb4aaff5222 +md5=b7843e8690c790f8e743d37bb75c25a8 +cksum=3700408796 upstream_url=https://pypi.io/packages/source/a/argon2_cffi/argon2-cffi-VERSION.tar.gz diff --git a/build/pkgs/argon2_cffi/package-version.txt b/build/pkgs/argon2_cffi/package-version.txt index e43bba47d76..54d3ad73646 100644 --- a/build/pkgs/argon2_cffi/package-version.txt +++ b/build/pkgs/argon2_cffi/package-version.txt @@ -1 +1 @@ -20.1.0 +21.3.0 From 4814efc25836152485561c821b8c9c0c8241031d Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 26 Nov 2022 17:53:47 -0800 Subject: [PATCH 572/632] build/pkgs/attrs: Update to 22.1.0 --- build/pkgs/attrs/checksums.ini | 6 +++--- build/pkgs/attrs/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/attrs/checksums.ini b/build/pkgs/attrs/checksums.ini index f329e28cb08..291539baec1 100644 --- a/build/pkgs/attrs/checksums.ini +++ b/build/pkgs/attrs/checksums.ini @@ -1,5 +1,5 @@ tarball=attrs-VERSION.tar.gz -sha1=693de5a8890c6f7bad4edd6ade6971ab3eaf416b -md5=5a9b5e9ceebc380a13fb93235b11bbda -cksum=2935089723 +sha1=16d99f8e6f84309a4e399babc2e237da87b445ad +md5=0487081b7ead8753fc46cf7c6d1e28e3 +cksum=3993993002 upstream_url=https://pypi.io/packages/source/a/attrs/attrs-VERSION.tar.gz diff --git a/build/pkgs/attrs/package-version.txt b/build/pkgs/attrs/package-version.txt index 8bd85214a48..ee5c2446981 100644 --- a/build/pkgs/attrs/package-version.txt +++ b/build/pkgs/attrs/package-version.txt @@ -1 +1 @@ -21.4.0 +22.1.0 From 6b9fbb1dbd16a21182ffdf575450de691d4aa099 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 26 Nov 2022 17:54:22 -0800 Subject: [PATCH 573/632] build/pkgs/pip: Update to 22.3.1 --- build/pkgs/pip/checksums.ini | 6 +++--- build/pkgs/pip/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/pip/checksums.ini b/build/pkgs/pip/checksums.ini index cc58418441f..1ae66fbc20a 100644 --- a/build/pkgs/pip/checksums.ini +++ b/build/pkgs/pip/checksums.ini @@ -1,5 +1,5 @@ tarball=pip-VERSION.tar.gz -sha1=a6d9d6b04f17acb11f214db9835f4b66e12a1fe2 -md5=f0dd02265e7ccd2f8758c840fba64810 -cksum=1176625017 +sha1=29167fffe19874a74247fe92f4fdba1bb1221c61 +md5=996f58a94fe0b8b82b6795c42bd171ba +cksum=537001443 upstream_url=https://pypi.io/packages/source/p/pip/pip-VERSION.tar.gz diff --git a/build/pkgs/pip/package-version.txt b/build/pkgs/pip/package-version.txt index 937387f33bb..4c3dad97552 100644 --- a/build/pkgs/pip/package-version.txt +++ b/build/pkgs/pip/package-version.txt @@ -1 +1 @@ -22.3 +22.3.1 From 7432ba944dfd76c5310d5cedfed113e828e85c47 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 26 Nov 2022 18:06:50 -0800 Subject: [PATCH 574/632] build/pkgs/texttable: Update to 1.6.7 --- build/pkgs/texttable/checksums.ini | 6 +++--- build/pkgs/texttable/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/texttable/checksums.ini b/build/pkgs/texttable/checksums.ini index 9f7f062fc11..380a205bdd3 100644 --- a/build/pkgs/texttable/checksums.ini +++ b/build/pkgs/texttable/checksums.ini @@ -1,5 +1,5 @@ tarball=texttable-VERSION.tar.gz -sha1=f818f1ea121513b54a417ffa6fe8bb26821913d7 -md5=15faadc07ba44d337cc1675ea6092a02 -cksum=4013109377 +sha1=25e1b92e02c8e919dc0da053efbe8c4874418a8d +md5=83eb15fb541dd857ff051a8d0c979b9c +cksum=3183998721 upstream_url=https://pypi.io/packages/source/t/texttable/texttable-VERSION.tar.gz diff --git a/build/pkgs/texttable/package-version.txt b/build/pkgs/texttable/package-version.txt index 9edc58bb1dd..400084b1bf2 100644 --- a/build/pkgs/texttable/package-version.txt +++ b/build/pkgs/texttable/package-version.txt @@ -1 +1 @@ -1.6.4 +1.6.7 From 366691865cfd4f36c48f6409e4a7da073472382b Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 26 Nov 2022 18:07:00 -0800 Subject: [PATCH 575/632] build/pkgs/stack_data: Update to 0.6.1 --- build/pkgs/stack_data/checksums.ini | 6 +++--- build/pkgs/stack_data/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/stack_data/checksums.ini b/build/pkgs/stack_data/checksums.ini index 13d92e56bd8..625aaf0578f 100644 --- a/build/pkgs/stack_data/checksums.ini +++ b/build/pkgs/stack_data/checksums.ini @@ -1,5 +1,5 @@ tarball=stack_data-VERSION.tar.gz -sha1=5e69d397ae31e6dcd995765d4ab51cb6b897fb11 -md5=eedee8944e6e08ddb3195f968553e861 -cksum=64651866 +sha1=58ed9cb32a42e07dbc18356d06f8db96475bc0f2 +md5=05c8c6c58c02280bc87b6851e40d38e6 +cksum=1485401259 upstream_url=https://pypi.io/packages/source/s/stack_data/stack_data-VERSION.tar.gz diff --git a/build/pkgs/stack_data/package-version.txt b/build/pkgs/stack_data/package-version.txt index a918a2aa18d..ee6cdce3c29 100644 --- a/build/pkgs/stack_data/package-version.txt +++ b/build/pkgs/stack_data/package-version.txt @@ -1 +1 @@ -0.6.0 +0.6.1 From e96e7a64284e6e24ca50d57fe612610b3b52a6e6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 26 Nov 2022 18:50:44 -0800 Subject: [PATCH 576/632] build/pkgs/matplotlib: Update to 3.6.2 --- build/pkgs/matplotlib/checksums.ini | 6 +++--- build/pkgs/matplotlib/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/matplotlib/checksums.ini b/build/pkgs/matplotlib/checksums.ini index b2055242c4d..cbf7302ffb1 100644 --- a/build/pkgs/matplotlib/checksums.ini +++ b/build/pkgs/matplotlib/checksums.ini @@ -1,5 +1,5 @@ tarball=matplotlib-VERSION.tar.gz -sha1=8db4a2bb5c5a56b4ff9a33b59d134404774d85d5 -md5=5a77336c6c5549bed5f9631c734dedc5 -cksum=602312637 +sha1=2b78c671f95d52c65154a0dc68372a97582768e5 +md5=77ca9a5b42152c9e2aeca1556f08f5ce +cksum=3201608 upstream_url=https://pypi.io/packages/source/m/matplotlib/matplotlib-VERSION.tar.gz diff --git a/build/pkgs/matplotlib/package-version.txt b/build/pkgs/matplotlib/package-version.txt index 87ce492908a..b72762837ea 100644 --- a/build/pkgs/matplotlib/package-version.txt +++ b/build/pkgs/matplotlib/package-version.txt @@ -1 +1 @@ -3.5.2 +3.6.2 From 18a9e232160df764758fb1da52c3ae1f4e70b420 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 26 Nov 2022 18:51:02 -0800 Subject: [PATCH 577/632] build/pkgs/matplotlib_inline: Update to 0.1.6 --- build/pkgs/matplotlib_inline/checksums.ini | 6 +++--- build/pkgs/matplotlib_inline/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/matplotlib_inline/checksums.ini b/build/pkgs/matplotlib_inline/checksums.ini index e0fa917bb26..73d1d152d74 100644 --- a/build/pkgs/matplotlib_inline/checksums.ini +++ b/build/pkgs/matplotlib_inline/checksums.ini @@ -1,5 +1,5 @@ tarball=matplotlib-inline-VERSION.tar.gz -sha1=36cf63817e1e3df521c7aa8c2612f890b4ae7990 -md5=7af8f8ae6ea5217134dd7bd115c78ac3 -cksum=3542531131 +sha1=a09347e3f2eaa6f9453c773132bf4bd9d38e2163 +md5=aded9a57e2f526f76b3a4851d5528d4f +cksum=3152771377 upstream_url=https://pypi.io/packages/source/m/matplotlib_inline/matplotlib-inline-VERSION.tar.gz diff --git a/build/pkgs/matplotlib_inline/package-version.txt b/build/pkgs/matplotlib_inline/package-version.txt index d917d3e26ad..c946ee6160c 100644 --- a/build/pkgs/matplotlib_inline/package-version.txt +++ b/build/pkgs/matplotlib_inline/package-version.txt @@ -1 +1 @@ -0.1.2 +0.1.6 From ed959c1b55b5cc0adf78a25531d1852dd829e9a5 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 26 Nov 2022 20:40:28 -0800 Subject: [PATCH 578/632] build/pkgs/setuptools_scm: Update to 7.0.5 --- build/pkgs/setuptools_scm/checksums.ini | 6 +++--- build/pkgs/setuptools_scm/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/setuptools_scm/checksums.ini b/build/pkgs/setuptools_scm/checksums.ini index a6f5ff1395b..d4c9ee1a391 100644 --- a/build/pkgs/setuptools_scm/checksums.ini +++ b/build/pkgs/setuptools_scm/checksums.ini @@ -1,5 +1,5 @@ tarball=setuptools_scm-VERSION.tar.gz -sha1=0f34eba670121f9c41939ca8d805687de359f71c -md5=b4e02bf8e62ed49142ea7b42a68671d7 -cksum=1975160274 +sha1=b8c9447fe6dfc44107c9cbd2fd82314aad3cb95e +md5=0df4e7fd923e4983cd65786efaa0e0d0 +cksum=3196930290 upstream_url=https://pypi.io/packages/source/s/setuptools_scm/setuptools_scm-VERSION.tar.gz diff --git a/build/pkgs/setuptools_scm/package-version.txt b/build/pkgs/setuptools_scm/package-version.txt index a4c853ea2ea..2be8aeb6b14 100644 --- a/build/pkgs/setuptools_scm/package-version.txt +++ b/build/pkgs/setuptools_scm/package-version.txt @@ -1 +1 @@ -6.4.2 +7.0.5 From df9d613c576a21c93e5e67a5113f3fd555a06209 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 26 Nov 2022 20:43:22 -0800 Subject: [PATCH 579/632] build/pkgs/setuptools_scm_git_archive: Update to 1.4 --- build/pkgs/setuptools_scm_git_archive/checksums.ini | 6 +++--- build/pkgs/setuptools_scm_git_archive/package-version.txt | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/pkgs/setuptools_scm_git_archive/checksums.ini b/build/pkgs/setuptools_scm_git_archive/checksums.ini index 9a773eebc56..ed010b0f53e 100644 --- a/build/pkgs/setuptools_scm_git_archive/checksums.ini +++ b/build/pkgs/setuptools_scm_git_archive/checksums.ini @@ -1,5 +1,5 @@ tarball=setuptools_scm_git_archive-VERSION.tar.gz -sha1=eda18924f3917e94cc54227660e3437d47f53bb9 -md5=1c9351fa5cebd12e76488737a7c78f2e -cksum=188023730 +sha1=5e893fcb35f9633ea1431138c1d90a5f2687b36d +md5=df3933d33c49c5d9aca06715b4c65370 +cksum=2554004806 upstream_url=https://pypi.io/packages/source/s/setuptools_scm_git_archive/setuptools_scm_git_archive-VERSION.tar.gz diff --git a/build/pkgs/setuptools_scm_git_archive/package-version.txt b/build/pkgs/setuptools_scm_git_archive/package-version.txt index 9459d4ba2a0..c068b2447cc 100644 --- a/build/pkgs/setuptools_scm_git_archive/package-version.txt +++ b/build/pkgs/setuptools_scm_git_archive/package-version.txt @@ -1 +1 @@ -1.1 +1.4 From 0ecabe5b52d42b014845aeb48473291e8fdf14f6 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sat, 26 Nov 2022 20:52:22 -0800 Subject: [PATCH 580/632] build/pkgs/contourpy: New (matplotlib dependency) --- build/pkgs/contourpy/SPKG.rst | 18 ++++++++++++++++++ build/pkgs/contourpy/checksums.ini | 5 +++++ build/pkgs/contourpy/dependencies | 4 ++++ build/pkgs/contourpy/install-requires.txt | 1 + build/pkgs/contourpy/package-version.txt | 1 + build/pkgs/contourpy/spkg-install.in | 2 ++ build/pkgs/contourpy/type | 1 + 7 files changed, 32 insertions(+) create mode 100644 build/pkgs/contourpy/SPKG.rst create mode 100644 build/pkgs/contourpy/checksums.ini create mode 100644 build/pkgs/contourpy/dependencies create mode 100644 build/pkgs/contourpy/install-requires.txt create mode 100644 build/pkgs/contourpy/package-version.txt create mode 100644 build/pkgs/contourpy/spkg-install.in create mode 100644 build/pkgs/contourpy/type diff --git a/build/pkgs/contourpy/SPKG.rst b/build/pkgs/contourpy/SPKG.rst new file mode 100644 index 00000000000..f05a24b6922 --- /dev/null +++ b/build/pkgs/contourpy/SPKG.rst @@ -0,0 +1,18 @@ +contourpy: Python library for calculating contours of 2D quadrilateral grids +============================================================================ + +Description +----------- + +Python library for calculating contours of 2D quadrilateral grids + +License +------- + +BSD-3-Clause + +Upstream Contact +---------------- + +https://pypi.org/project/contourpy/ + diff --git a/build/pkgs/contourpy/checksums.ini b/build/pkgs/contourpy/checksums.ini new file mode 100644 index 00000000000..2b346a32ccc --- /dev/null +++ b/build/pkgs/contourpy/checksums.ini @@ -0,0 +1,5 @@ +tarball=contourpy-VERSION.tar.gz +sha1=f8dac7a79be96e2b8f085f79ba386dba54e99e99 +md5=0ed85863802b1323708b400ae7e7bbd7 +cksum=2680473500 +upstream_url=https://pypi.io/packages/source/c/contourpy/contourpy-VERSION.tar.gz diff --git a/build/pkgs/contourpy/dependencies b/build/pkgs/contourpy/dependencies new file mode 100644 index 00000000000..c3a19ef4fe1 --- /dev/null +++ b/build/pkgs/contourpy/dependencies @@ -0,0 +1,4 @@ +$(PYTHON) | $(PYTHON_TOOLCHAIN) pybind11 + +---------- +All lines of this file are ignored except the first. diff --git a/build/pkgs/contourpy/install-requires.txt b/build/pkgs/contourpy/install-requires.txt new file mode 100644 index 00000000000..4c311115d71 --- /dev/null +++ b/build/pkgs/contourpy/install-requires.txt @@ -0,0 +1 @@ +contourpy diff --git a/build/pkgs/contourpy/package-version.txt b/build/pkgs/contourpy/package-version.txt new file mode 100644 index 00000000000..af0b7ddbffd --- /dev/null +++ b/build/pkgs/contourpy/package-version.txt @@ -0,0 +1 @@ +1.0.6 diff --git a/build/pkgs/contourpy/spkg-install.in b/build/pkgs/contourpy/spkg-install.in new file mode 100644 index 00000000000..37ac1a53437 --- /dev/null +++ b/build/pkgs/contourpy/spkg-install.in @@ -0,0 +1,2 @@ +cd src +sdh_pip_install . diff --git a/build/pkgs/contourpy/type b/build/pkgs/contourpy/type new file mode 100644 index 00000000000..a6a7b9cd726 --- /dev/null +++ b/build/pkgs/contourpy/type @@ -0,0 +1 @@ +standard From 8169b3a9c502177e22b852894997d4fc724c7374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 27 Nov 2022 09:26:24 +0100 Subject: [PATCH 581/632] some formatting for NOTE:: blocks --- src/sage/geometry/newton_polygon.py | 2 +- src/sage/libs/linkages/padics/relaxed/flint.pxi | 4 ++-- src/sage/misc/latex_standalone.py | 2 +- src/sage/rings/padics/factory.py | 10 +++++----- src/sage/rings/padics/local_generic_element.pyx | 6 +++--- .../rings/padics/padic_capped_absolute_element.pyx | 2 +- src/sage/rings/padics/padic_fixed_mod_element.pyx | 6 +++--- src/sage/rings/padics/padic_generic_element.pyx | 11 ++++++++--- src/sage/rings/tate_algebra.py | 2 +- src/sage/rings/tate_algebra_element.pyx | 6 +++--- src/sage/rings/tate_algebra_ideal.pyx | 10 +++++----- 11 files changed, 33 insertions(+), 28 deletions(-) diff --git a/src/sage/geometry/newton_polygon.py b/src/sage/geometry/newton_polygon.py index dd3d58f58b5..4f253741ab7 100644 --- a/src/sage/geometry/newton_polygon.py +++ b/src/sage/geometry/newton_polygon.py @@ -272,7 +272,7 @@ def __pow__(self, exp, ignored=None): This Newton polygon scaled by a factor ``exp``. - NOTE:: + .. NOTE:: If ``self`` is the Newton polygon of a polynomial `f`, then ``self^exp`` is the Newton polygon of `f^{exp}`. diff --git a/src/sage/libs/linkages/padics/relaxed/flint.pxi b/src/sage/libs/linkages/padics/relaxed/flint.pxi index 3422f3ff328..5955ea23e18 100644 --- a/src/sage/libs/linkages/padics/relaxed/flint.pxi +++ b/src/sage/libs/linkages/padics/relaxed/flint.pxi @@ -259,7 +259,7 @@ cdef inline void digit_smallest(cdigit res, cdigit carry, cdigit a, PowComputer_ - ``a`` -- a ``cdigit``, the digit to reduce - ``prime_pow`` -- the PowComputer for the ring - NOTE:: + .. NOTE:: This function assumes that ``a`` is always reduced in the usual sense, that is belongs to the range `[0, p-1]`. @@ -383,7 +383,7 @@ cdef inline void element_get_slice(fmpz_poly_t res, fmpz_poly_t x, slong start, - ``start`` -- an integer, the start position of the slice - ``length`` -- an integer, the length of the slice - NOTE:: + .. NOTE:: The function only sets up a pointer to the requested slice (the slice is not copied). Hence any future modification diff --git a/src/sage/misc/latex_standalone.py b/src/sage/misc/latex_standalone.py index cdfdd6b6cbb..2e6874dde77 100644 --- a/src/sage/misc/latex_standalone.py +++ b/src/sage/misc/latex_standalone.py @@ -346,7 +346,7 @@ def _repr_(self): It contains the first few and last few lines of the content. - NOTE:: + .. NOTE:: Use ``print(t)`` or ``str(t)`` to show or get the full content. diff --git a/src/sage/rings/padics/factory.py b/src/sage/rings/padics/factory.py index 72506764209..044dcae0bed 100644 --- a/src/sage/rings/padics/factory.py +++ b/src/sage/rings/padics/factory.py @@ -2949,12 +2949,12 @@ def ZpLF(p, prec=None, *args, **kwds): See documentation for :func:`Zp` for a description of the input parameters. - NOTE: + .. NOTE:: - The precision is tracked using automatic differentiation - techniques (see [CRV2018]_ and [CRV2014]_). - Floating point `p`-adic numbers are used for the computation - of the differential (which is then not exact). + The precision is tracked using automatic differentiation + techniques (see [CRV2018]_ and [CRV2014]_). + Floating point `p`-adic numbers are used for the computation + of the differential (which is then not exact). EXAMPLES:: diff --git a/src/sage/rings/padics/local_generic_element.pyx b/src/sage/rings/padics/local_generic_element.pyx index 0733e0a483f..0571aa5cf4c 100644 --- a/src/sage/rings/padics/local_generic_element.pyx +++ b/src/sage/rings/padics/local_generic_element.pyx @@ -737,10 +737,10 @@ cdef class LocalGenericElement(CommutativeRingElement): The square root or the list of all square roots of this element. - NOTE: + .. NOTE:: - The square root is chosen (resp. the square roots are ordered) in - a deterministic way, which is compatible with change of precision. + The square root is chosen (resp. the square roots are ordered) in + a deterministic way, which is compatible with change of precision. EXAMPLES:: diff --git a/src/sage/rings/padics/padic_capped_absolute_element.pyx b/src/sage/rings/padics/padic_capped_absolute_element.pyx index e48abd895ae..69d5b474f20 100644 --- a/src/sage/rings/padics/padic_capped_absolute_element.pyx +++ b/src/sage/rings/padics/padic_capped_absolute_element.pyx @@ -320,7 +320,7 @@ cdef class pAdicCappedAbsoluteElement(CAElement): this valuation (and beyond) to see if they can contribute to the series. - NOTE:: + .. NOTE:: The function does not check that its argument ``self`` is 1 in the residue field. If this assumption is not fulfilled diff --git a/src/sage/rings/padics/padic_fixed_mod_element.pyx b/src/sage/rings/padics/padic_fixed_mod_element.pyx index a53b0da7525..06a43a09610 100644 --- a/src/sage/rings/padics/padic_fixed_mod_element.pyx +++ b/src/sage/rings/padics/padic_fixed_mod_element.pyx @@ -389,7 +389,7 @@ cdef class pAdicFixedModElement(FMElement): this valuation (and beyond) to see if they can contribute to the series. - NOTE:: + .. NOTE:: The function does not check that its argument ``self`` is 1 in the residue field. If this assumption is not fulfilled @@ -459,7 +459,7 @@ cdef class pAdicFixedModElement(FMElement): - ``aprec`` -- an integer, the precision to which to compute the exponential - NOTE:: + .. NOTE:: The function does not check that its argument ``self`` is the disk of convergence of ``exp``. If this assumption is not @@ -523,7 +523,7 @@ cdef class pAdicFixedModElement(FMElement): method. See :meth:`log` for more details about the possible algorithms. - NOTE:: + .. NOTE:: The function does not check that its argument ``self`` is the disk of convergence of ``exp``. If this assumption is not diff --git a/src/sage/rings/padics/padic_generic_element.pyx b/src/sage/rings/padics/padic_generic_element.pyx index 4248994b88e..44fb57e0178 100644 --- a/src/sage/rings/padics/padic_generic_element.pyx +++ b/src/sage/rings/padics/padic_generic_element.pyx @@ -2004,7 +2004,10 @@ cdef class pAdicGenericElement(LocalGenericElement): - ``self`` -- a p-adic element - ``p`` -- a prime (default: None). If specified, will make sure that p==self.parent().prime() - NOTE: The optional argument p is used for consistency with the valuation methods on integer and rational. + .. NOTE:: + + The optional argument p is used for consistency with the valuation + methods on integer and rational. OUTPUT: @@ -2126,8 +2129,10 @@ cdef class pAdicGenericElement(LocalGenericElement): - ``self`` -- a p-adic element - ``p`` -- a prime (default: ``None``). If specified, will make sure that ``p == self.parent().prime()`` - NOTE: The optional argument p is used for consistency with the valuation methods on integer and rational. + .. NOTE:: + The optional argument p is used for consistency with the valuation + methods on integer and rational. OUTPUT: @@ -2456,7 +2461,7 @@ cdef class pAdicGenericElement(LocalGenericElement): this valuation (and beyond) to see if they can contribute to the series. - NOTE:: + .. NOTE:: The function does not check that its argument ``self`` is 1 in the residue field. If this assumption is not fulfilled diff --git a/src/sage/rings/tate_algebra.py b/src/sage/rings/tate_algebra.py index 701278b2eb1..5bb4fc21679 100644 --- a/src/sage/rings/tate_algebra.py +++ b/src/sage/rings/tate_algebra.py @@ -1148,7 +1148,7 @@ def precision_cap(self): """ Return the precision cap of this Tate algebra. - NOTE:: + .. NOTE:: The precision cap is the truncation precision used for arithmetic operations computed by diff --git a/src/sage/rings/tate_algebra_element.pyx b/src/sage/rings/tate_algebra_element.pyx index 02b8305cd17..0ef1f8d86fe 100644 --- a/src/sage/rings/tate_algebra_element.pyx +++ b/src/sage/rings/tate_algebra_element.pyx @@ -1715,9 +1715,9 @@ cdef class TateAlgebraElement(CommutativeAlgebraElement): the precision at which the result is computed, if ``None``, the result is truncated according to the cap of the parent - NOTE: + .. NOTE:: - The ``n``-th root is computed as `\exp(\frac 1 n \log(f))`. + The ``n``-th root is computed as `\exp(\frac 1 n \log(f))`. EXAMPLES:: @@ -3318,7 +3318,7 @@ cdef class TateAlgebraElement(CommutativeAlgebraElement): - ``divisors`` -- a series, or a list of series - NOTE:: + NOTE: The condition on the remainder is that it has diff --git a/src/sage/rings/tate_algebra_ideal.pyx b/src/sage/rings/tate_algebra_ideal.pyx index ca4907a5994..58025c6069d 100644 --- a/src/sage/rings/tate_algebra_ideal.pyx +++ b/src/sage/rings/tate_algebra_ideal.pyx @@ -116,9 +116,9 @@ class TateAlgebraIdeal(Ideal_generic): - it is sorted, in the sense that the leading term of `g_i` is greater than the leading of `g_{i+1}` for all `i`. - NOTE: + .. NOTE:: - The result of this method is cached. + The result of this method is cached. EXAMPLES:: @@ -422,10 +422,10 @@ def groebner_basis_buchberger(I, prec, py_integral): Grobner basis of the ideal generated by the same generators over the ring over the ring of integers - NOTE:: + .. NOTE:: - This function is not meant to be called directly, but through the - ``groebner_basis`` method of Tate algebra ideals. + This function is not meant to be called directly, but through the + ``groebner_basis`` method of Tate algebra ideals. EXAMPLES:: From 8fc9e87186d9ae1483fe1b8ae8ffdd1c071e5c77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 27 Nov 2022 09:52:03 +0100 Subject: [PATCH 582/632] less use of type "long" in pyx files (outside of p-adics) --- src/sage/libs/ecl.pyx | 18 ++++++++---------- src/sage/libs/mpmath/ext_impl.pyx | 4 +--- src/sage/libs/ntl/ntl_ZZ.pyx | 5 +---- src/sage/libs/ntl/ntl_ZZ_p.pyx | 4 +--- src/sage/libs/ntl/ntl_ZZ_pE.pyx | 4 +--- src/sage/libs/ntl/ntl_lzz_p.pyx | 2 +- src/sage/libs/ntl/ntl_lzz_pX.pyx | 2 +- src/sage/rings/integer.pyx | 9 +++------ src/sage/rings/rational.pyx | 18 +++++------------- src/sage/rings/real_mpfr.pyx | 4 +--- 10 files changed, 23 insertions(+), 47 deletions(-) diff --git a/src/sage/libs/ecl.pyx b/src/sage/libs/ecl.pyx index 2cfac57f544..8f46570313d 100644 --- a/src/sage/libs/ecl.pyx +++ b/src/sage/libs/ecl.pyx @@ -429,16 +429,14 @@ cdef cl_object python_to_ecl(pyobj, bint read_strings) except NULL: return ECL_NIL elif pyobj is None: return ECL_NIL - elif isinstance(pyobj,long): - if pyobj >= MOST_NEGATIVE_FIXNUM and pyobj <= MOST_POSITIVE_FIXNUM: + elif isinstance(pyobj, int): + if MOST_NEGATIVE_FIXNUM <= pyobj <= MOST_POSITIVE_FIXNUM: return ecl_make_integer(pyobj) else: return python_to_ecl(Integer(pyobj), read_strings) - elif isinstance(pyobj,int): - return ecl_make_integer(pyobj) - elif isinstance(pyobj,float): + elif isinstance(pyobj, float): return ecl_make_doublefloat(pyobj) - elif isinstance(pyobj,unicode): + elif isinstance(pyobj, unicode): try: s = str_to_bytes(pyobj, 'ascii') except UnicodeEncodeError: @@ -452,22 +450,22 @@ cdef cl_object python_to_ecl(pyobj, bint read_strings) except NULL: return ecl_safe_funcall(read_from_string_clobj, o) else: return o - elif isinstance(pyobj,bytes): + elif isinstance(pyobj, bytes): s=pyobj if read_strings: return ecl_safe_read_string(s) else: return ecl_cstring_to_base_string_or_nil(s) - elif isinstance(pyobj,Integer): + elif isinstance(pyobj, Integer): if pyobj >= MOST_NEGATIVE_FIXNUM and pyobj <= MOST_POSITIVE_FIXNUM: return ecl_make_integer(pyobj) else: return ecl_bignum_from_mpz( (pyobj).value ) - elif isinstance(pyobj,Rational): + elif isinstance(pyobj, Rational): return ecl_make_ratio( python_to_ecl( (pyobj).numerator(), read_strings ), python_to_ecl( (pyobj).denominator(), read_strings )) - elif isinstance(pyobj,EclObject): + elif isinstance(pyobj, EclObject): return (pyobj).obj elif isinstance(pyobj, list): L = ECL_NIL diff --git a/src/sage/libs/mpmath/ext_impl.pyx b/src/sage/libs/mpmath/ext_impl.pyx index f9b0ff2d42c..15557561806 100644 --- a/src/sage/libs/mpmath/ext_impl.pyx +++ b/src/sage/libs/mpmath/ext_impl.pyx @@ -44,10 +44,8 @@ from sage.rings.integer cimport Integer from sage.libs.gmp.pylong cimport * cdef mpz_set_integer(mpz_t v, x): - if isinstance(x, long): + if isinstance(x, int): mpz_set_pylong(v, x) - elif isinstance(x, int): - mpz_set_si(v, PyInt_AS_LONG(x)) elif isinstance(x, Integer): mpz_set(v, (x).value) else: diff --git a/src/sage/libs/ntl/ntl_ZZ.pyx b/src/sage/libs/ntl/ntl_ZZ.pyx index 783bceb4121..48a329f3055 100644 --- a/src/sage/libs/ntl/ntl_ZZ.pyx +++ b/src/sage/libs/ntl/ntl_ZZ.pyx @@ -85,11 +85,8 @@ cdef class ntl_ZZ(): """ if isinstance(v, ntl_ZZ): self.x = (v).x - elif isinstance(v, long): - # Note: This case should be first since on Python 3 long is int - PyLong_to_ZZ(&self.x, v) elif isinstance(v, int): - ZZ_conv_from_int(self.x, PyInt_AS_LONG(v)) + PyLong_to_ZZ(&self.x, v) elif isinstance(v, Integer): self.set_from_sage_int(v) elif v is not None: diff --git a/src/sage/libs/ntl/ntl_ZZ_p.pyx b/src/sage/libs/ntl/ntl_ZZ_p.pyx index 469d62f2812..b5511abb891 100644 --- a/src/sage/libs/ntl/ntl_ZZ_p.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_p.pyx @@ -117,11 +117,9 @@ cdef class ntl_ZZ_p(): if v is not None: if isinstance(v, ntl_ZZ_p): self.x = (v).x - elif isinstance(v, long): + elif isinstance(v, int): PyLong_to_ZZ(&temp, v) self.x = ZZ_to_ZZ_p(temp) - elif isinstance(v, int): - self.x = int_to_ZZ_p(v) elif isinstance(v, Integer): mpz_to_ZZ(&temp, (v).value) self.x = ZZ_to_ZZ_p(temp) diff --git a/src/sage/libs/ntl/ntl_ZZ_pE.pyx b/src/sage/libs/ntl/ntl_ZZ_pE.pyx index 9e2d06bb58e..ed45e34f0be 100644 --- a/src/sage/libs/ntl/ntl_ZZ_pE.pyx +++ b/src/sage/libs/ntl/ntl_ZZ_pE.pyx @@ -117,11 +117,9 @@ cdef class ntl_ZZ_pE(): tmp_zzpx = ntl_ZZ_pX(v, self.c.pc) self.c.restore_c() # allocating tmp_zzpx can change the current modulus trac #25790 self.x = ZZ_pX_to_ZZ_pE(tmp_zzpx.x) - elif isinstance(v, long): + elif isinstance(v, int): PyLong_to_ZZ(&temp, v) self.x = ZZ_to_ZZ_pE(temp) - elif isinstance(v, int): - self.x = long_to_ZZ_pE(v) elif isinstance(v, ntl_ZZ_p): self.x = ZZ_p_to_ZZ_pE((v).x) elif isinstance(v, ntl_ZZ): diff --git a/src/sage/libs/ntl/ntl_lzz_p.pyx b/src/sage/libs/ntl/ntl_lzz_p.pyx index 26575dd99ec..983e3056404 100644 --- a/src/sage/libs/ntl/ntl_lzz_p.pyx +++ b/src/sage/libs/ntl/ntl_lzz_p.pyx @@ -139,7 +139,7 @@ cdef class ntl_zz_p(): self.c = modulus elif isinstance(modulus, Integer): self.c = ntl_zz_pContext(modulus) - elif isinstance(modulus, long): + elif isinstance(modulus, int): self.c = ntl_zz_pContext(modulus) else: try: diff --git a/src/sage/libs/ntl/ntl_lzz_pX.pyx b/src/sage/libs/ntl/ntl_lzz_pX.pyx index 50d9447b295..98bd0d58658 100644 --- a/src/sage/libs/ntl/ntl_lzz_pX.pyx +++ b/src/sage/libs/ntl/ntl_lzz_pX.pyx @@ -155,7 +155,7 @@ cdef class ntl_zz_pX(): self.c = modulus elif isinstance(modulus, Integer): self.c = ntl_zz_pContext(modulus) - elif isinstance(modulus, long): + elif isinstance(modulus, int): self.c = ntl_zz_pContext(modulus) else: try: diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index be8d08412fd..7cac3687e87 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -632,11 +632,8 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): if isinstance(x, Integer): set_from_Integer(self, x) - elif isinstance(x, long): - mpz_set_pylong(self.value, x) - elif isinstance(x, int): - mpz_set_si(self.value, PyInt_AS_LONG(x)) + mpz_set_pylong(self.value, x) elif isinstance(x, float): n = long(x) @@ -683,7 +680,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): if b == 2: # we use a faster method for j from 0 <= j < len(x): otmp = x[j] - if isinstance(otmp, (int, long)): + if isinstance(otmp, int): if ( otmp) == 1: mpz_setbit(self.value, j) if ( otmp) != 0: @@ -707,7 +704,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): elif is_numpy_type(type(x)): import numpy if isinstance(x, numpy.integer): - mpz_set_pylong(self.value, long(x)) + mpz_set_pylong(self.value, int(x)) return elif type(x) is gmpy2.mpz: diff --git a/src/sage/rings/rational.pyx b/src/sage/rings/rational.pyx index f215dff7e67..6964d3a17d1 100644 --- a/src/sage/rings/rational.pyx +++ b/src/sage/rings/rational.pyx @@ -576,12 +576,8 @@ cdef class Rational(sage.structure.element.FieldElement): if isinstance(x, Rational): set_from_Rational(self, x) - elif isinstance(x, long): - mpz_set_pylong(mpq_numref(self.value), x) - elif isinstance(x, int): - i = x - mpq_set_si(self.value, i, 1) + mpz_set_pylong(mpq_numref(self.value), x) elif isinstance(x, integer.Integer): set_from_Integer(self, x) @@ -629,19 +625,15 @@ cdef class Rational(sage.structure.element.FieldElement): num = x[0] denom = x[1] - if isinstance(num, long): + if isinstance(num, int): mpz_set_pylong(mpq_numref(self.value), num) - elif isinstance(num, int): # Python 2 only - mpz_set_si(mpq_numref(self.value), num) else: if not isinstance(num, integer.Integer): num = integer.Integer(num, base) mpz_set(mpq_numref(self.value), (num).value) - if isinstance(denom, long): + if isinstance(denom, int): mpz_set_pylong(mpq_denref(self.value), denom) - elif isinstance(denom, int): # Python 2 only - mpz_set_si(mpq_denref(self.value), denom) else: if not isinstance(denom, integer.Integer): denom = integer.Integer(denom, base) @@ -3695,7 +3687,7 @@ cdef class Rational(sage.structure.element.FieldElement): 32/3 """ if isinstance(x, Rational): - if isinstance(y, (int, long, integer.Integer)): + if isinstance(y, (int, integer.Integer)): return (x)._lshift(y) if isinstance(y, Rational): if mpz_cmp_si(mpq_denref((y).value), 1) != 0: @@ -3743,7 +3735,7 @@ cdef class Rational(sage.structure.element.FieldElement): 1/8 """ if isinstance(x, Rational): - if isinstance(y, (int, long, integer.Integer)): + if isinstance(y, (int, integer.Integer)): return (x)._rshift(y) if isinstance(y, Rational): if mpz_cmp_si(mpq_denref((y).value), 1) != 0: diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index 09ace5ec1eb..3727cbff3f3 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -1472,11 +1472,9 @@ cdef class RealNumber(sage.structure.element.RingElement): elif isinstance(x, Gen) and typ((x).g) == t_REAL: _gen = x self._set_from_GEN_REAL(_gen.g) - elif isinstance(x, long): + elif isinstance(x, int): x = Integer(x) mpfr_set_z(self.value, (x).value, parent.rnd) - elif isinstance(x, int): - mpfr_set_si(self.value, x, parent.rnd) elif isinstance(x, float): mpfr_set_d(self.value, x, parent.rnd) elif isinstance(x, complex) and x.imag == 0: From 6d59adca722d846d1f9d618a38c5b3e8bd3595ea Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 27 Nov 2022 19:45:19 +0100 Subject: [PATCH 583/632] fix order of multiplication in binary_factorizations --- src/sage/categories/coxeter_groups.py | 65 +++++++++++++++++++++------ 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/src/sage/categories/coxeter_groups.py b/src/sage/categories/coxeter_groups.py index b7ee2ac9fe9..bb310b76109 100644 --- a/src/sage/categories/coxeter_groups.py +++ b/src/sage/categories/coxeter_groups.py @@ -1235,6 +1235,12 @@ def _test_has_descent(self, **options): sage: W = CoxeterGroups().example() sage: W._test_has_descent() + + sage: W = Permutations(4) + sage: W._test_has_descent() + + sage: W = SignedPermutations(3) + sage: W._test_has_descent() """ tester = self._tester(**options) s = self.simple_reflections() @@ -1254,12 +1260,12 @@ def _test_has_descent(self, **options): tester.assertEqual(s[i].has_descent(j, positive=True), i != j) if i == j: continue - u = s[i] * s[j] - v = s[j] * s[i] - tester.assertTrue((s[i] * s[j]).has_descent(i, side='left')) - tester.assertTrue((s[i] * s[j]).has_descent(j, side='right')) - tester.assertEqual((s[i] * s[j]).has_descent(j, side='left'), u == v) - tester.assertEqual((s[i] * s[j]).has_descent(i, side='right'), u == v) + u = s[i].apply_simple_reflection_right(j) + v = s[j].apply_simple_reflection_right(i) + tester.assertTrue(u.has_descent(i, side='left')) + tester.assertTrue(u.has_descent(j, side='right')) + tester.assertEqual(u.has_descent(j, side='left'), u == v) + tester.assertEqual(u.has_descent(i, side='right'), u == v) def _test_descents(self, **options): """ @@ -1297,6 +1303,12 @@ def _test_coxeter_relations(self, **options): sage: cm = CartanMatrix([[2,-5,0],[-2,2,-1],[0,-1,2]]) sage: W = WeylGroup(cm) sage: W._test_coxeter_relations() + + sage: W = Permutations(4) + sage: W._test_coxeter_relations() + + sage: W = SignedPermutations(3) + sage: W._test_coxeter_relations() """ tester = self._tester(**options) s = self.simple_reflections() @@ -1868,6 +1880,26 @@ def absolute_le(self, other): False sage: w1.absolute_le(w1) True + + TESTS: + + Check that this is independent of the implementation of the group, see :trac:`34799`:: + + sage: W1 = WeylGroup(['A',2]) + sage: W2 = Permutations(3) + sage: P = lambda pi: W2(list(pi.to_permutation())) + sage: d1 = set((P(w1), P(w2)) for w1 in W1 for w2 in W1 if w1.absolute_le(w2)) + sage: d2 = set((w1, w2) for w1 in W2 for w2 in W2 if w1.absolute_le(w2)) + sage: d1 == d2 + True + + sage: W1 = WeylGroup(['B',2]) + sage: W2 = SignedPermutations(2) + sage: P = lambda pi: W2(list(pi.to_permutation())) + sage: d1 = set((P(w1), P(w2)) for w1 in W1 for w2 in W1 if w1.absolute_le(w2)) + sage: d2 = set((w1, w2) for w1 in W2 for w2 in W2 if w1.absolute_le(w2)) + sage: d1 == d2 + True """ if self == other: return True @@ -2055,6 +2087,16 @@ def binary_factorizations(self, predicate=ConstantFunction(True)): sage: w0.binary_factorizations().category() Category of finite enumerated sets + + Check that this is independent of the implementation of the group, see :trac:`34799`:: + + sage: W1 = WeylGroup(['A',3]) + sage: W2 = Permutations(4) + sage: P = lambda pi: W2(list(pi.to_permutation())) + sage: d1 = {P(pi): set((P(w[0]), P(w[1])) for w in pi.binary_factorizations()) for pi in W1} + sage: d2 = {pi: set(pi.binary_factorizations()) for pi in W2} + sage: d1 == d2 + True """ from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet_forest W = self.parent() @@ -2064,11 +2106,11 @@ def binary_factorizations(self, predicate=ConstantFunction(True)): s = W.simple_reflections() def succ(u_v): - (u, v) = u_v + u, v = u_v for i in v.descents(side='left'): - u1 = u * s[i] + u1 = u.apply_simple_reflection_right(i) if i == u1.first_descent() and predicate(u1): - yield (u1, s[i] * v) + yield u1, v.apply_simple_reflection_left(i) return RecursivelyEnumeratedSet_forest(((W.one(), self),), succ, category=FiniteEnumeratedSets()) @@ -2214,7 +2256,6 @@ def lower_cover_reflections(self, side='right'): [s2*s3*s2, s3, s1] """ - if side == 'left': self = self.inverse() return [x[1] for x in self.bruhat_lower_covers_reflections()] @@ -2261,7 +2302,6 @@ def cover_reflections(self, side='right'): [s4, s2, s1*s2*s1, s3*s4*s3] """ - if side == 'left': self = self.inverse() return [x[1] for x in self.bruhat_upper_covers_reflections()] @@ -2596,7 +2636,6 @@ def apply_demazure_product(self, element, side='right', s2*s3*s4*s1*s2*s3*s4*s2*s3*s2*s1 """ - # if self and element have the same parent if self.parent().is_parent_of(element): the_word = element.reduced_word() @@ -2639,7 +2678,6 @@ def min_demazure_product_greater(self, element): s4*s2 """ - # if self and element have the same parent if self.parent().is_parent_of(element): the_word = element.reduced_word() @@ -2772,7 +2810,6 @@ def deodhar_lift_down(self, w, index_set): s2*s3*s2 """ - vmin = self.coset_representative(index_set) wmin = w.coset_representative(index_set) if not wmin.bruhat_le(vmin): From 37d60d4b87569ed4524fc76c5aeeb2984e683c92 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Mon, 28 Nov 2022 07:51:46 +0100 Subject: [PATCH 584/632] tests also the option setting multiplication order --- src/sage/categories/coxeter_groups.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/sage/categories/coxeter_groups.py b/src/sage/categories/coxeter_groups.py index bb310b76109..0b353313517 100644 --- a/src/sage/categories/coxeter_groups.py +++ b/src/sage/categories/coxeter_groups.py @@ -1238,6 +1238,9 @@ def _test_has_descent(self, **options): sage: W = Permutations(4) sage: W._test_has_descent() + sage: sage.combinat.permutation.Permutations.options.mult = "r2l" + sage: W._test_has_descent() + sage: sage.combinat.permutation.Permutations.options._reset() sage: W = SignedPermutations(3) sage: W._test_has_descent() @@ -1306,6 +1309,9 @@ def _test_coxeter_relations(self, **options): sage: W = Permutations(4) sage: W._test_coxeter_relations() + sage: sage.combinat.permutation.Permutations.options.mult = "r2l" + sage: W._test_coxeter_relations() + sage: sage.combinat.permutation.Permutations.options._reset() sage: W = SignedPermutations(3) sage: W._test_coxeter_relations() @@ -1892,6 +1898,11 @@ def absolute_le(self, other): sage: d2 = set((w1, w2) for w1 in W2 for w2 in W2 if w1.absolute_le(w2)) sage: d1 == d2 True + sage: sage.combinat.permutation.Permutations.options.mult = "r2l" + sage: d3 = set((w1, w2) for w1 in W2 for w2 in W2 if w1.absolute_le(w2)) + sage: d1 == d3 + True + sage: sage.combinat.permutation.Permutations.options._reset() sage: W1 = WeylGroup(['B',2]) sage: W2 = SignedPermutations(2) @@ -2097,6 +2108,11 @@ def binary_factorizations(self, predicate=ConstantFunction(True)): sage: d2 = {pi: set(pi.binary_factorizations()) for pi in W2} sage: d1 == d2 True + sage: sage.combinat.permutation.Permutations.options.mult = "r2l" + sage: d3 = {pi: set(pi.binary_factorizations()) for pi in W2} + sage: d1 == d3 + True + sage: sage.combinat.permutation.Permutations.options._reset() """ from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet_forest W = self.parent() From 3b5f11c7d1cdc9b10b8642693b4ebf470f29b8f2 Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Fri, 25 Nov 2022 13:42:54 +0000 Subject: [PATCH 585/632] Add note for typo in reference for T-sequences construction --- src/sage/combinat/t_sequences.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/sage/combinat/t_sequences.py b/src/sage/combinat/t_sequences.py index 44dc73bcd89..fbd08ca30ca 100644 --- a/src/sage/combinat/t_sequences.py +++ b/src/sage/combinat/t_sequences.py @@ -31,7 +31,7 @@ """ -# **************************************************************************** +# *************************************************************************** # Copyright (C) 2022 Matteo Cati matteo.cati@keble.ox.ac.uk # # This program is free software: you can redistribute it and/or modify @@ -337,6 +337,13 @@ def T_sequences_construction_I(turyn_sequences, check=True): Traceback (most recent call last): ... AssertionError + + .. NOTE:: + + The construction detailed in [Seb2017]_ contains a typo. The first two sequences are + defined as `T_1 = \frac{1}{2}(X+Y); 0_{l-1}` and `T_2 = \frac{1}{2}(X-Y); 0_{l-1}`, + but the correct formulas are `T_1 = \frac{1}{2}(X+U); 0_{l-1}` and + `T_2 = \frac{1}{2}(X-U); 0_{l-1}`. """ assert len(turyn_sequences) == 4 From dcb2dda1836ca3a4b9b971ac7807301d5ac83405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 28 Nov 2022 13:58:24 +0100 Subject: [PATCH 586/632] fix some compilation warnings --- src/sage/structure/parent.pyx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index 585ec9e559e..21447d96b1a 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -114,7 +114,6 @@ from sage.cpython.type cimport can_assign_class cimport sage.categories.morphism as morphism cimport sage.categories.map as map from sage.structure.debug_options cimport debug -from sage.structure.richcmp cimport rich_to_bool from sage.structure.sage_object cimport SageObject from sage.misc.cachefunc import cached_method from sage.misc.lazy_attribute import lazy_attribute From 5a4ba58a3ebe5f882d0aa91ece68a7cdf3df3ec0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 29 Nov 2022 09:21:46 +0100 Subject: [PATCH 587/632] remove another unused import --- src/sage/rings/finite_rings/integer_mod.pyx | 2 +- src/sage/rings/padics/pow_computer_relative.pyx | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/rings/finite_rings/integer_mod.pyx b/src/sage/rings/finite_rings/integer_mod.pyx index 58218d9a193..d268ff97c9a 100644 --- a/src/sage/rings/finite_rings/integer_mod.pyx +++ b/src/sage/rings/finite_rings/integer_mod.pyx @@ -98,7 +98,7 @@ cimport sage.rings.integer from sage.rings.integer cimport Integer from sage.structure.coerce cimport py_scalar_to_element -from sage.structure.richcmp import rich_to_bool_sgn, rich_to_bool +from sage.structure.richcmp cimport rich_to_bool_sgn, rich_to_bool import sage.structure.element cimport sage.structure.element coerce_binop = sage.structure.element.coerce_binop diff --git a/src/sage/rings/padics/pow_computer_relative.pyx b/src/sage/rings/padics/pow_computer_relative.pyx index f28a46f03b7..29cee93968c 100644 --- a/src/sage/rings/padics/pow_computer_relative.pyx +++ b/src/sage/rings/padics/pow_computer_relative.pyx @@ -34,7 +34,6 @@ from cysignals.signals cimport sig_on, sig_off from sage.libs.gmp.mpz cimport mpz_init, mpz_clear, mpz_pow_ui from cpython.object cimport Py_EQ, Py_NE -from sage.structure.richcmp cimport richcmp_not_equal from sage.rings.integer cimport Integer from sage.rings.integer_ring import ZZ from sage.misc.cachefunc import cached_method From b936e188336b054ac3a3eb545cfc9c3caa21744a Mon Sep 17 00:00:00 2001 From: Matteo Cati Date: Tue, 29 Nov 2022 16:06:26 +0000 Subject: [PATCH 588/632] Fix documentation of T sequences --- src/sage/combinat/t_sequences.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/combinat/t_sequences.py b/src/sage/combinat/t_sequences.py index fbd08ca30ca..3477908156a 100644 --- a/src/sage/combinat/t_sequences.py +++ b/src/sage/combinat/t_sequences.py @@ -1,7 +1,7 @@ r""" T-sequences -T-sequences are sets of four (-1, 0, 1) sequences of length `t` where +T-sequences are tuples of four (-1, 0, 1) sequences of length `t` where for every `i` exactly one sequence has a nonzero entry at index `i` and for which the nonperiodic autocorrelation function is equal to zero (i.e. they are complementary). See Definition 7.5 of [Seb2017]_. @@ -10,7 +10,7 @@ if Turyn sequences of length `l` exists, there will be T-sequences of length `4l-1` and `2l-1`. -Turyn sequences are sets of four (-1, +1) sequences `X, U, Y, V` of length +Turyn sequences are tuples of four (-1, +1) sequences `X, U, Y, V` of length `l`, `l`, `l-1`, `l-1` with nonperiodic autocorrelation equal to zero and the additional constraints that: From 021fbe5fa512b8eec5698eb7b1922b8581477fba Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Tue, 29 Nov 2022 16:53:25 +0000 Subject: [PATCH 589/632] adding ref to the typo in old paper, and cosmetic changes --- src/doc/en/reference/references/index.rst | 14 +++++++------- src/sage/combinat/t_sequences.py | 14 ++++++++------ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 8dd552086ed..95df514117f 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1771,8 +1771,8 @@ REFERENCES: .. [CRSKKY1989] \G. Cohen, D. Rubie, J. Seberry, C. Koukouvinos, S. Kounias, M. Yamada, *A survey of base sequences, disjoint complementary sequences and OD(4t;t,t,t,t)*. - JCMCC. The Journal of Combinatorial Mathematics and Combinatorial Computing, - vol 5, 1989. + JCMCC. The Journal of Combinatorial Mathematics and Combinatorial Computing, + **5** (1989), 69-104. .. [CRST2006] \M. Chudnovsky, N. Robertson, P. Seymour, R. Thomas. *The strong perfect graph theorem*. @@ -1842,7 +1842,7 @@ REFERENCES: *Signature-based algorithms for Gröbner bases over Tate algebras*, :arxiv:`2002.04491` (2020) -.. [CW1972] \J. Cooper and J. Wallis. +.. [CW1972] \J. Cooper and J. Wallis. *A construction for Hadamard arrays*, Bulletin of the Australian Mathematical Society 7(2) (1972): 269-277. :doi:`10.1017/S0004972700045019` @@ -5276,9 +5276,9 @@ REFERENCES: *Wide-open encryption design offers flexible implementations*; in Cryptologia, (1985), pp. 75-91. -.. [Seb2017] \J. Seberry, +.. [Seb2017] \J. Seberry, *Orthogonal designs: Hadamard matrices, quadratic forms and algebras*. - (2017). :doi:`10.1007/978-3-319-59032-5`` + Springer 2017. :doi:`10.1007/978-3-319-59032-5` .. [SE1962] \N. E. Steenrod and D. B. A. Epstein, Cohomology operations, Ann. of Math. Stud. 50 (Princeton University @@ -5850,8 +5850,8 @@ REFERENCES: .. [TTWL2009] Trebst, Troyer, Wang and Ludwig, A short introduction to Fibonacci anyon models, :arxiv:`0902.3275`. -.. [Tur1974] \R. J. Turyn *Hadamard matrices, Baumert-Hall units, - four-symbol sequences, pulse compression, and surface wave encodings*. +.. [Tur1974] \R. J. Turyn *Hadamard matrices, Baumert-Hall units, + four-symbol sequences, pulse compression, and surface wave encodings*. Journal of Combinatorial Theory, Series A 16.3 (1974), pp 313–333. :doi:`10.1016/0097-3165(74)90056-9` diff --git a/src/sage/combinat/t_sequences.py b/src/sage/combinat/t_sequences.py index 3477908156a..70198a02b50 100644 --- a/src/sage/combinat/t_sequences.py +++ b/src/sage/combinat/t_sequences.py @@ -302,7 +302,7 @@ def T_sequences_construction_I(turyn_sequences, check=True): Construct T-sequences of length `2l-1` from Turyn sequences of length `l`. Given Turyn sequences `X, U, Y, V`, the T-sequences are constructed as described in - theorem 7.7 of [Seb2017]_: + Theorem 7.7 of [Seb2017]_: .. MATH:: @@ -330,6 +330,7 @@ def T_sequences_construction_I(turyn_sequences, check=True): [0, 0, 0, 0, 0, 1, 0]] TESTS:: + sage: seqs = turyn_sequences_smallcases(4) sage: is_T_sequences_set(T_sequences_construction_I(seqs)) True @@ -339,11 +340,12 @@ def T_sequences_construction_I(turyn_sequences, check=True): AssertionError .. NOTE:: - - The construction detailed in [Seb2017]_ contains a typo. The first two sequences are + + The construction detailed in [Seb2017]_ contains a typo. The first two sequences are defined as `T_1 = \frac{1}{2}(X+Y); 0_{l-1}` and `T_2 = \frac{1}{2}(X-Y); 0_{l-1}`, - but the correct formulas are `T_1 = \frac{1}{2}(X+U); 0_{l-1}` and - `T_2 = \frac{1}{2}(X-U); 0_{l-1}`. + but the correct formulas are `T_1 = \frac{1}{2}(X+U); 0_{l-1}` and + `T_2 = \frac{1}{2}(X-U); 0_{l-1}`. A very similar typo can be seen in the proof of + Theorem 9 in [CRSKKY1989]_ as well. """ assert len(turyn_sequences) == 4 @@ -534,4 +536,4 @@ def T_sequences_smallcases(t, existence=False, check=True): if existence: return False - raise ValueError(f'T Sequences of length {t} not yet implemented.') \ No newline at end of file + raise ValueError(f'T Sequences of length {t} not yet implemented.') From bd025a2e87389b9b7f5573049ad62cdd087e2bf0 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Tue, 29 Nov 2022 17:40:23 +0000 Subject: [PATCH 590/632] refs to biblio file, trimmed trailing whitespaces, docstring fix --- src/doc/en/reference/references/index.rst | 28 +++- src/sage/combinat/matrices/hadamard_matrix.py | 149 +++++++++--------- 2 files changed, 95 insertions(+), 82 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 95df514117f..0ebf28466b1 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1793,11 +1793,11 @@ REFERENCES: and groups*, 3rd. ed., Grundlehren der Mathematischen Wissenschaften, vol. 290, Springer-Verlag, New York, 1999. -.. [CS1988] Conway, J. H., and N. J. A. Sloane. “Low-Dimensional Lattices. IV. - The Mass Formula.” Proceedings of the Royal Society of London. +.. [CS1988] Conway, J. H., and N. J. A. Sloane. *Low-Dimensional Lattices. IV. + The Mass Formula*, Proceedings of the Royal Society of London. Series A, Mathematical and Physical Sciences, vol. 419, no. 1857, 1988, pp. 259-286. -.. [CS2003] \John E. Cremona and Michael Stoll. On The Reduction Theory of Binary Forms. +.. [CS2003] \John E. Cremona and Michael Stoll. *On The Reduction Theory of Binary Forms*. Journal für die reine und angewandte Mathematik, 565 (2003), 79-99. .. [CS2006] \J. E. Cremona, and S. Siksek, Computing a Lower Bound for the @@ -2793,6 +2793,10 @@ REFERENCES: Can. J. Math. 22 (1970) 597-614. :doi:`10.4153/CJM-1970-067-9` +.. [GS70s] \J.M. Goethals and J. J. Seidel, + *A skew Hadamard matrix of order 36*, + J. Aust. Math. Soc. 11(1970), 343-344 + .. [GS1975] \J.M. Goethals, and J. J. Seidel, *The regular two-graph on 276 vertices*, Discrete Mathematics 12, no. 2 (1975): 143-158. @@ -2849,8 +2853,13 @@ REFERENCES: .. [Ha2005] Gerhard Haring. [Online] Available: http://osdir.com/ml/python.db.pysqlite.user/2005-11/msg00047.html +.. [Ha83] \M. Hall, *Combinatorial Theory*, + 2nd edition, Wiley, 1983 + .. [Hac2016] \M. Hachimori. http://infoshako.sk.tsukuba.ac.jp/~hachi/math/library/dunce_hat_eng.html +.. [HadaWiki] Hadamard matrices on Wikipedia, :wikipedia:`Hadamard_matrix` + .. [Haf2004] Paul R. Hafner. *On the Graphs of Hoffman-Singleton and Higman-Sims*. The Electronic Journal of Combinatorics 11 (2004), #R77. http://www.combinatorics.org/Volume_11/PDF/v11i1r77.pdf @@ -3039,6 +3048,9 @@ REFERENCES: *The algebra of binary search trees*, :arxiv:`math/0401089v2`. +.. [Hora] \K. J. Horadam, *Hadamard Matrices and Their Applications*, + Princeton University Press, 2006. + .. [HP2003] \W. C. Huffman, V. Pless, Fundamentals of Error-Correcting Codes, Cambridge Univ. Press, 2003. @@ -3658,6 +3670,9 @@ REFERENCES: cohomologie*, in *Élie Cartan et les mathématiques d'aujourd'hui*, Astérisque hors série (1985), p. 257 +.. [KoSt08] \C. Koukouvinos, S. Stylianou, *On skew-Hadamard matrices*, + Discrete Math. **308** (2008) 2723-2731 + .. [KP2002] Volker Kaibel and Marc E. Pfetsch, "Computing the Face Lattice of a Polytope from its Vertex-Facet Incidences", Computational Geometry: Theory and Applications, Volume @@ -5380,6 +5395,8 @@ REFERENCES: systems: Algorithmic, game-theoretic, and logical foundations.* Cambridge University Press, 2008. +.. [SloaHada] \N.J.A. Sloane's Library of Hadamard Matrices, at https://neilsloane.com/hadamard/ + .. [SMMK2013] \T. Suzaki, K. Minematsu, S. Morioka, and E. Kobayashi, *TWINE: A lightweight block cipher for multiple platforms*; in SAC, (2012), pp. 338-354. @@ -6105,9 +6122,12 @@ REFERENCES: *Numerical modular symbols for elliptic curves*. Math. Comp. 87 (2018), no. 313, 2393–2423. +.. [Wall71] \J. Wallis, *A skew-Hadamard matrix of order 92*, + Bull. Aust. Math. Soc. **5** (1971), 203-204 + .. [WW1972] \J. Wallis and A.L. Whiteman, Some classes of Hadamard matrices with constant diagonal, - Bull. Austral. Math. Soc. 7(1972), 233-249 + Bull. Austral. Math. Soc. **7** (1972), 233-249 .. [WW1991] Michelle Wachs and Dennis White, *p, q-Stirling numbers and set partition statistics*, Journal of Combinatorial diff --git a/src/sage/combinat/matrices/hadamard_matrix.py b/src/sage/combinat/matrices/hadamard_matrix.py index c257c0783a1..4fea4bfdf2e 100644 --- a/src/sage/combinat/matrices/hadamard_matrix.py +++ b/src/sage/combinat/matrices/hadamard_matrix.py @@ -31,7 +31,7 @@ The module below implements the Paley constructions (see for example [Hora]_) and the Sylvester construction. It also allows you to pull a -Hadamard matrix from the database at [HadaSloa]_. +Hadamard matrix from the database at [SloaHada]_. AUTHORS: @@ -39,11 +39,11 @@ REFERENCES: -.. [HadaSloa] \N.J.A. Sloane's Library of Hadamard Matrices, at - http://neilsloane.com/hadamard/ -.. [HadaWiki] Hadamard matrices on Wikipedia, :wikipedia:`Hadamard_matrix` -.. [Hora] \K. J. Horadam, Hadamard Matrices and Their Applications, - Princeton University Press, 2006. +- [SloaHada]_ + +- [HadaWiki]_ + +- [Hora]_ """ #***************************************************************************** @@ -254,7 +254,7 @@ def hadamard_matrix_williamson_type(a, b, c, d, check=True): sage: c = [ 1, -1, -1] sage: d = [ 1, -1, -1] sage: M = hadamard_matrix_williamson_type(a,b,c,d,check=True) - + TESTS:: sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_williamson_type, is_hadamard_matrix @@ -293,7 +293,7 @@ def williamson_type_quadruples_smallcases(n, existence=False): r""" Quadruples of matrices that can be used to construct Williamson type Hadamard matrices. - This function contains for some values of n, four `n\times n` matrices used in the + This function contains for some values of n, four `n\times n` matrices used in the Williamson construction of Hadamard matrices. Namely, the function returns the first row of 4 `n\times n` circulant matrices with the properties described in :func:`sage.combinat.matrices.hadamard_matrix.hadamard_matrix_williamson_type`. @@ -340,12 +340,12 @@ def williamson_type_quadruples_smallcases(n, existence=False): 43: ([1, 1, -1, -1, -1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 1], [1, 1, 1, -1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, 1], [1, 1, -1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1, -1, 1], - [1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1]), + [1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 1, -1, -1, -1]), } if existence: return n in db - + if n not in db: raise ValueError("The Williamson type quadruple of order %s is not yet implemented." % n) a, b, c, d = map(vector, db[n]) @@ -355,7 +355,7 @@ def williamson_hadamard_matrix_smallcases(n, existence=False, check=True): r""" Construct Williamson type Hadamard matrices for some small values of n. - This function uses the data contained in + This function uses the data contained in :func:`sage.combinat.matrices.hadamard_matrix.williamson_type_quadruples_smallcases` to create Hadamard matrices of the Williamson type, using the construction from :func:`sage.combinat.matrices.hadamard_matrix.hadamard_matrix_williamson_type`. @@ -381,7 +381,7 @@ def williamson_hadamard_matrix_smallcases(n, existence=False, check=True): ValueError: The Williamson type Hadamard matrix of order 100 is not yet implemented. """ assert n%4 == 0 - + if not williamson_type_quadruples_smallcases(n//4, existence=True): if existence: return False @@ -434,15 +434,15 @@ def construction_four_symbol_delta_code_I(X, Y, Z, W): r""" Construct 4-symbol `\delta` code of length `2n+1`. - The 4-symbol `\delta` code is constructed from sequences `X, Y, Z, W` of + The 4-symbol `\delta` code is constructed from sequences `X, Y, Z, W` of length `n+1`, `n+1`, `n`, `n` satisfying for all `s > 0`: .. MATH:: - N_X(s) + N_Y(s) + N_Z(s) + N_W(s) = 0 - + N_X(s) + N_Y(s) + N_Z(s) + N_W(s) = 0 + where `N_A(s)` is the nonperiodic correlation function: - + .. MATH:: N_A(s) = \sum_{i=1}^{n-s}a_ia_{i+s} @@ -472,11 +472,11 @@ def construction_four_symbol_delta_code_I(X, Y, Z, W): A tuple containing the 4-symbol `\delta` code of length `2n+1`. EXAMPLES:: - + sage: from sage.combinat.matrices.hadamard_matrix import construction_four_symbol_delta_code_I sage: construction_four_symbol_delta_code_I([1, 1], [1, -1], [1], [1]) ([1, 1, 1], [1, 1, -1], [1, -1, 1], [1, -1, -1]) - + TESTS:: sage: construction_four_symbol_delta_code_I([1, 1], [1, -1], [1, 1], [1]) @@ -506,14 +506,14 @@ def construction_four_symbol_delta_code_II(X, Y, Z, W): r""" Construct 4-symbol `\delta` code of length `4n+3`. - The 4-symbol `\delta` code is constructed from sequences `X, Y, Z, W` of + The 4-symbol `\delta` code is constructed from sequences `X, Y, Z, W` of length `n+1`, `n+1`, `n`, `n` satisfying for all `s > 0`: .. MATH:: - N_X(s) + N_Y(s) + N_Z(s) + N_W(s) = 0 - + N_X(s) + N_Y(s) + N_Z(s) + N_W(s) = 0 + where `N_A(s)` is the nonperiodic correlation function: - + .. MATH:: N_A(s) = \sum_{i=1}^{n-s}a_ia_{i+s} @@ -544,14 +544,14 @@ def construction_four_symbol_delta_code_II(X, Y, Z, W): A tuple containing the four 4-symbol `\delta` code of length `4n+3`. EXAMPLES:: - + sage: from sage.combinat.matrices.hadamard_matrix import construction_four_symbol_delta_code_II sage: construction_four_symbol_delta_code_II([1, 1], [1, -1], [1], [1]) ([1, 1, 1, 1, 1, -1, 1], [1, 1, 1, 1, -1, -1, -1], [1, 1, 1, -1, -1, 1, 1], [1, 1, 1, -1, 1, 1, -1]) - + TESTS:: sage: construction_four_symbol_delta_code_II([1, 1], [1, -1], [1, 1], [1]) @@ -588,9 +588,9 @@ def alternate(seq1, seq2): def four_symbol_delta_code_smallcases(n, existence=False): r""" Return the 4-symobl `\delta` code of length `n` if available. - + The 4-symbol `\delta` codes are constructed using :func:`construction_four_symbol_delta_code_I` - or :func:`construction_four_symbol_delta_code_II`. + or :func:`construction_four_symbol_delta_code_II`. The base sequences used are taken from [Tur1974]_. INPUT: @@ -598,7 +598,7 @@ def four_symbol_delta_code_smallcases(n, existence=False): - ``n`` -- integer, the length of the desired 4-symbol `\delta` code. - ``existence`` -- boolean, if true only check if the sequences are available. - + EXAMPLES:: sage: from sage.combinat.matrices.hadamard_matrix import four_symbol_delta_code_smallcases @@ -606,7 +606,7 @@ def four_symbol_delta_code_smallcases(n, existence=False): ([1, -1, 1], [1, -1, -1], [1, 1, 1], [1, 1, -1]) sage: four_symbol_delta_code_smallcases(3, existence=True) True - + TESTS:: sage: four_symbol_delta_code_smallcases(17) @@ -635,7 +635,7 @@ def four_symbol_delta_code_smallcases(n, existence=False): return True X, Y, Z, W = db[(n-3)//4] T1, T2, T3, T4 = construction_four_symbol_delta_code_II(X, Y, Z, W) - + if existence: return False @@ -660,7 +660,7 @@ def _construction_goethals_seidel_matrix(A, B ,C, D): -DR & -C\top R & B\top R & A \end{array}\right) - Where `R` is the anti-diagonal matrix with all nonzero entries + Where `R` is the anti-diagonal matrix with all nonzero entries equal to one. INPUT: @@ -698,16 +698,18 @@ def _construction_goethals_seidel_matrix(A, B ,C, D): return block_matrix([[ A, B*R, C*R, D*R], [-B*R, A, -D.T*R, C.T*R], [-C*R, D.T*R, A, -B.T*R], - [-D*R, -C.T*R, B.T*R, A]]) + [-D*R, -C.T*R, B.T*R, A]]) def hadamard_matrix_cooper_wallis_construction(x1, x2, x3, x4, A, B, C, D, check=True): r""" Create an Hadamard matrix using the contruction detailed in [CW1972]_. - Given four circulant matrices `X_1`, X_2, X_3, X_4` of order `n` with entries (0, 1, -1) such that the elementwise - product of two distinct matrices is always equal to `0` and that `\sum_{i=1}^{4}X_iX_i^\top = nI_n`, - and four matrices `A, B, C, D` of order `m` with elements (1, -1) such that `MN^\top = NM^\top` for all distinct `M`, `N` - and `AA^\top + BB^\top + CC^\top + DD^\top = 4mI_n`, we construct an Hadamard matrix of order `4nm`. + Given four circulant matrices `X_1`, X_2, X_3, X_4` of order `n` with entries (0, 1, -1) + such that the entrywise product of two distinct matrices is always equal to `0` and that + `\sum_{i=1}^{4}X_iX_i^\top = nI_n` holds, and four matrices `A, B, C, D` of order `m` with + elements (1, -1) such that `MN^\top = NM^\top` for all distinct `M`, `N` and + `AA^\top + BB^\top + CC^\top + DD^\top = 4mI_n` holds, we construct an Hadamard matrix + of order `4nm`. INPUT: @@ -727,7 +729,7 @@ def hadamard_matrix_cooper_wallis_construction(x1, x2, x3, x4, A, B, C, D, check - ``D`` -- the matrix described above. - - ``check`` -- a boolean, if true (default) check that the resulting matrix is Hadamard + - ``check`` -- a boolean, if true (default) check that the resulting matrix is Hadamard before returing it. EXAMPLES:: @@ -775,16 +777,16 @@ def hadamard_matrix_cooper_wallis_construction(x1, x2, x3, x4, A, B, C, D, check for j in range(i+1, 4): assert will_matrices[i]*will_matrices[j].T == will_matrices[j]*will_matrices[i].T assert A*A.T + B*B.T + C*C.T + D*D.T == 4*m*I(m) - + e1 = _construction_goethals_seidel_matrix(X1, X2, X3, X4) e2 = _construction_goethals_seidel_matrix(X2, -X1, X4, -X3) e3 = _construction_goethals_seidel_matrix(X3, -X4, -X1, X2) e4 = _construction_goethals_seidel_matrix(X4, X3, -X2, -X1) - + H = e1.tensor_product(A) + e2.tensor_product(B) + e3.tensor_product(C) + e4.tensor_product(D) if check: assert is_hadamard_matrix(H) - return H + return H def hadamard_matrix_cooper_wallis_smallcases(n, check=True, existence=False): r""" @@ -795,11 +797,11 @@ def hadamard_matrix_cooper_wallis_smallcases(n, check=True, existence=False): It constructs the matrices `X_1`, `X_2`, `X_3`, `X_4` using either T-matrices or the T-sequences from :func:`sage.combinat.t_sequences.t_sequences_smallcases`. The matrices `A`, `B`, `C`, `D` are taken from :func:`williamson_type_quadruples_smallcases`. - + Data for T-matrices of order 67 is taken from [Saw1985]_. INPUT: - + - ``n`` -- integer, the order of the matrix to be constructed. - ``check`` -- boolean: if True (default), check the the matrix is an Hadamard matrix before returning. @@ -825,7 +827,7 @@ def hadamard_matrix_cooper_wallis_smallcases(n, check=True, existence=False): 28 x 28 dense matrix over Integer Ring... If ``existence`` is set to True, the function returns a boolean :: - + sage: hadamard_matrix_cooper_wallis_smallcases(20, existence=True) True @@ -861,21 +863,21 @@ def hadamard_matrix_cooper_wallis_smallcases(n, check=True, existence=False): if (T_seq_len in db or T_sequences_smallcases(T_seq_len, existence=True)) and williamson_type_quadruples_smallcases(will_size, existence=True): if existence: return True - + e1, e2, e3, e4 = None, None, None, None if T_seq_len in db: e1, e2, e3, e4 = db[T_seq_len] else: e1, e2, e3, e4 = T_sequences_smallcases(T_seq_len, check=False) - - will_matrices = williamson_type_quadruples_smallcases(will_size) + + will_matrices = williamson_type_quadruples_smallcases(will_size) A, B, C, D = map(matrix.circulant, will_matrices) M = hadamard_matrix_cooper_wallis_construction(e1, e2, e3, e4, A, B, C, D, check=False) - + if check: assert is_hadamard_matrix(M) return M - + if existence: return False raise ValueError("The Cooper-Wallis construction for Hadamard matrices of order %s is not yet implemented." % n) @@ -884,20 +886,20 @@ def _get_baumert_hall_units(n, existence=False): r""" Construct Baumert-Hall units of size `n` from available 4-symbol `\delta` codes. - The construction is detailed in Theroem 2 from [Tur1974]_, and is based on the + The construction is detailed in Theroem 2 from [Tur1974]_, and is based on the Goethals-Seidel construction of Hadamard matrices. We need a 4-symbol `\delta` code to detail the first row of circulant matrices M1, M2, M3, M4 used in the construction. INPUT: - + - ``n`` -- integer, the size of the Baumert-Hall units. - + - ``existence`` -- boolean (default False): if true only check whether the units can be contructed. OUTPUT: - If ``existence`` is true, return a boolean representing whether the Baumert-Hall units can + If ``existence`` is true, return a boolean representing whether the Baumert-Hall units can be constructed. Otherwise, return a tuple containing the four Baumert-Hall units. EXAMPLES:: @@ -908,7 +910,7 @@ def _get_baumert_hall_units(n, existence=False): 28 x 28 dense matrix over Integer Ring, 28 x 28 dense matrix over Integer Ring, 28 x 28 dense matrix over Integer Ring) - + TESTS:: sage: _get_baumert_hall_units(116, existence=True) @@ -946,20 +948,20 @@ def _get_baumert_hall_units(n, existence=False): M2hat = matrix(ZZ, 0.25*(M1-M2-M3+M4)) M3hat = matrix(ZZ, 0.25*(M1+M2-M3-M4)) M4hat = matrix(ZZ, 0.25*(M1-M2+M3-M4)) - - e1 = _construction_goethals_seidel_matrix(M1hat, -M2hat, -M3hat, -M4hat) + + e1 = _construction_goethals_seidel_matrix(M1hat, -M2hat, -M3hat, -M4hat) e2 = _construction_goethals_seidel_matrix(M2hat, M1hat, M4hat, -M3hat) e3 = _construction_goethals_seidel_matrix(M3hat, -M4hat, M1hat, M2hat) e4 = _construction_goethals_seidel_matrix(M4hat, M3hat, -M2hat, M1hat) return e1, e2, e3, e4 - + def hadamard_matrix_turyn_type(a, b, c, d, e1, e2, e3, e4, check=True): r""" Construction of Turyn type Hadamard matrix. Given `n\times n` circulant matrices `A`, `B`, `C`, `D` with 1,-1 entries, - satisfying `AA^\top + BB^\top + CC^\top + DD^\top = 4nI`, and a set of - Baumert-Hall units of order `4t`, one can construct a Hadamard matrix of order + satisfying `AA^\top + BB^\top + CC^\top + DD^\top = 4nI`, and a set of + Baumert-Hall units of order `4t`, one can construct a Hadamard matrix of order `4tn` as detailed by Turyn in [Tur1974]_. INPUT: @@ -977,7 +979,7 @@ def hadamard_matrix_turyn_type(a, b, c, d, e1, e2, e3, e4, check=True): - ``e2`` -- Matrix representing the second Baumert-Hall unit. - ``e3`` -- Matrix representing the third Baumert-Hall unit. - + - ``e4`` -- Matrix representing the fourth Baumert-Hall unit. - ``check`` -- Whether to check that the output is an Hadamard matrix before returning it. @@ -1020,7 +1022,7 @@ def hadamard_matrix_turyn_type(a, b, c, d, e1, e2, e3, e4, check=True): assert abs(e1[i, j]) + abs(e2[i, j]) + abs(e3[i, j]) + abs(e4[i, j]) == 1 assert e1*e1.T == t*I(t4) and e2*e2.T == t*I(t4) and e3*e3.T == t*I(t4) and e4*e4.T == t*I(t4) - + units = [e1, e2, e3, e4] for i in range(len(units)): for j in range(i+1, len(units)): @@ -1036,13 +1038,13 @@ def turyn_type_hadamard_matrix_smallcases(n, existence=False, check=True): r""" Construct an Hadamard matrix of order `n` from available 4-symbol `\delta` codes and Williamson quadruples. - The function looks for Baumert-Hall units and Williamson type matrices from + The function looks for Baumert-Hall units and Williamson type matrices from :func:`four_symbol_delta_code_smallcases` and :func:`williamson_type_quadruples_smallcases` and use them to construct an Hadamard matrix with the Turyn construction defined in :func:`hadamard_matrix_turyn_type`. INPUT: - + - ``n`` -- integer, the order of the matrix to be constructed. - ``existence`` -- boolean (default False): if True, only check if matrix exists. @@ -1077,11 +1079,11 @@ def turyn_type_hadamard_matrix_smallcases(n, existence=False, check=True): if _get_baumert_hall_units(units_size, existence=True) and williamson_type_quadruples_smallcases(will_size, existence=True): if existence: return True - + e1, e2, e3, e4 = _get_baumert_hall_units(units_size) a, b, c, d = williamson_type_quadruples_smallcases(will_size) return hadamard_matrix_turyn_type(a, b, c, d, e1, e2, e3, e4, check=check) - + if existence: return False raise ValueError("The Turyn type construction for Hadamard matrices of order %s is not yet implemented." % n) @@ -1322,7 +1324,7 @@ def hadamard_matrix(n,existence=False, check=True): elif hadamard_matrix_cooper_wallis_smallcases(n, existence=True): if existence: return True - M = hadamard_matrix_cooper_wallis_smallcases(n, check=False) + M = hadamard_matrix_cooper_wallis_smallcases(n, check=False) elif turyn_type_hadamard_matrix_smallcases(n, existence=True): if existence: return True @@ -1332,7 +1334,7 @@ def hadamard_matrix(n,existence=False, check=True): return True M = skew_hadamard_matrix(n, check=False) elif regular_symmetric_hadamard_matrix_with_constant_diagonal(n, 1, existence=True) is True: - if existence: + if existence: return True M = regular_symmetric_hadamard_matrix_with_constant_diagonal(n, 1) else: @@ -1800,17 +1802,11 @@ def williamson_goethals_seidel_skew_hadamard_matrix(a, b, c, d, check=True): REFERENCES: - .. [GS70s] \J.M. Goethals and J. J. Seidel, - *A skew Hadamard matrix of order 36*, - J. Aust. Math. Soc. 11(1970), 343-344 + - [GS70s]_ - .. [Wall71] \J. Wallis, - *A skew-Hadamard matrix of order 92*, - Bull. Aust. Math. Soc. 5(1971), 203-204 + - [Wall71]_ - .. [KoSt08] \C. Koukouvinos, S. Stylianou - *On skew-Hadamard matrices*, - Discrete Math. 308(2008) 2723-2731 + - [KoSt08]_ """ n = len(a) A,B,C,D=map(matrix.circulant, [a,b,c,d]) @@ -1965,10 +1961,7 @@ def skew_hadamard_matrix(n,existence=False, skew_normalize=True, check=True): REFERENCES: - .. [Ha83] \M. Hall, - Combinatorial Theory, - 2nd edition, - Wiley, 1983 + - [Ha83]_ """ if n < 1: raise ValueError("parameter n must be strictly positive") From eb72ef050d94dd32cbb1499243af5de4e9eec22c Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Tue, 29 Nov 2022 21:06:01 +0000 Subject: [PATCH 591/632] avoid is_GapElement --- src/sage/rings/finite_rings/element_givaro.pyx | 8 ++++++-- src/sage/rings/finite_rings/element_ntl_gf2e.pyx | 10 +++++++--- src/sage/rings/finite_rings/element_pari_ffelt.pyx | 8 ++++++-- src/sage/rings/finite_rings/integer_mod_ring.py | 13 +++++++++++-- 4 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/finite_rings/element_givaro.pyx b/src/sage/rings/finite_rings/element_givaro.pyx index 42f93cceb75..790121a62dc 100644 --- a/src/sage/rings/finite_rings/element_givaro.pyx +++ b/src/sage/rings/finite_rings/element_givaro.pyx @@ -72,7 +72,11 @@ from cypari2.stack cimport clear_stack from sage.structure.parent cimport Parent -from sage.interfaces.gap import is_GapElement + +try: + from sage.interfaces.gap import GapElement +except ImportError: + GapElement = () cdef object is_IntegerMod cdef object Integer @@ -435,7 +439,7 @@ cdef class Cache_givaro(Cache_base): elif isinstance(e, sage.libs.gap.element.GapElement_FiniteField): return e.sage(ring=self.parent) - elif is_GapElement(e): + elif isinstance(e, GapElement): from sage.libs.gap.libgap import libgap return libgap(e).sage(ring=self.parent) diff --git a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx index ebe16dcdcd8..fefd492f952 100644 --- a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx +++ b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx @@ -52,11 +52,15 @@ from sage.arith.long cimport pyobject_to_long from .element_pari_ffelt import FiniteFieldElement_pari_ffelt from .finite_field_ntl_gf2e import FiniteField_ntl_gf2e +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + from sage.libs.gap.element import GapElement_FiniteField -from sage.interfaces.gap import is_GapElement +try: + from sage.interfaces.gap import GapElement +except ImportError: + GapElement = () -from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing cdef object is_IntegerMod cdef object IntegerModRing_generic @@ -369,7 +373,7 @@ cdef class Cache_ntl_gf2e(Cache_base): elif isinstance(e, GapElement_FiniteField): return e.sage(ring=self._parent) - elif is_GapElement(e): + elif isinstance(e, GapElement): from sage.libs.gap.libgap import libgap return libgap(e).sage(ring=self._parent) diff --git a/src/sage/rings/finite_rings/element_pari_ffelt.pyx b/src/sage/rings/finite_rings/element_pari_ffelt.pyx index 0ee972a7b61..d427f1a5282 100644 --- a/src/sage/rings/finite_rings/element_pari_ffelt.pyx +++ b/src/sage/rings/finite_rings/element_pari_ffelt.pyx @@ -29,7 +29,6 @@ from .element_base cimport FinitePolyExtElement from .integer_mod import IntegerMod_abstract import sage.rings.integer -from sage.interfaces.gap import is_GapElement from sage.modules.free_module_element import FreeModuleElement from sage.rings.integer cimport Integer from sage.rings.polynomial.polynomial_element import Polynomial @@ -39,6 +38,11 @@ from sage.structure.element cimport Element, ModuleElement, RingElement from sage.structure.richcmp cimport rich_to_bool +try: + from sage.interfaces.gap import GapElement +except ImportError: + GapElement = () + cdef GEN _INT_to_FFELT(GEN g, GEN x) except NULL: """ Convert the t_INT `x` to an element of the field of definition of @@ -504,7 +508,7 @@ cdef class FiniteFieldElement_pari_ffelt(FinitePolyExtElement): elif isinstance(x, str): self.construct_from(self._parent.polynomial_ring()(x)) - elif is_GapElement(x): + elif isinstance(x, GapElement): try: from sage.libs.gap.libgap import libgap self.construct_from(libgap(x).sage(ring=self._parent)) diff --git a/src/sage/rings/finite_rings/integer_mod_ring.py b/src/sage/rings/finite_rings/integer_mod_ring.py index 7049f2d6b11..01eab795111 100644 --- a/src/sage/rings/finite_rings/integer_mod_ring.py +++ b/src/sage/rings/finite_rings/integer_mod_ring.py @@ -77,7 +77,10 @@ from sage.structure.factory import UniqueFactory from sage.structure.richcmp import richcmp, richcmp_method -from sage.interfaces.gap import is_GapElement +try: + from sage.interfaces.gap import GapElement +except ImportError: + GapElement = () class IntegerModFactory(UniqueFactory): r""" @@ -1180,13 +1183,19 @@ def _element_constructor_(self, x): 4 sage: libgap(a.sage()) == a True + + better syntax for libgap interface:: + + sage: a = libgap.Z(13)^2 + sage: libgap(a.sage()) == a + True """ try: return integer_mod.IntegerMod(self, x) except (NotImplementedError, PariError): raise TypeError("error coercing to finite field") except TypeError: - if is_GapElement(x): + if isinstance(x, GapElement): from sage.libs.gap.libgap import libgap return libgap(x).sage() raise # Continue up with the original TypeError From 1c4a69fdfee7ccf9aebb40bbc29b4453852cacb1 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Tue, 29 Nov 2022 21:20:49 +0000 Subject: [PATCH 592/632] use GapElement from an abstract superclass --- src/sage/interfaces/abc.py | 3 +++ src/sage/interfaces/gap.py | 4 +++- src/sage/rings/finite_rings/element_givaro.pyx | 5 +---- src/sage/rings/finite_rings/element_ntl_gf2e.pyx | 5 +---- src/sage/rings/finite_rings/element_pari_ffelt.pyx | 5 +---- src/sage/rings/finite_rings/integer_mod_ring.py | 5 +---- 6 files changed, 10 insertions(+), 17 deletions(-) create mode 100644 src/sage/interfaces/abc.py diff --git a/src/sage/interfaces/abc.py b/src/sage/interfaces/abc.py new file mode 100644 index 00000000000..ab10a01f819 --- /dev/null +++ b/src/sage/interfaces/abc.py @@ -0,0 +1,3 @@ +class GapElement: + + pass diff --git a/src/sage/interfaces/gap.py b/src/sage/interfaces/gap.py index c80a847d7e9..1f5fc58e9db 100644 --- a/src/sage/interfaces/gap.py +++ b/src/sage/interfaces/gap.py @@ -202,6 +202,8 @@ from sage.interfaces.tab_completion import ExtraTabCompletion from sage.structure.element import ModuleElement +import sage.interfaces.abc + import re import os import io @@ -1524,7 +1526,7 @@ def gap_reset_workspace(max_workspace_size=None, verbose=False): @instancedoc -class GapElement(GapElement_generic): +class GapElement(GapElement_generic, sage.interfaces.abc.GapElement): def __getitem__(self, n): """ EXAMPLES:: diff --git a/src/sage/rings/finite_rings/element_givaro.pyx b/src/sage/rings/finite_rings/element_givaro.pyx index 790121a62dc..d7a9817f980 100644 --- a/src/sage/rings/finite_rings/element_givaro.pyx +++ b/src/sage/rings/finite_rings/element_givaro.pyx @@ -73,10 +73,7 @@ from cypari2.stack cimport clear_stack from sage.structure.parent cimport Parent -try: - from sage.interfaces.gap import GapElement -except ImportError: - GapElement = () +from sage.interfaces.abc import GapElement cdef object is_IntegerMod cdef object Integer diff --git a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx index fefd492f952..46601ecd5e2 100644 --- a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx +++ b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx @@ -56,10 +56,7 @@ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.libs.gap.element import GapElement_FiniteField -try: - from sage.interfaces.gap import GapElement -except ImportError: - GapElement = () +from sage.interfaces.abc import GapElement cdef object is_IntegerMod diff --git a/src/sage/rings/finite_rings/element_pari_ffelt.pyx b/src/sage/rings/finite_rings/element_pari_ffelt.pyx index d427f1a5282..be70aa56d39 100644 --- a/src/sage/rings/finite_rings/element_pari_ffelt.pyx +++ b/src/sage/rings/finite_rings/element_pari_ffelt.pyx @@ -38,10 +38,7 @@ from sage.structure.element cimport Element, ModuleElement, RingElement from sage.structure.richcmp cimport rich_to_bool -try: - from sage.interfaces.gap import GapElement -except ImportError: - GapElement = () +from sage.interfaces.abc import GapElement cdef GEN _INT_to_FFELT(GEN g, GEN x) except NULL: """ diff --git a/src/sage/rings/finite_rings/integer_mod_ring.py b/src/sage/rings/finite_rings/integer_mod_ring.py index 01eab795111..54b7840e488 100644 --- a/src/sage/rings/finite_rings/integer_mod_ring.py +++ b/src/sage/rings/finite_rings/integer_mod_ring.py @@ -77,10 +77,7 @@ from sage.structure.factory import UniqueFactory from sage.structure.richcmp import richcmp, richcmp_method -try: - from sage.interfaces.gap import GapElement -except ImportError: - GapElement = () +from sage.interfaces.abc import GapElement class IntegerModFactory(UniqueFactory): r""" From f0a77039bb2be3eb7cd06c036aaa8085652db403 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Wed, 30 Nov 2022 13:45:27 +0900 Subject: [PATCH 593/632] Add back doctest --- src/sage/plot/graphics.py | 6 +++++- src/sage/plot/plot.py | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/sage/plot/graphics.py b/src/sage/plot/graphics.py index 8d620b5c625..7d6242ebc5e 100644 --- a/src/sage/plot/graphics.py +++ b/src/sage/plot/graphics.py @@ -2419,7 +2419,11 @@ def _matplotlib_tick_formatter(self, subplot, base=(10, 10), LogFormatterMathtext(base=base[0])(n, pos).replace( "\\mathdefault", "")) else: - x_formatter = FuncFormatter(lambda n, pos: '$%s$' % latex(n)) + # circumvent the problem of symbolic tick values (trac #34693) + if isinstance(x_locator, FixedLocator): + x_formatter = FixedFormatter(['$%s$' % latex(n) for n in x_locator.locs]) + else: + x_formatter = FuncFormatter(lambda n, pos: '$%s$' % latex(n)) elif isinstance(x_formatter, (list, tuple)): if (not isinstance(ticks[0], (list, tuple)) or len(ticks[0]) != len(x_formatter)): diff --git a/src/sage/plot/plot.py b/src/sage/plot/plot.py index cef7876cddb..4de4e7e752e 100644 --- a/src/sage/plot/plot.py +++ b/src/sage/plot/plot.py @@ -1741,9 +1741,14 @@ def b(n): return lambda x: bessel_J(n, x) + 0.5*(n-1) :: - sage: plot(2*x+1,(x,0,5),ticks=[[0,1,e,pi,sqrt(20)],2],tick_formatter="latex") # not tested (broken with matplotlib 3.6) + sage: plot(2*x + 1,(x, 0, 5), ticks=[[0, 1, e, pi, sqrt(20)], 2], tick_formatter="latex") Graphics object consisting of 1 graphics primitive + .. PLOT:: + + g = plot(2*x + 1, (x, 0, 5), ticks=[[0, 1, e, pi, sqrt(20)], 2], tick_formatter="latex") + sphinx_plot(g) + This is particularly useful when setting custom ticks in multiples of `pi`. :: From 9e5f25fc08ac6dbcc360d9af83a03ddba8cb313a Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 30 Nov 2022 09:04:22 +0100 Subject: [PATCH 594/632] not len(u) != not u --- src/sage/algebras/iwahori_hecke_algebra.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/sage/algebras/iwahori_hecke_algebra.py b/src/sage/algebras/iwahori_hecke_algebra.py index 671cd52fecf..900039c7909 100644 --- a/src/sage/algebras/iwahori_hecke_algebra.py +++ b/src/sage/algebras/iwahori_hecke_algebra.py @@ -2282,9 +2282,14 @@ def _decompose_into_generators(self, u): {(1,): -1, (1, 2, 1): 1} sage: Cp._decompose_into_generators(W([1,2,3,1,2])) # optional - coxeter3 {(1,): 1, (1, 2, 1): -1, (1, 2, 1, 3, 2): 1, (1, 3, 2): -1} + + TESTS:: + + sage: Cp._decompose_into_generators(W([])) # optional - coxeter3 + {(): 1} """ # l(y) = 0 or 1 - if not u: + if not len(u): return {(): 1} if len(u) == 1: return {(u[0],): 1} From 8e1394276ada51dc69b2efb098b917010c631ef4 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Wed, 30 Nov 2022 09:25:32 +0100 Subject: [PATCH 595/632] use new function also elsewhere, fix typo --- src/sage/combinat/sf/monomial.py | 6 +++--- src/sage/combinat/sf/sfa.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/sf/monomial.py b/src/sage/combinat/sf/monomial.py index f833be60c3b..96fcacb11d0 100644 --- a/src/sage/combinat/sf/monomial.py +++ b/src/sage/combinat/sf/monomial.py @@ -189,7 +189,7 @@ def from_polynomial_exp(self, p): INPUT: - ``self`` -- a monomial symmetric function basis - - ``p`` -- a multivariate polynomial over the same base ring as ``self`` + - ``p`` -- a polynomial over the same base ring as ``self`` OUTPUT: @@ -228,8 +228,8 @@ def from_polynomial_exp(self, p): :func:`Partition`, :meth:`Partition.to_exp` """ assert self.base_ring() == p.parent().base_ring() - return self.sum_of_terms((Partition(exp=monomial), coeff) - for monomial, coeff in p.dict().items()) + from sage.combinat.sf.sfa import _from_polynomial + return _from_polynomial(p, self) def antipode_by_coercion(self, element): r""" diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index 793ca21ecd3..a7e00839deb 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -6471,7 +6471,7 @@ def _to_polynomials(lf, R): .. SEEALSO:: - :func:`_from_polynomials` + :func:`_from_polynomial` EXAMPLES:: From 8371dd75013d5b973333b6e577e7ef17bbf042c1 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Wed, 30 Nov 2022 21:17:33 +0900 Subject: [PATCH 596/632] Missing part of the fix --- src/sage/plot/graphics.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/sage/plot/graphics.py b/src/sage/plot/graphics.py index 7d6242ebc5e..019ad79c7a4 100644 --- a/src/sage/plot/graphics.py +++ b/src/sage/plot/graphics.py @@ -2366,7 +2366,7 @@ def _matplotlib_tick_formatter(self, subplot, base=(10, 10), elif x_locator == []: x_locator = NullLocator() elif isinstance(x_locator, list): - x_locator = FixedLocator(x_locator) + x_locator = FixedLocator([float(x) for x in x_locator]) else: # x_locator is a number which can be made a float from sage.functions.other import ceil, floor if floor(xmax / x_locator) - ceil(xmin / x_locator) > 1: @@ -2387,7 +2387,7 @@ def _matplotlib_tick_formatter(self, subplot, base=(10, 10), elif y_locator == []: y_locator = NullLocator() elif isinstance(y_locator, list): - y_locator = FixedLocator(y_locator) + y_locator = FixedLocator([float(y) for y in y_locator]) else: # y_locator is a number which can be made a float from sage.functions.other import ceil, floor if floor(ymax / y_locator) - ceil(ymin / y_locator) > 1: @@ -2421,7 +2421,7 @@ def _matplotlib_tick_formatter(self, subplot, base=(10, 10), else: # circumvent the problem of symbolic tick values (trac #34693) if isinstance(x_locator, FixedLocator): - x_formatter = FixedFormatter(['$%s$' % latex(n) for n in x_locator.locs]) + x_formatter = FixedFormatter(['$%s$' % latex(n) for n in ticks[0]]) else: x_formatter = FuncFormatter(lambda n, pos: '$%s$' % latex(n)) elif isinstance(x_formatter, (list, tuple)): @@ -2448,7 +2448,11 @@ def _matplotlib_tick_formatter(self, subplot, base=(10, 10), LogFormatterMathtext(base=base[1])(n, pos).replace( "\\mathdefault", "")) else: - y_formatter = FuncFormatter(lambda n, pos: '$%s$' % latex(n)) + # circumvent the problem of symbolic tick values (trac #34693) + if isinstance(y_locator, FixedLocator): + y_formatter = FixedFormatter(['$%s$' % latex(n) for n in ticks[1]]) + else: + y_formatter = FuncFormatter(lambda n, pos: '$%s$' % latex(n)) elif isinstance(y_formatter, (list, tuple)): if (not isinstance(ticks[1], (list, tuple)) or len(ticks[1]) != len(y_formatter)): From a8741d8c60dc3e173b18cc373c4c203fcc2cec4f Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Wed, 30 Nov 2022 21:23:19 +0900 Subject: [PATCH 597/632] Edit the doctest --- src/sage/plot/plot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/plot/plot.py b/src/sage/plot/plot.py index 4de4e7e752e..01712c0230e 100644 --- a/src/sage/plot/plot.py +++ b/src/sage/plot/plot.py @@ -1741,12 +1741,12 @@ def b(n): return lambda x: bessel_J(n, x) + 0.5*(n-1) :: - sage: plot(2*x + 1,(x, 0, 5), ticks=[[0, 1, e, pi, sqrt(20)], 2], tick_formatter="latex") + sage: plot(2*x + 1,(x, 0, 5), ticks=[[0, 1, e, pi, sqrt(20)], [1, 2, 2*e + 1, 2*pi + 1, 2*sqrt(20) + 1]], tick_formatter="latex") Graphics object consisting of 1 graphics primitive .. PLOT:: - g = plot(2*x + 1, (x, 0, 5), ticks=[[0, 1, e, pi, sqrt(20)], 2], tick_formatter="latex") + g = plot(2*x + 1,(x, 0, 5), ticks=[[0, 1, e, pi, sqrt(20)], [1, 2, 2*e + 1, 2*pi + 1, 2*sqrt(20) + 1]], tick_formatter="latex") sphinx_plot(g) This is particularly useful when setting custom ticks in multiples of `pi`. From 64589686c261d33e6b5aff2589bcae8af004bcc6 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Thu, 1 Dec 2022 08:23:04 +0900 Subject: [PATCH 598/632] More edis of the doctest --- src/sage/plot/plot.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/plot/plot.py b/src/sage/plot/plot.py index 01712c0230e..b2621ad3fd7 100644 --- a/src/sage/plot/plot.py +++ b/src/sage/plot/plot.py @@ -1741,15 +1741,15 @@ def b(n): return lambda x: bessel_J(n, x) + 0.5*(n-1) :: - sage: plot(2*x + 1,(x, 0, 5), ticks=[[0, 1, e, pi, sqrt(20)], [1, 2, 2*e + 1, 2*pi + 1, 2*sqrt(20) + 1]], tick_formatter="latex") + sage: plot(2*x + 1,(x, 0, 5), ticks=[[0, 1, e, pi, sqrt(20)], [1, 3, 2*e + 1, 2*pi + 1, 2*sqrt(20) + 1]], tick_formatter="latex") Graphics object consisting of 1 graphics primitive .. PLOT:: - g = plot(2*x + 1,(x, 0, 5), ticks=[[0, 1, e, pi, sqrt(20)], [1, 2, 2*e + 1, 2*pi + 1, 2*sqrt(20) + 1]], tick_formatter="latex") + g = plot(2*x + 1,(x, 0, 5), ticks=[[0, 1, e, pi, sqrt(20)], [1, 3, 2*e + 1, 2*pi + 1, 2*sqrt(20) + 1]], tick_formatter="latex") sphinx_plot(g) - This is particularly useful when setting custom ticks in multiples of `pi`. + This is particularly useful when setting custom ticks in multiples of `\pi`. :: From a52bc66349aadbc1d8a76e43279f8887f4a81447 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Thu, 1 Dec 2022 14:44:18 +0100 Subject: [PATCH 599/632] remove unused import --- src/sage/combinat/sf/sfa.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/sf/sfa.py b/src/sage/combinat/sf/sfa.py index a7e00839deb..61f273af1b1 100644 --- a/src/sage/combinat/sf/sfa.py +++ b/src/sage/combinat/sf/sfa.py @@ -228,7 +228,7 @@ from sage.combinat.free_module import CombinatorialFreeModule from sage.matrix.constructor import matrix from sage.structure.factorization import Factorization -from sage.structure.element import parent, coerce_binop +from sage.structure.element import coerce_binop from sage.misc.misc_c import prod from sage.data_structures.blas_dict import convert_remove_zeroes, linear_combination from copy import copy From f93022471db0b5ceebb0d127a89aed8796865df4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 1 Dec 2022 14:45:47 +0100 Subject: [PATCH 600/632] pep cleanup in parallel/ --- src/sage/parallel/decorate.py | 72 +++++++++++------------ src/sage/parallel/map_reduce.py | 28 ++++----- src/sage/parallel/multiprocessing_sage.py | 3 +- src/sage/parallel/ncpus.py | 18 +++--- src/sage/parallel/parallelism.py | 16 ++--- src/sage/parallel/use_fork.py | 17 +++--- 6 files changed, 78 insertions(+), 76 deletions(-) diff --git a/src/sage/parallel/decorate.py b/src/sage/parallel/decorate.py index c14518af570..34cb4860d36 100644 --- a/src/sage/parallel/decorate.py +++ b/src/sage/parallel/decorate.py @@ -23,12 +23,12 @@ def normalize_input(a): INPUT: - - ``a`` -- object + - ``a`` -- object OUTPUT: - - ``args`` -- tuple - - ``kwds`` -- dictionary + - ``args`` -- tuple + - ``kwds`` -- dictionary EXAMPLES:: @@ -41,7 +41,7 @@ def normalize_input(a): sage: sage.parallel.decorate.normalize_input( 5 ) ((5,), {}) """ - if isinstance(a, tuple) and len(a) == 2 and isinstance(a[0],tuple) and isinstance(a[1],dict): + if isinstance(a, tuple) and len(a) == 2 and isinstance(a[0], tuple) and isinstance(a[1], dict): return a elif isinstance(a, tuple): return (a, {}) @@ -97,11 +97,11 @@ def __call__(self, f): INPUT: - - ``f`` -- Python callable object or function + - ``f`` -- Python callable object or function OUTPUT: - - Decorated version of ``f`` + - Decorated version of ``f`` EXAMPLES:: @@ -131,8 +131,8 @@ def __init__(self, parallel, func): """ .. note:: - This is typically accessed indirectly through - :meth:`Parallel.__call__`. + This is typically accessed indirectly through + :meth:`Parallel.__call__`. INPUT: @@ -159,13 +159,11 @@ def __call__(self, *args, **kwds): 4 sage: sorted(pf([2,3])) [(((2,), {}), 4), (((3,), {}), 9)] - """ - if len(args) > 0 and isinstance(args[0], (list, -types.GeneratorType)): + """ + if len(args) > 0 and isinstance(args[0], (list, types.GeneratorType)): return self.parallel.p_iter(self.func, (normalize_input(a) -for a in args[0])) - else: - return self.func(*args, **kwds) + for a in args[0])) + return self.func(*args, **kwds) def __get__(self, instance, owner): """ @@ -174,7 +172,7 @@ def __get__(self, instance, owner): .. note:: - This is the key to fixing :trac:`11461`. + This is the key to fixing :trac:`11461`. EXAMPLES: @@ -217,14 +215,14 @@ def __get__(self, instance, owner): [(((2,), {}), 4), (((3,), {}), 9)] """ try: - #If this ParallelFunction object is accessed as an - #attribute of a class or instance, the underlying function - #should be "accessed" in the same way. + # If this ParallelFunction object is accessed as an + # attribute of a class or instance, the underlying function + # should be "accessed" in the same way. new_func = self.func.__get__(instance, owner) except AttributeError: - #This will happen if a non-function attribute is - #decorated. For example, an expression that's an - #attribute of a class. + # This will happen if a non-function attribute is + # decorated. For example, an expression that's an + # attribute of a class. new_func = self.func return ParallelFunction(self.parallel, new_func) @@ -413,8 +411,6 @@ def parallel(p_iter='fork', ncpus=None, **kwds): return Parallel(p_iter, ncpus, **kwds) - - ################################################################### # The @fork decorator -- evaluate a function with no side effects # in memory, so the only side effects (if any) are on disk. @@ -437,10 +433,10 @@ def __init__(self, timeout=0, verbose=False): """ INPUT: - - ``timeout`` -- (default: 0) kill the subprocess after it has run this - many seconds (wall time), or if ``timeout`` is zero, do not kill it. - - ``verbose`` -- (default: ``False``) whether to print anything about - what the decorator does (e.g., killing the subprocess) + - ``timeout`` -- (default: 0) kill the subprocess after it has run this + many seconds (wall time), or if ``timeout`` is zero, do not kill it. + - ``verbose`` -- (default: ``False``) whether to print anything about + what the decorator does (e.g., killing the subprocess) EXAMPLES:: @@ -456,11 +452,11 @@ def __call__(self, f): """ INPUT: - - ``f`` -- a function + - ``f`` -- a function OUTPUT: - - A decorated function. + - A decorated function. EXAMPLES:: @@ -481,18 +477,18 @@ def h(*args, **kwds): def fork(f=None, timeout=0, verbose=False): """ - Decorate a function so that when called it runs in a forked - subprocess. This means that it won't have any in-memory - side effects on the parent Sage process. The pexpect interfaces - are all reset. + Decorate a function so that when called it runs in a forked subprocess. + + This means that it will not have any in-memory side effects on the + parent Sage process. The pexpect interfaces are all reset. INPUT: - - ``f`` -- a function - - ``timeout`` -- (default: 0) if positive, kill the subprocess after - this many seconds (wall time) - - ``verbose`` -- (default: ``False``) whether to print anything - about what the decorator does (e.g., killing the subprocess) + - ``f`` -- a function + - ``timeout`` -- (default: 0) if positive, kill the subprocess after + this many seconds (wall time) + - ``verbose`` -- (default: ``False``) whether to print anything + about what the decorator does (e.g., killing the subprocess) .. warning:: diff --git a/src/sage/parallel/map_reduce.py b/src/sage/parallel/map_reduce.py index 8881d7a01ac..7307a68dd44 100644 --- a/src/sage/parallel/map_reduce.py +++ b/src/sage/parallel/map_reduce.py @@ -410,7 +410,7 @@ - ``worker._request`` -- a :class:`~multiprocessing.queues.SimpleQueue` storing steal request submitted to ``worker``. - ``worker._read_task``, ``worker._write_task`` -- a - :class:`~multiprocessing.queues.Pipe` used to transfert node during steal. + :class:`~multiprocessing.queues.Pipe` used to transfer node during steal. - ``worker._thief`` -- a :class:`~threading.Thread` which is in charge of stealing from ``worker._todo``. @@ -1196,9 +1196,9 @@ def get_results(self, timeout=None): active_proc = self._nprocess while active_proc > 0: try: - logger.debug('Waiting on results; active_proc: %s, ' - 'timeout: %s, aborted: %s' % - (active_proc, timeout, self._aborted.value)) + logger.debug('Waiting on results; active_proc: {}, ' + 'timeout: {}, aborted: {}'.format( + active_proc, timeout, self._aborted.value)) newres = self._results.get(timeout=timeout) except queue.Empty: logger.debug('Timed out waiting for results; aborting') @@ -1249,13 +1249,13 @@ def finish(self): if not self._aborted.value: logger.debug("Joining worker processes...") for worker in self._workers: - logger.debug("Joining %s" % worker.name) + logger.debug(f"Joining {worker.name}") worker.join() logger.debug("Joining done") else: logger.debug("Killing worker processes...") for worker in self._workers: - logger.debug("Terminating %s" % worker.name) + logger.debug(f"Terminating {worker.name}") worker.terminate() logger.debug("Killing done") @@ -1527,9 +1527,9 @@ def print_communication_statistics(self, blocksize=16): # local function (see e.g: # https://stackoverflow.com/questions/2609518/python-nested-function-scopes). - def pstat(name, start, end, ist): + def pstat(name, start, end, istat): res[0] += ("\n" + name + " ".join( - "%4i" % (self._stats[i][ist]) for i in range(start, end))) + "%4i" % (self._stats[i][istat]) for i in range(start, end))) for start in range(0, self._nprocess, blocksize): end = min(start + blocksize, self._nprocess) res[0] = ("#proc: " + @@ -1613,17 +1613,17 @@ def _thief(self): for ireq in iter(self._request.get, AbortError): reqs += 1 target = self._mapred._workers[ireq] - logger.debug("Got a Steal request from %s" % target.name) + logger.debug(f"Got a Steal request from {target.name}") self._mapred._signal_task_start() try: work = self._todo.popleft() except IndexError: target._write_task.send(None) - logger.debug("Failed Steal %s" % target.name) + logger.debug(f"Failed Steal {target.name}") self._mapred._signal_task_done() else: target._write_task.send(work) - logger.debug("Succesful Steal %s" % target.name) + logger.debug(f"Successful Steal {target.name}") thefts += 1 except AbortError: logger.debug("Thief aborted") @@ -1668,10 +1668,10 @@ def steal(self): while node is None: victim = self._mapred.random_worker() if victim is not self: - logger.debug("Trying to steal from %s" % victim.name) + logger.debug(f"Trying to steal from {victim.name}") victim._request.put(self._iproc) self._stats[0] += 1 - logger.debug("waiting for steal answer from %s" % victim.name) + logger.debug(f"waiting for steal answer from {victim.name}") node = self._read_task.recv() # logger.debug("Request answer: %s" % (node,)) if node is AbortError: @@ -1713,7 +1713,7 @@ def run(self): PROFILER.runcall(self.run_myself) output = profile + str(self._iproc) - logger.warn("Profiling in %s ..." % output) + logger.warn(f"Profiling in {output} ...") PROFILER.dump_stats(output) else: self.run_myself() diff --git a/src/sage/parallel/multiprocessing_sage.py b/src/sage/parallel/multiprocessing_sage.py index 0cd628a6d3b..be9b980aba9 100644 --- a/src/sage/parallel/multiprocessing_sage.py +++ b/src/sage/parallel/multiprocessing_sage.py @@ -8,7 +8,7 @@ # Distributed under the terms of (any version of) the GNU # General Public License (GPL). The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ +# https://www.gnu.org/licenses/ ################################################################################ from multiprocessing import Pool @@ -16,6 +16,7 @@ from sage.misc.fpickle import pickle_function, call_pickled_function from . import ncpus + def pyprocessing(processes=0): """ Return a parallel iterator using a given number of processes diff --git a/src/sage/parallel/ncpus.py b/src/sage/parallel/ncpus.py index 0dcdd16fe17..dfb6c3c6734 100644 --- a/src/sage/parallel/ncpus.py +++ b/src/sage/parallel/ncpus.py @@ -33,6 +33,7 @@ import os import subprocess + def ncpus(): """ Detects the number of effective CPUs in the system. @@ -52,22 +53,25 @@ def ncpus(): else: return int(n) - #for Linux, Unix and MacOS + # for Linux, Unix and MacOS if hasattr(os, "sysconf"): if "SC_NPROCESSORS_ONLN" in os.sysconf_names: - #Linux and Unix + # Linux and Unix ncpus = os.sysconf("SC_NPROCESSORS_ONLN") if isinstance(ncpus, int) and ncpus > 0: return ncpus else: - #MacOS X - #deprecated: return int(os.popen2("sysctl -n hw.ncpu")[1].read()) - process = subprocess.Popen("sysctl -n hw.ncpu", shell=True, stdin=subprocess.PIPE, stdout = subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True) + # MacOS X + # deprecated: return int(os.popen2("sysctl -n hw.ncpu")[1].read()) + process = subprocess.Popen("sysctl -n hw.ncpu", shell=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, close_fds=True) return int(process.stdout.read()) - #for Windows + # for Windows if "NUMBER_OF_PROCESSORS" in os.environ: ncpus = int(os.environ["NUMBER_OF_PROCESSORS"]) if ncpus > 0: return ncpus - #return the default value + # return the default value return 1 diff --git a/src/sage/parallel/parallelism.py b/src/sage/parallel/parallelism.py index b974616a784..8aafe232fee 100644 --- a/src/sage/parallel/parallelism.py +++ b/src/sage/parallel/parallelism.py @@ -28,6 +28,7 @@ from sage.parallel.ncpus import ncpus from sage.rings.integer import Integer + class Parallelism(Singleton, SageObject): r""" Singleton class for managing the number of processes used in parallel @@ -106,9 +107,12 @@ def __init__(self): sage: TestSuite(par).run() """ - self._default = ncpus() # default number of proc. used in parallelizations - self._nproc = {'tensor' : 1, 'linbox' : 1} # dict. of number of processes to be used - # (keys: computational field) + self._default = ncpus() + # default number of proc. used in parallelizations + + self._nproc = {'tensor': 1, 'linbox': 1} + # dict. of number of processes to be used + # (keys: computational field) def _repr_(self): r""" @@ -242,7 +246,7 @@ def set(self, field=None, nproc=None): if nproc is None: self._nproc[field] = self._default else: - if not isinstance(nproc, (int,Integer)): + if not isinstance(nproc, (int, Integer)): raise TypeError("nproc must be integer") self._nproc[field] = nproc @@ -281,7 +285,6 @@ def get(self, field): "implemented in Parallelism()") return self._nproc[field] - def get_all(self): r""" Return the number of processes which will be used in parallel @@ -307,7 +310,6 @@ def get_all(self): """ return self._nproc - def set_default(self, nproc=None): r""" Set the default number of processes to be launched in parallel @@ -343,7 +345,7 @@ def set_default(self, nproc=None): if nproc is None: self._default = ncpus() else: - if not isinstance(nproc,(int,Integer)): + if not isinstance(nproc, (int, Integer)): raise TypeError("nproc must be integer") self._default = nproc diff --git a/src/sage/parallel/use_fork.py b/src/sage/parallel/use_fork.py index 758a572a2c2..69f2ede8420 100644 --- a/src/sage/parallel/use_fork.py +++ b/src/sage/parallel/use_fork.py @@ -2,15 +2,15 @@ Parallel iterator built using the ``fork()`` system call """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2010 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 shutil import rmtree @@ -41,7 +41,7 @@ class WorkerData(): sage: W.starttime # random 1499330252.463206 """ - def __init__(self, input, starttime=None, failure=""): + def __init__(self, input_value, starttime=None, failure=""): r""" See the class documentation for description of the inputs. @@ -50,7 +50,7 @@ def __init__(self, input, starttime=None, failure=""): sage: from sage.parallel.use_fork import WorkerData sage: W = WorkerData(42) """ - self.input = input + self.input = input_value self.starttime = starttime or walltime() self.failure = failure @@ -197,8 +197,7 @@ def __call__(self, f, inputs): if T - W.starttime > timeout: if self.verbose: print( - "Killing subprocess %s with input %s which took too long" - % (pid, W.input) ) + "Killing subprocess %s with input %s which took too long" % (pid, W.input)) os.kill(pid, signal.SIGKILL) W.failure = " (timed out)" except KeyError: @@ -206,7 +205,7 @@ def __call__(self, f, inputs): pass else: # collect data from process that successfully terminated - sobj = os.path.join(dir, '%s.sobj'%pid) + sobj = os.path.join(dir, '%s.sobj' % pid) try: with open(sobj, "rb") as file: data = file.read() @@ -219,7 +218,7 @@ def __call__(self, f, inputs): except Exception as E: answer = "INVALID DATA {}".format(E) - out = os.path.join(dir, '%s.out'%pid) + out = os.path.join(dir, '%s.out' % pid) try: with open(out) as file: sys.stdout.write(file.read()) From 58800b09c3d043a1f30c0541b9f7be2f1061aa9d Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Fri, 2 Dec 2022 10:33:29 +0900 Subject: [PATCH 601/632] A tiny edit to kick off testing --- src/sage/plot/plot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/plot/plot.py b/src/sage/plot/plot.py index b2621ad3fd7..abc92e5cf32 100644 --- a/src/sage/plot/plot.py +++ b/src/sage/plot/plot.py @@ -1741,12 +1741,12 @@ def b(n): return lambda x: bessel_J(n, x) + 0.5*(n-1) :: - sage: plot(2*x + 1,(x, 0, 5), ticks=[[0, 1, e, pi, sqrt(20)], [1, 3, 2*e + 1, 2*pi + 1, 2*sqrt(20) + 1]], tick_formatter="latex") + sage: plot(2*x + 1, (x, 0, 5), ticks=[[0, 1, e, pi, sqrt(20)], [1, 3, 2*e + 1, 2*pi + 1, 2*sqrt(20) + 1]], tick_formatter="latex") Graphics object consisting of 1 graphics primitive .. PLOT:: - g = plot(2*x + 1,(x, 0, 5), ticks=[[0, 1, e, pi, sqrt(20)], [1, 3, 2*e + 1, 2*pi + 1, 2*sqrt(20) + 1]], tick_formatter="latex") + g = plot(2*x + 1, (x, 0, 5), ticks=[[0, 1, e, pi, sqrt(20)], [1, 3, 2*e + 1, 2*pi + 1, 2*sqrt(20) + 1]], tick_formatter="latex") sphinx_plot(g) This is particularly useful when setting custom ticks in multiples of `\pi`. From dac5eceb0ff5a9476a198ac2122b16c5aa956749 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Thu, 1 Dec 2022 23:24:31 -0800 Subject: [PATCH 602/632] build/pkgs/matplotlib/dependencies: Add contourpy --- build/pkgs/matplotlib/dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/matplotlib/dependencies b/build/pkgs/matplotlib/dependencies index f13fd3f367a..cfcf3edda7e 100644 --- a/build/pkgs/matplotlib/dependencies +++ b/build/pkgs/matplotlib/dependencies @@ -1,4 +1,4 @@ -$(PYTHON) numpy freetype pillow dateutil pyparsing tornado six cycler qhull fonttools | $(PYTHON_TOOLCHAIN) kiwisolver certifi setuptools_scm_git_archive +$(PYTHON) numpy freetype pillow dateutil pyparsing tornado six cycler qhull fonttools contourpy | $(PYTHON_TOOLCHAIN) kiwisolver certifi setuptools_scm_git_archive ---------- All lines of this file are ignored except the first. From 6e5ea791a3d846cfd70d9b361f168f6c2fa66a2b Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Sat, 3 Dec 2022 00:10:56 +0000 Subject: [PATCH 603/632] move import of libgap into a function --- src/sage/rings/finite_rings/element_ntl_gf2e.pyx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx index 46601ecd5e2..416867dd8b3 100644 --- a/src/sage/rings/finite_rings/element_ntl_gf2e.pyx +++ b/src/sage/rings/finite_rings/element_ntl_gf2e.pyx @@ -54,8 +54,6 @@ from .finite_field_ntl_gf2e import FiniteField_ntl_gf2e from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -from sage.libs.gap.element import GapElement_FiniteField - from sage.interfaces.abc import GapElement @@ -308,6 +306,7 @@ cdef class Cache_ntl_gf2e(Cache_base): cdef FiniteField_ntl_gf2eElement x cdef FiniteField_ntl_gf2eElement g cdef Py_ssize_t i + from sage.libs.gap.element import GapElement_FiniteField if is_IntegerMod(e): e = e.lift() From 7aac17573027833982f6c5fe64ab66959cea4ccb Mon Sep 17 00:00:00 2001 From: Lucas Fiegl Date: Sat, 3 Dec 2022 06:28:09 +0000 Subject: [PATCH 604/632] Remove numerical separators from RealLiterals to avoid conversion issues --- src/sage/rings/real_mpfr.pyx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index 09ace5ec1eb..e9dc4d24671 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -5678,11 +5678,13 @@ cdef class RealLiteral(RealNumber): 1.3000000000000000000000000000000000000000000000000000000000 sage: 1.3 + 1.2 2.50000000000000 + sage: RR(1_0000.000000000000000000000000000000000000) + 10000.0000000000 """ RealNumber.__init__(self, parent, x, base) if isinstance(x, str): self.base = base - self.literal = x + self.literal = x.replace('_', '') def __neg__(self): """ From adc231ef98032f86f42167a98dd86f3800f1ee9f Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Sun, 4 Dec 2022 15:59:28 +0000 Subject: [PATCH 605/632] remove is_GapElement from number fields code --- src/sage/rings/number_field/number_field.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index a24552a0c6f..4e2200dd41f 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -159,6 +159,8 @@ from sage.modules.free_module_element import vector from sage.rings.real_mpfr import RR +from sage.interfaces.abc import GapElement + _NumberFields = NumberFields() @@ -1819,7 +1821,7 @@ def _element_constructor_(self, x, check=True): raise TypeError("%s has unsupported PARI type %s" % (x, x.type())) x = self.absolute_polynomial().parent()(x) return self._element_class(self, x) - elif sage.interfaces.gap.is_GapElement(x): + elif isinstance(x, GapElement): s = x._sage_repr() if self.variable_name() in s: return self._convert_from_str(s) @@ -11143,8 +11145,7 @@ def _element_constructor_(self, x, check=True): return NumberField_absolute._element_constructor_(self, x) elif isinstance(x, pari_gen): return NumberField_absolute._element_constructor_(self, x, check=check) - elif (sage.interfaces.gap.is_GapElement(x) or - isinstance(x, sage.libs.gap.element.GapElement)): + elif isinstance(x, (sage.libs.gap.element.GapElement, GapElement)): return self._coerce_from_gap(x) elif isinstance(x, str): return self._convert_from_str(x) From 7c67a1089852beca7b5e0b80ad3cb2a319f0ceef Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Sun, 4 Dec 2022 16:22:31 +0000 Subject: [PATCH 606/632] deprecating is_GapElement --- src/sage/interfaces/gap.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/sage/interfaces/gap.py b/src/sage/interfaces/gap.py index 1f5fc58e9db..6e1c2355341 100644 --- a/src/sage/interfaces/gap.py +++ b/src/sage/interfaces/gap.py @@ -1635,16 +1635,24 @@ def _instancedoc_(self): def is_GapElement(x): """ + Deprecated function + + Use isinstance(x,GapElement) + Returns True if x is a GapElement. EXAMPLES:: sage: from sage.interfaces.gap import is_GapElement sage: is_GapElement(gap(2)) + doctest:...: DeprecationWarning: The function is_GapElement is deprecated. Use isinstance(x, GapElement) instead + See https://trac.sagemath.org/34823 for details. True sage: is_GapElement(2) False """ + from sage.misc.superseded import deprecation + deprecation(34823, "The function is_GapElement is deprecated. Use isinstance(x, GapElement) instead") return isinstance(x, GapElement) def gfq_gap_to_sage(x, F): From 6e1f451239e57919bae8c8ff505b310599f98499 Mon Sep 17 00:00:00 2001 From: Martin Rubey Date: Sun, 4 Dec 2022 20:03:17 +0100 Subject: [PATCH 607/632] remove unused import --- src/sage/combinat/sf/monomial.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/combinat/sf/monomial.py b/src/sage/combinat/sf/monomial.py index 96fcacb11d0..c36716cfa3c 100644 --- a/src/sage/combinat/sf/monomial.py +++ b/src/sage/combinat/sf/monomial.py @@ -22,7 +22,7 @@ import sage.libs.symmetrica.all as symmetrica from sage.rings.integer import Integer from sage.rings.infinity import infinity -from sage.combinat.partition import Partition, _Partitions +from sage.combinat.partition import _Partitions from sage.arith.misc import multinomial, factorial, binomial From 6c75e9b0cc5679ee9ea5773ca1586eacf438e378 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Sun, 4 Dec 2022 21:56:40 +0000 Subject: [PATCH 608/632] better rst, point at sage.interfaces.abc to use --- src/sage/interfaces/gap.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/interfaces/gap.py b/src/sage/interfaces/gap.py index 6e1c2355341..c2f8008171d 100644 --- a/src/sage/interfaces/gap.py +++ b/src/sage/interfaces/gap.py @@ -1637,22 +1637,22 @@ def is_GapElement(x): """ Deprecated function - Use isinstance(x,GapElement) + Use :func:`isinstance` (of :class:`sage.interfaces.abc.GapElement`) instead. - Returns True if x is a GapElement. + Returns True if ``x`` is :class:`GapElement` EXAMPLES:: sage: from sage.interfaces.gap import is_GapElement sage: is_GapElement(gap(2)) - doctest:...: DeprecationWarning: The function is_GapElement is deprecated. Use isinstance(x, GapElement) instead + doctest:...: DeprecationWarning: The function is_GapElement is deprecated. Use isinstance(x, sage.interfaces.abc.GapElement) instead See https://trac.sagemath.org/34823 for details. True sage: is_GapElement(2) False """ from sage.misc.superseded import deprecation - deprecation(34823, "The function is_GapElement is deprecated. Use isinstance(x, GapElement) instead") + deprecation(34823, "The function is_GapElement is deprecated. Use isinstance(x, sage.interfaces.abc.GapElement) instead") return isinstance(x, GapElement) def gfq_gap_to_sage(x, F): From e99db3af3a814e9accdd10a915f651aabe9faa91 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Sun, 4 Dec 2022 14:04:14 -0800 Subject: [PATCH 609/632] src/sage/interfaces/gap.py: Reviewer edits --- src/sage/interfaces/gap.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/interfaces/gap.py b/src/sage/interfaces/gap.py index c2f8008171d..7ae1792e9de 100644 --- a/src/sage/interfaces/gap.py +++ b/src/sage/interfaces/gap.py @@ -1635,26 +1635,26 @@ def _instancedoc_(self): def is_GapElement(x): """ - Deprecated function + Return True if ``x`` is a :class:`GapElement` - Use :func:`isinstance` (of :class:`sage.interfaces.abc.GapElement`) instead. - - Returns True if ``x`` is :class:`GapElement` + This function is deprecated; use :func:`isinstance` + (of :class:`sage.interfaces.abc.GapElement`) instead. EXAMPLES:: sage: from sage.interfaces.gap import is_GapElement sage: is_GapElement(gap(2)) - doctest:...: DeprecationWarning: The function is_GapElement is deprecated. Use isinstance(x, sage.interfaces.abc.GapElement) instead + doctest:...: DeprecationWarning: the function is_GapElement is deprecated; use isinstance(x, sage.interfaces.abc.GapElement) instead See https://trac.sagemath.org/34823 for details. True sage: is_GapElement(2) False """ from sage.misc.superseded import deprecation - deprecation(34823, "The function is_GapElement is deprecated. Use isinstance(x, sage.interfaces.abc.GapElement) instead") + deprecation(34823, "the function is_GapElement is deprecated; use isinstance(x, sage.interfaces.abc.GapElement) instead") return isinstance(x, GapElement) + def gfq_gap_to_sage(x, F): """ INPUT: From 6c34279634da03100c2bff5ef0480622119642a2 Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Tue, 6 Dec 2022 04:00:34 +0900 Subject: [PATCH 610/632] Uniformize headlines --- .../noncommutative_polynomial_rings/index.rst | 11 ++------- .../rings/polynomial/ore_function_element.py | 4 +--- .../rings/polynomial/ore_function_field.py | 2 +- .../polynomial/ore_polynomial_element.pyx | 2 +- .../rings/polynomial/ore_polynomial_ring.py | 2 +- src/sage/rings/polynomial/plural.pyx | 2 +- .../polynomial/skew_polynomial_element.pyx | 2 +- .../skew_polynomial_finite_field.pyx | 24 +++++++++---------- .../skew_polynomial_finite_order.pyx | 18 +++++++------- .../rings/polynomial/skew_polynomial_ring.py | 2 +- 10 files changed, 30 insertions(+), 39 deletions(-) diff --git a/src/doc/en/reference/noncommutative_polynomial_rings/index.rst b/src/doc/en/reference/noncommutative_polynomial_rings/index.rst index dca4c3fe931..1915006f832 100644 --- a/src/doc/en/reference/noncommutative_polynomial_rings/index.rst +++ b/src/doc/en/reference/noncommutative_polynomial_rings/index.rst @@ -1,8 +1,8 @@ Noncommutative Polynomials ========================== -Univariate Ore polynomial rings -------------------------------- +Univariate Ore Polynomials +-------------------------- .. toctree:: :maxdepth: 1 @@ -13,13 +13,6 @@ Univariate Ore polynomial rings sage/rings/polynomial/skew_polynomial_element sage/rings/polynomial/skew_polynomial_finite_order sage/rings/polynomial/skew_polynomial_finite_field - -Fraction field of Ore polynomial rings --------------------------------------- - -.. toctree:: - :maxdepth: 1 - sage/rings/polynomial/ore_function_field sage/rings/polynomial/ore_function_element diff --git a/src/sage/rings/polynomial/ore_function_element.py b/src/sage/rings/polynomial/ore_function_element.py index 69f394a3e74..d21ed8d1ed4 100644 --- a/src/sage/rings/polynomial/ore_function_element.py +++ b/src/sage/rings/polynomial/ore_function_element.py @@ -1,12 +1,11 @@ r""" -An element in the fraction field of a Ore polynomial ring. +Fraction field elements of Ore polynomial rings AUTHOR: - Xavier Caruso (2020-05) """ - # *************************************************************************** # Copyright (C) 2020 Xavier Caruso # @@ -17,7 +16,6 @@ # https://www.gnu.org/licenses/ # *************************************************************************** - from sage.structure.richcmp import richcmp, op_EQ, op_NE from sage.misc.cachefunc import cached_method from sage.misc.latex import latex diff --git a/src/sage/rings/polynomial/ore_function_field.py b/src/sage/rings/polynomial/ore_function_field.py index 5cdaa302990..f223f9799dd 100644 --- a/src/sage/rings/polynomial/ore_function_field.py +++ b/src/sage/rings/polynomial/ore_function_field.py @@ -1,5 +1,5 @@ r""" -Fraction fields of Ore polynomial rings. +Fraction fields of Ore polynomial rings Sage provides support for building the fraction field of any Ore polynomial ring and performing basic operations in it. diff --git a/src/sage/rings/polynomial/ore_polynomial_element.pyx b/src/sage/rings/polynomial/ore_polynomial_element.pyx index a5cade9325f..674c93c8d99 100644 --- a/src/sage/rings/polynomial/ore_polynomial_element.pyx +++ b/src/sage/rings/polynomial/ore_polynomial_element.pyx @@ -1,5 +1,5 @@ r""" -Univariate Ore Polynomials +Univariate Ore polynomials This module provides the :class:`~sage.rings.polynomial.skew_polynomial_element.OrePolynomial`, diff --git a/src/sage/rings/polynomial/ore_polynomial_ring.py b/src/sage/rings/polynomial/ore_polynomial_ring.py index 0914fcb822b..f7eacf3112a 100644 --- a/src/sage/rings/polynomial/ore_polynomial_ring.py +++ b/src/sage/rings/polynomial/ore_polynomial_ring.py @@ -1,5 +1,5 @@ r""" -Univariate Ore Polynomial Rings +Univariate Ore polynomial rings This module provides the :class:`~sage.rings.polynomial.ore_polynomial_ring.OrePolynomialRing`, diff --git a/src/sage/rings/polynomial/plural.pyx b/src/sage/rings/polynomial/plural.pyx index 3209824ec88..1c9f35e56ba 100644 --- a/src/sage/rings/polynomial/plural.pyx +++ b/src/sage/rings/polynomial/plural.pyx @@ -1,5 +1,5 @@ r""" -Noncommutative Polynomials via libSINGULAR/Plural +Noncommutative polynomials via libSINGULAR/Plural This module provides specialized and optimized implementations for noncommutative multivariate polynomials over many coefficient rings, via the diff --git a/src/sage/rings/polynomial/skew_polynomial_element.pyx b/src/sage/rings/polynomial/skew_polynomial_element.pyx index c47515141fd..11a6e19cfe4 100644 --- a/src/sage/rings/polynomial/skew_polynomial_element.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_element.pyx @@ -1,5 +1,5 @@ r""" -Univariate Skew Polynomials +Univariate skew polynomials This module provides the :class:`~sage.rings.polynomial.skew_polynomial_element.SkewPolynomial`. diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx index 289784b94b3..107077623d6 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -1,7 +1,7 @@ r""" -Univariate Dense Skew Polynomials over Finite Fields +Univariate dense skew polynomials over finite fields -This module provides the +This module provides the class:`~sage.rings.polynomial.skew_polynomial_finite_field.SkewPolynomial_finite_field_dense`, which constructs a single univariate skew polynomial over a finite field equipped with the Frobenius endomorphism. Among other things, it implements @@ -336,7 +336,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): ....: N = R._reduced_norm_factor_uniform() ....: counts[N] += 1 sage: counts # random - {z + 1: 969, z + 2: 31} + {z + 1: 969, z + 2: 31} """ skew_ring = self._parent F = self._reduced_norm_factored() @@ -390,7 +390,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): sage: for D in rightdiv: ....: assert P.is_right_divisible_by(D), "not right divisible" ....: assert D.is_irreducible(), "not irreducible" - + sage: P = S.random_element(degree=10) sage: leftdiv = [ f for f in P.left_irreducible_divisors() ] # indirect doctest sage: len(leftdiv) == P.count_irreducible_divisors() @@ -401,7 +401,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): ....: assert P.is_left_divisible_by(D), "not left divisible" ....: assert D.is_irreducible(), "not irreducible" """ - cdef SkewPolynomial_finite_field_dense NS, P, Q, R, P1, Q1, L, V, g, d + cdef SkewPolynomial_finite_field_dense NS, P, Q, R, P1, Q1, L, V, g, d cdef Py_ssize_t i, m, degrandom if not self: return @@ -697,8 +697,8 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): .. NOTE:: - One can prove that there are always as many left - irreducible monic divisors as right irreducible + One can prove that there are always as many left + irreducible monic divisors as right irreducible monic divisors. EXAMPLES:: @@ -936,7 +936,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): INPUT: - - ``uniform`` -- a boolean (default: ``False``); whether the + - ``uniform`` -- a boolean (default: ``False``); whether the output irreducible divisor should be uniformly distributed among all possibilities @@ -966,7 +966,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): sage: F.value() == a True - There is a priori no guarantee on the distribution of the + There is a priori no guarantee on the distribution of the factorizations we get. Passing in the keyword ``uniform=True`` ensures the output is uniformly distributed among all factorizations:: @@ -1077,9 +1077,9 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): ....: assert F.value() == a, "factorization has a different value" ....: for d,_ in F: ....: assert d.is_irreducible(), "a factor is not irreducible" - - Note that the algorithm used in this method is probabilistic. - As a consequence, if we call it two times with the same input, + + Note that the algorithm used in this method is probabilistic. + As a consequence, if we call it two times with the same input, we can get different orderings:: sage: factorizations2 = [ F for F in a.factorizations() ] diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx index 951fcd22c97..9f4b3e62e63 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx @@ -1,5 +1,5 @@ r""" -Univariate Dense Skew Polynomials over a field equipped with a finite order automorphism +Univariate dense skew polynomials over a field with a finite order automorphism AUTHOR:: @@ -190,13 +190,13 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): By default, the name of the central variable is usually ``z`` (see :meth:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_finite_order.center` - for more details about this). - However, the user can specify a different variable name if desired:: + for more details about this). + However, the user can specify a different variable name if desired:: sage: a.reduced_trace(var='u') 3*u + 4 - When passing in ``var=False``, a tuple of coefficients (instead of + When passing in ``var=False``, a tuple of coefficients (instead of an actual polynomial) is returned:: sage: a.reduced_trace(var=False) @@ -265,20 +265,20 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): By default, the name of the central variable is usually ``z`` (see :meth:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_finite_order.center` - for more details about this). + for more details about this). However, the user can specify a different variable name if desired:: sage: a.reduced_norm(var='u') u^3 + 4*u^2 + 4 - When passing in ``var=False``, a tuple of coefficients (instead of + When passing in ``var=False``, a tuple of coefficients (instead of an actual polynomial) is returned:: sage: a.reduced_norm(var=False) (4, 0, 4, 1) TESTS: - + We check that `N` is a multiple of `a`:: sage: S(N).is_right_divisible_by(a) @@ -320,7 +320,7 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): """ if self._norm is None: if self.is_zero(): - self._norm = 0 + self._norm = 0 else: parent = self._parent section = parent._embed_constants.section() @@ -387,7 +387,7 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): By default, the name of the variable of the reduced characteristic polynomial is ``x`` and the name of central variable is usually ``z`` (see :meth:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_finite_order.center` - for more details about this). + for more details about this). The user can speciify different names if desired:: sage: a.reduced_charpoly(var='T') # variable name for the caracteristic polynomial diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index c31a386c226..7d0c12538dc 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -1,5 +1,5 @@ r""" -Skew Univariate Polynomial Rings +Univariate skew polynomial rings This module provides the :class:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing`. From 6c8a771adf9990d0609f98834e5b960ec5f85fcd Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 8 Nov 2021 06:39:27 -0500 Subject: [PATCH 611/632] Trac #32841: drop unused _mul_zn_poly() function. This function isn't used anywhere in the sage library, and according to its own documentation, "is a technology preview. It might disappear or be replaced without a deprecation warning." Today is that day. --- .../polynomial/polynomial_zmod_flint.pyx | 69 +------------------ 1 file changed, 1 insertion(+), 68 deletions(-) diff --git a/src/sage/rings/polynomial/polynomial_zmod_flint.pyx b/src/sage/rings/polynomial/polynomial_zmod_flint.pyx index fd92a626555..15958758d18 100644 --- a/src/sage/rings/polynomial/polynomial_zmod_flint.pyx +++ b/src/sage/rings/polynomial/polynomial_zmod_flint.pyx @@ -1,4 +1,4 @@ -# distutils: libraries = gmp NTL_LIBRARIES zn_poly +# distutils: libraries = gmp NTL_LIBRARIES # distutils: extra_compile_args = NTL_CFLAGS # distutils: include_dirs = NTL_INCDIR # distutils: library_dirs = NTL_LIBDIR @@ -63,13 +63,6 @@ include "sage/libs/flint/nmod_poly_linkage.pxi" # and then the interface include "polynomial_template.pxi" -cdef extern from "zn_poly/zn_poly.h": - ctypedef struct zn_mod_struct: - pass - cdef void zn_mod_init(zn_mod_struct *mod, unsigned long m) - cdef void zn_mod_clear(zn_mod_struct *mod) - cdef void zn_array_mul(unsigned long* res, unsigned long* op1, size_t n1, unsigned long* op2, size_t n2, zn_mod_struct *mod) - from sage.libs.flint.fmpz_poly cimport * from sage.libs.flint.nmod_poly cimport * @@ -420,66 +413,6 @@ cdef class Polynomial_zmod_flint(Polynomial_template): else: raise IndexError("Polynomial coefficient index must be nonnegative.") - def _mul_zn_poly(self, other): - r""" - Returns the product of two polynomials using the zn_poly library. - - See http://www.math.harvard.edu/~dmharvey/zn_poly/ for details - on zn_poly. - - INPUT: - - - self: Polynomial - - right: Polynomial (over same base ring as self) - - OUTPUT: (Polynomial) the product self*right. - - - EXAMPLES:: - - sage: P. = PolynomialRing(GF(next_prime(2^30))) - sage: f = P.random_element(1000) - sage: g = P.random_element(1000) - sage: f*g == f._mul_zn_poly(g) - True - - sage: P. = PolynomialRing(Integers(100)) - sage: P - Univariate Polynomial Ring in x over Ring of integers modulo 100 - sage: r = (10*x)._mul_zn_poly(10*x); r - 0 - sage: r.degree() - -1 - - ALGORITHM: - - uses David Harvey's zn_poly library. - - NOTE: This function is a technology preview. It might - disappear or be replaced without a deprecation warning. - """ - cdef Polynomial_zmod_flint _other = self._parent.coerce(other) - - cdef type t = type(self) - cdef Polynomial_zmod_flint r = t.__new__(t) - r._parent = (self)._parent - r._cparent = (self)._cparent - - cdef unsigned long p = self._parent.modulus() - cdef unsigned long n1 = self.x.length - cdef unsigned long n2 = _other.x.length - - cdef zn_mod_struct zn_mod - - nmod_poly_init2(&r.x, p, n1 + n2 -1 ) - - zn_mod_init(&zn_mod, p) - zn_array_mul( r.x.coeffs, self.x.coeffs, n1, _other.x.coeffs, n2, &zn_mod) - r.x.length = n1 + n2 -1 - _nmod_poly_normalise(&r.x) - zn_mod_clear(&zn_mod) - return r - cpdef Polynomial _mul_trunc_(self, Polynomial right, long n): """ Return the product of this polynomial and other truncated to the From c938e59469884a2a3d63567f9c38c7a4c2b57ff0 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 8 Nov 2021 10:45:17 -0500 Subject: [PATCH 612/632] Trac #32841: add missing include to hypellfrob.cpp. This file uses the assert() macro provided by in C++. In the past it was included transitively; but soon it won't be, so. --- src/sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.cpp b/src/sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.cpp index 7af598473ab..5dc503c2897 100644 --- a/src/sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.cpp +++ b/src/sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.cpp @@ -28,6 +28,7 @@ #include "recurrences_ntl.h" #include "recurrences_zn_poly.h" +#include NTL_CLIENT From abc091ec0ed260bdc8e2bfd9dbe4a174b2af8024 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 8 Nov 2021 06:48:18 -0500 Subject: [PATCH 613/632] Trac #32841: drop zn_poly interval_products() implementation. Our interval_products_wrapper() wrapped three different implementations: * ntl_interval_products (ZZ_p version) * ntl_interval_products (zz_p version) * zn_poly_interval_products But "the zn_poly version occasionally fails," and when that happens, the NTL implementation is used anyway. Moreover the wrapper takes a "force_ntl" parameter that allows the user to skip the zn_poly attempt entirely. This is the only place that zn_poly is actually used in sagelib anymore, so this commit removes the underlying zn_poly implementation, and eliminates the "force_ntl" parameter that was propagated throughout the "hypellfrob" code. Now NTL is always used, as if force_ntl was enabled. --- .gitignore | 1 - .../hyperelliptic_curves/hypellfrob.pyx | 10 +- .../hypellfrob/hypellfrob.cpp | 75 +-- .../hypellfrob/hypellfrob.h | 17 +- .../hypellfrob/recurrences_zn_poly.cpp | 552 ------------------ .../hypellfrob/recurrences_zn_poly.h | 158 ----- 6 files changed, 18 insertions(+), 795 deletions(-) delete mode 100644 src/sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_zn_poly.cpp delete mode 100644 src/sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_zn_poly.h diff --git a/.gitignore b/.gitignore index d598d2e633f..e62fa805ecd 100644 --- a/.gitignore +++ b/.gitignore @@ -157,7 +157,6 @@ __pycache__/ !/src/sage/rings/polynomial/weil/power_sums.c !/src/sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.cpp !/src/sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_ntl.cpp -!/src/sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_zn_poly.cpp !/src/sage/stats/distributions/dgs_bern.c !/src/sage/stats/distributions/dgs_gauss_dp.c !/src/sage/stats/distributions/dgs_gauss_mp.c diff --git a/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx b/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx index 18ad918ac62..c08bb3cfb53 100644 --- a/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx +++ b/src/sage/schemes/hyperelliptic_curves/hypellfrob.pyx @@ -1,8 +1,8 @@ # distutils: language = c++ -# distutils: sources = sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.cpp sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_ntl.cpp sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_zn_poly.cpp -# distutils: depends = sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.h sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_ntl.h sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_zn_poly.h +# distutils: sources = sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.cpp sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_ntl.cpp +# distutils: depends = sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.h sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_ntl.h # distutils: include_dirs = sage/libs/ntl/ sage/schemes/hyperelliptic_curves/hypellfrob/ NTL_INCDIR -# distutils: libraries = gmp NTL_LIBRARIES zn_poly +# distutils: libraries = gmp NTL_LIBRARIES # distutils: extra_compile_args = NTL_CFLAGS # distutils: library_dirs = NTL_LIBDIR # distutils: extra_link_args = NTL_LIBEXTRA @@ -59,7 +59,7 @@ cdef extern from "hypellfrob.h": void interval_products_wrapper \ "hypellfrob::hypellfrob_interval_products_wrapper" \ (mat_ZZ_p_c &output, const mat_ZZ_p_c &M0, const mat_ZZ_p_c &M1, - const vector[ZZ_c] target, int force_ntl) + const vector[ZZ_c] target) def interval_products(M0, M1, target): @@ -146,7 +146,7 @@ def interval_products(M0, M1, target): numintervals = len(target)/2 out.SetDims(dim, dim*numintervals) - interval_products_wrapper(out, mm0, mm1, targ, 1) + interval_products_wrapper(out, mm0, mm1, targ) sig_off() R = M0.matrix_space() diff --git a/src/sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.cpp b/src/sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.cpp index 5dc503c2897..ee55e41e919 100644 --- a/src/sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.cpp +++ b/src/sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.cpp @@ -26,7 +26,6 @@ #include "hypellfrob.h" #include "recurrences_ntl.h" -#include "recurrences_zn_poly.h" #include @@ -75,17 +74,10 @@ The intervals are supplied in "target", simply as the list There are three possible underlying implementations: * ntl_interval_products (ZZ_p version), * ntl_interval_products (zz_p version), - * zn_poly_interval_products. -This function is a wrapper which takes ZZ_p input, calls one of the three +This function is a wrapper which takes ZZ_p input, calls one of the two above implementations depending on the size of the current ZZ_p modulus, and produces output in ZZ_p format. -If the force_ntl flag is set, it will never use the zn_poly version. - -Note that the zn_poly version occasionally fails; this happens more frequently -for smaller p, but is extremely rare for larger p. This wrapper detects this -and falls back on the zz_p/ZZ_p versions, which should never fail. - PRECONDITIONS: Let d = b[n-1] - a[0]. Then 2, 3, ... 1 + floor(sqrt(d)) must all be invertible. @@ -93,58 +85,10 @@ and falls back on the zz_p/ZZ_p versions, which should never fail. */ void interval_products_wrapper(vector& output, const mat_ZZ_p& M0, const mat_ZZ_p& M1, - const vector& target, - int force_ntl = 0) + const vector& target) { const ZZ& modulus = ZZ_p::modulus(); - if (!force_ntl && modulus <= (1UL << (ULONG_BITS - 1)) - 1) - { - // Small modulus; let's try using zn_poly if we're allowed. - // (we don't allow the full ULONG_BITS bits, because I'm worried I - // sometimes use NTL code which requires longs rather than ulongs.) - zn_mod_t mod; - zn_mod_init(mod, to_ulong(modulus)); - - int r = M0.NumRows(); - - // convert to zn_mod format - vector > M0_temp(r, vector(r)); - vector > M1_temp(r, vector(r)); - vector > > output_temp(target.size()/2, M0_temp); - - for (int x = 0; x < r; x++) - for (int y = 0; y < r; y++) - { - M0_temp[y][x] = to_ulong(rep(M0[y][x])); - M1_temp[y][x] = to_ulong(rep(M1[y][x])); - } - - int success = zn_poly_interval_products(output_temp, M0_temp, M1_temp, - target, mod); - zn_mod_clear(mod); - - // note: if we failed, we fall back on the ZZ_p or zz_p versions below - if (success) - { - // convert back to ZZ_p format - output.clear(); - mat_ZZ_p temp; - temp.SetDims(r, r); - for (size_t i = 0; i < target.size()/2; i++) - { - for (int x = 0; x < r; x++) - for (int y = 0; y < r; y++) - temp[y][x] = output_temp[i][y][x]; - output.push_back(temp); - } - - return; - } - - // failed - } - if (!modulus.SinglePrecision()) { // Large modulus.... go straight to the ZZ_p version @@ -183,11 +127,10 @@ void interval_products_wrapper(vector& output, void hypellfrob_interval_products_wrapper(mat_ZZ_p& output, const mat_ZZ_p& M0, const mat_ZZ_p& M1, - const vector& target, - int force_ntl = 0) + const vector& target) { vector mat_vector; - interval_products_wrapper(mat_vector, M0, M1, target, force_ntl); + interval_products_wrapper(mat_vector, M0, M1, target); int r = M0.NumRows(); output.SetDims(r, r * mat_vector.size()); @@ -328,7 +271,7 @@ void padic_invert_matrix(mat_ZZ_p& B, const mat_ZZ_p& A, const ZZ& p, int N) /* The main function exported from this module. See hypellfrob.h for information. */ -int matrix(mat_ZZ& output, const ZZ& p, int N, const ZZX& Q, int force_ntl) +int matrix(mat_ZZ& output, const ZZ& p, int N, const ZZX& Q) { // check input conditions if (N < 1 || p < 3) @@ -522,8 +465,8 @@ int matrix(mat_ZZ& output, const ZZ& p, int N, const ZZX& Q, int force_ntl) vector MH, DH; MH.reserve(L); DH.reserve(L); - interval_products_wrapper(MH, MH0[j], MH1[j], s, force_ntl); - interval_products_wrapper(DH, DH0[j], DH1[j], s, force_ntl); + interval_products_wrapper(MH, MH0[j], MH1[j], s); + interval_products_wrapper(DH, DH0[j], DH1[j], s); // Use the vandermonde matrix to extend all the way up to L if necessary. if (L > N) @@ -710,8 +653,8 @@ int matrix(mat_ZZ& output, const ZZ& p, int N, const ZZX& Q, int force_ntl) } modulusV.restore(); vector MV, DV; - interval_products_wrapper(MV, MV0, MV1, s, force_ntl); - interval_products_wrapper(DV, DV0, DV1, s, force_ntl); + interval_products_wrapper(MV, MV0, MV1, s); + interval_products_wrapper(DV, DV0, DV1, s); // Divide out each MV[j] by DV[j]. Note that for 1 <= j < N, both of them // should be divisible by p too, so we take that into account here. diff --git a/src/sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.h b/src/sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.h index e25629ced56..23a57464c48 100644 --- a/src/sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.h +++ b/src/sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.h @@ -44,18 +44,11 @@ The intervals are supplied in "target", simply as the list There are three possible underlying implementations: * ntl_interval_products (ZZ_p version), - * ntl_interval_products (zz_p version), - * zn_poly_interval_products. -This function is a wrapper which takes ZZ_p input, calls one of the three + * ntl_interval_products (zz_p version) +This function is a wrapper which takes ZZ_p input, calls one of the two above implementations depending on the size of the current ZZ_p modulus, and produces output in ZZ_p format. -If the force_ntl flag is set, it will never use the zn_poly version. - -Note that the zn_poly version occasionally fails; this happens more frequently -for smaller p, but is extremely rare for larger p. This wrapper detects this -and falls back on the zz_p/ZZ_p versions, which should never fail. - PRECONDITIONS: Let d = b[n-1] - a[0]. Then 2, 3, ... 1 + floor(sqrt(d)) must all be invertible. @@ -63,8 +56,7 @@ and falls back on the zz_p/ZZ_p versions, which should never fail. */ void hypellfrob_interval_products_wrapper(NTL::mat_ZZ_p& output, const NTL::mat_ZZ_p& M0, const NTL::mat_ZZ_p& M1, - const std::vector& target, - int force_ntl); + const std::vector& target); /* Computes frobenius matrix for given p, to precision p^N, for the @@ -82,8 +74,7 @@ RETURN VALUE: will not check that p is prime. That's up to you.) */ -int matrix(NTL::mat_ZZ& output, const NTL::ZZ& p, int N, const NTL::ZZX& Q, - int force_ntl = 0); +int matrix(NTL::mat_ZZ& output, const NTL::ZZ& p, int N, const NTL::ZZX& Q); }; diff --git a/src/sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_zn_poly.cpp b/src/sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_zn_poly.cpp deleted file mode 100644 index b9023adb489..00000000000 --- a/src/sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_zn_poly.cpp +++ /dev/null @@ -1,552 +0,0 @@ -/* ============================================================================ - - recurrences_zn_poly.cpp: recurrences solved via zn_poly arithmetic - - This file is part of hypellfrob (version 2.1.1). - - Copyright (C) 2007, 2008, David Harvey - - 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. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -============================================================================ */ - - -#include "recurrences_zn_poly.h" - - -NTL_CLIENT - - -namespace hypellfrob { - - -Shifter::~Shifter() -{ - zn_array_mulmid_precomp1_clear(kernel_precomp); - free(input_twist); -} - - -Shifter::Shifter(ulong d, ulong a, ulong b, const zn_mod_t mod) -{ - this->d = d; - this->mod = mod; - - input_twist = (ulong*) malloc(sizeof(ulong) * (3*d + 3)); - output_twist = input_twist + d + 1; - scratch = output_twist + d + 1; - - ZZ modulus; - modulus = zn_mod_get (mod); - - // ------------------------ compute input_twist ------------------------- - - // prod = (d!)^(-1) - ulong prod = 1; - for (ulong i = 2; i <= d; i++) - prod = zn_mod_mul(prod, i, mod); - prod = to_ulong(InvMod(to_ZZ(prod), modulus)); - - // input_twist[i] = ((d-i)!)^(-1) - input_twist[0] = prod; - for (ulong i = 1; i <= d; i++) - input_twist[i] = zn_mod_mul(input_twist[i-1], d - (i-1), mod); - - // input_twist[i] = ((d-i)!*i!)^(-1) - for (ulong i = 0; i <= d/2; i++) - { - input_twist[i] = zn_mod_mul(input_twist[i], input_twist[d-i], mod); - input_twist[d-i] = input_twist[i]; - } - - // input_twist[i] = \prod_{0 <= j <= d, j != i} (i-j)^(-1) - // = (-1)^(d-i) ((d-i)!*i!)^(-1) - for (long i = d - 1; i >= 0; i -= 2) - input_twist[i] = zn_mod_neg(input_twist[i], mod); - - // ----------------- compute output_twist and kernel -------------------- - - // need some temp space: - // c, accum, accum_inv each of length 2d+1 - ulong* c = (ulong*) malloc(sizeof(ulong) * (6*d+3)); - ulong* accum = c + 2*d + 1; - ulong* accum_inv = accum + 2*d + 1; - ulong* kernel = c; // overwrites c - - // c[i] = c_i = a + (i-d)*b for 0 <= i <= 2d - c[0] = zn_mod_sub(a, zn_mod_mul(zn_mod_reduce(d, mod), b, mod), mod); - for (ulong i = 1; i <= 2*d; i++) - c[i] = zn_mod_add(c[i-1], b, mod); - - // accum[i] = c_0 * c_1 * ... * c_i for 0 <= i <= 2d - accum[0] = c[0]; - for (ulong i = 1; i <= 2*d; i++) - accum[i] = zn_mod_mul(accum[i-1], c[i], mod); - - // accum_inv[i] = (c_0 * c_1 * ... * c_i)^(-1) for 0 <= i <= 2d - accum_inv[2*d] = to_ulong(InvMod(to_ZZ(accum[2*d]), modulus)); - - for (long i = 2*d - 1; i >= 0; i--) - accum_inv[i] = zn_mod_mul(accum_inv[i+1], c[i+1], mod); - - // output_twist[i] = b^{-d} * c_i * c_{i+1} * ... * c_{i+d} - // for 0 <= i <= d - ulong factor = to_long(PowerMod(to_ZZ(b), -((long)d), modulus)); - output_twist[0] = zn_mod_mul(factor, accum[d], mod); - for (ulong i = 1; i <= d; i++) - output_twist[i] = zn_mod_mul(zn_mod_mul(factor, accum[i+d], mod), - accum_inv[i-1], mod); - - // kernel[i] = (c_i)^(-1) for 0 <= i <= 2d - kernel[0] = accum_inv[0]; - for (ulong i = 1; i <= 2*d; i++) - kernel[i] = zn_mod_mul(accum_inv[i], accum[i-1], mod); - - // precompute FFT of kernel - zn_array_mulmid_precomp1_init(kernel_precomp, kernel, 2*d+1, d+1, mod); - - free(c); -} - - -void Shifter::shift(ulong* output, const ulong* input) -{ - // multiply inputs pointwise by input_twist - for (ulong i = 0; i <= d; i++) - scratch[i] = zn_mod_mul(input[i], input_twist[i], mod); - - // do middle product - zn_array_mulmid_precomp1_execute(output, scratch, kernel_precomp); - - // multiply outputs pointwise by output_twist - for (ulong i = 0; i <= d; i++) - output[i] = zn_mod_mul(output[i], output_twist[i], mod); -} - - -/* - Checks whether large_evaluate() will succeed for these choices of k and u. - Returns 1 if okay, otherwise 0. -*/ -int check_params(ulong k, ulong u, const zn_mod_t mod) -{ - ulong n = zn_mod_get (mod); - - if (k >= n || u >= n) - return 0; - - if (k <= 1) - return 1; - - if (k == n - 1) - return 0; - - ulong k2 = k / 2; - - // need the following elements to be invertible: - // u - // 1, 2, ..., k + 1 - // k2 + i*u for -k2 <= i <= k2 - ulong prod = u; - for (ulong i = 2; i <= k; i++) - prod = zn_mod_mul(prod, i, mod); - ulong temp = zn_mod_mul(k2, zn_mod_sub(1, u, mod), mod); - for (ulong i = 0; i <= 2*k2; i++) - { - prod = zn_mod_mul(prod, temp, mod); - temp = zn_mod_add(temp, u, mod); - } - - ZZ x, y; - x = prod; - y = n; - if (GCD(x, y) != 1) - return 0; - - // check recursively below - return check_params(k2, u, mod); -} - - - -/* - Let M0 and M1 be square matrices of size r*r. Let M(x) = M0 + x*M1; this - is a matrix of linear polys in x. The matrices M0 and M1 are passed in - row-major order. - - Let P(x) = M(x+1) M(x+2) ... M(x+k); this is a matrix of polynomials of - degree k. - - This class attempts to compute the matrices - P(0), P(u), P(2u), ..., P(ku). - - Usage: - - * Call the constructor. - - * Call evaluate() with half == 0. This computes the first half of the - outputs, specifically P(mu) for 0 <= m <= k2, where k2 = floor(k/2). - The (i, j) entry of P(mu) is stored in output[i*r+j][m+offset]. Each array - output[i*r+j] must be preallocated to length k2 + 3. (The extra two matrices - at the end are used for scratch space.) - - * Call evaluate() with half == 1. This computes the second half, namely - P(mu) for k2 + 1 <= m <= k. The (i, j) entry of P(mu) is stored in - output[i*r+j][m-(k2+1)+offset]. Each array output[i*r+j] must be - preallocated to length k2 + 3. It's okay for this second half to overwrite - the first half generated earlier (in fact this property is used by - zn_poly_interval_products to conserve memory). - - The computation may fail for certain bad choices of k and u. Let p = residue - characteristic (i.e. assume mod is p^m for some m). Typically this function - gets called for k ~ u and k*u ~ M*p, for some M much smaller than p. In this - situation, I expect most choices of (k, u) are not bad. Failure is very - bad: the program will crash. Luckily, you can call check_params() to test - whether (k, u) is bad. If it's bad, you probably should just increment u - until you find one that's not bad. (Sorry, I haven't done a proper analysis - of the situation yet.) The main routines fall back on the zz_pX version if - they can't find a good parameter choice. - - Must have 0 <= k < n and 0 < u < n. - -*/ -LargeEvaluator::LargeEvaluator(int r, ulong k, ulong u, - const vector >& M0, - const vector >& M1, - const zn_mod_t& mod) : M0(M0), M1(M1), mod(mod) -{ - assert(k < zn_mod_get(mod)); - assert(u < zn_mod_get(mod)); - assert(r >= 1); - assert(k >= 1); - - this->r = r; - this->k = k; - this->k2 = k / 2; - this->odd = k & 1; - this->u = u; - this->shifter = NULL; -} - - -LargeEvaluator::~LargeEvaluator() -{ - if (this->shifter) - delete shifter; -} - - -void LargeEvaluator::evaluate(int half, vector& output, - ulong offset) -{ - // base cases - - if (k == 0) - { - // identity matrix - for (int x = 0; x < r; x++) - for (int y = 0; y < r; y++) - output[y*r + x].data[offset] = (x == y); - return; - } - - if (k == 1) - { - // evaluate M(1) (for half == 0) or M(u+1) (for half == 1) - for (int x = 0; x < r; x++) - for (int y = 0; y < r; y++) - { - ulong temp = zn_mod_add(M0[y][x], M1[y][x], mod); - output[y*r + x].data[offset] = half ? - zn_mod_add(temp, zn_mod_mul(u, M1[y][x], mod), mod) : temp; - } - return; - } - - // recursive case - - // Let Q(x) = M(x+1) ... M(x+k2), where k2 = floor(k/2). - // Then we have either - // P(x) = Q(x) Q(x+k2) if k is even - // P(x) = Q(x) Q(x+k2) M(x+k) if k is odd - - if (half == 0) - { - // This is the first time through this function. - // Allocate scratch space. - scratch.resize(r*r); - for (int i = 0; i < r*r; i++) - scratch[i].resize(k2 + 3); - - { - // Recursively compute Q(0), Q(u), ..., Q(k2*u). - // These are stored in scratch. - LargeEvaluator recurse(r, k2, u, M0, M1, mod); - recurse.evaluate_all(scratch); - } - - // Precomputations for value-shifting - shifter = new Shifter(k2, k2, u, mod); - } - else // half == 1 - { - // Shift original sequence by (k2+1)*u to obtain - // Q((k2+1)*u), Q((k2+2)*u), ..., Q((2*k2+1)*u) - Shifter big_shifter(k2, zn_mod_mul(k2+1, u, mod), u, mod); - for (int i = 0; i < r*r; i++) - big_shifter.shift(scratch[i].data, scratch[i].data); - } - - // Let H = (k2+1)*u*half, so now scratch contains - // Q(H), Q(H + u), ..., Q(H + k2*u). - - // Shift by k2 to obtain Q(H + k2), Q(H + u + k2), ..., Q(H + k2*u + k2). - // Results are stored directly in output array. We put them one slot - // to the right, to make room for inplace matrix multiplies later on. - // (If k is odd, they're shifted right by yet another slot, to make room - // for another round of multiplies.) - for (int i = 0; i < r*r; i++) - shifter->shift(output[i].data + offset + odd + 1, scratch[i].data); - - // If k is odd, right-multiply each Q(H + i*u + k2) by M(H + i*u + k) - // (results are stored in output array, shifted one entry to the left) - if (odd) - { - ulong_array cruft(r*r); // for storing M(H + i*u + k) - ulong point = k; // evaluation point - if (half) - point = zn_mod_add(point, zn_mod_mul(k2 + 1, u, mod), mod); - - for (ulong i = 0; i <= k2; i++, point = zn_mod_add(point, u, mod)) - { - // compute M(H + i*u + k) = M0 + M1*point - for (int x = 0; x < r; x++) - for (int y = 0; y < r; y++) - cruft.data[y*r + x] = zn_mod_add(M0[y][x], - zn_mod_mul(M1[y][x], point, mod), mod); - - // multiply - for (int x = 0; x < r; x++) - for (int y = 0; y < r; y++) - { - ulong accum = 0; - for (int z = 0; z < r; z++) - accum = zn_mod_add(accum, - zn_mod_mul(output[y*r + z].data[offset + i + 2], - cruft.data[z*r + x], mod), mod); - output[y*r + x].data[offset + i + 1] = accum; - } - } - } - - ulong n = zn_mod_get(mod); - - // Multiply to obtain P(H), P(H + u), ..., P(H + k2*u) - // (except for the last one, in the second half, if k is even) - // Store results directly in output array. - for (ulong i = 0; i + (half && !odd) <= k2; i++) - for (int x = 0; x < r; x++) - for (int y = 0; y < r; y++) - { - ulong sum_hi = 0; - ulong sum_lo = 0; - for (int z = 0; z < r; z++) - { - ulong hi, lo; - ZNP_MUL_WIDE(hi, lo, scratch[y*r + z].data[i], - output[z*r + x].data[offset + i + 1]); - ZNP_ADD_WIDE(sum_hi, sum_lo, sum_hi, sum_lo, hi, lo); - if (sum_hi >= n) - sum_hi -= n; - } - output[y*r + x].data[offset + i] = - zn_mod_reduce_wide(sum_hi, sum_lo, mod); - } -} - - -/* - Evaluates both halves, storing results in output array. -*/ -void LargeEvaluator::evaluate_all(vector& output) -{ - evaluate(0, output, 0); - evaluate(1, output, k/2 + 1); -} - - - -/* -See interval_products_wrapper(). - -NOTE 1: - I haven't proved that the algorithm here always succeeds, although I expect - that it almost always will, especially when p is very large. If it - succeeds, it returns 1. If it fails, it returns 0 (practically instantly). - In the latter case, at least the caller can fall back on - ntl_interval_products(). - -NOTE 2: - The algorithm here is similar to ntl_interval_products(). However, it - doesn't do the "refining step" -- it just handles the smaller intervals - in the naive fashion. Also, instead of breaking intervals into power-of-four - lengths, it just does the whole thing in one chunk. The performance ends up - being smoother, but it's harder to prove anything about avoiding - non-invertible elements. Hence the caveat in Note 1. - -*/ -int zn_poly_interval_products(vector > >& output, - const vector >& M0, - const vector >& M1, - const vector& target, const zn_mod_t& mod) -{ - output.resize(target.size() / 2); - - // select k such that k*(k+1) >= total length of intervals - ulong k; - { - ZZ len = target.back() - target.front(); - ZZ kk = SqrRoot(len); - k = to_ulong(kk); - if (kk * (kk + 1) < len) - k++; - } - - int r = M0.size(); - - // try to find good parameters u and k - ulong u = k; - for (int trial = 0; ; trial++, u++) - { - if (check_params(k, u, mod)) - break; // found some good parameters - if (trial == 5) - return 0; // too many failures, give up - } - - ulong n = zn_mod_get(mod); - - // shift M0 over to account for starting index - vector > M0_shifted = M0; - ulong shift = target.front() % n; - for (int x = 0; x < r; x++) - for (int y = 0; y < r; y++) - M0_shifted[y][x] = zn_mod_add(M0[y][x], - zn_mod_mul(shift, M1[y][x], mod), mod); - - // prepare for evaluating products over the big intervals - // [0, k), [u, u+k), ..., [ku, ku+k) - LargeEvaluator evaluator(r, k, u, M0_shifted, M1, mod); - - vector big(r*r); - for (int i = 0; i < r*r; i++) - big[i].resize(k/2 + 3); // space for half the products, plus two more - - // evaluate the first half of the products - evaluator.evaluate(0, big, 0); - // flag indicating which half we currently have stored in the "big" array - int half = 0; - - vector > accum(r, vector(r)); - vector > temp1(r, vector(r)); - vector > temp2(r, vector(r)); - - // for each target interval.... - for (size_t i = 0; i < target.size()/2; i++) - { - // doing interval [s0, s1) - ZZ s0 = target[2*i]; - ZZ s1 = target[2*i+1]; - - // product accumulated so far is [t0, t1). - ZZ t0 = s0; - ZZ t1 = s0; - for (int x = 0; x < r; x++) - for (int y = 0; y < r; y++) - accum[x][y] = (x == y); // identity matrix - - while (t1 < s1) - { - // if we are exactly on the left end of a big interval, and the - // big interval fits inside the target interval, then roll it in - if ((t1 - target[0]) % u == 0 && t1 + k <= s1) - { - // compute which "big" interval we are rolling in - size_t index = to_ulong((t1 - target[0]) / u); - - if (index >= k/2 + 1) - { - // if the "big" interval is in the second half, and we haven't - // computed the second half of the intervals yet, then go and - // compute them (overwriting the first half) - if (half == 0) - { - evaluator.evaluate(1, big, 0); - half = 1; - } - index -= (k/2 + 1); - } - - for (int x = 0; x < r; x++) - for (int y = 0; y < r; y++) - temp1[y][x] = big[y*r + x].data[index]; - - t1 += k; - } - else - { - // otherwise just multiply by the single matrix M(t1 + 1) - ulong e = (t1 + 1) % n; - - for (int x = 0; x < r; x++) - for (int y = 0; y < r; y++) - temp1[y][x] = zn_mod_add(M0[y][x], - zn_mod_mul(M1[y][x], e, mod), mod); - - t1++; - } - - // multiply by whichever matrix we picked up above - for (int x = 0; x < r; x++) - for (int y = 0; y < r; y++) - { - ulong sum_hi = 0; - ulong sum_lo = 0; - for (int z = 0; z < r; z++) - { - ulong hi, lo; - ZNP_MUL_WIDE(hi, lo, accum[y][z], temp1[z][x]); - ZNP_ADD_WIDE(sum_hi, sum_lo, sum_hi, sum_lo, hi, lo); - if (sum_hi >= n) - sum_hi -= n; - } - temp2[y][x] = zn_mod_reduce_wide(sum_hi, sum_lo, mod); - } - - accum.swap(temp2); - } - - // store result in output array - output[i] = accum; - } - - return 1; -} - - -}; // namespace hypellfrob - - -// ----------------------- end of file diff --git a/src/sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_zn_poly.h b/src/sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_zn_poly.h deleted file mode 100644 index c5e936036c4..00000000000 --- a/src/sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_zn_poly.h +++ /dev/null @@ -1,158 +0,0 @@ -/* ============================================================================ - - recurrences_zn_poly.h: header for recurrences_zn_poly.cpp - - This file is part of hypellfrob (version 2.1.1). - - Copyright (C) 2007, 2008, David Harvey - - 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. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -============================================================================ */ - - -#include -#include -#include - - - -namespace hypellfrob { - - -/* -This struct stores precomputed information that can then be used to shift -evaluation values of a polynomial F(x) of degree d. - -Specifically, given the values - F(0), F(b), F(2*b), ..., F(d*b), -the shift() method computes - F(a), F(a + b), F(a + 2*b), ..., F(a + d*b). - -PRECONDITIONS: - d >= 0 - 1, 2, ..., d + 1 are invertible - a + i*b are invertible for -d <= i <= d - -*/ -struct Shifter -{ - ulong d; - - // input_twist is a vector of length d + 1. - // The i-th entry is \prod_{0 <= j <= d, j != i} (i-j)^(-1). - // todo: this is symmetric, so can make it d/2 + 1, but need to - // deal with even/odd cases separately? - ulong* input_twist; - - // output_twist is a vector of length d + 1. - // The i-th entry is b^(-d) \prod_{0 <= j <= d} (a + (i-j)*b). - ulong* output_twist; - - // precomputed info for performing middle product against a "kernel" - // polynomial of degree 2d. The coefficients of "kernel" are - // (a + k*b)^(-1) for -d <= k <= d. - zn_array_mulmid_precomp1_t kernel_precomp; - - // Scratch space for shift(), length d + 1 - ulong* scratch; - - // zn_poly modulus object - const zn_mod_struct* mod; - - ~Shifter(); - - // Constructor (performs various precomputations) - Shifter(ulong d, ulong a, ulong b, const zn_mod_t mod); - - // Shifts evaluation values as described above. - // Assumes both output and input have length d + 1. - void shift(ulong* output, const ulong* input); -}; - - -/* - Grrr..... I would rather use vector, but then strictly speaking - I can't be guaranteed that the data is stored in a simple array format, - so here I go rolling my own.... -*/ -struct ulong_array -{ - ulong* data; - - ulong_array() - { - data = NULL; - } - - ulong_array(ulong amount) - { - data = (ulong*) malloc(sizeof(ulong) * amount); - } - - ~ulong_array() - { - if (data) - free(data); - } - - void resize(ulong amount) - { - if (data) - data = (ulong*) realloc(data, sizeof(ulong) * amount); - else - data = (ulong*) malloc(sizeof(ulong) * amount); - } -}; - - - -int check_params(ulong k, ulong u, const zn_mod_t mod); - - - -struct LargeEvaluator -{ - int r; - ulong k, u, k2, odd; - const std::vector >& M0; - const std::vector >& M1; - const zn_mod_t& mod; - Shifter* shifter; - std::vector scratch; - - LargeEvaluator(int r, ulong k, ulong u, - const std::vector >& M0, - const std::vector >& M1, - const zn_mod_t& mod); - - ~LargeEvaluator(); - - void evaluate(int half, std::vector& output, ulong offset); - void evaluate_all(std::vector& output); -}; - - - -int zn_poly_interval_products( - std::vector > >& output, - const std::vector >& M0, - const std::vector >& M1, - const std::vector& target, const zn_mod_t& mod); - - -}; - -// ----------------------- end of file From a113340357198f69c4e26a7656e05cac431dad24 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 8 Nov 2021 06:37:37 -0500 Subject: [PATCH 614/632] Trac #32841: remove zn_poly from distutils "libraries" fields. --- src/sage/modular/modsym/p1list.pyx | 2 +- src/sage/modular/pollack_stevens/dist.pyx | 2 +- src/sage/rings/fraction_field_FpT.pyx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/modular/modsym/p1list.pyx b/src/sage/modular/modsym/p1list.pyx index 880462748cf..c5da10a234a 100644 --- a/src/sage/modular/modsym/p1list.pyx +++ b/src/sage/modular/modsym/p1list.pyx @@ -1,4 +1,4 @@ -# distutils: libraries = gmp zn_poly +# distutils: libraries = gmp # distutils: extra_compile_args = -D_XPG6 r""" diff --git a/src/sage/modular/pollack_stevens/dist.pyx b/src/sage/modular/pollack_stevens/dist.pyx index c52b9db5f0a..3261d09d2c2 100644 --- a/src/sage/modular/pollack_stevens/dist.pyx +++ b/src/sage/modular/pollack_stevens/dist.pyx @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# distutils: libraries = gmp zn_poly +# distutils: libraries = gmp # distutils: extra_compile_args = -D_XPG6 """ `p`-adic distributions spaces diff --git a/src/sage/rings/fraction_field_FpT.pyx b/src/sage/rings/fraction_field_FpT.pyx index 58ee9bf3050..710aa930ca3 100644 --- a/src/sage/rings/fraction_field_FpT.pyx +++ b/src/sage/rings/fraction_field_FpT.pyx @@ -1,4 +1,4 @@ -# distutils: libraries = gmp NTL_LIBRARIES zn_poly +# distutils: libraries = gmp NTL_LIBRARIES # distutils: extra_compile_args = NTL_CFLAGS # distutils: include_dirs = NTL_INCDIR # distutils: library_dirs = NTL_LIBDIR From 146bc5e62dd76a438cb0e98be8f457398f9ba083 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 8 Nov 2021 06:24:49 -0500 Subject: [PATCH 615/632] Trac #32841: remove zn_poly from the library order. --- src/sage_setup/library_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage_setup/library_order.py b/src/sage_setup/library_order.py index 0b450b37239..f40690f8d22 100644 --- a/src/sage_setup/library_order.py +++ b/src/sage_setup/library_order.py @@ -32,7 +32,7 @@ "brial_groebner", "m4rie", ] + aliases.get("M4RI_LIBRARIES", []) + [ - "zn_poly", "gap", + "gap", ] + aliases.get("GDLIB_LIBRARIES", []) + aliases.get("LIBPNG_LIBRARIES", []) + [ "m", "readline", "Lfunction", ] + aliases.get("CBLAS_LIBRARIES", []) + aliases.get("ZLIB_LIBRARIES", []) From 076969b10872b4316cb92c672b3bda74d5e75d86 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 8 Nov 2021 06:29:34 -0500 Subject: [PATCH 616/632] Trac #32841: remove zn_poly dependency from sagelib. --- build/pkgs/sagelib/dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/sagelib/dependencies b/build/pkgs/sagelib/dependencies index 810713712ed..abf21122c87 100644 --- a/build/pkgs/sagelib/dependencies +++ b/build/pkgs/sagelib/dependencies @@ -1,4 +1,4 @@ -FORCE $(SCRIPTS) arb boost_cropped $(BLAS) brial cliquer cypari cysignals cython ecl eclib ecm flint libgd gap giac givaro glpk gmpy2 gsl iml jinja2 jupyter_core lcalc lrcalc_python libbraiding libhomfly libpng linbox m4ri m4rie memory_allocator mpc mpfi mpfr $(MP_LIBRARY) ntl numpy pari pip pkgconfig planarity ppl pplpy primesieve primecount primecountpy pycygwin $(PYTHON) requests rw sage_conf singular symmetrica zn_poly $(PCFILES) | $(PYTHON_TOOLCHAIN) sage_setup +FORCE $(SCRIPTS) arb boost_cropped $(BLAS) brial cliquer cypari cysignals cython ecl eclib ecm flint libgd gap giac givaro glpk gmpy2 gsl iml jinja2 jupyter_core lcalc lrcalc_python libbraiding libhomfly libpng linbox m4ri m4rie memory_allocator mpc mpfi mpfr $(MP_LIBRARY) ntl numpy pari pip pkgconfig planarity ppl pplpy primesieve primecount primecountpy pycygwin $(PYTHON) requests rw sage_conf singular symmetrica $(PCFILES) | $(PYTHON_TOOLCHAIN) sage_setup ---------- All lines of this file are ignored except the first. From d800f6cf3e1adc77a6d404dc828dfe5f26bb64e1 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 8 Nov 2021 06:22:54 -0500 Subject: [PATCH 617/632] Trac #32841: remove the zn_poly SPKG. --- build/pkgs/zn_poly/SPKG.rst | 96 ------------------------- build/pkgs/zn_poly/checksums.ini | 4 -- build/pkgs/zn_poly/distros/arch.txt | 1 - build/pkgs/zn_poly/distros/conda.txt | 1 - build/pkgs/zn_poly/distros/debian.txt | 1 - build/pkgs/zn_poly/distros/fedora.txt | 1 - build/pkgs/zn_poly/distros/freebsd.txt | 1 - build/pkgs/zn_poly/distros/nix.txt | 1 - build/pkgs/zn_poly/distros/opensuse.txt | 1 - build/pkgs/zn_poly/distros/repology.txt | 2 - build/pkgs/zn_poly/package-version.txt | 1 - build/pkgs/zn_poly/spkg-check.in | 90 ----------------------- build/pkgs/zn_poly/spkg-configure.m4 | 8 --- build/pkgs/zn_poly/spkg-install.in | 92 ------------------------ build/pkgs/zn_poly/type | 1 - 15 files changed, 301 deletions(-) delete mode 100644 build/pkgs/zn_poly/SPKG.rst delete mode 100644 build/pkgs/zn_poly/checksums.ini delete mode 100644 build/pkgs/zn_poly/distros/arch.txt delete mode 100644 build/pkgs/zn_poly/distros/conda.txt delete mode 100644 build/pkgs/zn_poly/distros/debian.txt delete mode 100644 build/pkgs/zn_poly/distros/fedora.txt delete mode 100644 build/pkgs/zn_poly/distros/freebsd.txt delete mode 100644 build/pkgs/zn_poly/distros/nix.txt delete mode 100644 build/pkgs/zn_poly/distros/opensuse.txt delete mode 100644 build/pkgs/zn_poly/distros/repology.txt delete mode 100644 build/pkgs/zn_poly/package-version.txt delete mode 100644 build/pkgs/zn_poly/spkg-check.in delete mode 100644 build/pkgs/zn_poly/spkg-configure.m4 delete mode 100644 build/pkgs/zn_poly/spkg-install.in delete mode 100644 build/pkgs/zn_poly/type diff --git a/build/pkgs/zn_poly/SPKG.rst b/build/pkgs/zn_poly/SPKG.rst deleted file mode 100644 index b04ca2b274d..00000000000 --- a/build/pkgs/zn_poly/SPKG.rst +++ /dev/null @@ -1,96 +0,0 @@ -zn_poly: C library for polynomial arithmetic in Z/nZ[x] -======================================================= - -Description ------------ - -zn_poly is a C library for polynomial arithmetic in Z/nZ[x], where n is -any modulus that fits into an unsigned long. - -Website: https://gitlab.com/sagemath/zn_poly - -Note: Original website is at https://web.maths.unsw.edu.au/~davidharvey/code/zn_poly/ but is -no longer maintained. Sage maintains an "official" continuation of the -project at the above link. - -License -------- - -GPL V2 or V3. Some of the code has been copied from other projects - see -the file src/COPYING for details. - - -Upstream Contact ----------------- - -- David Harvey -- \E. M. Bray - -Dependencies ------------- - -- GMP/MPIR -- (some) Python (to create the Makefile) -- GNU patch -- NTL apparently only if we configured zn_poly differently (same for - FLINT) - - -Special Update/Build Instructions ---------------------------------- - -- Make sure the patches still apply. - - Especially changes in ``makemakefile.py`` may also require changes to - ``spkg-install`` (and perhaps also ``spkg-check``). - -- There's also a ``--use-flint`` option to ``configure``; no idea what - it does, - and we currently don't use it either. - -- TODO: -- Use ``make install`` instead of manually "installing" (copying and - symlinking) the [shared] libraries and header files. This requires - further - tweaking of ``makemakefile.py``, since it currently only installs a - static - library and the headers. - -- If everything's fine, i.e., no problems arise, some comments and - especially some code I currently just commented out can certainly be removed. - (-leif, 04/2012) - -- The version number "0.9.p11" is used as a doctest in the function - package_versions in sage/misc/packages.py, so if this package gets - upgraded, that doctest needs to be changed. - -Patches -~~~~~~~ - -- All patches from Sage have been merged into upstream. These include: -- makemakefile.py.patch: - - Improves the Python script creating the Makeefile for better use at - least within Sage; see patch for details. (Last modified at #12433, - which added and changed a lot.) - -- profiler.c.patch, zn_poly.h.patch: - - Fix potential redefinition of ``ulong`` (in combination with other - headers). - -- mpn_mulmid-tune.c.patch, mulmid-tune.c.patch, mul-tune.c.patch: - - Fix "jump into scope of identifier with variably modified type" - errors. (See #8771). - -- mpn_mulmid-test.c.patch: - - Fix a potential problem when the value of ZNP_mpn_smp_kara_thresh is - SIZE_MAX, this is usually irrealistic but can happen at least on - linux on power7 with gcc-4.7.1 (see #14098). - -- fix_fudge_factor_in_nuss-test.c.patch: - - As the name says; fix provided by upstream (David Harvey); see - #13947. diff --git a/build/pkgs/zn_poly/checksums.ini b/build/pkgs/zn_poly/checksums.ini deleted file mode 100644 index 25cdb79c3b3..00000000000 --- a/build/pkgs/zn_poly/checksums.ini +++ /dev/null @@ -1,4 +0,0 @@ -tarball=zn_poly-VERSION.tar.gz -sha1=ac13121e8ba03f12edd22b0a2e0ae3b3a79fc26e -md5=81f09badfe4f941159df70805681d9eb -cksum=2478176718 diff --git a/build/pkgs/zn_poly/distros/arch.txt b/build/pkgs/zn_poly/distros/arch.txt deleted file mode 100644 index 5ff8997d672..00000000000 --- a/build/pkgs/zn_poly/distros/arch.txt +++ /dev/null @@ -1 +0,0 @@ -zn_poly diff --git a/build/pkgs/zn_poly/distros/conda.txt b/build/pkgs/zn_poly/distros/conda.txt deleted file mode 100644 index 5ff8997d672..00000000000 --- a/build/pkgs/zn_poly/distros/conda.txt +++ /dev/null @@ -1 +0,0 @@ -zn_poly diff --git a/build/pkgs/zn_poly/distros/debian.txt b/build/pkgs/zn_poly/distros/debian.txt deleted file mode 100644 index f155e4856dd..00000000000 --- a/build/pkgs/zn_poly/distros/debian.txt +++ /dev/null @@ -1 +0,0 @@ -libzn-poly-dev diff --git a/build/pkgs/zn_poly/distros/fedora.txt b/build/pkgs/zn_poly/distros/fedora.txt deleted file mode 100644 index 631dd81dfe1..00000000000 --- a/build/pkgs/zn_poly/distros/fedora.txt +++ /dev/null @@ -1 +0,0 @@ -zn_poly zn_poly-devel diff --git a/build/pkgs/zn_poly/distros/freebsd.txt b/build/pkgs/zn_poly/distros/freebsd.txt deleted file mode 100644 index 8231d562435..00000000000 --- a/build/pkgs/zn_poly/distros/freebsd.txt +++ /dev/null @@ -1 +0,0 @@ -math/zn_poly diff --git a/build/pkgs/zn_poly/distros/nix.txt b/build/pkgs/zn_poly/distros/nix.txt deleted file mode 100644 index 5ff8997d672..00000000000 --- a/build/pkgs/zn_poly/distros/nix.txt +++ /dev/null @@ -1 +0,0 @@ -zn_poly diff --git a/build/pkgs/zn_poly/distros/opensuse.txt b/build/pkgs/zn_poly/distros/opensuse.txt deleted file mode 100644 index 4bbe6043915..00000000000 --- a/build/pkgs/zn_poly/distros/opensuse.txt +++ /dev/null @@ -1 +0,0 @@ -zn_poly-devel diff --git a/build/pkgs/zn_poly/distros/repology.txt b/build/pkgs/zn_poly/distros/repology.txt deleted file mode 100644 index 597d1f28efa..00000000000 --- a/build/pkgs/zn_poly/distros/repology.txt +++ /dev/null @@ -1,2 +0,0 @@ -zn-poly -libzn-poly diff --git a/build/pkgs/zn_poly/package-version.txt b/build/pkgs/zn_poly/package-version.txt deleted file mode 100644 index 2003b639c40..00000000000 --- a/build/pkgs/zn_poly/package-version.txt +++ /dev/null @@ -1 +0,0 @@ -0.9.2 diff --git a/build/pkgs/zn_poly/spkg-check.in b/build/pkgs/zn_poly/spkg-check.in deleted file mode 100644 index c7831aed276..00000000000 --- a/build/pkgs/zn_poly/spkg-check.in +++ /dev/null @@ -1,90 +0,0 @@ -############################################################################### -# -# zn_poly Sage check script -# -############################################################################### - -############################################################################### -# Set up environment variables: -############################################################################### - -CFLAGS="$CFLAGS_O3 -fPIC" -CXXFLAGS="$CXXFLAGS_O3 -fPIC" - -# Work around a bug in GCC 4.7.0 which breaks the build on Itanium CPUs with -# '-O3', '-O2' and '-O1' (cf. #12765, #12751, and the bug URL below.) -if [ "`uname -m`" = ia64 ] && [ "`testcc.sh $CC`" = GCC ]; then - gcc_version=`$CC -dumpversion` - case "$gcc_version" in - 4.7.*) - CFLAGS="$CFLAGS -O0 -finline-functions -fschedule-insns" - CXXFLAGS="$CXXFLAGS -O0 -finline-functions -fschedule-insns" - echo >&2 "Warning: Disabling almost all optimization due to a bug in (at least)" - echo >&2 " GCC 4.7.0 on Itanium, which otherwise would break the build." - echo >&2 " See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=48496" - echo >&2 " for current status and further details." - echo >&2 " (And please report to e.g. sage-devel in case you feel this bug" - echo >&2 " should already be fixed in GCC $gcc_version.)" - esac -fi - -export CFLAGS CPPFLAGS CXXFLAGS LDFLAGS # Partially redundant, but safe. - -# Actually, these flags have been written to the Makefile during 'configure'. -# Only CC, CPP and CXX settings from the environment currently override the -# ones in the Makefile (which was generated from a patched 'makemakefile.py'). -# -leif 04/2012 - -case "$UNAME" in - SunOS) - if ! (ld --version 2>&1 | grep GNU >/dev/null); then - # Assume it's the Sun linker; change '-soname' to '-h': - # The following is only supported by the Makefile generated by our - # patched 'makemakefile.py': - export SONAME_FLAG=-h - fi;; - *) unset SONAME_FLAG # Leave the Makefile default; for safety. -esac - -unset SHARED_FLAG # Currently leave the Makefile default ('-shared'); for safety. - -############################################################################### -# Build the 'test' program (if it's not already built) and run it: -############################################################################### - -cd src/ - -# The methods for testing zn_poly are more complex than those of most other -# packages. A 'make check' does some quick tests. These are run from -# 'spkg-install' to check zn_poly is not obviously disfunctional. -# -# However, a more comprehensive set of tests can be run by first building a -# test program, then running that test program, which we do here. -# -# To quote from the file src/README: -# -# make check -# Runs some quick tests to make sure that everything appears to be working. -# -# make test -# Builds a test program. Running "test/test all" runs the whole zn_poly test -# suite, which tests zn_poly much more thoroughly than "make check". - -echo -echo "Now building zn_poly's extensive test suite (if not already built)..." -# $MAKE test CC="$CC" CXX="$CXX" # See comment above. We don't have to pass these. -$MAKE test # Make sure the test program is built. -if [ $? -ne 0 ] || [ ! -x test/test ]; then - echo >&2 "Error: zn_poly failed to build its 'test' program," - echo >&2 " so zn_poly's extensive test suite cannot be run." - exit 1 -fi - -echo -echo "Now running zn_poly's extensive test suite..." -test/test all # Run the extensive test suite. -if [ $? -ne 0 ]; then - echo >&2 "Error: zn_poly failed to pass its extensive test suite." - exit 1 -fi -echo "zn_poly has passed its extensive test suite." diff --git a/build/pkgs/zn_poly/spkg-configure.m4 b/build/pkgs/zn_poly/spkg-configure.m4 deleted file mode 100644 index c3e47c7621b..00000000000 --- a/build/pkgs/zn_poly/spkg-configure.m4 +++ /dev/null @@ -1,8 +0,0 @@ -SAGE_SPKG_CONFIGURE([zn_poly], [ - SAGE_SPKG_DEPCHECK([gmp], [ - AC_CHECK_HEADER([zn_poly/zn_poly.h], [ - AC_SEARCH_LIBS([zn_mod_init], [zn_poly], [ - ], [sage_spkg_install_zn_poly=yes]) - ], [sage_spkg_install_zn_poly=yes]) - ]) -]) diff --git a/build/pkgs/zn_poly/spkg-install.in b/build/pkgs/zn_poly/spkg-install.in deleted file mode 100644 index 6215a2416e0..00000000000 --- a/build/pkgs/zn_poly/spkg-install.in +++ /dev/null @@ -1,92 +0,0 @@ -############################################################################### -# Set up environment variables: -############################################################################### - -CFLAGS="$CFLAGS_O3 -fPIC" -CXXFLAGS="$CXXFLAGS_O3 -fPIC" - -export CFLAGS CPPFLAGS CXXFLAGS LDFLAGS # Partially redundant, but safe. - -# (Actually the currently generated Makefile won't use any of the above from the -# environment; instead we have to pass them with special options to 'configure', -# which we do below. Furthermore, CPPFLAGS and CXXFLAGS are only supported by -# our patched 'makemakefile.py', which is called by 'configure'.) - -case "$UNAME" in - SunOS) - if ! (ld --version 2>&1 | grep GNU >/dev/null); then - # Assume it's the Sun linker; change '-soname' to '-h': - # The following is only supported by the Makefile generated by our - # patched 'makemakefile.py': - export SONAME_FLAG=-h - fi;; - *) unset SONAME_FLAG # Leave the Makefile default; for safety. -esac - -unset SHARED_FLAG # Currently leave the Makefile default ('-shared'); for safety. - -cd src/ - -############################################################################### -# Configure, tune, build and test zn_poly: -############################################################################### - -echo "Now generating Makefile for zn_poly..." -# Note: The '--cppflags' and '--cxxflags' options are added by our patch to -# 'makemakefile.py', and aren't available with vanilla upstream. -# Moreover, the generated Makefile now takes CC, CXX and CPP from the -# environment, so no need to pass them later explicitly to 'make'. -sage-bootstrap-python makemakefile.py >Makefile \ - --prefix="$SAGE_LOCAL" --cflags="$CFLAGS" --ldflags="$LDFLAGS" \ - --cppflags="$CPPFLAGS" --cxxflags="$CXXFLAGS" \ - --gmp-prefix="$SAGE_LOCAL" || sdh_die "Error generating Makefile." - -echo "Now building zn_poly with its tuning parameters..." -sdh_make - -# Run the brief test suite: -echo -echo "Now building and running zn_poly's quick self-test..." -sdh_make check - -echo -echo "zn_poly's *quick* test suite passed." -if [ "$SAGE_CHECK" != yes ]; then - echo "A more comprehensive test suite can be run if SAGE_CHECK is" - echo "exported to \"yes\", but it takes about 10x as long to run." -fi - -############################################################################### -# Build and manually install the shared library: -############################################################################### - -echo -echo "Now building and installing zn_poly's shared library..." - -case "$UNAME" in -"Darwin") - sdh_make libzn_poly.dylib - sdh_install libzn_poly.dylib "$SAGE_LOCAL/lib" - install_name_tool -id ${SAGE_LOCAL}/lib/libzn_poly.dylib \ - "$SAGE_DESTDIR_LOCAL/lib/libzn_poly.dylib" - ;; -"CYGWIN") - sdh_make libzn_poly.dll.a - sdh_install cygzn_poly.dll "$SAGE_LOCAL/bin" - sdh_install libzn_poly*.dll.a "$SAGE_LOCAL/lib" - ;; -*) - # Linux, SunOS/Solaris, etc.: - sdh_make libzn_poly.so - sdh_install libzn_poly*.so "$SAGE_LOCAL/lib" - ;; -esac - -############################################################################### -# Manually install the header files: -############################################################################### - -echo -echo "Now installing zn_poly's header files..." -sdh_install include/{zn_poly,wide_arith}.h "$SAGE_LOCAL/include/zn_poly" -echo "Finished installing zn_poly." diff --git a/build/pkgs/zn_poly/type b/build/pkgs/zn_poly/type deleted file mode 100644 index a6a7b9cd726..00000000000 --- a/build/pkgs/zn_poly/type +++ /dev/null @@ -1 +0,0 @@ -standard From d059d607a269b257ceb2167aeb6c9fe898716e0b Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 8 Nov 2021 06:29:01 -0500 Subject: [PATCH 618/632] Trac #32841: use zlib instead of zn_poly in a sage-package comment. The zn_poly package has been removed, so this could only confuse people if we left it in. --- build/bin/sage-package | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/bin/sage-package b/build/bin/sage-package index eeebbcd2c2e..ccb3ad194a7 100755 --- a/build/bin/sage-package +++ b/build/bin/sage-package @@ -18,7 +18,7 @@ # arb # autotools # [...] -# zn_poly +# zlib # # * Find the package name given a tarball filename # From e6830764b05894de8c1bbbf833dca5e910a22e80 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 8 Nov 2021 06:32:09 -0500 Subject: [PATCH 619/632] Trac #32841: use zlib instead of zn_poly in package list examples. We have a few doctests that list all sage packages, and expect zn_poly to be the last one listed. Having removed zn_poly, however, zlib is now the terminal package. Here we update all such tests. --- build/sage_bootstrap/app.py | 4 ++-- build/sage_bootstrap/cmdline.py | 4 ++-- src/sage/misc/package.py | 9 ++++----- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/build/sage_bootstrap/app.py b/build/sage_bootstrap/app.py index 66da2665e84..c9523e8cdc7 100644 --- a/build/sage_bootstrap/app.py +++ b/build/sage_bootstrap/app.py @@ -58,7 +58,7 @@ def list_cls(self, *package_classes, **filters): arb autotools [...] - zn_poly + zlib $ sage -package list --has-file=spkg-configure.m4 :experimental: perl_term_readline_gnu @@ -68,7 +68,7 @@ def list_cls(self, *package_classes, **filters): boost_cropped brial [...] - zn_poly + zlib """ log.debug('Listing packages') pc = PackageClass(*package_classes, **filters) diff --git a/build/sage_bootstrap/cmdline.py b/build/sage_bootstrap/cmdline.py index aea51cb48f0..ca89b4fedac 100644 --- a/build/sage_bootstrap/cmdline.py +++ b/build/sage_bootstrap/cmdline.py @@ -73,13 +73,13 @@ arb autotools [...] - zn_poly + zlib $ sage --package list :standard: | sort arb backports_ssl_match_hostname [...] - zn_poly + zlib """ diff --git a/src/sage/misc/package.py b/src/sage/misc/package.py index 849ee71f3fb..975ddc50766 100644 --- a/src/sage/misc/package.py +++ b/src/sage/misc/package.py @@ -26,8 +26,7 @@ 'alabaster', 'arb', ... - 'zlib', - 'zn_poly'] + 'zlib'] Functions --------- @@ -265,7 +264,7 @@ def list_packages(*pkg_types: str, pkg_sources: List[str] = ['normal', 'pip', 's 'arb', 'babel', ... - 'zn_poly'] + 'zlib'] sage: sage_conf_info = L['sage_conf'] # optional - sage_spkg sage: sage_conf_info.type # optional - sage_spkg 'standard' @@ -498,8 +497,8 @@ def package_versions(package_type, local=False): sage: std = package_versions('standard', local=True) # optional - sage_spkg sage: 'gap' in std # optional - sage_spkg True - sage: std['zn_poly'] # optional - sage_spkg, random - ('0.9.p12', '0.9.p12') + sage: std['zlib'] # optional - sage_spkg, random + ('1.2.11.p0', '1.2.11.p0') """ return {pkg.name: (pkg.installed_version, pkg.remote_version) for pkg in list_packages(package_type, local=local).values()} From 46f7623a002aaf8ccbe73e046d67b0623287e014 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 8 Nov 2021 06:25:03 -0500 Subject: [PATCH 620/632] Trac #32841: remove zn_poly TARGETS from cygwin github workflows. The zn_poly SPKG has been removed, so we should not try to build it as part of the cygwin workflows. --- .github/workflows/ci-cygwin-minimal.yml | 1044 ++++++++++++++++++++++ .github/workflows/ci-cygwin-standard.yml | 12 +- 2 files changed, 1051 insertions(+), 5 deletions(-) create mode 100644 .github/workflows/ci-cygwin-minimal.yml diff --git a/.github/workflows/ci-cygwin-minimal.yml b/.github/workflows/ci-cygwin-minimal.yml new file mode 100644 index 00000000000..9f4a11de432 --- /dev/null +++ b/.github/workflows/ci-cygwin-minimal.yml @@ -0,0 +1,1044 @@ +name: CI cygwin-minimal + +on: + pull_request: + types: [opened, synchronize] + push: + tags: + - '*' + workflow_dispatch: + # Allow to run manually + +env: + MAKE: make -j8 + SAGE_NUM_THREADS: 3 + CYGWIN: winsymlinks:native + EXTRA_CONFIGURE_ARGS: --enable-fat-binary + SAGE_LOCAL: /opt/sage-${{ github.sha }} + +jobs: + +############################################## stage-i ########################################## + + cygwin-stage-i-a: + env: + STAGE: i-a + # builds openblas + TARGETS: iml gsl + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the $SAGE_LOCAL/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --remove-files "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + # upload-artifact@v2 does not support whitespace in file names. + # so we tar up the directory ourselves + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-i-b: + env: + STAGE: i-b + TARGETS: cython setuptools_scm kiwisolver dateutil cycler pyparsing certifi pkgconfig pplpy + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the $SAGE_LOCAL/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --remove-files "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + # upload-artifact@v2 does not support whitespace in file names. + # so we tar up the directory ourselves + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + +############################################## stage-ii ########################################## + + cygwin-stage-ii-a: + env: + STAGE: ii-a + PREVIOUS_STAGES: i-* + TARGETS: cvxopt rpy2 + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-i-a, cygwin-stage-i-b] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\dash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-*.tar && tar --create --listed-incremental=/tmp/sage-local.snar --file /dev/null "${{ env.SAGE_LOCAL }}"' + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the $SAGE_LOCAL/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + # upload-artifact@v2 does not support whitespace in file names. + # so we tar up the directory ourselves + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-ii-b: + env: + STAGE: ii-b + PREVIOUS_STAGES: i-* + TARGETS: singular maxima gap pari gfan palp flintqs arb ecm givaro + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-i-a, cygwin-stage-i-b] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\dash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-*.tar && tar --create --listed-incremental=/tmp/sage-local.snar --file /dev/null "${{ env.SAGE_LOCAL }}"' + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the $SAGE_LOCAL/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-ii-c: + env: + STAGE: ii-c + PREVIOUS_STAGES: i-* + TARGETS: cypari eclib fplll linbox giac + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-i-a, cygwin-stage-i-b] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\dash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-*.tar && tar --create --listed-incremental=/tmp/sage-local.snar --file /dev/null "${{ env.SAGE_LOCAL }}"' + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the $SAGE_LOCAL/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-ii-d: + env: + STAGE: ii-d + PREVIOUS_STAGES: i-* + TARGETS: ipython ipywidgets notebook + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-i-a, cygwin-stage-i-b] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\dash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-*.tar && tar --create --listed-incremental=/tmp/sage-local.snar --file /dev/null "${{ env.SAGE_LOCAL }}"' + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the $SAGE_LOCAL/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-ii-e: + env: + STAGE: ii-e + PREVIOUS_STAGES: i-* + TARGETS: threejs tachyon pillow jmol m4rie sympy lrcalc lcalc symmetrica cliquer libbraiding planarity rw elliptic_curves combinatorial_designs sympow + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-i-a, cygwin-stage-i-b] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\dash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-*.tar && tar --create --listed-incremental=/tmp/sage-local.snar --file /dev/null "${{ env.SAGE_LOCAL }}"' + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the $SAGE_LOCAL/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + +############################################## stage-iii ########################################## + + cygwin-stage-iii-a: + env: + STAGE: iii-a + PREVIOUS_STAGES: ii-* + TARGETS: sagelib + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-ii-a, cygwin-stage-ii-b, cygwin-stage-ii-c, cygwin-stage-ii-d, cygwin-stage-ii-e] + + runs-on: windows-latest + + continue-on-error: true + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\dash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-*.tar && tar --create --listed-incremental=/tmp/sage-local.snar --file /dev/null "${{ env.SAGE_LOCAL }}"' + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the $SAGE_LOCAL/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + # upload-artifact@v2 does not support whitespace in file names. + # so we tar up the directory ourselves + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-iii-b: + env: + STAGE: iii-b + PREVIOUS_STAGES: ii-* + TARGETS: networkx + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-ii-a, cygwin-stage-ii-b, cygwin-stage-ii-c, cygwin-stage-ii-d, cygwin-stage-ii-e] + + runs-on: windows-latest + + continue-on-error: true + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\dash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-*.tar && tar --create --listed-incremental=/tmp/sage-local.snar --file /dev/null "${{ env.SAGE_LOCAL }}"' + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the $SAGE_LOCAL/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + +############################################## stage-iv ########################################## + + cygwin-stage-iv: + env: + STAGE: iv + PREVIOUS_STAGES: iii-* + TARGETS: build + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-iii-a, cygwin-stage-iii-b] + + runs-on: windows-latest + + continue-on-error: true + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\dash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-*.tar && tar --create --listed-incremental=/tmp/sage-local.snar --file /dev/null "${{ env.SAGE_LOCAL }}"' + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the $SAGE_LOCAL/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + +############################################## stage-v ########################################### + + cygwin-stage-v-a: + env: + STAGE: v-a + PREVIOUS_STAGES: iv + TARGETS: ptest-nodoc + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-iv] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\dash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-*.tar && tar --create --listed-incremental=/tmp/sage-local.snar --file /dev/null "${{ env.SAGE_LOCAL }}"' + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the $SAGE_LOCAL/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-v-b: + env: + STAGE: v-b + PREVIOUS_STAGES: iv + TARGETS: 4ti2 pynormaliz topcom lrslib latte_int cryptominisat + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-iv] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\dash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-*.tar && tar --create --listed-incremental=/tmp/sage-local.snar --file /dev/null "${{ env.SAGE_LOCAL }}"' + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the $SAGE_LOCAL/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-v-c: + env: + STAGE: v-c + PREVIOUS_STAGES: iv + TARGETS: sage_numerical_backends_coin + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-iv] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\dash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-*.tar && tar --create --listed-incremental=/tmp/sage-local.snar --file /dev/null "${{ env.SAGE_LOCAL }}"' + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the $SAGE_LOCAL/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-v-d: + env: + STAGE: v-d + PREVIOUS_STAGES: iv + TARGETS: qepcad barvinok isl qhull primecount plantri kenzo libsemigroups mcqd meataxe mpfrcx openssl p_group_cohomology rst2ipynb sirocco tdlib tides + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-iv] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\dash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-*.tar && tar --create --listed-incremental=/tmp/sage-local.snar --file /dev/null "${{ env.SAGE_LOCAL }}"' + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the local/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() + + cygwin-stage-v-e: + env: + STAGE: v-e + PREVIOUS_STAGES: iv + TARGETS: doc-html + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + + needs: [cygwin-stage-iv] + + runs-on: windows-latest + + strategy: + fail-fast: false + matrix: + pkgs: [minimal] + steps: + - run: | + git config --global core.autocrlf false + git config --global core.symlinks true + - uses: actions/checkout@v1 + - name: install cygwin and test prerequisites with choco + shell: bash {0} + run: | + choco --version + PACKAGES="python39 python39-pip" + choco install $PACKAGES --source cygwin + - uses: actions/download-artifact@v2 + with: + name: ${{ env.LOCAL_ARTIFACT_NAME }} + path: C:\\tools\\cygwin\\tmp + - name: Extract sage-local artifact + run: | + C:\\tools\\cygwin\\bin\\dash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && .github/workflows/extract-sage-local.sh /tmp/sage-local-*.tar && tar --create --listed-incremental=/tmp/sage-local.snar --file /dev/null "${{ env.SAGE_LOCAL }}"' + - name: tox + run: | + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cat /proc/cpuinfo' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'python3.8 -m pip install tox' + C:\\tools\\cygwin\\bin\\bash -l -x -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && PREFIX="${{ env.SAGE_LOCAL }}" tox -e local-cygwin-choco-${{ matrix.pkgs }} -- $TARGETS' + - name: Prepare logs artifact + shell: bash + run: | + mkdir -p "artifacts/$LOGS_ARTIFACT_NAME"; for a in "${{ env.SAGE_LOCAL }}"/var/tmp/sage/build/*; do if [ -d "$a" ]; then tar -c --remove-files -f "artifacts/$LOGS_ARTIFACT_NAME/$(basename "$a").tar" "$a"; fi; done; cp -r logs/* "artifacts/$LOGS_ARTIFACT_NAME" + if: always() + - uses: actions/upload-artifact@v2 + with: + path: artifacts + name: ${{ env.LOGS_ARTIFACT_NAME }} + if: always() + - name: Print out logs for immediate inspection + # The markup in the output is a GitHub Actions logging command + # https://help.github.com/en/actions/automating-your-workflow-with-github-actions/development-tools-for-github-actions + shell: bash + run: | + find "artifacts/$LOGS_ARTIFACT_NAME" -type f -name "*.log" -exec sh -c 'if tail -20 "{}" 2>/dev/null | grep "^Error" >/dev/null; then echo :":"error file={}:":" ==== LOG FILE {} CONTAINS AN ERROR ====; cat {} ; fi' \; + if: always() + - name: Prepare sage-local artifact + # We specifically use the cygwin tar so that symlinks are saved/restored correctly on Windows. + # We remove the local/lib64 link, which will be recreated by the next stage. + run: | + C:\\tools\\cygwin\\bin\\bash -l -c 'cd $(cygpath -u "$GITHUB_WORKSPACE") && rm -f "${{ env.SAGE_LOCAL }}"/lib64; tar -cf /tmp/sage-local-${{ env.STAGE }}.tar --listed-incremental=/tmp/sage-local.snar "${{ env.SAGE_LOCAL }}"' + if: always() + - uses: actions/upload-artifact@v2 + with: + path: C:\\tools\\cygwin\\tmp\\sage-local-${{ env.STAGE }}.tar + name: ${{ env.LOCAL_ARTIFACT_NAME }} + if: always() diff --git a/.github/workflows/ci-cygwin-standard.yml b/.github/workflows/ci-cygwin-standard.yml index b1655e7696f..e35637f9da1 100644 --- a/.github/workflows/ci-cygwin-standard.yml +++ b/.github/workflows/ci-cygwin-standard.yml @@ -61,11 +61,13 @@ jobs: needs: [cygwin-stage-i-a, cygwin-stage-i-b] cygwin-stage-ii-e: - uses: ./.github/workflows/cygwin.yml - with: - stage: ii-e - previous_stages: i-* - targets: threejs tachyon pillow jmol m4rie sympy lrcalc lcalc symmetrica cliquer libbraiding planarity rw elliptic_curves combinatorial_designs zn_poly sympow + env: + STAGE: ii-e + PREVIOUS_STAGES: i-* + TARGETS: threejs tachyon pillow jmol m4rie sympy lrcalc lcalc symmetrica cliquer libbraiding planarity rw elliptic_curves combinatorial_designs sympow + LOCAL_ARTIFACT_NAME: sage-local-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + LOGS_ARTIFACT_NAME: logs-commit-${{ github.sha }}-cygwin-${{ matrix.pkgs }} + needs: [cygwin-stage-i-a, cygwin-stage-i-b] ############################################## stage-iii ########################################## From 8699ed0e3e933d88bd199dd61fc5055b0a0101f1 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 8 Nov 2021 06:26:20 -0500 Subject: [PATCH 621/632] Trac #32841: remove zn_poly section from COPYING.txt. The zn_poly SPKG has been removed, so its license no longer affects that of the larger SageMath project. --- COPYING.txt | 50 -------------------------------------------------- 1 file changed, 50 deletions(-) diff --git a/COPYING.txt b/COPYING.txt index a3e9cc66458..a785477fb4f 100644 --- a/COPYING.txt +++ b/COPYING.txt @@ -130,7 +130,6 @@ tachyon Modified BSD threejs MIT License tornado Apache License zlib Custom (Modified BSD) -zn_poly GPLv2 or GPLv3 (no later versions, see below) CONTACT INFO: William Stein; wstein@gmail.com; @@ -1346,53 +1345,4 @@ Copyright (C) 1995-2004 Jean-loup Gailly and Mark Adler Jean-loup Gailly jloup@gzip.org Mark Adler madler@alumni.caltech.edu - -================================================================================ - -zn_poly: a library for polynomial arithmetic (version 0.9) - -Copyright (C) 2007, 2008, David Harvey - -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) version 3 of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License, -along with this program (see gpl-2.0.txt and gpl-3.0.txt). If not, -see . - -Licensing notes: - -(1) -zn_poly is NOT released under the "GPL v2 or later" or "GPL v3 or later". -Both v2 and v3 are fine, but for now I am excluding later versions. If you -need zn_poly under a different license, ask me and I'll consider it. - -(2) -zn_poly incorporates small amounts of code from other projects: - - (2a) - The file "wide_arith.h" includes some assembly macros from the file - "longlong.h" in GMP 4.2.1; see http://gmplib.org/. The copyright to this - code is held by the Free Software Foundation, and it was released under - "LGPL v2.1 or later". - - (2b) - The file "wide_arith.h" also includes assembly macros from the file - "SPMM_ASM.h" in NTL 5.4.1; see http://www.shoup.net/ntl/. The copyright - to this code is held by Victor Shoup, and it was released under "GPL v2 or - later". - - (2c) - The filer "profiler.h" contains x86 cycle counting code from the file - "profiler.h" in FLINT 1.0; see http://www.flintlib.org/. The copyright - to this code is held by William Hart, and it was released under "GPL v2 or - later". - =============================================================================== From d59085668a564ef9bc00267a8531515884c1303c Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Thu, 13 Jan 2022 17:52:36 -0500 Subject: [PATCH 622/632] Trac #32841: remove mentions of zn_poly from README.md. Being alphabetically ultimate, the "zn_poly" package appears as an example in a few places within our README.md. This commit replaces those examples with "zlib" instead, as zn_poly has been removed. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index dc4139a8172..e18b17adda5 100644 --- a/README.md +++ b/README.md @@ -409,7 +409,7 @@ SAGE_ROOT Root directory (sage-x.y in Sage tarball) │ └── pkgs Every package is a subdirectory here │ ├── 4ti2/ │ … -│ └── zn_poly/ +│ └── zlib/ ├── configure Top-level configure script ├── COPYING.txt Copyright information ├── pkgs Source trees of Python distribution packages @@ -456,7 +456,7 @@ SAGE_ROOT Root directory (sage-x.y in Sage tarball) │ └── pkgs Build logs of individual packages │ ├── alabaster-0.7.12.log │ … -│ └── zn_poly-0.9.2.log +│ └── zlib-1.2.11.log ├── m4 M4 macros for generating the configure script │ └── *.m4 ├── Makefile Running "make" uses this file @@ -470,7 +470,7 @@ SAGE_ROOT Root directory (sage-x.y in Sage tarball) ├── upstream Source tarballs of packages │ ├── Babel-2.9.1.tar.gz │ … -│ └── zn_poly-0.9.2.tar.gz +│ └── zlib-1.2.11.tar.gz ├── venv -> SAGE_VENV Convenience symlink to the virtual environment └── VERSION.txt ``` From da17a806d7647c4a7cf381cf212e35351aca43d6 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 8 Nov 2021 06:31:15 -0500 Subject: [PATCH 623/632] Trac #32841: remove zn_poly mention from a thematic tutorial. This tutorial mentions the Harvey-Kedlaya algorithm that "depends only on NTL and zn_poly." But zn_poly has just been excised from that module, and its SPKG removed from SageMath. This commit changes the wording to say "depends only on NTL" instead. --- .../explicit_methods_in_number_theory/birds_other.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/birds_other.rst b/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/birds_other.rst index 09e8bce9a95..bdd5cc1d360 100644 --- a/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/birds_other.rst +++ b/src/doc/en/thematic_tutorials/explicit_methods_in_number_theory/birds_other.rst @@ -5,8 +5,8 @@ The Matrix of Frobenius on Hyperelliptic Curves Sage has a highly optimized implementation of the Harvey-Kedlaya algorithm for computing the matrix of Frobenius associated to a curve over a finite field. This is an implementation by David Harvey, which -is GPL'd and depends only on NTL and zn_poly (a C library in Sage for -fast arithmetic in :math:`(\ZZ/n\ZZ)[x]`). +is GPL'd and depends only on NTL (a C library in Sage for fast +arithmetic in :math:`(\ZZ/n\ZZ)[x]`). We import the hypellfrob function and call it on a polynomial over :math:`\ZZ`. From 5507887612ca82a472116ca6780ee1dc58284b9b Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 8 Nov 2021 06:34:25 -0500 Subject: [PATCH 624/632] Trac #32841: don't mention zn_poly in flint header comments. The flint_wrap.h and flint_ntl_wrap.h headers mention "zn_poly and pari" as reasons for doing something specific. The zn_poly SPKG has been removed, however; so we update those comments to mention only pari. --- src/sage/libs/flint/flint_ntl_wrap.h | 2 +- src/sage/libs/flint/flint_wrap.h | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/libs/flint/flint_ntl_wrap.h b/src/sage/libs/flint/flint_ntl_wrap.h index 841d990817d..c0ac48af7aa 100644 --- a/src/sage/libs/flint/flint_ntl_wrap.h +++ b/src/sage/libs/flint/flint_ntl_wrap.h @@ -8,7 +8,7 @@ #include -/* Save previous definition of ulong if any, as zn_poly and pari also use it */ +/* Save previous definition of ulong if any, as pari also uses it */ #pragma push_macro("ulong") #undef ulong diff --git a/src/sage/libs/flint/flint_wrap.h b/src/sage/libs/flint/flint_wrap.h index ae02e473a95..1bfb23d0182 100644 --- a/src/sage/libs/flint/flint_wrap.h +++ b/src/sage/libs/flint/flint_wrap.h @@ -1,8 +1,8 @@ #ifndef SAGE_FLINT_WRAP_H #define SAGE_FLINT_WRAP_H -/* Using flint headers together in the same module as headers from some other - * libraries (zn_poly, pari, possibly others) as it defines the macros ulong - * and slong all over the place. +/* Using flint headers together in the same module as headers from + * some other libraries (pari, possibly others) as it defines the + * macros ulong and slong all over the place. * * What's worse is they are defined to types from GMP (mp_limb_t and * mp_limb_signed_t respectively) which themselves can have system-dependent @@ -16,7 +16,7 @@ #include -/* Save previous definition of ulong if any, as zn_poly and pari also use it */ +/* Save previous definition of ulong if any, as pari also uses it */ /* Should work on GCC, clang, MSVC */ #pragma push_macro("ulong") #undef ulong From a5ac5fc48da94f324ee0a344d28e49b5272a425d Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Wed, 7 Dec 2022 21:03:24 +0000 Subject: [PATCH 625/632] later zn_poly appearances should go, too --- build/pkgs/zn_poly/dependencies | 4 ---- build/pkgs/zn_poly/distros/void.txt | 1 - src/MANIFEST.in | 1 - 3 files changed, 6 deletions(-) delete mode 100644 build/pkgs/zn_poly/dependencies delete mode 100644 build/pkgs/zn_poly/distros/void.txt diff --git a/build/pkgs/zn_poly/dependencies b/build/pkgs/zn_poly/dependencies deleted file mode 100644 index 42dc0e9c107..00000000000 --- a/build/pkgs/zn_poly/dependencies +++ /dev/null @@ -1,4 +0,0 @@ -$(MP_LIBRARY) - ----------- -All lines of this file are ignored except the first. diff --git a/build/pkgs/zn_poly/distros/void.txt b/build/pkgs/zn_poly/distros/void.txt deleted file mode 100644 index 5ff8997d672..00000000000 --- a/build/pkgs/zn_poly/distros/void.txt +++ /dev/null @@ -1 +0,0 @@ -zn_poly diff --git a/src/MANIFEST.in b/src/MANIFEST.in index 3a63d07fc32..2e7d8a2d05a 100644 --- a/src/MANIFEST.in +++ b/src/MANIFEST.in @@ -31,7 +31,6 @@ include sage/rings/padics/transcendantal.c include sage/rings/polynomial/weil/power_sums.c include sage/schemes/hyperelliptic_curves/hypellfrob/hypellfrob.cpp include sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_ntl.cpp -include sage/schemes/hyperelliptic_curves/hypellfrob/recurrences_zn_poly.cpp include sage/stats/distributions/dgs_bern.c include sage/stats/distributions/dgs_gauss_dp.c include sage/stats/distributions/dgs_gauss_mp.c From 69ac8d4e67d3d1198967c2b11a4116b4504e09c9 Mon Sep 17 00:00:00 2001 From: Dima Pasechnik Date: Wed, 7 Dec 2022 23:21:05 +0000 Subject: [PATCH 626/632] replaced ~~s with ^^^s rst determines the subheading levels dynamically, if it sees Foo --- Bar ^^^ and then Baz --- Oops ~~~~ it can't figure out whether Oops heading should be bigger or smaller than Bar heading. Thus the warning, fixed here. --- src/doc/en/installation/source.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/en/installation/source.rst b/src/doc/en/installation/source.rst index 879c15f4847..bc68e186898 100644 --- a/src/doc/en/installation/source.rst +++ b/src/doc/en/installation/source.rst @@ -453,7 +453,7 @@ Additional software ------------------- Recommended programs -~~~~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^^^^ The following programs are recommended. They are not strictly required at build time or at run time, @@ -506,7 +506,7 @@ On Debian/Ubuntu, the following system packages are recommended. - ``libavdevice-dev`` (to produce animations) Tcl/Tk -~~~~~~ +^^^^^^ If you want to use `Tcl/Tk `_ libraries in Sage, you need to install the Tcl/Tk and its development headers before building @@ -546,7 +546,7 @@ Step-by-step installation procedure ----------------------------------- General procedure -~~~~~~~~~~~~~~~~~ +^^^^^^^^^^^^^^^^^ #. Follow the procedure in the file `README.md `_ in ``SAGE_ROOT``. From 0334221fa92400361cb839ad2cc83d160408c782 Mon Sep 17 00:00:00 2001 From: "John H. Palmieri" Date: Fri, 9 Dec 2022 11:53:10 -0800 Subject: [PATCH 627/632] trac 34582: remove unused variable 'multigrade' --- src/sage/algebras/commutative_dga.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/algebras/commutative_dga.py b/src/sage/algebras/commutative_dga.py index cc06e907887..a74826b1ecd 100644 --- a/src/sage/algebras/commutative_dga.py +++ b/src/sage/algebras/commutative_dga.py @@ -958,12 +958,10 @@ def __classcall__(cls, base, names=None, degrees=None, R=None, I=None, category= # Deal with multigrading: convert lists and tuples to elements # of an additive abelian group. if degrees: - multigrade = False try: rank = len(list(degrees[0])) G = AdditiveAbelianGroup([0] * rank) degrees = [G(vector(d)) for d in degrees] - multigrade = True except TypeError: # The entries of degrees are not iterables, so # treat as singly-graded. From 7d0372b65e1f7e3c99d7baaee307f1e024a5a3a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 9 Dec 2022 21:19:58 +0100 Subject: [PATCH 628/632] reviewer's commit fixing several details --- .../polynomial/multi_polynomial_ideal.py | 40 +++++++++---------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index b61f42f72b5..b3833a9361f 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -551,20 +551,19 @@ def _groebner_cover(self): r""" Compute the Gröbner cover of the ideal. - The Gröbner cover is a partition of the space of parmeters, + The Gröbner cover is a partition of the space of parameters, such that the Gröbner basis is constant for each of the parts. OUTPUT: A list of parts. Each element of this list contains: - - The leading monomials of the Gröbner basis in the part - - The Gröbner basis in the part - - A list of components of the part. Each component is - has two lists of equations. The parameters in the - part are those that satisfy the first list of - equations, but do not satisfy the second one. - + - The leading monomials of the Gröbner basis in the part + - The Gröbner basis in the part + - A list of components of the part. Each component is + two lists of equations. The parameters in the + part are those that satisfy the first list of + equations, but do not satisfy the second one. EXAMPLES:: @@ -579,15 +578,14 @@ def _groebner_cover(self): [(2*a + 3)*z + (a + 9), (12*a + 18)*y + (-a^2 - 23*a - 36), (4*a + 6)*x + (-a^2 - a + 12)], [[[0], [[(2*a + 3)]]]]], [[1], [1], [[[(2*a + 3)], [[1]]]]]] - """ from sage.rings.fraction_field import FractionField_generic - F = self.base_ring() from sage.rings.polynomial.multi_polynomial_ring_base import is_MPolynomialRing from sage.rings.polynomial.polynomial_ring import is_PolynomialRing + F = self.base_ring() if (not isinstance(F, FractionField_generic) or - (not is_MPolynomialRing(F.ring()) and not is_PolynomialRing (F.ring()))): - raise TypeError("The base ring must be a field with parameters") + (not is_MPolynomialRing(F.ring()) and not is_PolynomialRing(F.ring()))): + raise TypeError("the base ring must be a field with parameters") from sage.libs.singular.function import singular_function, lib from sage.arith.functions import lcm lib("grobcov.lib") @@ -597,6 +595,7 @@ def _groebner_cover(self): polynomials.append(f * lcm([c.denominator() for c in f.coefficients()])) return grobcov(self.ring().ideal(polynomials)) + class MPolynomialIdeal_singular_repr( MPolynomialIdeal_singular_base_repr): """ @@ -4621,8 +4620,9 @@ def groebner_cover(self): r""" Compute the Gröbner cover of the ideal, over a field with parameters. - The Groebner cover is a partition of the space of parameters, such that the - Groebner basis in each part is given by the same expression. + The Groebner cover is a partition of the space of parameters, + such that the Groebner basis in each part is given by the same + expression. EXAMPLES:: @@ -4637,21 +4637,18 @@ def groebner_cover(self): and Y is defined by: 2*a^2 + 3*a: [(2*a^2 + 3*a)*z + (8*a + 1), (12*a^2 + 18*a)*y + (-20*a^2 - 35*a - 2), (4*a + 6)*x + 11], Quasi-affine subscheme X - Y of Affine Space of dimension 1 over Rational Field, where X is defined by: - a + ... and Y is defined by: 1: [1], Quasi-affine subscheme X - Y of Affine Space of dimension 1 over Rational Field, where X is defined by: - 2*a + 3 + ... and Y is defined by: 1: [1]} - - """ - gc = self._groebner_cover() - from sage.schemes.affine import affine_subscheme from sage.schemes.affine.affine_space import AffineSpace + gc = self._groebner_cover() F = self.base_ring() - A = AffineSpace(F.base_ring(), F.ngens(), list(F.gens_dict().keys())) + A = AffineSpace(F.base_ring(), F.ngens(), list(F.gens_dict())) result = {} ring = F.ring() for segment in gc: @@ -4663,7 +4660,6 @@ def groebner_cover(self): result[Y.complement(X)] = segment[1] return result - def change_ring(self, P): r""" Return the ideal ``I`` in ``P`` spanned by From e6bb8e6bae03cb7760ccf5674a08cbc59d7e25fd Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Fri, 9 Dec 2022 12:39:59 -0800 Subject: [PATCH 629/632] build/pkgs/openblas/dependencies: Remove python --- build/pkgs/openblas/dependencies | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pkgs/openblas/dependencies b/build/pkgs/openblas/dependencies index a475340050b..57ddf74f36e 100644 --- a/build/pkgs/openblas/dependencies +++ b/build/pkgs/openblas/dependencies @@ -1,4 +1,4 @@ -gfortran | $(PYTHON) +gfortran ---------- All lines of this file are ignored except the first. From 12d13b393b384da2863ce09f24d125db4759d0c9 Mon Sep 17 00:00:00 2001 From: Lorenz Panny Date: Sat, 10 Dec 2022 15:06:09 +0800 Subject: [PATCH 630/632] replace now-incorrect comment by assertion --- src/sage/schemes/elliptic_curves/ell_finite_field.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/schemes/elliptic_curves/ell_finite_field.py b/src/sage/schemes/elliptic_curves/ell_finite_field.py index f489f51b5a4..7a884014d23 100644 --- a/src/sage/schemes/elliptic_curves/ell_finite_field.py +++ b/src/sage/schemes/elliptic_curves/ell_finite_field.py @@ -973,7 +973,9 @@ def abelian_group(self): x = S.discrete_log(T) Q -= x * n1//nQ * P - Q.set_order(n2, check=False) # verifies n2*Q == 0 + assert not n2 * Q # by construction + Q.set_order(n2, check=False) + gens = P, Q orders = [T.order() for T in gens] # cached From bb7ee856390710fc17caf02615de16f5adc66cf2 Mon Sep 17 00:00:00 2001 From: Release Manager Date: Sun, 11 Dec 2022 12:15:05 +0100 Subject: [PATCH 631/632] Updated SageMath version to 9.8.beta5 --- .zenodo.json | 8 ++++---- VERSION.txt | 2 +- build/pkgs/configure/checksums.ini | 6 +++--- build/pkgs/configure/package-version.txt | 2 +- build/pkgs/sage_conf/install-requires.txt | 2 +- build/pkgs/sage_docbuild/install-requires.txt | 2 +- build/pkgs/sage_setup/install-requires.txt | 2 +- build/pkgs/sage_sws2rst/install-requires.txt | 2 +- build/pkgs/sagelib/install-requires.txt | 2 +- build/pkgs/sagemath_categories/install-requires.txt | 2 +- build/pkgs/sagemath_environment/install-requires.txt | 2 +- build/pkgs/sagemath_objects/install-requires.txt | 2 +- build/pkgs/sagemath_repl/install-requires.txt | 2 +- pkgs/sage-conf/VERSION.txt | 2 +- pkgs/sage-conf_pypi/VERSION.txt | 2 +- pkgs/sage-docbuild/VERSION.txt | 2 +- pkgs/sage-setup/VERSION.txt | 2 +- pkgs/sage-sws2rst/VERSION.txt | 2 +- pkgs/sagemath-categories/VERSION.txt | 2 +- pkgs/sagemath-environment/VERSION.txt | 2 +- pkgs/sagemath-objects/VERSION.txt | 2 +- pkgs/sagemath-repl/VERSION.txt | 2 +- src/VERSION.txt | 2 +- src/bin/sage-version.sh | 6 +++--- src/sage/version.py | 6 +++--- 25 files changed, 34 insertions(+), 34 deletions(-) diff --git a/.zenodo.json b/.zenodo.json index 4e6641194eb..2768b09b8ed 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.8.beta4", - "version": "9.8.beta4", + "title": "sagemath/sage: 9.8.beta5", + "version": "9.8.beta5", "upload_type": "software", - "publication_date": "2022-11-21", + "publication_date": "2022-12-11", "creators": [ { "affiliation": "SageMath.org", @@ -15,7 +15,7 @@ "related_identifiers": [ { "scheme": "url", - "identifier": "https://github.com/sagemath/sage/tree/9.8.beta4", + "identifier": "https://github.com/sagemath/sage/tree/9.8.beta5", "relation": "isSupplementTo" }, { diff --git a/VERSION.txt b/VERSION.txt index ba4e46788bd..801642460d4 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 9.8.beta4, Release Date: 2022-11-21 +SageMath version 9.8.beta5, Release Date: 2022-12-11 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index d0c804a6fd3..74e443193ab 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=2182ab811aa4bbed15ce26d64157e7fc973007cd -md5=70f8ac127a92d707714053527eb06944 -cksum=2846814243 +sha1=cd10867f6ee013a5f7fcfa6c9c69b1aed1939399 +md5=dbbcb33cd3ea96259a902cdffad84ab9 +cksum=423684136 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 99a265076ef..16688549047 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -4c696a60b4ffefe6e9a198cbb4bd7bf12823ef3a +325b7c119c0cf14948e6052709a814bcd6e3f477 diff --git a/build/pkgs/sage_conf/install-requires.txt b/build/pkgs/sage_conf/install-requires.txt index c890e648a67..3a857a04842 100644 --- a/build/pkgs/sage_conf/install-requires.txt +++ b/build/pkgs/sage_conf/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-conf ~= 9.8b4 +sage-conf ~= 9.8b5 diff --git a/build/pkgs/sage_docbuild/install-requires.txt b/build/pkgs/sage_docbuild/install-requires.txt index 35da0e99bbb..8cf6b33c657 100644 --- a/build/pkgs/sage_docbuild/install-requires.txt +++ b/build/pkgs/sage_docbuild/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-docbuild ~= 9.8b4 +sage-docbuild ~= 9.8b5 diff --git a/build/pkgs/sage_setup/install-requires.txt b/build/pkgs/sage_setup/install-requires.txt index c020dd4b7fc..59c19f3b157 100644 --- a/build/pkgs/sage_setup/install-requires.txt +++ b/build/pkgs/sage_setup/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-setup ~= 9.8b4 +sage-setup ~= 9.8b5 diff --git a/build/pkgs/sage_sws2rst/install-requires.txt b/build/pkgs/sage_sws2rst/install-requires.txt index e2197377b3f..0519af13e68 100644 --- a/build/pkgs/sage_sws2rst/install-requires.txt +++ b/build/pkgs/sage_sws2rst/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-sws2rst ~= 9.8b4 +sage-sws2rst ~= 9.8b5 diff --git a/build/pkgs/sagelib/install-requires.txt b/build/pkgs/sagelib/install-requires.txt index c66a399a69c..8f36ec274c2 100644 --- a/build/pkgs/sagelib/install-requires.txt +++ b/build/pkgs/sagelib/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagelib ~= 9.8b4 +sagelib ~= 9.8b5 diff --git a/build/pkgs/sagemath_categories/install-requires.txt b/build/pkgs/sagemath_categories/install-requires.txt index ba664945cd1..80e1acdaab9 100644 --- a/build/pkgs/sagemath_categories/install-requires.txt +++ b/build/pkgs/sagemath_categories/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-categories ~= 9.8b4 +sagemath-categories ~= 9.8b5 diff --git a/build/pkgs/sagemath_environment/install-requires.txt b/build/pkgs/sagemath_environment/install-requires.txt index aa6c86145f3..9f6db871278 100644 --- a/build/pkgs/sagemath_environment/install-requires.txt +++ b/build/pkgs/sagemath_environment/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-environment ~= 9.8b4 +sagemath-environment ~= 9.8b5 diff --git a/build/pkgs/sagemath_objects/install-requires.txt b/build/pkgs/sagemath_objects/install-requires.txt index 14d20cf2459..0b7fff7433d 100644 --- a/build/pkgs/sagemath_objects/install-requires.txt +++ b/build/pkgs/sagemath_objects/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-objects ~= 9.8b4 +sagemath-objects ~= 9.8b5 diff --git a/build/pkgs/sagemath_repl/install-requires.txt b/build/pkgs/sagemath_repl/install-requires.txt index c03509f6f5c..0849e6ab28d 100644 --- a/build/pkgs/sagemath_repl/install-requires.txt +++ b/build/pkgs/sagemath_repl/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-repl ~= 9.8b4 +sagemath-repl ~= 9.8b5 diff --git a/pkgs/sage-conf/VERSION.txt b/pkgs/sage-conf/VERSION.txt index ad07d6bad83..4980863c2f9 100644 --- a/pkgs/sage-conf/VERSION.txt +++ b/pkgs/sage-conf/VERSION.txt @@ -1 +1 @@ -9.8.beta4 +9.8.beta5 diff --git a/pkgs/sage-conf_pypi/VERSION.txt b/pkgs/sage-conf_pypi/VERSION.txt index ad07d6bad83..4980863c2f9 100644 --- a/pkgs/sage-conf_pypi/VERSION.txt +++ b/pkgs/sage-conf_pypi/VERSION.txt @@ -1 +1 @@ -9.8.beta4 +9.8.beta5 diff --git a/pkgs/sage-docbuild/VERSION.txt b/pkgs/sage-docbuild/VERSION.txt index ad07d6bad83..4980863c2f9 100644 --- a/pkgs/sage-docbuild/VERSION.txt +++ b/pkgs/sage-docbuild/VERSION.txt @@ -1 +1 @@ -9.8.beta4 +9.8.beta5 diff --git a/pkgs/sage-setup/VERSION.txt b/pkgs/sage-setup/VERSION.txt index ad07d6bad83..4980863c2f9 100644 --- a/pkgs/sage-setup/VERSION.txt +++ b/pkgs/sage-setup/VERSION.txt @@ -1 +1 @@ -9.8.beta4 +9.8.beta5 diff --git a/pkgs/sage-sws2rst/VERSION.txt b/pkgs/sage-sws2rst/VERSION.txt index ad07d6bad83..4980863c2f9 100644 --- a/pkgs/sage-sws2rst/VERSION.txt +++ b/pkgs/sage-sws2rst/VERSION.txt @@ -1 +1 @@ -9.8.beta4 +9.8.beta5 diff --git a/pkgs/sagemath-categories/VERSION.txt b/pkgs/sagemath-categories/VERSION.txt index ad07d6bad83..4980863c2f9 100644 --- a/pkgs/sagemath-categories/VERSION.txt +++ b/pkgs/sagemath-categories/VERSION.txt @@ -1 +1 @@ -9.8.beta4 +9.8.beta5 diff --git a/pkgs/sagemath-environment/VERSION.txt b/pkgs/sagemath-environment/VERSION.txt index ad07d6bad83..4980863c2f9 100644 --- a/pkgs/sagemath-environment/VERSION.txt +++ b/pkgs/sagemath-environment/VERSION.txt @@ -1 +1 @@ -9.8.beta4 +9.8.beta5 diff --git a/pkgs/sagemath-objects/VERSION.txt b/pkgs/sagemath-objects/VERSION.txt index ad07d6bad83..4980863c2f9 100644 --- a/pkgs/sagemath-objects/VERSION.txt +++ b/pkgs/sagemath-objects/VERSION.txt @@ -1 +1 @@ -9.8.beta4 +9.8.beta5 diff --git a/pkgs/sagemath-repl/VERSION.txt b/pkgs/sagemath-repl/VERSION.txt index ad07d6bad83..4980863c2f9 100644 --- a/pkgs/sagemath-repl/VERSION.txt +++ b/pkgs/sagemath-repl/VERSION.txt @@ -1 +1 @@ -9.8.beta4 +9.8.beta5 diff --git a/src/VERSION.txt b/src/VERSION.txt index ad07d6bad83..4980863c2f9 100644 --- a/src/VERSION.txt +++ b/src/VERSION.txt @@ -1 +1 @@ -9.8.beta4 +9.8.beta5 diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index a32acb79bcb..e0f5723d009 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -4,6 +4,6 @@ # which stops "setup.py develop" from rewriting it as a Python file. : # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='9.8.beta4' -SAGE_RELEASE_DATE='2022-11-21' -SAGE_VERSION_BANNER='SageMath version 9.8.beta4, Release Date: 2022-11-21' +SAGE_VERSION='9.8.beta5' +SAGE_RELEASE_DATE='2022-12-11' +SAGE_VERSION_BANNER='SageMath version 9.8.beta5, Release Date: 2022-12-11' diff --git a/src/sage/version.py b/src/sage/version.py index aeb6bb8f0f5..dda1b246d28 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.8.beta4' -date = '2022-11-21' -banner = 'SageMath version 9.8.beta4, Release Date: 2022-11-21' +version = '9.8.beta5' +date = '2022-12-11' +banner = 'SageMath version 9.8.beta5, Release Date: 2022-12-11' From 2114066f877a28b7473bf9242b1bb11931f3ec3e Mon Sep 17 00:00:00 2001 From: Release Manager Date: Wed, 21 Dec 2022 19:18:31 +0100 Subject: [PATCH 632/632] Updated SageMath version to 9.8.beta6 --- .zenodo.json | 8 ++++---- VERSION.txt | 2 +- build/pkgs/configure/checksums.ini | 6 +++--- build/pkgs/configure/package-version.txt | 2 +- build/pkgs/sage_conf/install-requires.txt | 2 +- build/pkgs/sage_docbuild/install-requires.txt | 2 +- build/pkgs/sage_setup/install-requires.txt | 2 +- build/pkgs/sage_sws2rst/install-requires.txt | 2 +- build/pkgs/sagelib/install-requires.txt | 2 +- build/pkgs/sagemath_categories/install-requires.txt | 2 +- build/pkgs/sagemath_environment/install-requires.txt | 2 +- build/pkgs/sagemath_objects/install-requires.txt | 2 +- build/pkgs/sagemath_repl/install-requires.txt | 2 +- pkgs/sage-conf/VERSION.txt | 2 +- pkgs/sage-conf_pypi/VERSION.txt | 2 +- pkgs/sage-docbuild/VERSION.txt | 2 +- pkgs/sage-setup/VERSION.txt | 2 +- pkgs/sage-sws2rst/VERSION.txt | 2 +- pkgs/sagemath-categories/VERSION.txt | 2 +- pkgs/sagemath-environment/VERSION.txt | 2 +- pkgs/sagemath-objects/VERSION.txt | 2 +- pkgs/sagemath-repl/VERSION.txt | 2 +- src/VERSION.txt | 2 +- src/bin/sage-version.sh | 6 +++--- src/sage/version.py | 6 +++--- 25 files changed, 34 insertions(+), 34 deletions(-) diff --git a/.zenodo.json b/.zenodo.json index 2768b09b8ed..9ec77c66b2e 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.8.beta5", - "version": "9.8.beta5", + "title": "sagemath/sage: 9.8.beta6", + "version": "9.8.beta6", "upload_type": "software", - "publication_date": "2022-12-11", + "publication_date": "2022-12-21", "creators": [ { "affiliation": "SageMath.org", @@ -15,7 +15,7 @@ "related_identifiers": [ { "scheme": "url", - "identifier": "https://github.com/sagemath/sage/tree/9.8.beta5", + "identifier": "https://github.com/sagemath/sage/tree/9.8.beta6", "relation": "isSupplementTo" }, { diff --git a/VERSION.txt b/VERSION.txt index 801642460d4..d56e58910a3 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 9.8.beta5, Release Date: 2022-12-11 +SageMath version 9.8.beta6, Release Date: 2022-12-21 diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 03333a1fb87..67aef802c69 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=1a386a25f14fbe63670357ec77392f63fd142ba1 -md5=5e01b503da35b503e540eeba33efdda0 -cksum=3630131692 +sha1=03fa7a341ddf380aa4fe54a85b0c41debd2ffe7e +md5=6fdfcb62364de0e8c8d57f5d3a38c909 +cksum=3919137984 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 4ca4fcd4871..8bf4ba4a67a 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -5a4c299dbca2a9252b959265a44303e38590cf96 +7819a59980b78950a0eb4aa18c529efa380d687b diff --git a/build/pkgs/sage_conf/install-requires.txt b/build/pkgs/sage_conf/install-requires.txt index 3a857a04842..9c4fd579311 100644 --- a/build/pkgs/sage_conf/install-requires.txt +++ b/build/pkgs/sage_conf/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-conf ~= 9.8b5 +sage-conf ~= 9.8b6 diff --git a/build/pkgs/sage_docbuild/install-requires.txt b/build/pkgs/sage_docbuild/install-requires.txt index 8cf6b33c657..eaa09529ac8 100644 --- a/build/pkgs/sage_docbuild/install-requires.txt +++ b/build/pkgs/sage_docbuild/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-docbuild ~= 9.8b5 +sage-docbuild ~= 9.8b6 diff --git a/build/pkgs/sage_setup/install-requires.txt b/build/pkgs/sage_setup/install-requires.txt index 59c19f3b157..28f2d3022d3 100644 --- a/build/pkgs/sage_setup/install-requires.txt +++ b/build/pkgs/sage_setup/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-setup ~= 9.8b5 +sage-setup ~= 9.8b6 diff --git a/build/pkgs/sage_sws2rst/install-requires.txt b/build/pkgs/sage_sws2rst/install-requires.txt index 0519af13e68..06400496e54 100644 --- a/build/pkgs/sage_sws2rst/install-requires.txt +++ b/build/pkgs/sage_sws2rst/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-sws2rst ~= 9.8b5 +sage-sws2rst ~= 9.8b6 diff --git a/build/pkgs/sagelib/install-requires.txt b/build/pkgs/sagelib/install-requires.txt index 8f36ec274c2..ad8a88b331e 100644 --- a/build/pkgs/sagelib/install-requires.txt +++ b/build/pkgs/sagelib/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagelib ~= 9.8b5 +sagelib ~= 9.8b6 diff --git a/build/pkgs/sagemath_categories/install-requires.txt b/build/pkgs/sagemath_categories/install-requires.txt index 80e1acdaab9..6fbc27bd0c1 100644 --- a/build/pkgs/sagemath_categories/install-requires.txt +++ b/build/pkgs/sagemath_categories/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-categories ~= 9.8b5 +sagemath-categories ~= 9.8b6 diff --git a/build/pkgs/sagemath_environment/install-requires.txt b/build/pkgs/sagemath_environment/install-requires.txt index 9f6db871278..43c5019c37f 100644 --- a/build/pkgs/sagemath_environment/install-requires.txt +++ b/build/pkgs/sagemath_environment/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-environment ~= 9.8b5 +sagemath-environment ~= 9.8b6 diff --git a/build/pkgs/sagemath_objects/install-requires.txt b/build/pkgs/sagemath_objects/install-requires.txt index 0b7fff7433d..f949c43e074 100644 --- a/build/pkgs/sagemath_objects/install-requires.txt +++ b/build/pkgs/sagemath_objects/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-objects ~= 9.8b5 +sagemath-objects ~= 9.8b6 diff --git a/build/pkgs/sagemath_repl/install-requires.txt b/build/pkgs/sagemath_repl/install-requires.txt index 0849e6ab28d..39e13274171 100644 --- a/build/pkgs/sagemath_repl/install-requires.txt +++ b/build/pkgs/sagemath_repl/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-repl ~= 9.8b5 +sagemath-repl ~= 9.8b6 diff --git a/pkgs/sage-conf/VERSION.txt b/pkgs/sage-conf/VERSION.txt index 4980863c2f9..d0fa47d29f0 100644 --- a/pkgs/sage-conf/VERSION.txt +++ b/pkgs/sage-conf/VERSION.txt @@ -1 +1 @@ -9.8.beta5 +9.8.beta6 diff --git a/pkgs/sage-conf_pypi/VERSION.txt b/pkgs/sage-conf_pypi/VERSION.txt index 4980863c2f9..d0fa47d29f0 100644 --- a/pkgs/sage-conf_pypi/VERSION.txt +++ b/pkgs/sage-conf_pypi/VERSION.txt @@ -1 +1 @@ -9.8.beta5 +9.8.beta6 diff --git a/pkgs/sage-docbuild/VERSION.txt b/pkgs/sage-docbuild/VERSION.txt index 4980863c2f9..d0fa47d29f0 100644 --- a/pkgs/sage-docbuild/VERSION.txt +++ b/pkgs/sage-docbuild/VERSION.txt @@ -1 +1 @@ -9.8.beta5 +9.8.beta6 diff --git a/pkgs/sage-setup/VERSION.txt b/pkgs/sage-setup/VERSION.txt index 4980863c2f9..d0fa47d29f0 100644 --- a/pkgs/sage-setup/VERSION.txt +++ b/pkgs/sage-setup/VERSION.txt @@ -1 +1 @@ -9.8.beta5 +9.8.beta6 diff --git a/pkgs/sage-sws2rst/VERSION.txt b/pkgs/sage-sws2rst/VERSION.txt index 4980863c2f9..d0fa47d29f0 100644 --- a/pkgs/sage-sws2rst/VERSION.txt +++ b/pkgs/sage-sws2rst/VERSION.txt @@ -1 +1 @@ -9.8.beta5 +9.8.beta6 diff --git a/pkgs/sagemath-categories/VERSION.txt b/pkgs/sagemath-categories/VERSION.txt index 4980863c2f9..d0fa47d29f0 100644 --- a/pkgs/sagemath-categories/VERSION.txt +++ b/pkgs/sagemath-categories/VERSION.txt @@ -1 +1 @@ -9.8.beta5 +9.8.beta6 diff --git a/pkgs/sagemath-environment/VERSION.txt b/pkgs/sagemath-environment/VERSION.txt index 4980863c2f9..d0fa47d29f0 100644 --- a/pkgs/sagemath-environment/VERSION.txt +++ b/pkgs/sagemath-environment/VERSION.txt @@ -1 +1 @@ -9.8.beta5 +9.8.beta6 diff --git a/pkgs/sagemath-objects/VERSION.txt b/pkgs/sagemath-objects/VERSION.txt index 4980863c2f9..d0fa47d29f0 100644 --- a/pkgs/sagemath-objects/VERSION.txt +++ b/pkgs/sagemath-objects/VERSION.txt @@ -1 +1 @@ -9.8.beta5 +9.8.beta6 diff --git a/pkgs/sagemath-repl/VERSION.txt b/pkgs/sagemath-repl/VERSION.txt index 4980863c2f9..d0fa47d29f0 100644 --- a/pkgs/sagemath-repl/VERSION.txt +++ b/pkgs/sagemath-repl/VERSION.txt @@ -1 +1 @@ -9.8.beta5 +9.8.beta6 diff --git a/src/VERSION.txt b/src/VERSION.txt index 4980863c2f9..d0fa47d29f0 100644 --- a/src/VERSION.txt +++ b/src/VERSION.txt @@ -1 +1 @@ -9.8.beta5 +9.8.beta6 diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index e0f5723d009..98d88e4e5b5 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -4,6 +4,6 @@ # which stops "setup.py develop" from rewriting it as a Python file. : # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='9.8.beta5' -SAGE_RELEASE_DATE='2022-12-11' -SAGE_VERSION_BANNER='SageMath version 9.8.beta5, Release Date: 2022-12-11' +SAGE_VERSION='9.8.beta6' +SAGE_RELEASE_DATE='2022-12-21' +SAGE_VERSION_BANNER='SageMath version 9.8.beta6, Release Date: 2022-12-21' diff --git a/src/sage/version.py b/src/sage/version.py index dda1b246d28..a772cb6fbc4 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.8.beta5' -date = '2022-12-11' -banner = 'SageMath version 9.8.beta5, Release Date: 2022-12-11' +version = '9.8.beta6' +date = '2022-12-21' +banner = 'SageMath version 9.8.beta6, Release Date: 2022-12-21'