diff --git a/src/sage/categories/simplicial_sets.py b/src/sage/categories/simplicial_sets.py index c07871402fd..19ec792d9ab 100644 --- a/src/sage/categories/simplicial_sets.py +++ b/src/sage/categories/simplicial_sets.py @@ -8,13 +8,21 @@ # https://www.gnu.org/licenses/ # ***************************************************************************** +from sage.functions.generalized import sign +from sage.matrix.special import identity_matrix from sage.misc.cachefunc import cached_method +from sage.misc.lazy_import import lazy_import from sage.categories.category_singleton import Category_singleton from sage.categories.category_with_axiom import CategoryWithAxiom from sage.categories.sets_cat import Sets from sage.categories.homsets import HomsetsCategory +from sage.matrix.constructor import matrix +from sage.misc.misc_c import prod from sage.rings.infinity import Infinity from sage.rings.integer import Integer +from sage.rings.integer_ring import ZZ + +lazy_import('sage.matrix.constructor', 'matrix') class SimplicialSets(Category_singleton): @@ -360,10 +368,9 @@ def _universal_cover_dict(self): [1, f, f * f] """ from sage.groups.free_group import FreeGroup - skel = self.n_skeleton(2) - graph = skel.graph() - if not skel.is_connected(): - graph = graph.subgraph(skel.base_point()) + graph = self.graph() + if not self.is_connected(): + graph = graph.subgraph(self.base_point()) edges = [e[2] for e in graph.edges(sort=False)] spanning_tree = [e[2] for e in graph.min_spanning_tree()] gens = [e for e in edges if e not in spanning_tree] @@ -371,9 +378,9 @@ def _universal_cover_dict(self): FG = FreeGroup(len(gens), 'e') rels = [] - for f in skel.n_cells(2): + for f in self.n_cells(2): z = dict() - for i, sigma in enumerate(skel.faces(f)): + for i, sigma in enumerate(self.faces(f)): if sigma in spanning_tree: z[i] = FG.one() elif sigma.is_degenerate(): @@ -385,7 +392,7 @@ def _universal_cover_dict(self): z[i] = FG.one() rels.append(z[0]*z[1].inverse()*z[2]) G = FG.quotient(rels) - char = {g : G.gen(i) for i,g in enumerate(gens)} + char = {g: G.gen(i) for i, g in enumerate(gens)} for e in edges: if e not in gens: char[e] = G.one() @@ -506,7 +513,7 @@ def covering_map(self, character): grelems.append(cells_dict[(f.nondegenerate(), g)].apply_degeneracies(*f.degeneracies())) faces_dict[cell] = grelems cover = SimplicialSet(faces_dict, base_point=cells_dict[(self.base_point(), G.one())]) - cover_map_data = {c : s[0] for (s,c) in cells_dict.items()} + cover_map_data = {c: s[0] for (s, c) in cells_dict.items()} return SimplicialSetMorphism(data=cover_map_data, domain=cover, codomain=self) def cover(self, character): @@ -574,6 +581,414 @@ def universal_cover(self): """ return self.universal_cover_map().domain() + def _canonical_twisting_operator(self): + r""" + The canonical twisting operator corresponds to the abelianization of the fundamental + group. It assigns each edge to the corresponding element in the algebra of the + abelianization of the fundamental group, which is a quotient of a Laurent polynomial + ring. + + EXAMPLES:: + + sage: X = simplicial_sets.Torus() + sage: d = X._canonical_twisting_operator() + sage: d + {(s_0 v_0, sigma_1): f3, (sigma_1, s_0 v_0): f2*f3^-1, (sigma_1, sigma_1): f2} + sage: list(d.values())[0].parent() + Multivariate Laurent Polynomial Ring in f2, f3 over Integer Ring + sage: Y = simplicial_sets.RealProjectiveSpace(2) + sage: d2 = Y._canonical_twisting_operator() + sage: d2 + {f: F1bar} + sage: list(d2.values())[0].parent() + Quotient of Univariate Laurent Polynomial Ring in F1 over Integer Ring by the ideal (-1 + F1^2) + + """ + G, d = self._universal_cover_dict() + phi = G.abelianization_map() + abelG, R, I, images = G.abelianization_to_algebra(ZZ) + QRP = R.quotient_ring(I) + res = dict() + for s, el in d.items(): + res[s] = QRP(prod(images[abs(a)-1]**sign(a) for a in el.Tietze())) + return res + + def twisted_chain_complex(self, twisting_operator=None, dimensions=None, augmented=False, + cochain=False, verbose=False, subcomplex=None, + check=False): + r""" + Return the normalized chain complex twisted by some operator. + + A twisting operator is a map from the set of simplices to some algebra. + The differentials are then twisted by this operator. + + INPUT: + + - ``twisting_operator`` -- a dictionary, associating the twist of each + simplex. If it is not given, the canonical one (associated to the + laurent polynomial ring abelianization of the fundamental group, ignoring + torsion) is used. + + - ``dimensions`` -- if ``None``, compute the chain complex in all + dimensions. If a list or tuple of integers, compute the + chain complex in those dimensions, setting the chain groups + in all other dimensions to zero. + + - ``augmented`` (optional, default ``False``) -- if ``True``, + return the augmented chain complex (that is, include a class + in dimension `-1` corresponding to the empty cell). + + - ``cochain`` (optional, default ``False``) -- if ``True``, + return the cochain complex (that is, the dual of the chain + complex). + + - ``verbose`` (optional, default ``False``) -- ignored. + + - ``subcomplex`` (optional, default ``None``) -- if present, + compute the chain complex relative to this subcomplex. + + - ``check`` (optional, default ``False``) -- If ``True``, make + sure that the chain complex is actually a chain complex: + the differentials are composable and their product is zero. + + The normalized chain complex of a simplicial set is isomorphic + to the chain complex obtained by modding out by degenerate + simplices, and the latter is what is actually constructed + here. + + EXAMPLES:: + + sage: W = simplicial_sets.Sphere(1).wedge(simplicial_sets.Sphere(2)) + sage: W.nondegenerate_simplices() + [*, sigma_1, sigma_2] + sage: s1 = W.nondegenerate_simplices()[1] + sage: L. = LaurentPolynomialRing(QQ) + sage: tw = {s1:t} + sage: ChC = W.twisted_chain_complex(tw) + sage: ChC.differential(1) + [-1 + t] + sage: ChC.differential(2) + [0] + + :: + + sage: X = simplicial_sets.Torus() + sage: C = X.twisted_chain_complex() + sage: C.differential(1) + [ f3 - 1 f2*f3^-1 - 1 f2 - 1] + sage: C.differential(2) + [ 1 f2*f3^-1] + [ f3 1] + [ -1 -1] + sage: C.differential(3) + [] + + :: + + sage: Y = simplicial_sets.RealProjectiveSpace(2) + sage: C = Y.twisted_chain_complex() + sage: C.differential(1) + [-1 + F1] + sage: C.differential(2) + [1 + F1] + sage: C.differential(3) + [] + + """ + from sage.homology.chain_complex import ChainComplex + from sage.structure.element import get_coercion_model + cm = get_coercion_model() + + if twisting_operator: + twop = twisting_operator + else: + di = self._canonical_twisting_operator() + if hasattr(list(di.values())[0], "lift"): + twop = {a: b.lift() for a, b in di.items()} + else: + twop = di + + def twist(s): + if s in twop: + return twop[s] + if s.dimension() > 1: + return twist(self.face(s,s.dimension())) + return 1 + base_ring = cm.common_parent(*twop.values()) + + if dimensions is None: + if not self.cells(): # Empty + if cochain: + return ChainComplex({-1: matrix(base_ring, 0, 0)}, + degree_of_differential=1) + return ChainComplex({0: matrix(base_ring, 0, 0)}, + degree_of_differential=-1) + dimensions = list(range(self.dimension() + 1)) + else: + if not isinstance(dimensions, (list, tuple, range)): + dimensions = list(range(dimensions - 1, dimensions + 2)) + else: + dimensions = [n for n in dimensions if n >= 0] + if not dimensions: + # Return the empty chain complex. + if cochain: + return ChainComplex(base_ring=base_ring, degree=1) + else: + return ChainComplex(base_ring=base_ring, degree=-1) + + differentials = {} + # Convert the tuple self._data to a dictionary indexed by the + # non-degenerate simplices. + if subcomplex: + X = self.quotient(subcomplex) + face_data = X.face_data() + nondegens = X.nondegenerate_simplices() + else: + face_data = self.face_data() + nondegens = self.nondegenerate_simplices() + # simplices: dictionary indexed by dimension, values the list + # of non-degenerate simplices in that dimension. + simplices = {} + for sigma in nondegens: + if sigma.dimension() in simplices: + simplices[sigma.dimension()].append(sigma) + else: + simplices[sigma.dimension()] = [sigma] + first = dimensions.pop(0) + if first in simplices: + rank = len(simplices[first]) + current = sorted(simplices[first]) + else: + rank = 0 + current = [] + if augmented and first == 0: + differentials[first-1] = matrix(base_ring, 0, 1) + differentials[first] = matrix(base_ring, 1, rank, + [1] * rank) + else: + differentials[first] = matrix(base_ring, 0, rank) + + for d in dimensions: + old_rank = rank + faces = {_[1]: _[0] for _ in enumerate(current)} + if d in simplices: + current = sorted(simplices[d]) + rank = len(current) + # old_rank: number of simplices in dimension d-1. + # faces: list of simplices in dimension d-1. + # rank: number of simplices in dimension d. + # current: list of simplices in dimension d. + if not faces: + differentials[d] = matrix(base_ring, old_rank, rank) + else: + matrix_data = {} + for col, sigma in enumerate(current): + sign = 1 + twists = len(face_data[sigma]) * [1] + twists[0] = twist(sigma) + for (ch, tau) in zip(twists, face_data[sigma]): + if tau.is_nondegenerate(): + row = faces[tau] + if (row, col) in matrix_data: + matrix_data[(row, col)] += sign*ch + else: + matrix_data[(row, col)] = sign*ch + sign *= -1 + + differentials[d] = matrix(base_ring, old_rank, + rank, matrix_data, sparse=False) + + else: + rank = 0 + current = [] + differentials[d] = matrix(base_ring, old_rank, rank, sparse=False) + + if cochain: + new_diffs = {} + for d in differentials: + new_diffs[d-1] = differentials[d].transpose() + return ChainComplex(new_diffs, degree_of_differential=1, + check=check) + return ChainComplex(differentials, degree_of_differential=-1, + check=check) + + def twisted_homology(self, n, reduced=False): + r""" + The `n`-th twisted homology module of the simplicial set with respect to + the abelianization of the fundamental_group. + + It is a module over a polynomial ring, including relations to make some + variables the multiplicative inverses of others. + + INPUT: + + - ``n`` - a positive integer. + + - ``reduced`` - (default: False) if set to True, the presentation matrix + will be reduced. + + EXAMPLES:: + + sage: X = simplicial_sets.Sphere(1).wedge(simplicial_sets.Sphere(2)) + sage: X.twisted_homology(1) + Quotient module by Submodule of Ambient free module of rank 0 over the integral domain Multivariate Polynomial Ring in f1, f1inv over Integer Ring + Generated by the rows of the matrix: + [] + sage: X.twisted_homology(2) + Quotient module by Submodule of Ambient free module of rank 1 over the integral domain Multivariate Polynomial Ring in f1, f1inv over Integer Ring + Generated by the rows of the matrix: + [f1*f1inv - 1] + + :: + + sage: Y = simplicial_sets.Torus() + sage: Y.twisted_homology(1) + Quotient module by Submodule of Ambient free module of rank 5 over the integral domain Multivariate Polynomial Ring in f2, f2inv, f3, f3inv over Integer Ring + Generated by the rows of the matrix: + [ 1 0 0 0 0] + [ 0 1 0 0 0] + [ 0 0 1 0 0] + [ 0 0 0 1 0] + [ 0 0 0 0 1] + [f2*f2inv - 1 0 0 0 0] + [ 0 f2*f2inv - 1 0 0 0] + [ 0 0 f2*f2inv - 1 0 0] + [ 0 0 0 f2*f2inv - 1 0] + [ 0 0 0 0 f2*f2inv - 1] + [f3*f3inv - 1 0 0 0 0] + [ 0 f3*f3inv - 1 0 0 0] + [ 0 0 f3*f3inv - 1 0 0] + [ 0 0 0 f3*f3inv - 1 0] + [ 0 0 0 0 f3*f3inv - 1] + sage: Y.twisted_homology(2) + Quotient module by Submodule of Ambient free module of rank 0 over the integral domain Multivariate Polynomial Ring in f2, f2inv, f3, f3inv over Integer Ring + Generated by the rows of the matrix: + [] + sage: Y.twisted_homology(1, reduced=True) + Quotient module by Submodule of Ambient free module of rank 5 over the integral domain Multivariate Polynomial Ring in f2, f2inv, f3, f3inv over Integer Ring + Generated by the rows of the matrix: + [1 0 0 0 0] + [0 1 0 0 0] + [0 0 1 0 0] + [0 0 0 1 0] + [0 0 0 0 1] + + TESTS:: + + sage: X = simplicial_sets.PresentationComplex(groups.presentation.FGAbelian((3,2))) + sage: TW2 = X.twisted_homology(2, reduced=True) + sage: M = TW2.relations_matrix() + sage: from sage.libs.singular.function import singular_function + sage: vdim = singular_function("vdim") + sage: vdim(M.T, ring=M.base_ring()) + // ** considering the image in Q[...] + // ** _ is no standard basis + 5 + sage: X.universal_cover().homology(2) + Z^5 + sage: from sage.libs.singular.function import singular_function + + """ + from sage.libs.singular.function import singular_function + from sage.libs.singular.option import opt_verb + opt_verb['not_warn_sb'] = True + singstd = singular_function("std") + singsyz = singular_function("syz") + singred = singular_function("reduce") + singlift = singular_function("lift") + G, d = self._universal_cover_dict() + phi = G.abelianization_map() + abelG, R, I, images = G.abelianization_to_algebra(ZZ) + CC = self.twisted_chain_complex() + M1 = CC.differential(n).T + M2 = CC.differential(n + 1).T + + def convert_to_polynomial(p): + if hasattr(p, "lift"): + return p.lift()._as_extended_polynomial() + else: + return p._as_extended_polynomial() + M1 = M1.apply_map(convert_to_polynomial) + M2 = M2.apply_map(convert_to_polynomial) + RP = R._extended_ring + IP = RP.ideal([convert_to_polynomial(g) for g in I]) + JP = R._extended_ring_ideal + GB = (IP+JP).groebner_basis() + GBI = RP.ideal(GB) + + def reduce_laurent(a): + return singred(a, GBI, ring=RP) + + def group_to_polynomial(el, RP): + res = RP.one() + for a in el.Tietze(): + if a > 0: + res *= RP.gen(2*a-2) + else: + res *= RP.gen(-2*a-1) + return res + + def mkernel(M): + if M.nrows() == 0: + return matrix(M.base_ring(), 0, 0) + if M.ncols() == 0: + return M.T + res = M + n = res.ncols() + for g in (IP+JP).gens(): + res = res.stack(g*identity_matrix(n)) + syz = matrix(singsyz(res.T, ring=res.base_ring())).T + trimmed = syz.T.submatrix(0, 0, syz.ncols(), M.nrows()) + trimmed = trimmed.apply_map(reduce_laurent) + to_delete = [i for (i, r) in enumerate(trimmed.rows()) if not r] + return trimmed.delete_rows(to_delete) + + def lift_to_submodule(S, M): + if S.nrows() == 0 or S.ncols() == 0: + return S + res = M + for g in GB: + res = res.stack(g*identity_matrix(M.ncols())) + singres = matrix(singlift(res.T, S.T,ring=res.base_ring())) + return singres.submatrix(0, 0, M.nrows(), S.nrows()) + + def mgb(M): + if M.nrows() == 0 or M.ncols() == 0: + return M + res = M + for g in GB: + res = res.stack(g*identity_matrix(M.ncols())) + sres = matrix(singstd(res.T, ring=RP)) + to_delete = [i for i, r in enumerate(sres.apply_map(reduce_laurent)) if not r] + return sres.delete_rows(to_delete) + M2 = border_matrix(n+1) + if M1.nrows() == 0: + opt_verb.reset_default() + return (RP**0).quotient_module([]) + K = mkernel(M1) + DK = mkernel(K) + if M2.nrows() > 0: + S = lift_to_submodule(M2, K) + if S.nrows() > 0 and S.ncols() > 0: + res = mgb(DK.stack(S.T)) + else: + res = DK + else: + res = mgb(DK) + resmat = mgb(res.apply_map(reduce_laurent)) + AM = RP ** K.nrows() + if resmat.ncols() == 0: + SM = AM.submodule([]) + opt_verb.reset_default() + return AM.quotient_module(SM) + for g in (IP+JP).gens(): + resmat = resmat.stack(g * identity_matrix(resmat.ncols())) + if reduced: + resmat = matrix(singstd(resmat.T, ring=RP)) + SM = AM.submodule(resmat) + opt_verb.reset_default() + return AM.quotient_module(SM) + def is_simply_connected(self): """ Return ``True`` if this pointed simplicial set is simply connected. diff --git a/src/sage/rings/polynomial/laurent_polynomial.pyx b/src/sage/rings/polynomial/laurent_polynomial.pyx index 8e5bd127993..03de4a1179f 100644 --- a/src/sage/rings/polynomial/laurent_polynomial.pyx +++ b/src/sage/rings/polynomial/laurent_polynomial.pyx @@ -321,6 +321,7 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): William Stein, David Joyner, and Robert Bradshaw - Travis Scrimshaw (09-2013): Cleaned-up and added a few extra methods """ + def __init__(self, parent, f, n=0): r""" Create the Laurent polynomial `t^n \cdot f`. @@ -352,15 +353,15 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): CommutativeAlgebraElement.__init__(self, parent) if isinstance(f, LaurentPolynomial_univariate): - n += (f).__n - if (f).__u._parent is parent._R: - f = (f).__u + n += (< LaurentPolynomial_univariate > f).__n + if (< LaurentPolynomial_univariate > f).__u._parent is parent._R: + f = (< LaurentPolynomial_univariate > f).__u else: - f = parent._R((f).__u) + f = parent._R((< LaurentPolynomial_univariate > f).__u) elif (not isinstance(f, Polynomial)) or (parent is not f.parent()): if isinstance(f, dict): v = min(f) if f else 0 - f = {i-v: c for i,c in f.items()} + f = {i-v: c for i, c in f.items()} n += v f = parent._R(f) @@ -556,7 +557,7 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): self.__n = 0 return # we already caught the infinity and zero cases - cdef long v = self.__u.valuation() + cdef long v = self.__u.valuation() self.__n += v self.__u = self.__u >> v @@ -593,11 +594,11 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): elif e == 0: var = "" else: - var = "*{}^{}".format(X,e) - s += "{}{}".format(x,var) + var = "*{}^{}".format(X, e) + s += "{}{}".format(x, var) first = False s = s.replace(" + -", " - ") - s = s.replace(" 1*"," ") + s = s.replace(" 1*", " ") s = s.replace(" -1*", " -") return s[1:] @@ -654,19 +655,19 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): elif e == 0: var = "" elif e > 0: - var = "|{}^{{{}}}".format(X,e) + var = "|{}^{{{}}}".format(X, e) if e >= 0: - s += "{}{}".format(x,var) - else: # negative e + s += "{}{}".format(x, var) + else: # negative e if e == -1: s += "\\frac{{{}}}{{{}}}".format(x, X) else: - s += "\\frac{{{}}}{{{}^{{{}}}}}".format(x, X,-e) + s += "\\frac{{{}}}{{{}^{{{}}}}}".format(x, X, -e) first = False s = s.replace(" + -", " - ") - s = s.replace(" 1|"," ") + s = s.replace(" 1|", " ") s = s.replace(" -1|", " -") - s = s.replace("|","") + s = s.replace("|", "") return s[1:] @@ -702,7 +703,7 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): # degrees cdef long result = 0 cdef long result_mon - cdef int i,j + cdef int i, j cdef long var_hash_name = hash(self.__u._parent._names[0]) for i in range(self.__u.degree()+1): result_mon = hash(self.__u[i]) @@ -761,7 +762,7 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): self.__u[start:stop:step] # error out, see issue #18940 stop = stop - self.__n if stop is not None else self.__u.degree() + 1 f = self.__u[:stop] - ret = self._new_c() + ret = self._new_c() ret.__u = f ret.__n = self.__n ret._normalize() @@ -914,11 +915,11 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): j = i - self.__n if j >= 0: self.__u._unsafe_mutate(j, value) - else: # off to the left + else: # off to the left if value != 0: self.__n = self.__n + j R = self._parent.base_ring() - coeffs = [value] + [R.zero() for _ in range(1,-j)] + self.__u.list() + coeffs = [value] + [R.zero() for _ in range(1, -j)] + self.__u.list() self.__u = self.__u._parent(coeffs) self._normalize() @@ -946,7 +947,7 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): ALGORITHM: Shift the unit parts to align them, then add. """ - cdef LaurentPolynomial_univariate right = right_m + cdef LaurentPolynomial_univariate right = right_m cdef long m cdef LaurentPolynomial_univariate ret @@ -970,8 +971,8 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): f1 = self.__u f2 = right.__u # 3. Add - ret = self._new_c() - ret.__u = (f1 + f2) + ret = self._new_c() + ret.__u = (f1 + f2) ret.__n = m ret._normalize() return ret @@ -990,7 +991,7 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): ALGORITHM: Shift the unit parts to align them, then subtract. """ - cdef LaurentPolynomial_univariate right = right_m + cdef LaurentPolynomial_univariate right = right_m cdef long m cdef LaurentPolynomial_univariate ret @@ -1010,8 +1011,8 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): f1 = self.__u << self.__n - m f2 = right.__u # 3. Subtract - ret = self._new_c() - ret.__u = (f1 - f2) + ret = self._new_c() + ret.__u = (f1 - f2) ret.__n = m ret._normalize() return ret @@ -1043,8 +1044,8 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): -1 - t^5 """ cdef LaurentPolynomial_univariate ret - ret = self._new_c() - ret.__u = -self.__u + ret = self._new_c() + ret.__u = -self.__u ret.__n = self.__n # No need to normalize return ret @@ -1059,10 +1060,10 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): sage: f*g x^-3 + x^-2 + x^-1 + x^8 """ - cdef LaurentPolynomial_univariate right = right_r + cdef LaurentPolynomial_univariate right = right_r cdef LaurentPolynomial_univariate ret - ret = self._new_c() - ret.__u = (self.__u * right.__u) + ret = self._new_c() + ret.__u = (self.__u * right.__u) ret.__n = self.__n + right.__n ret._normalize() return ret @@ -1077,8 +1078,8 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): 3*x^-3 + 3*x + 3*x^2 + 9*x^4 """ cdef LaurentPolynomial_univariate ret - ret = self._new_c() - ret.__u = self.__u._rmul_(c) + ret = self._new_c() + ret.__u = self.__u._rmul_(c) ret.__n = self.__n ret._normalize() return ret @@ -1093,8 +1094,8 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): 3*x^-3 + 3*x + 3*x^2 + 9*x^4 """ cdef LaurentPolynomial_univariate ret - ret = self._new_c() - ret.__u = self.__u._lmul_(c) + ret = self._new_c() + ret.__u = self.__u._lmul_(c) ret.__n = self.__n ret._normalize() return ret @@ -1171,10 +1172,10 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): sage: 1 // f 0 """ - cdef LaurentPolynomial_univariate right = rhs + cdef LaurentPolynomial_univariate right = rhs cdef LaurentPolynomial_univariate ret - ret = self._new_c() - ret.__u = (self.__u // right.__u) + ret = self._new_c() + ret.__u = (self.__u // right.__u) ret.__n = self.__n - right.__n ret._normalize() return ret @@ -1197,7 +1198,7 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): 1 + 4*t^2 + 6*t^4 + 4*t^6 + t^8 """ cdef LaurentPolynomial_univariate ret - ret = self._new_c() + ret = self._new_c() ret.__u = self.__u ret.__n = self.__n + k # No need to normalize @@ -1216,7 +1217,7 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): 1 + 4*t^2 + 6*t^4 + 4*t^6 + t^8 """ cdef LaurentPolynomial_univariate ret - ret = self._new_c() + ret = self._new_c() ret.__u = self.__u ret.__n = self.__n + k # No need to normalize @@ -1235,7 +1236,7 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): t^-14 + 4*t^-12 + 6*t^-10 + 4*t^-8 + t^-6 """ cdef LaurentPolynomial_univariate ret - ret = self._new_c() + ret = self._new_c() ret.__u = self.__u ret.__n = self.__n - k # No need to normalize @@ -1257,7 +1258,7 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): sage: (x^-2 + x)*(x^-2 + 1) / ((x^-5 + x^-8)*(x + 2)) (x^6 + x^4)/(x + 2) """ - cdef LaurentPolynomial_univariate right = rhs + cdef LaurentPolynomial_univariate right = rhs if right.__u.is_zero(): raise ZeroDivisionError return self * ~right @@ -1283,8 +1284,8 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): Fraction Field of Univariate Polynomial Ring in t over Rational Field """ cdef LaurentPolynomial_univariate ret - if self.__u.is_constant(): # this has a single term c*x^n - ret = self._new_c() + if self.__u.is_constant(): # this has a single term c*x^n + ret = self._new_c() if self.__u.is_unit(): ret.__u = self.__u.inverse_of_unit() ret.__n = -self.__n @@ -1415,9 +1416,9 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): sage: gcd((t^-2 + t)*(t + t^-1), (t^5 + t^8)*(1 + t^-2)) t^-3 + t^-1 + 1 + t^2 """ - b = self._parent(right) + b = self._parent(right) cdef LaurentPolynomial_univariate ret - ret = self._new_c() + ret = self._new_c() ret.__u = self.__u.gcd(b.__u) ret.__n = min(self.__n, b.__n) ret._normalize() @@ -1460,15 +1461,15 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): sage: num == q * den + r True """ - cdef LaurentPolynomial_univariate right = other + cdef LaurentPolynomial_univariate right = other q, r = self.__u.quo_rem(right.__u) cdef LaurentPolynomial_univariate ql, qr - ql = self._new_c() - ql.__u = q + ql = self._new_c() + ql.__u = q ql.__n = self.__n - right.__n ql._normalize() - qr = self._new_c() - qr.__u = r + qr = self._new_c() + qr.__u = r qr.__n = self.__n qr._normalize() return ql, qr @@ -1513,7 +1514,7 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): sage: f > g True """ - cdef LaurentPolynomial_univariate right = right_r + cdef LaurentPolynomial_univariate right = right_r zero = self._parent.base_ring().zero() @@ -1574,8 +1575,8 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): if n <= self.valuation(): return self._parent.zero() cdef LaurentPolynomial_univariate ret - ret = self._new_c() - ret.__u = self.__u.truncate(n - self.__n) + ret = self._new_c() + ret.__u = self.__u.truncate(n - self.__n) ret.__n = self.__n ret._normalize() return ret @@ -1676,7 +1677,6 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): """ return self.__n == 0 and self.__u.is_constant() - def is_square(self, root=False): r""" Return whether this Laurent polynomial is a square. @@ -1758,7 +1758,7 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): """ from copy import copy cdef LaurentPolynomial_univariate ret - ret = self._new_c() + ret = self._new_c() ret.__u = copy(self.__u) ret.__n = self.__n # No need to normalize @@ -1851,8 +1851,8 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): try: # call _derivative() recursively on coefficients u = [coeff._derivative(var) for coeff in self.__u.list(copy=False)] - ret = self._new_c() - ret.__u = self._parent._R(u) + ret = self._new_c() + ret.__u = self._parent._R(u) ret.__n = self.__n ret._normalize() return ret @@ -1866,8 +1866,8 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): cdef list a = self.__u.list(copy=True) for m in range(len(a)): a[m] *= n + m - ret = self._new_c() - ret.__u = self._parent._R(a) + ret = self._new_c() + ret.__u = self._parent._R(a) ret.__n = self.__n - 1 ret._normalize() return ret @@ -1923,16 +1923,16 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): cdef list a = self.__u.list(copy=False) if n < 0: - v = [a[i]/(n+i+1) for i in range(min(-1-n,len(a)))] + [0] + v = [a[i]/(n+i+1) for i in range(min(-1-n, len(a)))] + [0] else: v = [] - v += [a[i]/(n+i+1) for i in range(max(-n,0), len(a))] + v += [a[i]/(n+i+1) for i in range(max(-n, 0), len(a))] try: u = self._parent._R(v) except TypeError: raise ArithmeticError("coefficients of integral cannot be coerced into the base ring") - ret = self._new_c() - ret.__u = u + ret = self._new_c() + ret.__u = u ret.__n = n + 1 ret._normalize() return ret @@ -1965,7 +1965,7 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): """ if kwds: f = self.subs(**kwds) - if x: # If there are non-keyword arguments + if x: # If there are non-keyword arguments return f(*x) else: return f @@ -1993,14 +1993,14 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): """ cdef LaurentPolynomial_univariate u, d pf = self.__u.factor() - u = self._new_c() + u = self._new_c() u.__u = pf.unit() u.__n = self.__n u._normalize() f = [] for t in pf: - d = self._new_c() + d = self._new_c() d.__u = t[0] d.__n = 0 d._normalize() @@ -2047,6 +2047,29 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): """ return self.__u[-self.__n] + def _as_extended_polynomial(self): + """ + This Laurent polynomial seen as a polynomial in twice as many variables, + where half of the variables are the inverses of the other half. + + EXAMPLES:: + + sage: L. = LaurentPolynomialRing(QQ) + sage: f = t-t^-2 + sage: f._as_extended_polynomial() + -tinv^2 + t + sage: _.parent() + Multivariate Polynomial Ring in t, tinv over Rational Field + + """ + dres = {} + for (e, c) in self.dict().items(): + if e > 0: + dres[(e, 0)] = c + else: + dres[(0, -e)] = c + return self.parent()._extended_ring(dres) + @coerce_binop def divides(self, other): r""" diff --git a/src/sage/rings/polynomial/laurent_polynomial_mpair.pyx b/src/sage/rings/polynomial/laurent_polynomial_mpair.pyx index c0fd659591f..7832488a2ef 100644 --- a/src/sage/rings/polynomial/laurent_polynomial_mpair.pyx +++ b/src/sage/rings/polynomial/laurent_polynomial_mpair.pyx @@ -22,6 +22,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): """ Multivariate Laurent polynomials. """ + def __init__(self, parent, x, mon=None, reduce=True): """ Currently, one can only create LaurentPolynomials out of dictionaries @@ -120,8 +121,8 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): x = {k.esub(self._mon): x_k for k, x_k in x.iteritems()} elif (isinstance(x, LaurentPolynomial_mpair) and parent.variable_names() == x.parent().variable_names()): - self._mon = (x)._mon - x = (x)._poly + self._mon = ( < LaurentPolynomial_mpair > x)._mon + x = ( < LaurentPolynomial_mpair > x)._poly else: # since x should coerce into parent, _mon should be (0,...,0) self._mon = ETuple({}, int(parent.ngens())) self._poly = parent._R(x) @@ -279,26 +280,26 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): # self._parent.variable_names(), # self._parent.base_ring() # ) - cdef dict D = self._poly.dict() + cdef dict D = self._poly.dict() cdef ETuple e if i is None: e = None for k in D: if e is None: - e = k + e = k else: e = e.emin(k) if not e.is_constant(): - self._poly = (self._poly // self._poly._parent({e: 1})) + self._poly = (self._poly // self._poly._parent({e: 1})) self._mon = self._mon.eadd(e) else: e = None for k in D: if e is None or k[i] < e: - e = k[i] + e = k[i] if e > 0: - self._poly = (self._poly // self._poly._parent.gen(i)) + self._poly = (self._poly // self._poly._parent.gen(i)) self._mon = self._mon.eadd_p(e, i) cdef _compute_polydict(self) noexcept: @@ -312,7 +313,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): """ # cdef dict D = self._poly._mpoly_dict_recursive(self._parent.variable_names(), # self._parent.base_ring()) - cdef dict D = self._poly.dict() + cdef dict D = self._poly.dict() cdef dict DD if self._mon.is_constant(): self._prod = PolyDict(D) @@ -555,6 +556,34 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): for exp, coeff in self._poly.iterator_exp_coeff(): yield (coeff, P.element_class(P, one, exp.eadd(self._mon))) + def _as_extended_polynomial(self): + """ + This Laurent polynomial seen as a polynomial in twice as many variables, + where half of the variables are the inverses of the other half. + + EXAMPLES:: + + sage: L. = LaurentPolynomialRing(QQ) + sage: f = t1-t2^-2 + sage: f + t1 - t2^-2 + sage: f._as_extended_polynomial() + -t2inv^2 + t1 + sage: _.parent() + Multivariate Polynomial Ring in t1, t1inv, t2, t2inv over Rational Field + + """ + dres = {} + for (e, c) in self.dict().items(): + exps = [] + for t in e: + if t > 0: + exps += [t, 0] + else: + exps += [0, -t] + dres[tuple(exps)] = c + return self.parent()._extended_ring(dres) + def iterator_exp_coeff(self): """ Iterate over ``self`` as pairs of (ETuple, coefficient). @@ -621,7 +650,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): """ if parent(mon) != self._parent: raise TypeError("input must have the same parent") - cdef LaurentPolynomial_mpair m = mon + cdef LaurentPolynomial_mpair m = mon if m._prod is None: m._compute_polydict() if self._prod is None: @@ -710,7 +739,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): """ if mon.parent() is not self._parent: mon = self._parent(mon) - cdef LaurentPolynomial_mpair m = mon + cdef LaurentPolynomial_mpair m = mon if self._prod is None: self._compute_polydict() if m._prod is None: @@ -780,7 +809,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): """ if self._prod is None: self._compute_polydict() - return self._prod.dict() + return < dict > self._prod.dict() def _fraction_pair(self): """ @@ -822,7 +851,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): x + y + z + y^-1 """ cdef LaurentPolynomial_mpair ans = self._new_c() - cdef LaurentPolynomial_mpair right = _right + cdef LaurentPolynomial_mpair right = _right ans._mon, a, b = self._mon.combine_to_positives(right._mon) if not a.is_constant(): ans._poly = self._poly * self._poly._parent({a: 1}) @@ -847,7 +876,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): -y - z + y^-1 """ cdef LaurentPolynomial_mpair ans = self._new_c() - cdef LaurentPolynomial_mpair right = _right + cdef LaurentPolynomial_mpair right = _right cdef ETuple a, b ans._mon, a, b = self._mon.combine_to_positives(right._mon) if not a.is_constant(): @@ -883,7 +912,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): sage: (s+q)/(q^2*t^(-2)) s*q^-2*t^2 + q^-1*t^2 """ - cdef LaurentPolynomial_mpair right = rhs + cdef LaurentPolynomial_mpair right = rhs if right.is_zero(): raise ZeroDivisionError if right._poly.is_term(): @@ -972,8 +1001,8 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): x*y + x*z + 1 + y^-1*z """ cdef LaurentPolynomial_mpair ans = self._new_c() - ans._mon = self._mon.eadd((right)._mon) - ans._poly = self._poly * (right)._poly + ans._mon = self._mon.eadd(( < LaurentPolynomial_mpair > right)._mon) + ans._poly = self._poly * ( < LaurentPolynomial_mpair > right)._poly return ans cpdef _floordiv_(self, right) noexcept: @@ -1012,7 +1041,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): b + 1 """ cdef LaurentPolynomial_mpair ans = self._new_c() - cdef LaurentPolynomial_mpair rightl = right + cdef LaurentPolynomial_mpair rightl = right self._normalize() rightl._normalize() ans._mon = self._mon.esub(rightl._mon) @@ -1064,8 +1093,8 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): selfl._poly = self._poly selfl._mon = self._mon cdef LaurentPolynomial_mpair rightl = self._new_c() - rightl._poly = ( right)._poly - rightl._mon = ( right)._mon + rightl._poly = (< LaurentPolynomial_mpair > right)._poly + rightl._mon = (< LaurentPolynomial_mpair > right)._mon selfl._normalize() rightl._normalize() @@ -1098,15 +1127,15 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): """ if self._prod is None: self._compute_polydict() - if ( right)._prod is None: - ( right)._compute_polydict() + if (< LaurentPolynomial_mpair > right)._prod is None: + (< LaurentPolynomial_mpair > right)._compute_polydict() try: sortkey = self._parent.term_order().sortkey except AttributeError: sortkey = None - return self._prod.rich_compare((right)._prod, + return self._prod.rich_compare(( < LaurentPolynomial_mpair > right)._prod, op, sortkey) def exponents(self): @@ -1143,7 +1172,7 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): if not x: return self._poly.total_degree() + sum(self._mon) - cdef tuple g = self._parent.gens() + cdef tuple g = self._parent.gens() cdef Py_ssize_t i cdef bint no_generator_found = True for i in range(len(g)): @@ -1581,14 +1610,14 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): u = self.parent(pf.unit()) - cdef tuple g = self._parent.gens() + cdef tuple g = self._parent.gens() for i in self._mon.nonzero_positions(): u *= g[i] ** self._mon[i] cdef list f = [] cdef dict d for t in pf: - d = (t[0].dict()) + d = (t[0].dict()) if len(d) == 1: # monomials are units u *= self.parent(d) ** t[1] else: @@ -1700,14 +1729,14 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): val *= d[i]**v[i] df[v] = R(h(val)) - ans = self._new_c() + ans = self._new_c() ans._prod = PolyDict(df) ans._mon = self._mon if new_ring is None: S = self._poly._parent else: S = self._poly._parent.change_ring(R) - ans._poly = S({v.esub(ans._mon): df[v] for v in df}) + ans._poly = S({v.esub(ans._mon): df[v] for v in df}) if new_ring is not None: return new_ring(ans) return ans @@ -1763,8 +1792,8 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): x = 0 for i in range(n): if not mat.get_is_zero_unsafe(j, i): - x += ( v[i]) * int(mat.get_unsafe(j, i)) - if x < ( mon[j]): + x += (< int > v[i]) * int(mat.get_unsafe(j, i)) + if x < (< int > mon[j]): mon[j] = x exp[j] = x dr[ETuple(exp)] = d[v] @@ -1773,10 +1802,10 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): for v in dr: dr[v] = self._parent._base(h(dr[v])) - ans = self._new_c() + ans = self._new_c() ans._prod = PolyDict(dr) ans._mon = ETuple(mon) - ans._poly = self._poly._parent({v.esub(ans._mon): dr[v] for v in dr}) + ans._poly = self._poly._parent({v.esub(ans._mon): dr[v] for v in dr}) if new_ring is not None: return new_ring(ans) return ans @@ -1847,10 +1876,10 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): S = self._poly._parent else: S = self._poly._parent.change_ring(new_ring._base) - ans = self._new_c() + ans = self._new_c() ans._prod = PolyDict(dr) ans._mon = mon - ans._poly = S({v.esub(ans._mon): dr[v] for v in dr}) + ans._poly = S({v.esub(ans._mon): dr[v] for v in dr}) if new_ring is not None: return new_ring(ans) return ans diff --git a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py index c10cbb219b0..bbb6b9e1fc9 100644 --- a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py +++ b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py @@ -25,6 +25,7 @@ from sage.rings.infinity import infinity +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.ring import CommutativeRing from sage.structure.parent import Parent @@ -61,6 +62,13 @@ def __init__(self, R): self._one_element = self.element_class(self, R.one()) CommutativeRing.__init__(self, R.base_ring(), names=names, category=R.category()) + ernames = [] + for n in names: + ernames.append(n) + ernames.append(n + "inv") + ER = PolynomialRing(R.base_ring(), ernames) + self._extended_ring = ER + self._extended_ring_ideal = ER.ideal([ER.gen(2*i)*ER.gen(2*i+1)-1 for i in range(self._n)]) def ngens(self): """ diff --git a/src/sage/topology/simplicial_set.py b/src/sage/topology/simplicial_set.py index 3ee14ae2da8..e33465f1153 100644 --- a/src/sage/topology/simplicial_set.py +++ b/src/sage/topology/simplicial_set.py @@ -294,6 +294,7 @@ class AbstractSimplex_class(SageObject): Users should not call this directly, but instead use :func:`AbstractSimplex`. See that function for more documentation. """ + def __init__(self, dim, degeneracies=(), underlying=None, name=None, latex_name=None): """ @@ -3202,6 +3203,7 @@ class SimplicialSet_finite(SimplicialSet_arbitrary, GenericCellComplex): sage: X Y """ + def __init__(self, data, base_point=None, name=None, check=True, category=None, latex_name=None): r"""