def _naive_ideal(self, ring): r""" Return the "naive" subideal. INPUT: - ``ring`` -- the ambient ring of the ideal. OUTPUT: A subideal of the toric ideal in the polynomial ring ``ring``. EXAMPLES:: sage: A = matrix([[1,1,1],[0,1,2]]) sage: IA = ToricIdeal(A) sage: IA.ker() Free module of degree 3 and rank 1 over Integer Ring User basis matrix: [ 1 -2 1] sage: IA._naive_ideal(IA.ring()) Ideal (-z1^2 + z0*z2) of Multivariate Polynomial Ring in z0, z1, z2 over Rational Field """ x = ring.gens() binomials = [] for row in self.ker().matrix().rows(): xpos = prod(x[i]**max( row[i],0) for i in range(0,len(x))) xneg = prod(x[i]**max(-row[i],0) for i in range(0,len(x))) binomials.append(xpos - xneg) return ring.ideal(binomials)
def all_vv_verifs(self, u, v, dim): a,b = set(u).union(set(v)) a1,b1 = symbolic_max_plus_matrices_band(dim, 2, 'v', 'v', typ='sym') d1 = {a:a1, b:b1} a2,b2 = symbolic_max_plus_matrices_band(dim, 2, 'v', 'v', typ='full') d2 = {a:a2, b:b2} mu1 = prod(d1[i] for i in u) mu2 = prod(d2[i] for i in u) mv1 = prod(d1[i] for i in v) mv2 = prod(d2[i] for i in v) is_identity = is_vv_identity(u,v,dim) self.assertEqual(is_identity, mu1 == mv1, 'u = {} v = {} dim = {}'.format(u,v,dim)) self.assertEqual(mu1 == mv1, mu2 == mv2, 'u = {} v = {} dim = {}'.format(u,v,dim)) if is_identity: elts = [random_integer_max_plus_matrices_band( dim, -1000, 1000, ord('v'), ord('v')) \ for _ in range(100)] zo = {a:0, b:1} t0 = tuple(zo[i] for i in u) t1 = tuple(zo[i] for i in v) self.assertTrue(is_relation(t0,t1, elts, True), 'u = {} v = {} dim = {}'.format(u,v,dim)) self.assertTrue(is_relation(t0,t1,elts,False), 'u = {} v = {} dim = {}'.format(u,v,dim))
def d_vector(self): n = self.parent().rk one = self.parent().ambient_field()(1) factors = self.lift_to_field().factor() initial = [] non_initial = [] [(initial if x[1] > 0 and len(x[0].monomials()) == 1 else non_initial).append(x[0]**x[1]) for x in factors] initial = prod(initial+[one]).numerator() non_initial = prod(non_initial+[one]).denominator() v1 = vector(non_initial.exponents()[0][:n]) v2 = vector(initial.exponents()[0][:n]) return tuple(v1-v2)
def scalar(self, y): r""" Return the scalar product of ``self`` and ``y``. The scalar product is given by .. MATH:: (I_{\lambda}, I_{\mu}) = \delta_{\lambda,\mu} \frac{1}{a_{\lambda}}, where `a_{\lambda}` is given by .. MATH:: a_{\lambda} = q^{|\lambda| + 2 n(\lambda)} \prod_k \prod_{i=1}^{l_k} (1 - q^{-i}) where `n(\lambda) = \sum_i (i - 1) \lambda_i` and `\lambda = (1^{l_1}, 2^{l_2}, \ldots, m^{l_m})`. Note that `a_{\lambda}` can be interpreted as the number of automorphisms of a certain object in a category corresponding to `\lambda`. See Lemma 2.8 in [Schiffmann]_ for details. EXAMPLES:: sage: R.<q> = ZZ[] sage: H = HallAlgebra(R, q) sage: H[1].scalar(H[1]) 1/(q - 1) sage: H[2].scalar(H[2]) 1/(q^2 - q) sage: H[2,1].scalar(H[2,1]) 1/(q^5 - 2*q^4 + q^3) sage: H[1,1,1,1].scalar(H[1,1,1,1]) 1/(q^16 - q^15 - q^14 + 2*q^11 - q^8 - q^7 + q^6) sage: H.an_element().scalar(H.an_element()) (4*q^2 + 9)/(q^2 - q) """ q = self.parent()._q f = lambda la: ~( q**(sum(la) + 2*la.weighted_size()) * prod(prod((1 - q**-i) for i in range(1,k+1)) for k in la.to_exp()) ) y = self.parent()(y) ret = q.parent().zero() for mx, cx in self: cy = y.coefficient(mx) if cy != 0: ret += cx * cy * f(mx) return ret
def defining_polynomials(self): """ Return the pair of products of cyclotomic polynomials. EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp sage: Hyp(alpha_beta=([1/4,3/4],[0,0])).defining_polynomials() (x^2 + 1, x^2 - 2*x + 1) """ up = prod(cyclotomic_polynomial(d) for d in self._cyclo_up) down = prod(cyclotomic_polynomial(d) for d in self._cyclo_down) return (up, down)
def braid(self, n, K=QQ, names=None): r""" The braid arrangement. INPUT: - ``n`` -- integer - ``K`` -- field (default: ``QQ``) - ``names`` -- tuple of strings or ``None`` (default); the variable names for the ambient space OUTPUT: The hyperplane arrangement consisting of the `n(n-1)/2` hyperplanes `\{ x_i - x_j = 0 : 1 \leq i \leq j \leq n \}`. EXAMPLES:: sage: hyperplane_arrangements.braid(4) Arrangement of 6 hyperplanes of dimension 4 and rank 3 """ x = polygen(QQ, 'x') A = self.graphical(graphs.CompleteGraph(n), K, names=names) charpoly = prod(x-i for i in range(n)) A.characteristic_polynomial.set_cache(charpoly) return A
def e_one_star_patch(self, v, n): r""" Return the n-th iterated patch of normal vector v. INPUT: - ``v`` -- vector, the normal vector - ``n`` -- integer EXAMPLES:: sage: from slabbe.mult_cont_frac import FullySubtractive sage: FullySubtractive().e_one_star_patch((1,e,pi), 4) Patch of 21 faces """ from sage.combinat.e_one_star import E1Star, Patch, Face from sage.misc.misc_c import prod if v is None: v = (random(), random(), random()) it = self.coding_iterator(v) keys = [next(it) for _ in range(n)] D = self.dual_substitutions() L = prod(D[key] for key in reversed(keys)) dual_sub = E1Star(L) cube = Patch([Face((1,0,0),1), Face((0,1,0),2), Face((0,0,1),3)]) return dual_sub(cube)
def quantum_determinant(self, u=None): """ Return the quantum determinant of ``self``. The quantum determinant is defined by: .. MATH:: \operatorname{qdet}(u) = \sum_{\sigma \in S_n} (-1)^{\sigma} \prod_{k=1}^n T_{\sigma(k),k}(u - k + 1). EXAMPLES:: sage: Y = Yangian(QQ, 2, 2) sage: Y.quantum_determinant() u^4 + (-2 + t(1)[1,1] + t(1)[2,2])*u^3 + (1 - t(1)[1,1] + t(1)[1,1]*t(1)[2,2] - t(1)[1,2]*t(1)[2,1] - 2*t(1)[2,2] + t(2)[1,1] + t(2)[2,2])*u^2 + (-t(1)[1,1]*t(1)[2,2] + t(1)[1,1]*t(2)[2,2] + t(1)[1,2]*t(1)[2,1] - t(1)[1,2]*t(2)[2,1] - t(1)[2,1]*t(2)[1,2] + t(1)[2,2] + t(1)[2,2]*t(2)[1,1] - t(2)[1,1] - t(2)[2,2])*u - t(1)[1,1]*t(2)[2,2] + t(1)[1,2]*t(2)[2,1] + t(2)[1,1]*t(2)[2,2] - t(2)[1,2]*t(2)[2,1] + t(2)[2,2] """ if u is None: u = PolynomialRing(self.base_ring(), 'u').gen(0) from sage.combinat.permutation import Permutations n = self._n return sum(p.sign() * prod(self.defining_polynomial(p[k], k+1, u - k) for k in range(n)) for p in Permutations(n))
def Polya(G, poids): """ Implantation de la formule d'énumération de Pòlya INPUT: - ``G`` -- un groupe de permutations d'un ensemble `E` - ``poids`` -- la liste des poids `w(c)` des éléments `c` d'un ensemble `F` Cette fonction renvoie la série génératrice par poids des fonctions de `E` dans `F`: .. math:: \sum_{f\in E^F} \prod_{e\in E} w(f(e)) EXAMPLES: On calcule le nombre de colliers bicolores à rotation près:: sage: Polya(CyclicPermutationGroup(5), [1,1]) 8 Même chose, raffinée par nombre de perles d'une couleur donnée:: sage: q = QQ['q'].gen() sage: Polya(CyclicPermutationGroup(5), [1,q]) q^5 + q^4 + 2*q^3 + 2*q^2 + q + 1 .. TODO:: Rajouter ici les autres exemples! """ return sum(prod( p(k, poids) for k in type_cyclique(sigma)) for sigma in G) / G.cardinality()
def homogenous_symmetric_function(j, x): r""" Return a complete homogeneous symmetric polynomial (:wikipedia:`Complete_homogeneous_symmetric_polynomial`). INPUT: - ``j`` -- the degree as a nonnegative integer - ``x`` -- an iterable of variables OUTPUT: A polynomial of the common parent of all entries of ``x`` EXAMPLES:: sage: from sage.rings.polynomial.omega import homogenous_symmetric_function sage: P = PolynomialRing(ZZ, 'X', 3) sage: homogenous_symmetric_function(0, P.gens()) 1 sage: homogenous_symmetric_function(1, P.gens()) X0 + X1 + X2 sage: homogenous_symmetric_function(2, P.gens()) X0^2 + X0*X1 + X1^2 + X0*X2 + X1*X2 + X2^2 sage: homogenous_symmetric_function(3, P.gens()) X0^3 + X0^2*X1 + X0*X1^2 + X1^3 + X0^2*X2 + X0*X1*X2 + X1^2*X2 + X0*X2^2 + X1*X2^2 + X2^3 """ from sage.combinat.integer_vector import IntegerVectors from sage.misc.misc_c import prod return sum(prod(xx**pp for xx, pp in zip(x, p)) for p in IntegerVectors(j, length=len(x)))
def sum_of_partitions(self, la): r""" Return the sum over all sets partitions whose shape is ``la``, scaled by `\prod_i m_i!` where `m_i` is the multiplicity of `i` in ``la``. INPUT: - ``la`` -- an integer partition OUTPUT: - an element of ``self`` EXAMPLES:: sage: w = SymmetricFunctionsNonCommutingVariables(QQ).dual().w() sage: w.sum_of_partitions([2,1,1]) 2*w{{1}, {2}, {3, 4}} + 2*w{{1}, {2, 3}, {4}} + 2*w{{1}, {2, 4}, {3}} + 2*w{{1, 2}, {3}, {4}} + 2*w{{1, 3}, {2}, {4}} + 2*w{{1, 4}, {2}, {3}} """ la = Partition(la) c = prod([factorial(_) for _ in la.to_exp()]) P = SetPartitions() return self.sum_of_terms([(P(m), c) for m in SetPartitions(sum(la), la)], distinct=True)
def _homogeneous_generators_noncommutative_variables_zero_Hecke(self, r): r""" Returns the ``r^{th}`` homogeneous generator, viewed as an element inside the affine zero Hecke algebra. This is the sum of all cyclicly decreasing elements of order ``r``. INPUT: - ``r`` -- A positive integer OUTPUT: - An element of the affine zero Hecke algebra. EXAMPLES:: sage: g = SymmetricFunctions(QQ).kBoundedSubspace(3,1).K_kschur() sage: g._homogeneous_generators_noncommutative_variables_zero_Hecke(2) T1*T0 + T2*T0 + T0*T3 + T3*T2 + T3*T1 + T2*T1 sage: g._homogeneous_generators_noncommutative_variables_zero_Hecke(0) 1 """ from sage.combinat.root_system.weyl_group import WeylGroup from sage.algebras.iwahori_hecke_algebra import IwahoriHeckeAlgebraT W = WeylGroup(['A',self.k,1]) H = IwahoriHeckeAlgebraT(W, 0, base_ring = self.base_ring()) Hgens = H.algebra_generators() S = [w.reduced_word() for w in W.pieri_factors() if w.length() == r] return sum( (prod((Hgens[i] for i in w), 1) for w in S), 0 )
def bch_to_grs(self): r""" Returns the underlying GRS code from which ``self`` was derived. EXAMPLES:: sage: C = codes.BCHCode(GF(2), 15, 3) sage: RS = C.bch_to_grs() sage: RS [15, 13, 3] Reed-Solomon Code over GF(16) sage: C.generator_matrix() * RS.parity_check_matrix().transpose() == 0 True """ l = self.jump_size() b = self.offset() n = self.length() designed_distance = self.designed_distance() grs_dim = n - designed_distance + 1 alpha = self.primitive_root() alpha_l = alpha ** l alpha_b = alpha ** b evals = [alpha_l ** i for i in range(n)] pcm = [alpha_b ** i for i in range(n)] multipliers_product = [1/prod([evals[i] - evals[h] for h in range(n) if h != i]) for i in range(n)] column_multipliers = [multipliers_product[i]/pcm[i] for i in range(n)] return GeneralizedReedSolomonCode(evals, grs_dim, column_multipliers)
def pure_tensor(P, vects, indices=None): r""" Return the product of a collection of linear forms corresponding to vectors INPUT: - ``P`` -- a polynomial ring - ``vects`` -- a list of coefficient vectors - ``indices`` -- (default: ``None``) a collection of indices for the list ``vects`` corresponding to the linear forms which will be included in the product. Indices may be repeated. If ``None``, then each vector will be included in the product once. OUTPUT: - the corresponding product of linear forms in the polynomial ring ``P`` EXAMPLES: sage: P.<x, y> = PolynomialRing(QQ) sage: vects = [[1,0],[0,1],[1,1]] sage: pure_tensor(P, vects).factor() y * x * (x + y) sage: pure_tensor(P, vects, [0, 2, 2]).factor() x * (x + y)^2 """ if indices is None: indices = range(len(vects)) terms = [linear_form(P, vects[i]) for i in indices] return prod(terms, P.one())
def _convert_factors_(self, factors): r""" Helper method. Try to convert some ``factors`` to an element of one of the Cartesian factors and return the product of all these factors. INPUT: - ``factors`` -- a tuple or other iterable. OUTPUT: An element of this Cartesian product. EXAMPLES:: sage: from sage.rings.asymptotic.growth_group import GrowthGroup sage: G = GrowthGroup('x^ZZ * log(x)^QQ * y^QQ') sage: e1 = G._convert_factors_([x^2]) sage: (e1, e1.parent()) (x^2, Growth Group x^ZZ * log(x)^QQ * y^QQ) """ from sage.misc.misc_c import prod def get_factor(data): for factor in self.cartesian_factors(): try: return factor, factor(data) except (ValueError, TypeError): pass raise ValueError("%s is not in any of the factors of %s" % (data, self)) return prod(self.cartesian_injection(*get_factor(f)) for f in factors)
def m_to_s_stat(R, I, K): r""" Returns the statistic for the expansion of the Monomial basis element indexed by two compositions, as in formula (36) of Tevlin's "Noncommutative Analogs of Monomial Symmetric Functions, Cauchy Identity, and Hall Scalar Product". INPUT: - ``R`` -- A ring - ``I``, ``K`` -- compositions OUTPUT: - An integer EXAMPLES:: sage: from sage.combinat.ncsf_qsym.combinatorics import m_to_s_stat sage: m_to_s_stat(QQ,Composition([2,1]), Composition([1,1,1])) -1 sage: m_to_s_stat(QQ,Composition([3]), Composition([1,2])) -2 """ stat = 0 for J in Compositions(I.size()): if (I.is_finer(J) and K.is_finer(J)): pvec = [0] + Composition(I).refinement_splitting_lengths(J).partial_sums() pp = prod( R( len(I) - pvec[i] ) for i in range( len(pvec)-1 ) ) stat += R((-1)**(len(I)-len(K)) / pp * coeff_lp(K,J)) return stat
def tree_factorial(self): """ Returns the tree-factorial of ``self`` Definition: The tree-factorial `T!` of a tree `T` is the product `\prod_{v\in T}\#\mbox{children}(v)`. EXAMPLES:: sage: LT = LabelledOrderedTrees() sage: t = LT([LT([],label=6),LT([],label=1)],label=9) sage: t.tree_factorial() 3 sage: BinaryTree([[],[[],[]]]).tree_factorial() 15 TESTS:: sage: BinaryTree().tree_factorial() 1 """ nb = self.node_number() if nb <= 1: return 1 else: return nb*prod(s.tree_factorial() for s in self)
def is_symmetric(self): r""" Determine if a `NCSym^*` function, expressed in the `\mathbf{w}` basis, is symmetric. A function `f` in the `\mathbf{w}` basis is a symmetric function if it is in the image of `\chi^*`. That is to say we have .. MATH:: f = \sum_{\lambda} c_{\lambda} \prod_i m_i(\lambda)! \sum_{\lambda(A) = \lambda} \mathbf{w}_A where the second sum is over all set partitions `A` whose shape `\lambda(A)` is equal to `\lambda` and `m_i(\mu)` is the multiplicity of `i` in the partition `\mu`. OUTPUT: - ``True`` if `\lambda(A)=\lambda(B)` implies the coefficients of `\mathbf{w}_A` and `\mathbf{w}_B` are equal, ``False`` otherwise EXAMPLES:: sage: w = SymmetricFunctionsNonCommutingVariables(QQ).dual().w() sage: elt = w.sum_of_partitions([2,1,1]) sage: elt.is_symmetric() True sage: elt -= 3*w.sum_of_partitions([1,1]) sage: elt.is_symmetric() True sage: w = SymmetricFunctionsNonCommutingVariables(ZZ).dual().w() sage: elt = w.sum_of_partitions([2,1,1]) / 2 sage: elt.is_symmetric() False sage: elt = w[[1,3],[2]] sage: elt.is_symmetric() False sage: elt = w[[1],[2,3]] + w[[1,2],[3]] + 2*w[[1,3],[2]] sage: elt.is_symmetric() False """ d = {} R = self.base_ring() for A, coeff in self: la = A.shape() exp = prod([factorial(_) for _ in la.to_exp()]) if la not in d: if coeff / exp not in R: return False d[la] = [coeff, 1] else: if d[la][0] != coeff: return False d[la][1] += 1 # Make sure we've seen each set partition of the shape return all(d[la][1] == SetPartitions(la.size(), la).cardinality() for la in d)
def Ish(self, n, K=QQ, names=None): r""" Return the Ish arrangement. INPUT: - ``n`` -- integer - ``K`` -- field (default:``QQ``) - ``names`` -- tuple of strings or ``None`` (default); the variable names for the ambient space OUTPUT: The Ish arrangement, which is the set of `n(n-1)` hyperplanes. .. MATH:: \{ x_i - x_j = 0 : 1 \leq i \leq j \leq n \} \cup \{ x_1 - x_j = i : 1 \leq i \leq j \leq n \}. EXAMPLES:: sage: a = hyperplane_arrangements.Ish(3); a Arrangement of 6 hyperplanes of dimension 3 and rank 2 sage: a.characteristic_polynomial() x^3 - 6*x^2 + 9*x sage: b = hyperplane_arrangements.Shi(3) sage: b.characteristic_polynomial() x^3 - 6*x^2 + 9*x TESTS:: sage: a.characteristic_polynomial.clear_cache() # long time sage: a.characteristic_polynomial() # long time x^3 - 6*x^2 + 9*x REFERENCES: .. [AR] D. Armstrong, B. Rhoades "The Shi arrangement and the Ish arrangement" :arxiv:`1009.1655` """ H = make_parent(K, n, names) x = H.gens() hyperplanes = [] for i in range(n): for j in range(i+1, n): hyperplanes.append(x[i] - x[j]) hyperplanes.append(x[0] - x[j] - (i+1)) A = H(*hyperplanes) x = polygen(QQ, 'x') charpoly = x * sum([(-1)**k * stirling_number2(n, n-k) * prod([(x - 1 - j) for j in range(k, n-1)]) for k in range(0, n)]) A.characteristic_polynomial.set_cache(charpoly) return A
def __init__(self, params, asym=False): # set parameters (self.alpha, self.beta, self.rho, self.rho_f, self.eta, self.bound, self.n, self.k) = params self.x0 = ZZ(1) self.primes = [random_prime(2**self.eta, lbound = 2**(self.eta - 1), proof=False) for i in range(self.n)] primes = self.primes self.x0 = prod(primes) # generate CRT coefficients self.coeff = [ZZ((self.x0/p_i) * ZZ(Zmod(p_i)(self.x0/p_i)**(-1))) for p_i in primes] # generate secret g_i self.g = [random_prime(2**self.alpha, proof=False) for i in range(self.n)] # generate zs and zs^(-1) z = [] zinv = [] # generate z and z^(-1) if not asym: while True: z = ZZ.random_element(self.x0) try: zinv = ZZ(Zmod(self.x0)(z)**(-1)) break except ZeroDivisionError: ''' Error occurred, retry sampling ''' z, self.zinv = zip(*[(z,zinv) for i in range(self.k)]) else: # asymmetric version for i in range(self.k): while True: z_i = ZZ.random_element(self.x0) try: zinv_i = ZZ(Zmod(self.x0)(z_i)**(-1)) break except ZeroDivisionError: ''' Error occurred, retry sampling ''' z.append(z_i) zinv.append(zinv_i) self.zinv = zinv # generate p_zt zk = Zmod(self.x0)(1) self.p_zt = 0 for z_i in z: zk *= Zmod(self.x0)(z_i) for i in range(self.n): self.p_zt += Zmod(self.x0)(ZZ(Zmod(self.primes[i])(self.g[i])**(-1) * Zmod(self.primes[i])(zk)) * ZZ.random_element(2**self.beta) * (self.x0/self.primes[i])) self.p_zt = Zmod(self.x0)(self.p_zt)
def tropical_evaluation(f): try: # This is an hack to use the same code on polynomials, laurent polynomials and rational expressions f = f.parent().fraction_field()(f) num_exponents = f.numerator().exponents() if type(num_exponents[0]) != int: num_exponent = map(min, zip(*num_exponents)) else: num_exponent = [min(num_exponents)] den_exponents = f.denominator().exponents() if type(den_exponents[0]) != int: den_exponent = map(min, zip(*den_exponents)) else: den_exponent = [min(den_exponents)] variables = f.parent().gens() return prod(map(lambda x,p: x**p, variables,num_exponent))*prod(map(lambda x,p: x**(-p), variables,den_exponent)) except: # This should only happen when f is a constant return 1
def lee_osullivan_module(points, parameters, wy): r""" Returns the analytically straight-forward basis for the `\GF q[x]` module containing all interpolation polynomials, as according to Lee and O'Sullivan. The module is constructed in the following way: Let `R(x)` be the Lagrange interpolation polynomial through the sought interpolation points `(x_i, y_i)`, i.e. `R(x_i) = y_i`. Let `G(x) = \prod_{i=1}^n (x-x_i)`. Then the `i`'th row of the basis matrix of the module is the coefficient-vector of the following polynomial in `\GF q[x][y]`: `P_i(x,y) = G(x)^{[i-s]} (y - R(x))^{i - [i-s]} y^{[i-s]}` , where `[a]` for real `a` is `a` when `a > 0` and 0 otherwise. It is easily seen that `P_i(x,y)` is an interpolation polynomial, i.e. it is zero with multiplicity at least `s` on each of the points `(x_i, y_i)`. INPUT: - ``points`` -- a list of tuples ``(xi, yi)`` such that we seek ``Q`` with ``(xi,yi)`` being a root of ``Q`` with multiplicity ``s``. - ``parameters`` -- (default: ``None``) a pair of integers, where: - the first integer is the multiplicity parameter `s` of Guruswami-Sudan algorithm and - the second integer is the list size parameter. - ``wy`` -- an integer, the `y`-weight, where we seek ``Q`` of low ``(1,wy)`` weighted degree. EXAMPLES:: sage: from sage.coding.guruswami_sudan.interpolation import lee_osullivan_module sage: F = GF(11) sage: points = [(F(0), F(2)), (F(1), F(5)), (F(2), F(0)), (F(3), F(4)), (F(4), F(9))\ , (F(5), F(1)), (F(6), F(9)), (F(7), F(10))] sage: params = (1, 1) sage: wy = 1 sage: lee_osullivan_module(points, params, wy) [x^8 + 5*x^7 + 3*x^6 + 9*x^5 + 4*x^4 + 2*x^3 + 9*x 0] [ 10*x^7 + 4*x^6 + 9*x^4 + 7*x^3 + 2*x^2 + 9*x + 9 1] """ s, l = parameters[0], parameters[1] F = points[0][0].parent() PF = F['x'] x = PF.gens()[0] R = PF.lagrange_polynomial(points) G = prod(x - points[i][0] for i in range(0, len(points))) PFy = PF['y'] y = PFy.gens()[0] ybasis = [(y-R)**i * G**(s-i) for i in range(0, s+1)] \ + [y**(i-s) * (y-R)**s for i in range(s+1, l+1)] def pad(lst): return lst + [0]*(l+1-len(lst)) modbasis = [pad(yb.coefficients(sparse=False)) for yb in ybasis] return matrix(PF, modbasis)
def cluster_variable(self, g_vector): g_vector = tuple(g_vector) if not g_vector in self.g_vectors_so_far(): # Should we let the self.F_polynomial below handle raising the exception? raise ValueError("This Cluster Variable has not been computed yet.") F_std = self.F_polynomial(g_vector).subs(self._yhat) g_mon = prod([self.ambient().gen(i)**g_vector[i] for i in xrange(self.rk)]) # LaurentPolynomial_mpair does not know how to compute denominators, we need to lift to its fraction field F_trop = self.ambient_field()(self.F_polynomial(g_vector).subs(self._y)).denominator() return self.retract(g_mon*F_std*F_trop)
def Y(self, j): r""" The j-th element of the Y-pattern in the universal coefficient semifield C.f. CA4 definition 3.10 Uses CA4 Proposition 3.13 """ Y = prod(map(lambda y,c: y**c, self.parent()._U.gens(), self.c_vector(j))) for i in range(self.parent()._n): Y *= self.F_polynomial(i)**self.b_matrix()[i,j] return self.parent()._U.fraction_field()(Y)
def logpp_binom(n, p, p_prec): """returns the (integral) power series p^n*(log_p(1+p*z)/log_p(1+p) choose n)""" #prod=1+0*z L = logpp_gam(p, p_prec) ans = prod([(L - j) for j in range(n)]) #for j in range(0,n): # prod=prod*(L-j) ans *= (p ** n) / factorial(n) return ps_normalize(ans.truncate(p_prec), p, p_prec)
def encode(self, m, S): ''' encodes a vector m (in Zmod(q)^n) to index set S ''' zinv = prod([self.zinv[i] for i in S]) m = vector(Zmod(self.q),m) zero = vector(Zmod(self.q),self.D_sigmap_I()) # random encoding of 0 c = self.Rq(list(zero + m)) return c * zinv
def on_basis(A): k = A.size() ret = R.zero() if n < k: return ret for p in Permutations(k): if P(p.to_cycles()) == A: # -1 for indexing ret += R.sum(prod(x[I[i]][I[p[i] - 1]] for i in range(k)) for I in Subsets(range(n), k)) return ret
def logp_binom(n, p, p_prec): """returns the (integral) power series (log_p(1+z)/log_p(1+p) choose n)""" #prod=1+0*z if n == 0: return PolynomialRing(QQ, 'y')(1) L = logp_gam(p, p_prec) ans = prod([(L - j) for j in range(n)]) #for j in range(0,n): # prod=prod*(L-j) ans = ans / factorial(n) return ps_normalize(ans.truncate(p_prec+1), p, p_prec) #Do we need the +1?
def index(self): r""" Return the index of self in the full modular group. This is equal to the index in `SL(2, \ZZ / N\ZZ)` of the image of this group modulo `\Gamma(N)`. EXAMPLE:: sage: sage.modular.arithgroup.congroup_generic.CongruenceSubgroupFromGroup(MatrixGroup([matrix(Zmod(2), 2, [1,1,1,0])])).index() 2 """ return prod([p**(3*e-2)*(p*p-1) for (p,e) in self.level().factor()]) // self.image_mod_n().order()
def encode(self, m, S): ''' encodes a vector m (in ZZ^n) to index set S ''' c = Zmod(self.x0)(0) for i in range(self.n): r_i = ZZ.random_element(2**self.rho) c += Zmod(self.x0)((m[i] + self.g[i] * r_i) * self.coeff[i]) zinv = prod([self.zinv[i] for i in S]) return Zmod(self.x0)(c * zinv)
def homogeneous_noncommutative_variables(self, la): r""" Give the homogeneous function indexed by `la`, viewed inside the Nil-Coxeter algebra. This is only defined in finite type `A`, `B` and affine types `A^{(1)}`, `B^{(1)}`, `C^{(1)}`, `D^{(1)}`. INPUT: - ``la`` -- a partition with first part bounded by the rank of the Weyl group EXAMPLES:: sage: U = NilCoxeterAlgebra(WeylGroup(['B',2,1])) sage: U.homogeneous_noncommutative_variables([2,1]) u[1,2,0] + 2*u[2,1,0] + u[0,2,0] + u[0,2,1] + u[1,2,1] + u[2,1,2] + u[2,0,2] + u[1,0,2] TESTS:: sage: U = NilCoxeterAlgebra(WeylGroup(['B',2,1])) sage: U.homogeneous_noncommutative_variables([]) 1 """ return prod( self.homogeneous_generator_noncommutative_variables(p) for p in la)
def nu2(self): r""" Return the number of elliptic points of order 2 for this congruence subgroup `\Gamma_0(N)`. The number of these is given by a standard formula: 0 if `N` is divisible by 4 or any prime congruent to -1 mod 4, and otherwise `2^d` where d is the number of odd primes dividing `N`. EXAMPLES:: sage: Gamma0(2).nu2() 1 sage: Gamma0(4).nu2() 0 sage: Gamma0(21).nu2() 0 sage: Gamma0(1105).nu2() 8 sage: [Gamma0(n).nu2() for n in [1..19]] [1, 1, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 2, 0, 0] """ n = self.level() if n % 4 == 0: return ZZ(0) return prod([1 + kronecker_symbol(-4, p) for p, _ in n.factor()])
def m_to_s_stat(R, I, K): r""" Return the coefficient of the complete non-commutative symmetric function `S^K` in the expansion of the monomial non-commutative symmetric function `M^I` with respect to the complete basis over the ring `R`. This is the coefficient in formula (36) of Tevlin's paper [Tev2007]_. INPUT: - ``R`` -- A ring, supposed to be a `\QQ`-algebra - ``I``, ``K`` -- compositions OUTPUT: - The coefficient of `S^K` in the expansion of `M^I` in the complete basis of the non-commutative symmetric functions over ``R``. EXAMPLES:: sage: from sage.combinat.ncsf_qsym.combinatorics import m_to_s_stat sage: m_to_s_stat(QQ, Composition([2,1]), Composition([1,1,1])) -1 sage: m_to_s_stat(QQ, Composition([3]), Composition([1,2])) -2 sage: m_to_s_stat(QQ, Composition([2,1,2]), Composition([2,1,2])) 8/3 """ stat = 0 for J in Compositions(I.size()): if (I.is_finer(J) and K.is_finer(J)): pvec = [0] + Composition(I).refinement_splitting_lengths(J).partial_sums() pp = prod( R( len(I) - pvec[i] ) for i in range( len(pvec)-1 ) ) stat += R((-1)**(len(I)-len(K)) / pp * coeff_lp(K,J)) return stat
def _span_of_forms_in_weight(forms, weight, prec, stop_dim=None, use_random=False): r""" Utility function. Given a nonempty list of pairs ``(k,f)``, where `k` is an integer and `f` is a power series, and a weight l, return all weight l forms obtained by multiplying together the given forms. INPUT: - ``forms`` -- list of pairs `(k, f)` with k an integer and f a power series (all over the same base ring) - ``weight`` -- an integer - ``prec`` -- an integer (less than or equal to the precision of all the forms in ``forms``) -- precision to use in power series computations. - ``stop_dim`` -- an integer: stop as soon as we have enough forms to span a submodule of this rank (a saturated one if the base ring is `\ZZ`). Ignored if ``use_random`` is False. - ``use_random`` -- which algorithm to use. If True, tries random products of the generators of the appropriate weight until a large enough submodule is found (determined by ``stop_dim``). If False, just tries everything. Note that if the given forms do generate the whole space, then ``use_random=True`` will often be quicker (particularly if the weight is large); but if the forms don't generate, the randomized algorithm is no help and will actually be substantially slower, because it needs to do repeated echelon form calls to check if vectors are in a submodule, while the non-randomized algorithm just echelonizes one enormous matrix at the end. EXAMPLES:: sage: import sage.modular.modform.ring as f sage: forms = [(4, 240*eisenstein_series_qexp(4,5)), (6,504*eisenstein_series_qexp(6,5))] sage: f._span_of_forms_in_weight(forms, 12, prec=5) Vector space of degree 5 and dimension 2 over Rational Field Basis matrix: [ 1 0 196560 16773120 398034000] [ 0 1 -24 252 -1472] sage: f._span_of_forms_in_weight(forms, 24, prec=5) Vector space of degree 5 and dimension 3 over Rational Field Basis matrix: [ 1 0 0 52416000 39007332000] [ 0 1 0 195660 12080128] [ 0 0 1 -48 1080] sage: ModularForms(1, 24).q_echelon_basis(prec=5) [ 1 + 52416000*q^3 + 39007332000*q^4 + O(q^5), q + 195660*q^3 + 12080128*q^4 + O(q^5), q^2 - 48*q^3 + 1080*q^4 + O(q^5) ] Test the alternative randomized algorithm:: sage: f._span_of_forms_in_weight(forms, 24, prec=5, use_random=True, stop_dim=3) Vector space of degree 5 and dimension 3 over Rational Field Basis matrix: [ 1 0 0 52416000 39007332000] [ 0 1 0 195660 12080128] [ 0 0 1 -48 1080] """ t = verbose('multiplying forms up to weight %s' % weight) # Algorithm: run through the monomials of the appropriate weight, and build # up the vector space they span. n = len(forms) R = forms[0][1].base_ring() V = R**prec W = V.zero_submodule() shortforms = [f[1].truncate_powerseries(prec) for f in forms] # List of weights from sage.combinat.integer_vector_weighted import WeightedIntegerVectors wts = list(WeightedIntegerVectors(weight, [f[0] for f in forms])) t = verbose("calculated weight list", t) N = len(wts) if use_random: if stop_dim is None: raise ValueError("stop_dim must be provided if use_random is True") shuffle(wts) for c in range(N): w = V( prod(shortforms[i]**wts[c][i] for i in range(n)).padded_list(prec)) if w in W: continue W = V.span(list(W.gens()) + [w]) if stop_dim and W.rank() == stop_dim: if R != ZZ or W.index_in_saturation() == 1: verbose("Succeeded after %s of %s" % (c, N), t) return W verbose("Nothing worked", t) return W else: G = [ V(prod(forms[i][1]**c[i] for i in range(n)).padded_list(prec)) for c in wts ] t = verbose('found %s candidates' % N, t) W = V.span(G) verbose('span has dimension %s' % W.rank(), t) return W
def attack(m, q, r=4, sigma=3.0, subfield_only=False): K = CyclotomicField(m, 'z') z = K.gen() OK = K.ring_of_integers() G = K.galois_group() n = euler_phi(m) mprime = m / r nprime = euler_phi(mprime) Gprime = [tau for tau in G if tau(z**r) == z**r] R = PolynomialRing(IntegerRing(), 'a') a = R.gen() phim = a**n + 1 D = DiscreteGaussianDistributionIntegerSampler(sigma) print "sampling f,g" while True: f = sum([D() * z**i for i in range(n)]) fx = sum([f[i] * a**i for i in range(n)]) res = inverse(fx, phim, q) if res[0]: f_inv = sum([res[1][i] * z**i for i in range(n)]) print "f_inv * f = %s (mod %d)" % ((f * f_inv).mod(q), q) break g = sum([D() * z**i for i in range(n)]) print "done sampling f, g" #h = [g*f^{-1)]_q h = (g * f_inv).mod(q) lognorm_f = log(f.vector().norm(), 2) lognorm_g = log(g.vector().norm(), 2) print "f*h - g = %s" % (f * h - g).mod(q) print "log q = ", log(q, 2).n(precision) print "log |f| = %s, log |g| = %s" % (lognorm_f.n(precision), lognorm_g.n(precision)) print "log |(f,g)| = ", log( sqrt(f.vector().norm()**2 + g.vector().norm()**2), 2).n(precision) print "begin computing N(f), N(g), N(h), Tr(h), fbar" fprime = norm(f, Gprime) gprime = norm(g, Gprime) hprime = norm(h, Gprime).mod(q) htr = trace(h, Gprime) fbar = prod([tau(f) for tau in Gprime[1:]]) print "end computing N(f), N(g), N(h), Tr(h), fbar" lognorm_fp = log(fprime.vector().norm(), 2) lognorm_gp = log(gprime.vector().norm(), 2) print "%d * log |f| - log |f'| = %s" % (r, r * lognorm_f.n(precision) - lognorm_fp.n(precision)) print "log |(f', g')| = ", log( sqrt(fprime.vector().norm()**2 + gprime.vector().norm()**2), 2).n(precision) print "log |N(f), Tr(g fbar)| = ", log( sqrt(fprime.vector().norm()**2 + trace(g * fbar, Gprime).vector().norm()**2), 2).n(precision) #(fprime, gprime) lies in the lattice \Lambda_hprime^q print "f'*h' - g' = %s " % (hprime * fprime - gprime).mod(q) print "N(f) Tr(h) - Tr(g fbar) = %s" % (htr * fprime - trace(g * fbar, Gprime)).mod(q) if not subfield_only: ntru_full = NTRU(h, K, q) full_sv = ntru_full.shortest_vector() print "log |v| = %s" % log(full_sv.norm(), 2).n(precision) ntru_subfield = NTRU_subfield(hprime, q, nprime, r) ntru_trace_subfield = NTRU_subfield(htr, q, nprime, r) print "begin computing Shortest Vector of subfield lattice" norm_sv = ntru_subfield.shortest_vector() tr_sv = ntru_trace_subfield.shortest_vector() print "end computing Shortest Vector of subfield lattice" norm_xp = sum( [coerce(Integer, norm_sv[i]) * z**(r * i) for i in range(nprime)]) tr_xp = sum( [coerce(Integer, tr_sv[i]) * z**(r * i) for i in range(nprime)]) print "Norm map: log |(x',y')| = ", log(norm_sv.norm(), 2).n(precision) print "Trace map: log |(x', y')| = ", log(tr_sv.norm(), 2).n(precision) #test if xprime belongs to <fprime> mat = [] for i in range(nprime): coordinate = (fprime * z**(r * i)).vector().list() mat.append([coordinate[r * j] for j in range(nprime)]) FL = IntegerLattice(mat) print norm_sv[:nprime] in FL print tr_sv[:nprime] in FL norm_x = norm_xp norm_y = mod_q(norm_x * h, q) tr_x = tr_xp tr_y = mod_q(tr_x * h, q) print "Norm map: log |(x,y)| = ", log( sqrt(norm_x.vector().norm()**2 + norm_y.vector().norm()**2), 2).n(precision) print "Trace map: log |(x,y)| = ", log( sqrt(tr_x.vector().norm()**2 + tr_y.vector().norm()**2), 2).n(precision)
def sample(self, S): # draw an element of Rq from a Gaussian distribution of Z^n (with param sigmaprime) # then encode at index set S return self.D_sigmap_ZZ() * prod([self.zinv[i] for i in S])
def affine_algebraic_patch(self, cone=None, names=None): r""" Return the affine patch corresponding to ``cone`` as an affine algebraic scheme. INPUT: - ``cone`` -- a :class:`Cone <sage.geometry.cone.ConvexRationalPolyhedralCone>` `\sigma` of the fan. It can be omitted for an affine toric variety, in which case the single generating cone is used. OUTPUT: An :class:`affine algebraic subscheme <sage.schemes.affine.affine_subscheme.AlgebraicScheme_subscheme_affine>` corresponding to the patch `\mathop{Spec}(\sigma^\vee \cap M)` associated to the cone `\sigma`. See also :meth:`affine_patch`, which expresses the patches as subvarieties of affine toric varieties instead. REFERENCES: .. David A. Cox, "The Homogeneous Coordinate Ring of a Toric Variety", Lemma 2.2. :arxiv:`alg-geom/9210008v2` EXAMPLES:: sage: P2.<x,y,z> = toric_varieties.P2() sage: cone = P2.fan().generating_cone(0) sage: V = P2.subscheme(x^3+y^3+z^3) sage: V.affine_algebraic_patch(cone) Closed subscheme of Affine Space of dimension 2 over Rational Field defined by: z0^3 + z1^3 + 1 sage: cone = Cone([(0,1),(2,1)]) sage: A2Z2.<x,y> = AffineToricVariety(cone) sage: A2Z2.affine_algebraic_patch() Closed subscheme of Affine Space of dimension 3 over Rational Field defined by: -z0*z1 + z2^2 sage: V = A2Z2.subscheme(x^2+y^2-1) sage: patch = V.affine_algebraic_patch(); patch Closed subscheme of Affine Space of dimension 3 over Rational Field defined by: -z0*z1 + z2^2, z0 + z1 - 1 sage: nbhd_patch = V.neighborhood([1,0]).affine_algebraic_patch(); nbhd_patch Closed subscheme of Affine Space of dimension 3 over Rational Field defined by: -z0*z1 + z2^2, z0 + z1 - 1 sage: nbhd_patch.embedding_center() (0, 1, 0) Here we got two defining equations. The first one describes the singularity of the ambient space and the second is the pull-back of `x^2+y^2-1` :: sage: lp = LatticePolytope([(1,0,0),(1,1,0),(1,1,1),(1,0,1),(-2,-1,-1)], ....: lattice=ToricLattice(3)) sage: X.<x,y,u,v,t> = CPRFanoToricVariety(Delta_polar=lp) sage: Y = X.subscheme(x*v+y*u+t) sage: cone = Cone([(1,0,0),(1,1,0),(1,1,1),(1,0,1)]) sage: Y.affine_algebraic_patch(cone) Closed subscheme of Affine Space of dimension 4 over Rational Field defined by: z0*z2 - z1*z3, z1 + z3 + 1 """ from sage.modules.free_module_element import vector from sage.misc.misc_c import prod ambient = self.ambient_space() fan = ambient.fan() if cone is None: assert ambient.is_affine() cone = fan.generating_cone(0) else: cone = fan.embed(cone) # R/I = C[sigma^dual cap M] R, I, dualcone = ambient._semigroup_ring(cone, names) # inhomogenize the Cox homogeneous polynomial with respect to the given cone inhomogenize = dict((ambient.coordinate_ring().gen(i), 1) for i in range(0, fan.nrays()) if i not in cone.ambient_ray_indices()) polynomials = [ p.subs(inhomogenize) for p in self.defining_polynomials() ] # map the monomial x^{D_m} to m, see reference. n_rho_matrix = cone.rays().matrix() def pullback_polynomial(p): result = R.zero() for coefficient, monomial in p: exponent = monomial.exponents()[0] exponent = [exponent[i] for i in cone.ambient_ray_indices()] exponent = vector(ZZ, exponent) m = n_rho_matrix.solve_right(exponent) assert all(x in ZZ for x in m), \ 'The polynomial '+str(p)+' does not define a ZZ-divisor!' m_coeffs = dualcone.Hilbert_coefficients(m) result += coefficient * prod( R.gen(i)**m_coeffs[i] for i in range(0, R.ngens())) return result # construct the affine algebraic scheme to use as patch polynomials = [pullback_polynomial(_) for _ in polynomials] from sage.schemes.affine.affine_space import AffineSpace patch_cover = AffineSpace(R) polynomials = list(I.gens()) + polynomials polynomials = [x for x in polynomials if not x.is_zero()] patch = patch_cover.subscheme(polynomials) # TODO: If the cone is not smooth, then the coordinate_ring() # of the affine toric variety is wrong; it should be the # G-invariant part. So we can't construct the embedding # morphism in that case. if cone.is_smooth(): x = ambient.coordinate_ring().gens() phi = [] for i in range(0, fan.nrays()): if i in cone.ambient_ray_indices(): phi.append(pullback_polynomial(x[i])) else: phi.append(1) patch._embedding_morphism = patch.hom(phi, self) else: patch._embedding_morphism = ( NotImplementedError, 'I only know how to construct embedding morphisms for smooth patches' ) try: point = self.embedding_center() except AttributeError: return patch # it remains to find the preimage of point # map m to the monomial x^{D_m}, see reference. F = ambient.coordinate_ring().fraction_field() image = [] for m in dualcone.Hilbert_basis(): x_Dm = prod([F.gen(i)**(m * n) for i, n in enumerate(fan.rays())]) image.append(x_Dm) patch._embedding_center = tuple(f(list(point)) for f in image) return patch
def __init__(self, cyclotomic=None, alpha_beta=None, gamma_list=None): r""" Creation of hypergeometric motives. INPUT: three possibilities are offered, each describing a quotient of products of cyclotomic polynomials. - ``cyclotomic`` -- a pair of lists of nonnegative integers, each integer `k` represents a cyclotomic polynomial `\Phi_k` - ``alpha_beta`` -- a pair of lists of rationals, each rational represents a root of unity - ``gamma_list`` -- a pair of lists of nonnegative integers, each integer `n` represents a polynomial `x^n - 1` In the last case, it is also allowed to send just one list of signed integers where signs indicate to which part the integer belongs to. EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp sage: Hyp(cyclotomic=([2],[1])) Hypergeometric data for [1/2] and [0] sage: Hyp(alpha_beta=([1/2],[0])) Hypergeometric data for [1/2] and [0] sage: Hyp(alpha_beta=([1/5,2/5,3/5,4/5],[0,0,0,0])) Hypergeometric data for [1/5, 2/5, 3/5, 4/5] and [0, 0, 0, 0] sage: Hyp(gamma_list=([5],[1,1,1,1,1])) Hypergeometric data for [1/5, 2/5, 3/5, 4/5] and [0, 0, 0, 0] sage: Hyp(gamma_list=([5,-1,-1,-1,-1,-1])) Hypergeometric data for [1/5, 2/5, 3/5, 4/5] and [0, 0, 0, 0] """ if gamma_list is not None: if isinstance(gamma_list[0], (list, tuple)): pos, neg = gamma_list gamma_list = pos + [-u for u in neg] cyclotomic = gamma_list_to_cyclotomic(gamma_list) if cyclotomic is not None: cyclo_up, cyclo_down = cyclotomic if any(x in cyclo_up for x in cyclo_down): raise ValueError('overlapping parameters not allowed') deg = sum(euler_phi(x) for x in cyclo_down) up_deg = sum(euler_phi(x) for x in cyclo_up) if up_deg != deg: msg = 'not the same degree: {} != {}'.format(up_deg, deg) raise ValueError(msg) cyclo_up.sort() cyclo_down.sort() alpha = cyclotomic_to_alpha(cyclo_up) beta = cyclotomic_to_alpha(cyclo_down) elif alpha_beta is not None: alpha, beta = alpha_beta if len(alpha) != len(beta): raise ValueError('alpha and beta not of the same length') alpha = sorted(u - floor(u) for u in alpha) beta = sorted(u - floor(u) for u in beta) cyclo_up = alpha_to_cyclotomic(alpha) cyclo_down = alpha_to_cyclotomic(beta) deg = sum(euler_phi(x) for x in cyclo_down) self._cyclo_up = tuple(cyclo_up) self._cyclo_down = tuple(cyclo_down) self._alpha = tuple(alpha) self._beta = tuple(beta) self._deg = deg self._gamma_array = cyclotomic_to_gamma(cyclo_up, cyclo_down) self._trace_coeffs = {} up = QQ.prod(capital_M(d) for d in cyclo_up) down = QQ.prod(capital_M(d) for d in cyclo_down) self._M_value = up / down if 0 in alpha: self._swap = HypergeometricData(alpha_beta=(beta, alpha)) if self.weight() % 2: self._sign_param = 1 else: if (deg % 2) != (0 in alpha): self._sign_param = prod(cyclotomic_polynomial(v).disc() for v in cyclo_down) else: self._sign_param = prod(cyclotomic_polynomial(v).disc() for v in cyclo_up)
def _sage_(self): """ EXAMPLES:: sage: m = lie('[[1,0,3,3],[12,4,-4,7],[-1,9,8,0],[3,-5,-2,9]]') # optional - lie sage: m.sage() # optional - lie [ 1 0 3 3] [12 4 -4 7] [-1 9 8 0] [ 3 -5 -2 9] """ t = self.type() if t == 'grp': raise ValueError( "cannot convert Lie groups to native Sage objects") elif t == 'mat': import sage.matrix.constructor data = sage_eval(str(self).replace('\n', '').strip()) return sage.matrix.constructor.matrix(data) elif t == 'pol': from sage.rings.all import PolynomialRing, QQ # Figure out the number of variables s = str(self) open_bracket = s.find('[') close_bracket = s.find(']') nvars = len(s[open_bracket:close_bracket].split(',')) # create the polynomial ring R = PolynomialRing(QQ, nvars, 'x') x = R.gens() pol = R.zero() # Split up the polynomials into terms terms = [] for termgrp in s.split(' - '): # The first entry in termgrp has # a negative coefficient termgrp = "-" + termgrp.strip() terms += termgrp.split('+') # Make sure we don't accidentally add a negative # sign to the first monomial if s[0] != "-": terms[0] = terms[0][1:] # go through all the terms in s for term in terms: xpos = term.find('X') coef = eval(term[:xpos].strip()) exps = eval(term[xpos + 1:].strip()) monomial = prod([x[i]**exps[i] for i in range(nvars)]) pol += coef * monomial return pol elif t == 'tex': return repr(self) elif t == 'vid': return None else: return ExpectElement._sage_(self)
def points(self, **kwds): r""" Return some or all rational points of a projective scheme. Over a finite field, all points are returned. Over an infinite field, all points satisfying the bound are returned. For a zero-dimensional subscheme, all points are returned regardless of whether the base ring is a field or not. For number fields, this uses the Doyle-Krumm algorithm 4 (algorithm 5 for imaginary quadratic) for computing algebraic numbers up to a given height [DK2013]_ or uses the chinese remainder theorem and points modulo primes for larger bounds. The algorithm requires floating point arithmetic, so the user is allowed to specify the precision for such calculations. Additionally, due to floating point issues, points slightly larger than the bound may be returned. This can be controlled by lowering the tolerance. INPUT: - ``bound`` - a real number - ``tolerance`` - a rational number in (0,1] used in doyle-krumm algorithm-4 - ``precision`` - the precision to use for computing the elements of bounded height of number fields. - ``algorithm`` - either 'sieve' or 'enumerate' algorithms can be used over `QQ`. If not specified, enumerate is used only for small height bounds. OUTPUT: - a list of rational points of a projective scheme EXAMPLES:: sage: P.<x,y,z,w> = ProductProjectiveSpaces([1, 1], QQ) sage: X = P.subscheme([x - y, z^2 - 2*w^2]) sage: X(P.base_ring()).points() [] :: sage: u = QQ['u'].0 sage: P.<x,y,z,w> = ProductProjectiveSpaces([1,1], NumberField(u^2 - 2, 'v')) sage: X = P.subscheme([x^2 - y^2, z^2 - 2*w^2]) sage: sorted(X(P.base_ring()).points()) [(-1 : 1 , -v : 1), (-1 : 1 , v : 1), (1 : 1 , -v : 1), (1 : 1 , v : 1)] :: sage: u = QQ['u'].0 sage: K = NumberField(u^2 + 1, 'v') sage: P.<x,y,z,w> = ProductProjectiveSpaces([1, 1], K) sage: P(K).points(bound=1) [(-1 : 1 , -1 : 1), (-1 : 1 , -v : 1), (-1 : 1 , 0 : 1), (-1 : 1 , v : 1), (-1 : 1 , 1 : 0), (-1 : 1 , 1 : 1), (-v : 1 , -1 : 1), (-v : 1 , -v : 1), (-v : 1 , 0 : 1), (-v : 1 , v : 1), (-v : 1 , 1 : 0), (-v : 1 , 1 : 1), (0 : 1 , -1 : 1), (0 : 1 , -v : 1), (0 : 1 , 0 : 1), (0 : 1 , v : 1), (0 : 1 , 1 : 0), (0 : 1 , 1 : 1), (v : 1 , -1 : 1), (v : 1 , -v : 1), (v : 1 , 0 : 1), (v : 1 , v : 1), (v : 1 , 1 : 0), (v : 1 , 1 : 1), (1 : 0 , -1 : 1), (1 : 0 , -v : 1), (1 : 0 , 0 : 1), (1 : 0 , v : 1), (1 : 0 , 1 : 0), (1 : 0 , 1 : 1), (1 : 1 , -1 : 1), (1 : 1 , -v : 1), (1 : 1 , 0 : 1), (1 : 1 , v : 1), (1 : 1 , 1 : 0), (1 : 1 , 1 : 1)] :: sage: P.<x,y,z,u,v> = ProductProjectiveSpaces([2, 1], GF(3)) sage: P(P.base_ring()).points() [(0 : 0 : 1 , 0 : 1), (0 : 0 : 1 , 1 : 0), (0 : 0 : 1 , 1 : 1), (0 : 0 : 1 , 2 : 1), (0 : 1 : 0 , 0 : 1), (0 : 1 : 0 , 1 : 0), (0 : 1 : 0 , 1 : 1), (0 : 1 : 0 , 2 : 1), (0 : 1 : 1 , 0 : 1), (0 : 1 : 1 , 1 : 0), (0 : 1 : 1 , 1 : 1), (0 : 1 : 1 , 2 : 1), (0 : 2 : 1 , 0 : 1), (0 : 2 : 1 , 1 : 0), (0 : 2 : 1 , 1 : 1), (0 : 2 : 1 , 2 : 1), (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 : 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), (2 : 1 : 1 , 0 : 1), (2 : 1 : 1 , 1 : 0), (2 : 1 : 1 , 1 : 1), (2 : 1 : 1 , 2 : 1), (2 : 2 : 1 , 0 : 1), (2 : 2 : 1 , 1 : 0), (2 : 2 : 1 , 1 : 1), (2 : 2 : 1 , 2 : 1)] :: sage: PP.<x,y,z,u,v> = ProductProjectiveSpaces([2,1], QQ) sage: X = PP.subscheme([x + y, u*u-v*u]) sage: X.rational_points(bound=2) [(-2 : 2 : 1 , 0 : 1), (-2 : 2 : 1 , 1 : 1), (-1 : 1 : 0 , 0 : 1), (-1 : 1 : 0 , 1 : 1), (-1 : 1 : 1 , 0 : 1), (-1 : 1 : 1 , 1 : 1), (-1/2 : 1/2 : 1 , 0 : 1), (-1/2 : 1/2 : 1 , 1 : 1), (0 : 0 : 1 , 0 : 1), (0 : 0 : 1 , 1 : 1), (1/2 : -1/2 : 1 , 0 : 1), (1/2 : -1/2 : 1 , 1 : 1), (1 : -1 : 1 , 0 : 1), (1 : -1 : 1 , 1 : 1), (2 : -2 : 1 , 0 : 1), (2 : -2 : 1 , 1 : 1)] better to enumerate with low codimension:: sage: PP.<x,y,z,u,v,a,b,c> = ProductProjectiveSpaces([2,1,2], QQ) sage: X = PP.subscheme([x*u^2*a, b*z*u*v,z*v^2*c ]) sage: len(X.rational_points(bound=1, algorithm='enumerate')) 232 """ B = kwds.pop('bound', 0) X = self.codomain() from sage.schemes.product_projective.space import is_ProductProjectiveSpaces if not is_ProductProjectiveSpaces(X) and X.base_ring() in Fields(): # no points if X.dimension() == -1: return [] # if X is zero-dimensional if X.dimension() == 0: points = set() # find points from all possible affine patches for I in xmrange([n + 1 for n in X.ambient_space().dimension_relative_components()]): [Y,phi] = X.affine_patch(I, True) aff_points = Y.rational_points() for PP in aff_points: points.add(phi(PP)) return list(points) R = self.value_ring() points = [] if is_RationalField(R): if not B > 0: raise TypeError("a positive bound B (= %s) must be specified"%B) alg = kwds.pop('algorithm', None) if alg is None: # sieve should only be called for subschemes and if the bound is not very small N = prod([k+1 for k in X.ambient_space().dimension_relative_components()]) if isinstance(X, AlgebraicScheme_subscheme) and B**N > 5000: from sage.schemes.product_projective.rational_point import sieve return sieve(X, B) else: from sage.schemes.product_projective.rational_point import enum_product_projective_rational_field return enum_product_projective_rational_field(self, B) elif alg == 'sieve': from sage.schemes.product_projective.rational_point import sieve return sieve(X, B) elif alg == 'enumerate': from sage.schemes.product_projective.rational_point import enum_product_projective_rational_field return enum_product_projective_rational_field(self, B) else: raise ValueError("algorithm must be 'sieve' or 'enumerate'") elif R in NumberFields(): if not B > 0: raise TypeError("a positive bound B (= %s) must be specified"%B) from sage.schemes.product_projective.rational_point import enum_product_projective_number_field return enum_product_projective_number_field(self, bound=B) elif is_FiniteField(R): from sage.schemes.product_projective.rational_point import enum_product_projective_finite_field return enum_product_projective_finite_field(self) else: raise TypeError("unable to enumerate points over %s" % R)
def to_ideal(exps): if len(exps)==0: return ring.zero_ideal() gens = [prod([v ** e for v, e in zip(ring.gens(), expo) if e != 0]) for expo in exps] return ring.ideal(gens or ring(1))
def covariant_z0(F, z0_cov=False, prec=53, emb=None, error_limit=0.000001): r""" Return the covariant and Julia invariant from Cremona-Stoll [CS2003]_. In [CS2003]_ and [HS2018]_ the Julia invariant is denoted as `\Theta(F)` or `R(F, z(F))`. Note that you may get faster convergence if you first move `z_0(F)` to the fundamental domain before computing the true covariant INPUT: - ``F`` -- binary form of degree at least 3 with no multiple roots - ``z0_cov`` -- boolean, compute only the `z_0` invariant. Otherwise, solve the minimization problem - ``prec``-- positive integer. precision to use in CC - ``emb`` -- embedding into CC - ``error_limit`` -- sets the error tolerance (default:0.000001) OUTPUT: a complex number, a real number EXAMPLES:: sage: from sage.rings.polynomial.binary_form_reduce import covariant_z0 sage: R.<x,y> = QQ[] sage: F = 19*x^8 - 262*x^7*y + 1507*x^6*y^2 - 4784*x^5*y^3 + 9202*x^4*y^4\ ....: - 10962*x^3*y^5 + 7844*x^2*y^6 - 3040*x*y^7 + 475*y^8 sage: covariant_z0(F, prec=80, z0_cov=True) (1.3832330115323681438175 + 0.31233552177413614978744*I, 3358.4074848663492819259) sage: F = -x^8 + 6*x^7*y - 7*x^6*y^2 - 12*x^5*y^3 + 27*x^4*y^4\ ....: - 4*x^3*y^5 - 19*x^2*y^6 + 10*x*y^7 - 5*y^8 sage: covariant_z0(F, prec=80) (0.64189877107807122203366 + 1.1852516565091601348355*I, 3134.5148284344627168276) :: sage: R.<x,y> = QQ[] sage: covariant_z0(x^3 + 2*x^2*y - 3*x*y^2, z0_cov=True)[0] 0.230769230769231 + 0.799408065031789*I sage: -1/covariant_z0(-y^3 + 2*y^2*x + 3*y*x^2, z0_cov=True)[0] 0.230769230769231 + 0.799408065031789*I :: sage: R.<x,y> = QQ[] sage: covariant_z0(2*x^2*y - 3*x*y^2, z0_cov=True)[0] 0.750000000000000 + 1.29903810567666*I sage: -1/covariant_z0(-x^3 - x^2*y + 2*x*y^2, z0_cov=True)[0] + 1 0.750000000000000 + 1.29903810567666*I :: sage: R.<x,y> = QQ[] sage: covariant_z0(x^2*y - x*y^2, prec=100) # tol 1e-28 (0.50000000000000000000000000003 + 0.86602540378443864676372317076*I, 1.5396007178390020386910634147) TESTS:: sage: R.<x,y>=QQ[] sage: covariant_z0(x^2 + 24*x*y + y^2) Traceback (most recent call last): ... ValueError: must be at least degree 3 sage: covariant_z0((x+y)^3, z0_cov=True) Traceback (most recent call last): ... ValueError: cannot have multiple roots for z0 invariant sage: covariant_z0(x^3 + 3*x*y + y) Traceback (most recent call last): ... TypeError: must be a binary form sage: covariant_z0(-2*x^2*y^3 + 3*x*y^4 + 127*y^5) Traceback (most recent call last): ... ValueError: cannot have a root with multiplicity >= 5/2 sage: covariant_z0((x^2+2*y^2)^2) Traceback (most recent call last): ... ValueError: must have at least 3 distinct roots """ R = F.parent() d = ZZ(F.degree()) if R.ngens() != 2 or any(sum(t) != d for t in F.exponents()): raise TypeError('must be a binary form') if d < 3: raise ValueError('must be at least degree 3') f = F.subs({R.gen(1): 1}).univariate_polynomial() if f.degree() < d: # we have a root at infinity if f.constant_coefficient() != 0: # invert so we find all roots! mat = matrix(ZZ, 2, 2, [0, -1, 1, 0]) else: t = 0 while f(t) == 0: t += 1 mat = matrix(ZZ, 2, 2, [t, -1, 1, 0]) else: mat = matrix(ZZ, 2, 2, [1, 0, 0, 1]) f = F(list(mat * vector(R.gens()))).subs({ R.gen(1): 1 }).univariate_polynomial() # now we have a single variable polynomial with all the roots of F K = ComplexField(prec=prec) if f.base_ring() != K: if emb is None: f = f.change_ring(K) else: f = f.change_ring(emb) roots = f.roots() if max(ex for _, ex in roots) > 1 or f.degree() < d - 1: if z0_cov: raise ValueError('cannot have multiple roots for z0 invariant') else: # just need a starting point for Newton's method f = f.lc() * prod([p for p, ex in f.factor() ]) # removes multiple roots if f.degree() < 3: raise ValueError('must have at least 3 distinct roots') roots = f.roots() roots = [p for p, _ in roots] # finding quadratic Q_0, gives us our covariant, z_0 dF = f.derivative() n = ZZ(f.degree()) PR = PolynomialRing(K, 'x,y') x, y = PR.gens() # finds Stoll and Cremona's Q_0 q = sum([(1/(dF(r).abs()**(2/(n-2)))) * ((x-(r*y)) * (x-(r.conjugate()*y)))\ for r in roots]) # this is Q_0 , always positive def as long as F has distinct roots A = q.monomial_coefficient(x**2) B = q.monomial_coefficient(x * y) C = q.monomial_coefficient(y**2) # need positive root try: z = ((-B + ((B**2) - (4 * A * C)).sqrt()) / (2 * A)) except ValueError: raise ValueError("not enough precision") if z.imag() < 0: z = (-B - ((B**2) - (4 * A * C)).sqrt()) / (2 * A) if z0_cov: FM = f # for Julia's invariant else: # solve the minimization problem for 'true' covariant CF = ComplexIntervalField( prec=prec) # keeps trac of our precision error z = CF(z) FM = F(list(mat * vector(R.gens()))).subs({ R.gen(1): 1 }).univariate_polynomial() from sage.rings.polynomial.complex_roots import complex_roots L1 = complex_roots(FM, min_prec=prec) L = [] err = z.diameter() # making sure multiplicity isn't too large using convergence conditions in paper for p, e in L1: if e >= d / 2: raise ValueError( 'cannot have a root with multiplicity >= %s/2' % d) for _ in range(e): L.append(p) RCF = PolynomialRing(CF, 'u,t') a = RCF(0) c = RCF(0) u, t = RCF.gens() for l in L: a += u**2 / ((t - l) * (t - l.conjugate()) + u**2) c += (t - l.real()) / ((t - l) * (t - l.conjugate()) + u**2) # Newton's Method, to find solutions. Error bound is less than diameter of our z err = z.diameter() zz = z.diameter() g1 = a.numerator() - d / 2 * a.denominator() g2 = c.numerator() G = vector([g1, g2]) J = jacobian(G, [u, t]) v0 = vector([z.imag(), z.real()]) # z0 as starting point # finds our correct z while err <= zz: NJ = J.subs({u: v0[0], t: v0[1]}) NJinv = NJ.inverse() # inverse for CIF matrix seems to return fractions not CIF elements, fix them if NJinv.base_ring() != CF: NJinv = matrix(CF, 2, 2, [ CF(zw.numerator() / zw.denominator()) for zw in NJinv.list() ]) w = z v0 = v0 - NJinv * G.subs({u: v0[0], t: v0[1]}) z = v0[1].constant_coefficient( ) + v0[0].constant_coefficient() * CF.gen(0) err = z.diameter() # precision zz = (w - z).abs() # difference in w and z else: if err > error_limit or err.is_NaN(): raise ValueError( "accuracy of Newton's root not within tolerance(%s > %s), increase precision" % (err, error_limit)) if z.imag() <= z.diameter(): raise ArithmeticError( "Newton's method converged to z not in the upper half plane") z = z.center() # Julia's invariant if FM.base_ring() != ComplexField(prec=prec): FM = FM.change_ring(ComplexField(prec=prec)) tF = z.real() uF = z.imag() th = FM.lc().abs()**2 for r, ex in FM.roots(): for _ in range(ex): th = th * ((((r - tF).abs())**2 + uF**2) / uF) # undo shift and invert (if needed) # since F \cdot m ~ m^(-1)\cdot z # we apply m to z to undo m acting on F l = mat * vector([z, 1]) return l[0] / l[1], th
def matprod(elts): return prod(reversed(elts), id_mat)
def diffop(word): dlog = [Rat(log(a).diff().canonicalize_radical()) for a in word] factors = [(Dx - sum(dlog[:i])) for i in range(len(dlog))] dop = prod(reversed([(Dx - sum(dlog[:i])) for i in range(len(dlog) + 1)])) dop = dop.numerator() return dop
def to_matrix(self, side="right", on_space="primal"): r""" Return ``self`` as a matrix acting on the underlying vector space. - ``side`` -- optional (default: ``"right"``) whether the action of ``self`` is on the ``"left"`` or on the ``"right"`` - ``on_space`` -- optional (default: ``"primal"``) whether to act as the reflection representation on the given basis, or to act on the dual reflection representation on the dual basis EXAMPLES:: sage: W = ReflectionGroup(['A',2]) # optional - gap3 sage: for w in W: # optional - gap3 ....: w.reduced_word() # optional - gap3 ....: [w.to_matrix(), w.to_matrix(on_space="dual")] # optional - gap3 [] [ [1 0] [1 0] [0 1], [0 1] ] [2] [ [ 1 1] [ 1 0] [ 0 -1], [ 1 -1] ] [1] [ [-1 0] [-1 1] [ 1 1], [ 0 1] ] [1, 2] [ [-1 -1] [ 0 -1] [ 1 0], [ 1 -1] ] [2, 1] [ [ 0 1] [-1 1] [-1 -1], [-1 0] ] [1, 2, 1] [ [ 0 -1] [ 0 -1] [-1 0], [-1 0] ] TESTS:: sage: W = ReflectionGroup(['F',4]) # optional - gap3 sage: all(w.to_matrix(side="left") == W.from_reduced_word(reversed(w.reduced_word())).to_matrix(side="right").transpose() for w in W) # optional - gap3 True sage: all(w.to_matrix(side="right") == W.from_reduced_word(reversed(w.reduced_word())).to_matrix(side="left").transpose() for w in W) # optional - gap3 True """ W = self.parent() if W._reflection_representation is None: if side == "left": w = ~self elif side == "right": w = self else: raise ValueError('side must be "left" or "right"') Delta = W.independent_roots() Phi = W.roots() M = Matrix([Phi[w(Phi.index(alpha)+1)-1] for alpha in Delta]) mat = W.base_change_matrix() * M else: refl_repr = W._reflection_representation id_mat = identity_matrix(QQ, refl_repr[W.index_set()[0]].nrows()) mat = prod([refl_repr[i] for i in self.reduced_word()], id_mat) if on_space == "primal": if side == "left": mat = mat.transpose() elif on_space == "dual": if side == "left": mat = mat.inverse() else: mat = mat.inverse().transpose() else: raise ValueError('on_space must be "primal" or "dual"') mat.set_immutable() return mat
def _Q_poly(self, a, m): r""" Return the element `Q^{(a)}_m` as a polynomial. We start with the relation .. MATH:: (Q^{(a)}_{m-1})^2 = Q^{(a)}_m Q^{(a)}_{m-2} + \mathcal{Q}_{a,m-1}, which implies .. MATH:: Q^{(a)}_m = \frac{Q^{(a)}_{m-1}^2 - \mathcal{Q}_{a,m-1}}{ Q^{(a)}_{m-2}}. This becomes our relation used for reducing the Q-system to the fundamental representations. For twisted Q-systems, we use .. MATH:: (Q^{(a)}_{m-1})^2 = Q^{(a)}_m Q^{(a)}_{m-2} + \prod_{b \neq a} (Q^{(b)}_{m-1})^{-A_{ba}}. .. NOTE:: This helper method is defined in order to use the division implemented in polynomial rings. EXAMPLES:: sage: Q = QSystem(QQ, ['A',8]) sage: Q._Q_poly(1, 2) q1^2 - q2 sage: Q._Q_poly(3, 2) q3^2 - q2*q4 sage: Q._Q_poly(6, 3) q6^3 - 2*q5*q6*q7 + q4*q7^2 + q5^2*q8 - q4*q6*q8 Twisted types:: sage: Q = QSystem(QQ, ['E',6,2], twisted=True) sage: Q._Q_poly(1,2) q1^2 - q2 sage: Q._Q_poly(2,2) q2^2 - q1*q3 sage: Q._Q_poly(3,2) -q2^2*q4 + q3^2 sage: Q._Q_poly(4,2) q4^2 - q3 sage: Q._Q_poly(3,3) 2*q1*q2^2*q4^2 - q1^2*q3*q4^2 + q2^4 - 2*q1*q2^2*q3 + q1^2*q3^2 - 2*q2^2*q3*q4 + q3^3 sage: Q = QSystem(QQ, ['D',4,3], twisted=True) sage: Q._Q_poly(1,2) q1^2 - q2 sage: Q._Q_poly(2,2) -q1^3 + q2^2 sage: Q._Q_poly(1,3) q1^3 + q1^2 - 2*q1*q2 sage: Q._Q_poly(2,3) 3*q1^4 - 2*q1^3*q2 - 3*q1^2*q2 + q2^3 + q2^2 """ if m == 0 or m == self._level: return self._poly.one() if m == 1: return self._poly.gen(self._Irev[a]) cm = self._cm m -= 1 # So we don't have to do it everywhere cur = self._Q_poly(a, m)**2 if self._twisted: ret = prod( self._Q_poly(b, m)**-cm[self._Irev[b], self._Irev[a]] for b in self._cm.dynkin_diagram().neighbors(a)) else: ret = self._poly.one() i = self._Irev[a] for b in self._cm.dynkin_diagram().neighbors(a): j = self._Irev[b] for k in range(-cm[i, j]): ret *= self._Q_poly(b, (m * cm[j, i] - k) // cm[i, j]) cur -= ret if m > 1: cur //= self._Q_poly(a, m - 1) return cur
def norm(t, G): return prod([tau(t) for tau in G])
def amortized_padic_H_values(self, t, testp=None, verbose=False, use_c=False): """ INPUT: - `t` -- a rational number OUTPUT: - a dictionary with inputs `p` and outputs the corresponding p-adic H value at `t`. TESTS:: sage: for cyca, cycb, t in [ ...: ([6], [1, 1], 331), ...: ([4, 2, 2], [3, 1, 1], 3678), ...: ([22], [1, 1, 20], 1337/507734), ...: ([5],[1,1,1,1], 2313), ...: ([12],[2,2,1,1], 313) ...:]: ...: H = AmortizingHypergeometricData(1000, cyclotomic=(cyca, cycb)) ...: for p, v in H.amortized_padic_H_values(t).items(): ...: if v != H.naive_padic_H_value(t=t, p=p, verbose=False): ...: print(p, cyca, cycb, t) """ ## TODO: skip over intermediate ranges with positive p-shift #pshift = self.pshift #for last_zero, s in reversed(list(enumerate(pshift))): # if s == 0: # break #breaks = self.breaks[:last_zero+2] # also include the endpoint of the last interval #pshift = pshift[:last_zero+1] #starts = breaks[:-1] #ends = breaks[1:] # TODO: fix global sign from (-p)^eta # forests = {} tmp = identity_matrix(2) vectors = {p: tmp for p in self._prime_range(t)[1][0]} # def update(p, A): # # If the interval was empty we get A=1 # if not (isinstance(A, Integer) and A == 1): # vectors[p][0] *= A # vectors[p][1] *= A[0,0] # vectors[p] %= p def multiplier(x): return -x if x else -1 def functional_eqn(a, g, c, d): return (multiplier(a - g - c / d) if a >= g else 1) * (multiplier(a - g - c / d + 1) if a <= g else 1) # feq_seed = prod(a if a else -1 for a in self._alpha) / prod(b if b else -1 for b in self._beta) # for p in self._prime_range(t)[1][0]: # R = vectors[p].base_ring() # update(p, self.fix_break(t, ZZ(0), p, R, feq_seed)) # if p == testp: # P_start = R(t)**1 * H.pochhammer_quotient(p, 1) # assert vectors[p][1] == P_start, "brk = 0, %s != %s" % (vectors[p][1], P_start) for start, end in zip(self.starts, self.ends): d = start.denominator() for pclass in range(d): if d.gcd(pclass) != 1: continue c = (d * start * (pclass - 1)) % d feq_seed = t * prod( functional_eqn(a, start, c, d) for a in self._alpha) / prod( functional_eqn(b, start, c, d) for b in self._beta) feq_seed_num = feq_seed.numer() feq_seed_den = feq_seed.denom() if pclass == 1: pmult = self.break_mults_p1[start] else: pmult = self.break_mults[start] fixbreak = matrix( ZZ, 2, 2, [feq_seed_den, 0, feq_seed_den * pmult, feq_seed_num]) # this updates vectors self.amortized_padic_H_values_interval( vectors, t, start, end, pclass, fixbreak, testp=testp, use_c=use_c, ) return {p: (vectors[p][1, 0] / vectors[p][0, 0]) % p for p in vectors}
def _tutte_polynomial_internal(G, x, y, edge_selector, cache=None): """ Does the recursive computation of the Tutte polynomial. INPUT: - ``G`` -- the graph - ``x,y`` -- the variables `x,y` respectively - ``edge_selector`` -- the heuristic for selecting edges used in the deletion contraction recurrence TESTS:: sage: P = graphs.CycleGraph(5) sage: P.tutte_polynomial() # indirect doctest x^4 + x^3 + x^2 + x + y """ if G.num_edges() == 0: return x.parent().one() def recursive_tp(graph=None): """ The recursive call -- used so that we do not have to specify the same arguments everywhere. """ if graph is None: graph = G return _tutte_polynomial_internal(graph, x, y, edge_selector, cache=cache) #Remove loops with removed_loops(G) as loops: if loops: return y**len(loops) * recursive_tp() uG = underlying_graph(G) em = edge_multiplicities(G) d = list(em.values()) def yy(start, end): return sum(y**i for i in range(start, end + 1)) #Lemma 1 if G.is_forest(): return prod(x + yy(1, d_i - 1) for d_i in d) #Theorem 1: from Haggard, Pearce, Royle 2008 blocks, cut_vertices = G.blocks_and_cut_vertices() if len(blocks) > 1: return prod([recursive_tp(G.subgraph(block)) for block in blocks]) components = G.connected_components_number() edge = edge_selector(G) unlabeled_edge = edge[:2] with removed_edge(G, edge): if G.connected_components_number() > components: with contracted_edge(G, unlabeled_edge): return x * recursive_tp() ################################## # We are in the biconnected case # ################################## # Theorem 4: from Haggard, Pearce, and Royle Note that the formula # at http://homepages.ecs.vuw.ac.nz/~djp/files/TOMS10.pdf is # slightly incorrect. The initial sum should only go to n-2 # instead of n (allowing for the last part of the recursion). # Additionally, the first operand of the final product should be # (x+y^{1...(d_n+d_{n-1}-1)}) instead of just (x+y^(d_n+d_{n-1}-1) if uG.num_verts() == uG.num_edges(): # G is a multi-cycle n = len(d) result = 0 for i in range(n - 2): term = (prod((x + yy(1, d_j - 1)) for d_j in d[i + 1:]) * prod( (yy(0, d_k - 1)) for d_k in d[:i])) result += term #The last part of the recursion result += (x + yy(1, d[-1] + d[-2] - 1)) * prod( yy(0, d_i - 1) for d_i in d[:-2]) return result # Theorem 3 from Haggard, Pearce, and Royle, adapted to multi-ears ear = Ear.find_ear(uG) if ear is not None: if (ear.is_cycle and ear.vertices == G.vertices()): #The graph is an ear (cycle) We should never be in this #case since we check for multi-cycles above return y + sum(x**i for i in range(1, ear.s)) else: with ear.removed_from(G): #result = sum(x^i for i in range(ear.s)) #single ear case result = sum( (prod(x + yy(1, em[e] - 1) for e in ear.unlabeled_edges[i + 1:]) * prod(yy(0, em[e] - 1) for e in ear.unlabeled_edges[:i])) for i in range(len(ear.unlabeled_edges))) result *= recursive_tp() with contracted_edge(G, [ear.end_points[0], ear.end_points[-1]]): result += prod( yy(0, em[e] - 1) for e in ear.unlabeled_edges) * recursive_tp() return result #Theorem 2 if len(em) == 1: # the graph is just a multiedge return x + sum(y**i for i in range(1, em[unlabeled_edge])) else: with removed_multiedge(G, unlabeled_edge): result = recursive_tp() with contracted_edge(G, unlabeled_edge): result += sum( y**i for i in range(em[unlabeled_edge])) * recursive_tp() return result
def amortized_padic_H_values_interval(self, ans, t, start, end, pclass, fixbreak, testp=None, use_c=False): r""" Return a dictionary p -> M[p] (0, P_m0)*A[p] = A[p][0,0] (S, P_m1) where P_m = t^m \prod_i (alpha_i)_m ^* / (beta_i)_m ^* mod p, m0 = floor(start(p-1)) + 1, m1 = floor(end(p-1)), S = \sum_{m = m0} ^{m1 - 1} P_m EXAMPLES:: sage: for cyca, cycb, start, end, p, t in [ ....: ([6], [1, 1], 0, 1/6, 97, 1), ....: ([4, 2, 2], [3, 1, 1], 1/3, 1/2, 97, 1), ....: ([22], [1, 1, 20], 3/20, 5/22, 1087, 1), ....: ([22], [1, 1, 20], 3/20, 5/22, 1087, 1337/507734), ....: ([22], [1, 1, 20], 3/20, 5/22, 1019, 1337/507734)]: ....: H = AmortizingHypergeometricData(p+40, cyclotomic=(cyca, cycb)) ....: pclass = p % start.denominator() ....: shift, offset = H._starts_to_rationals[start][pclass] ....: amortized = H.amortized_padic_H_values_interval(t=t, start=start, end=end, pclass=pclass) ....: t = GF(p)(t) ....: naive_sum = 0 ....: for k in range(floor(start*(p-1))+1, floor(end * (p-1))): ....: naive_sum += t**k * H.pochhammer_quotient(p, k) ....: naive_res = vector(GF(p), (naive_sum, t**floor(end * (p-1)) * H.pochhammer_quotient(p, floor(end * (p-1) )))) ....: M = matrix(GF(p), amortized[p]) ....: res = (vector(GF(p), [0,t**(floor(start*(p-1))+1) * H.pochhammer_quotient(p, floor(start*(p-1))+1 )])*M/M[0,0]) ....: if naive_res != res: ....: print(cyca, cycb, start, end, p, t, naive_res, res) """ d = start.denominator() shift, offset = self._starts_to_rationals[start][pclass] def mbound(p): # FIXME # in practice we are getting # prod up mbound - 1 # there is something wrong with the Tree # once we fix that, we should fix the bound here return max((end * (p - 1)).floor() - (start * (p - 1)).floor(), 1) f, g, mats = self._matrices(t=t, start=start, shift=shift, offset=offset) if use_c: k = f.parent().gen() y = self.interval_mults[start] M = matrix([[g, 0], [y * g, f]]) indices = self._prime_range(t)[d][pclass] remainder_forest( M, lambda p: p, #lambda p: mbound_c(p,start,end), mbound_dict_c(indices, start, end), kbase=1, indices=indices, V=fixbreak, ans=ans) else: forest = AccRemForest( self.N, cut_functions={None: mbound}, bottom_generator=mats, prec=1, primes=self._prime_range(t)[d][pclass], ) bottom = forest.tree_bottom() # Now we have a formula for # (0, P_m0)*A[p] = A[p][0,0] (\sum_{m = m0} ^{m1 - 1} P_m, P_m1) # with # m0 = floor(start * (p-1)) + 1 # m1 = floor(end * (p-1)) if testp in bottom: print( "amortized_padic_H_values_interval(t=%s, start=%s, end=%s, pclass=%s)" % (t, start, end, pclass)) p = testp R = GF(p) if bottom[testp] != 1: M = bottom[testp].change_ring(R) # FIXME why the -1? Probably because partial_factorial doesn't include right endpoint assert M == prod( elt.change_ring(GF(R)) for elt in mats( floor(end * (p - 1)) - floor(start * (p - 1)) - 1)) t = R(t) naive_sum = 0 pmult = self.interval_mults[start] for k in range( floor(start * (p - 1)) + 1, floor(end * (p - 1))): naive_sum += t**k * self.pochhammer_quotient(p, k) naive_sum *= pmult naive_res = vector( R, (naive_sum, t**floor(end * (p - 1)) * self.pochhammer_quotient(p, floor(end * (p - 1))))) res = vector(R, [ 0, t**(floor(start * (p - 1)) + 1) * self.pochhammer_quotient(p, floor(start * (p - 1)) + 1) ]) * M / M[0, 0] assert naive_res == res, "%s != %s, M = %s" % (naive_res, res, M) # set ans for k, M in bottom.items(): ans[k] = ans[k] * fixbreak * M
def divisor_of_order(self): """ Return a divisor of the order of this torsion subgroup of a modular abelian variety. OUTPUT: A divisor of this torsion subgroup. EXAMPLES:: sage: t = J0(37)[1].rational_torsion_subgroup() sage: t.divisor_of_order() 3 sage: J = J1(19) sage: J.rational_torsion_subgroup().divisor_of_order() 4383 sage: J = J0(45) sage: J.rational_cusp_subgroup().order() 32 sage: J.rational_cuspidal_subgroup().order() 64 sage: J.rational_torsion_subgroup().divisor_of_order() 64 """ try: return self._divisor_of_order except AttributeError: pass A = self.abelian_variety() N = A.level() if A.dimension() == 0: self._divisor_of_order = ZZ(1) return self._divisor_of_order # return the order of the cuspidal subgroup in the J0(p) case if A.is_J0() and N.is_prime(): self._divisor_of_order = QQ((A.level()-1)/12).numerator() return self._divisor_of_order # The elliptic curve case if A.dimension() == 1: self._divisor_of_order = A.elliptic_curve().torsion_order() return self._divisor_of_order # The J1(p) case if A.is_J1() and N.is_prime(): epsilons = [epsilon for epsilon in DirichletGroup(N) if not epsilon.is_trivial() and epsilon.is_even()] bernoullis = [epsilon.bernoulli(2) for epsilon in epsilons] self._divisor_of_order = ZZ(N/(2**(N-3))*prod(bernoullis)) return self._divisor_of_order # The Gamma0 case if all(is_Gamma0(G) for G in A.groups()): self._divisor_of_order = A.rational_cuspidal_subgroup().order() return self._divisor_of_order # Unhandled case self._divisor_of_order = ZZ(1) return self._divisor_of_order
def _conjugacy_representatives(self, max_block_length=ZZ(0), D=None): r""" Store conjugacy representatives up to block length ``max_block_length`` (a non-negative integer, default: 0) in the internal dictionary. Previously calculated data is reused. This is a helper function for e.g. :meth:`class_number`. The set of all (hyperbolic) conjugacy types of block length ``t`` is stored in ``self._conj_block[t]``. The set of all primitive representatives (so far) with discriminant ``D`` is stored in ``self._conj_prim[D]``. The set of all non-primitive representatives (so far) with discriminant ``D`` is stored in ``self._conj_nonprim[D]``. The case of non-positive discriminants is done manually. INPUT: - ``max_block_length`` -- A non-negative integer (default: ``0``), the maximal block length. - ``D`` -- An element/discriminant of the base ring or more generally an upper bound for the involved discriminants. If ``D != None`` then an upper bound for ``max_block_length`` is deduced from ``D`` (default: ``None``). EXAMPLES:: sage: from sage.modular.modform_hecketriangle.hecke_triangle_groups import HeckeTriangleGroup sage: G = HeckeTriangleGroup(n=5) sage: G.element_repr_method("conj") sage: G._conjugacy_representatives(2) sage: list(G._conj_block[2]) [((4, 1), (3, 1)), ((2, 2),), ((3, 2),), ((3, 1), (1, 1)), ((4, 1), (1, 1)), ((4, 1), (2, 1)), ((3, 1), (2, 1)), ((2, 1), (1, 1))] sage: [key for key in sorted(G._conj_prim)] [-4, lam - 3, 0, 4*lam, 7*lam + 6, 9*lam + 5, 15*lam + 6, 33*lam + 21] sage: for key in sorted(G._conj_prim): ....: print(G._conj_prim[key]) [[S], [S]] [[U], [U]] [[V(4)]] [[V(3)], [V(2)]] [[V(1)*V(4)]] [[V(3)*V(4)], [V(1)*V(2)]] [[V(1)*V(3)], [V(2)*V(4)]] [[V(2)*V(3)]] sage: [key for key in sorted(G._conj_nonprim)] [-lam - 2, lam - 3, 32*lam + 16] sage: for key in sorted(G._conj_nonprim): ....: print(G._conj_nonprim[key]) [[U^(-2)], [U^2], [U^(-2)], [U^2]] [[U^(-1)], [U^(-1)]] [[V(2)^2], [V(3)^2]] sage: G.element_repr_method("default") """ from sage.combinat.partition import OrderedPartitions from sage.combinat.combinat import tuples from sage.arith.all import divisors if not D is None: max_block_length = max(coerce_AA(0), coerce_AA((D + 4) / (self.lam()**2))).sqrt().floor() else: try: max_block_length = ZZ(max_block_length) if max_block_length < 0: raise TypeError except TypeError: raise ValueError( "max_block_length must be a non-negative integer!") if not hasattr(self, "_max_block_length"): self._max_block_length = ZZ(0) self._conj_block = {} self._conj_nonprim = {} self._conj_prim = {} # It is not clear how to define the class number for D=0: # Conjugacy classes are V(n-1)^(+-k) for arbitrary k # and the trivial class (what about self_conj_block[0]?). # # One way is to define it using the fixed points and in # that case V(n-1) would be a good representative. # The non-primitive case is unclear however... # # We set it here to ensure that 0 is enlisted as a discriminant... # self._conj_prim[ZZ(0)] = [] self._conj_prim[ZZ(0)].append(self.V(self.n() - 1)) self._elliptic_conj_reps() if max_block_length <= self._max_block_length: return def is_cycle(seq): length = len(seq) for n in divisors(length): if n < length and is_cycle_of_length(seq, n): return True return False def is_cycle_of_length(seq, n): for i in range(n, len(seq)): if seq[i] != seq[i % n]: return False return True j_list = range(1, self.n()) for t in range(self._max_block_length + 1, max_block_length + 1): t_ZZ = ZZ(t) if t_ZZ not in self._conj_block: self._conj_block[t_ZZ] = set() partitions = OrderedPartitions(t).list() for par in partitions: len_par = len(par) exp_list = tuples(j_list, len_par) for ex in exp_list: keep = True if len_par > 1: for k in range(-1, len_par - 1): if ex[k] == ex[k + 1]: keep = False break # We don't want powers of V(1) elif ex[0] == 1: keep = False # But: Do we maybe want powers of V(n-1)?? # For now we exclude the parabolic cases... elif ex[0] == self.n() - 1: keep = False if keep: conj_type = cyclic_representative( tuple((ZZ(ex[k]), ZZ(par[k])) for k in range(len_par))) self._conj_block[t_ZZ].add(conj_type) for el in self._conj_block[t_ZZ]: group_el = prod( [self.V(el[k][0])**el[k][1] for k in range(len(el))]) #if el != group_el.conjugacy_type(): # raise AssertionError("This shouldn't happen!") D = group_el.discriminant() if coerce_AA(D) < 0: raise AssertionError("This shouldn't happen!") if coerce_AA(D) == 0: raise AssertionError("This shouldn't happen!") #continue # The primitive cases #if group_el.is_primitive(): if not ((len(el) == 1 and el[0][1] > 1) or is_cycle(el)): if D not in self._conj_prim: self._conj_prim[D] = [] self._conj_prim[D].append(group_el) # The remaining cases else: if D not in self._conj_nonprim: self._conj_nonprim[D] = [] self._conj_nonprim[D].append(group_el) self._max_block_length = max_block_length
def possible_orders(self, proof=True): """ Return the possible orders of this torsion subgroup. Outside of special cases, this is done by computing a divisor and multiple of the order. INPUT: - ``proof`` -- a boolean (default: True) OUTPUT: - an array of positive integers The computation of the rational torsion order of J1(p) is conjectural and will only be used if proof=False. See Section 6.2.3 of [CES2003]_. EXAMPLES:: sage: J0(11).rational_torsion_subgroup().possible_orders() [5] sage: J0(33).rational_torsion_subgroup().possible_orders() [100, 200] sage: J1(13).rational_torsion_subgroup().possible_orders() [19] sage: J1(16).rational_torsion_subgroup().possible_orders() [1, 2, 4, 5, 10, 20] """ try: if proof: return self._possible_orders else: return self._possible_orders_proof_false except AttributeError: pass A = self.abelian_variety() N = A.level() # return the order of the cuspidal subgroup in the J0(p) case if A.is_J0() and N.is_prime(): self._possible_orders = [QQ((A.level()-1)/12).numerator()] self._possible_orders_proof_false = self._possible_orders return self._possible_orders # the elliptic curve case if A.dimension() == 1: self._possible_orders = [A.elliptic_curve().torsion_order()] self._possible_orders_proof_false = self._possible_orders return self._possible_orders # the conjectural J1(p) case if not proof and A.is_J1() and N.is_prime(): epsilons = [epsilon for epsilon in DirichletGroup(N) if not epsilon.is_trivial() and epsilon.is_even()] bernoullis = [epsilon.bernoulli(2) for epsilon in epsilons] self._possible_orders_proof_false = [ZZ(N/(2**(N-3))*prod(bernoullis))] return self._possible_orders_proof_false u = self.multiple_of_order() l = self.divisor_of_order() assert u % l == 0 O = [l * d for d in divisors(u//l)] self._possible_orders = O if u == l: self._possible_orders_proof_false = O return O
def __call__(self, s, prec=53): """ Evaluate this complex `L`-series at `s`. INPUT: - ``s`` -- complex number - ``prec`` -- integer (default: 53) the number of bits of precision used in computing the lseries of the newforms. OUTPUT: a complex number L(A, s). EXAMPLES:: sage: L = J0(23).lseries() sage: L(1) 0.248431866590600 sage: L(1, prec=100) 0.24843186659059968120725033931 sage: L = J0(389)[0].lseries() sage: L(1) # long time (2s) abstol 1e-10 -1.33139759782370e-19 sage: L(1, prec=100) # long time (2s) abstol 1e-20 6.0129758648142797032650287762e-39 sage: L.rational_part() 0 sage: L = J1(23)[0].lseries() sage: L(1) 0.248431866590600 sage: J = J0(11) * J1(11) sage: J.lseries()(1) 0.0644356903227915 sage: L = JH(17,[2]).lseries() sage: L(1) 0.386769938387780 """ abelian_variety = self.abelian_variety() # Check for easy dimension zero case if abelian_variety.dimension() == 0: return CC(1) try: factors = self.__factors[prec] return prod(L(s) for L in factors) except AttributeError: self.__factors = {} except KeyError: pass abelian_variety = self.abelian_variety() newforms = abelian_variety.newform_decomposition('a') factors = [ newform.lseries(embedding=i, prec=prec) for newform in newforms for i in range(newform.base_ring().degree()) ] self.__factors[prec] = factors return prod(L(s) for L in factors)
def multiple_of_order(self, maxp=None, proof=True): """ Return a multiple of the order. INPUT: - ``proof`` -- a boolean (default: True) The computation of the rational torsion order of J1(p) is conjectural and will only be used if proof=False. See Section 6.2.3 of [CES2003]_. EXAMPLES:: sage: J = J1(11); J Abelian variety J1(11) of dimension 1 sage: J.rational_torsion_subgroup().multiple_of_order() 5 sage: J = J0(17) sage: J.rational_torsion_subgroup().order() 4 This is an example where proof=False leads to a better bound and better performance. :: sage: J = J1(23) sage: J.rational_torsion_subgroup().multiple_of_order() # long time (2s) 9406793 sage: J.rational_torsion_subgroup().multiple_of_order(proof=False) 408991 """ try: if proof: return self._multiple_of_order else: return self._multiple_of_order_proof_false except AttributeError: pass A = self.abelian_variety() N = A.level() if A.dimension() == 0: self._multiple_of_order = ZZ(1) self._multiple_of_order_proof_false = self._multiple_of_order return self._multiple_of_order # return the order of the cuspidal subgroup in the J0(p) case if A.is_J0() and N.is_prime(): self._multiple_of_order = QQ((A.level()-1)/12).numerator() self._multiple_of_order_proof_false = self._multiple_of_order return self._multiple_of_order # The elliptic curve case if A.dimension() == 1: self._multiple_of_order = A.elliptic_curve().torsion_order() self._multiple_of_order_proof_false = self._multiple_of_order return self._multiple_of_order # The conjectural J1(p) case if not proof and A.is_J1() and N.is_prime(): epsilons = [epsilon for epsilon in DirichletGroup(N) if not epsilon.is_trivial() and epsilon.is_even()] bernoullis = [epsilon.bernoulli(2) for epsilon in epsilons] self._multiple_of_order_proof_false = ZZ(N/(2**(N-3))*prod(bernoullis)) return self._multiple_of_order_proof_false # The Gamma0 and Gamma1 case if all((is_Gamma0(G) or is_Gamma1(G) for G in A.groups())): self._multiple_of_order = self.multiple_of_order_using_frobp() return self._multiple_of_order # Unhandled case raise NotImplementedError("No implemented algorithm")
def _convert_factors_(self, factors): r""" Helper method. Try to convert some ``factors`` to an element of one of the Cartesian factors and return the product of all these factors. INPUT: - ``factors`` -- a tuple or other iterable. OUTPUT: An element of this Cartesian product. EXAMPLES:: sage: from sage.rings.asymptotic.growth_group import GrowthGroup sage: G = GrowthGroup('x^ZZ * log(x)^QQ * y^QQ') sage: e1 = G._convert_factors_([x^2]) sage: (e1, e1.parent()) (x^2, Growth Group x^ZZ * log(x)^QQ * y^QQ) :: sage: G = GrowthGroup('(QQ_+)^n * n^ZZ * UU^n') sage: n = SR.var('n') sage: G((-2)^n) 2^n*(-1)^n """ from sage.misc.misc_c import prod from .growth_group import PartialConversionValueError from .misc import combine_exceptions def get_factors(data): result = [] errors = [] for factor in self.cartesian_factors(): try: try: result.append((factor, factor(data))) break except PartialConversionValueError as e: try: element, todo = e.element.split() except NotImplementedError as nie: raise combine_exceptions( ValueError('cannot split {}: no splitting ' 'implemented'.format(e.element)), nie) except ValueError as ve: raise combine_exceptions( ValueError('cannot split {} after failed ' 'conversion into element of ' '{}'.format(e.element, factor)), ve) assert todo is not None result.append((factor, element)) data = todo except (ValueError, TypeError) as error: errors.append(error) if not result: raise combine_exceptions( ValueError('%s is not in any of the factors of %s' % (data, self)), *errors) return result return prod(self.cartesian_injection(*fs) for f in factors for fs in get_factors(f))
def RQ(delta): # this is the quotient R(F_0,z)/R(F_0,z(F)) for a generic z # at distance delta from j. See Lemma 4.2 in [HS2018]. cd = cosh(delta).n(prec=prec) sd = sinh(delta).n(prec=prec) return prod([cd + (cost*phi[0] + sint*phi[1])*sd for phi in phis])
def H_value(self, p, f, t, ring=None): """ Return the trace of the Frobenius, computed in terms of Gauss sums using the hypergeometric trace formula. INPUT: - `p` -- a prime number - `f` -- an integer such that `q = p^f` - `t` -- a rational parameter - ``ring`` -- optional (default ``UniversalCyclotomicfield``) The ring could be also ``ComplexField(n)`` or ``QQbar``. OUTPUT: an integer .. WARNING:: This is apparently working correctly as can be tested using ComplexField(70) as value ring. Using instead UniversalCyclotomicfield, this is much slower than the `p`-adic version :meth:`padic_H_value`. EXAMPLES: With values in the UniversalCyclotomicField (slow):: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp sage: H = Hyp(alpha_beta=([1/2]*4,[0]*4)) sage: [H.H_value(3,i,-1) for i in range(1,3)] [0, -12] sage: [H.H_value(5,i,-1) for i in range(1,3)] [-4, 276] sage: [H.H_value(7,i,-1) for i in range(1,3)] # not tested [0, -476] sage: [H.H_value(11,i,-1) for i in range(1,3)] # not tested [0, -4972] sage: [H.H_value(13,i,-1) for i in range(1,3)] # not tested [-84, -1420] With values in ComplexField:: sage: [H.H_value(5,i,-1, ComplexField(60)) for i in range(1,3)] [-4, 276] Check issue from :trac:`28404`:: sage: H1 = Hyp(cyclotomic=([1,1,1],[6,2])) sage: H2 = Hyp(cyclotomic=([6,2],[1,1,1])) sage: [H1.H_value(5,1,i) for i in range(2,5)] [1, -4, -4] sage: [H2.H_value(5,1,QQ(i)) for i in range(2,5)] [-4, 1, -4] REFERENCES: - [BeCoMe]_ (Theorem 1.3) - [Benasque2009]_ """ alpha = self._alpha beta = self._beta t = QQ(t) if 0 in alpha: return self._swap.H_value(p, f, ~t, ring) if ring is None: ring = UniversalCyclotomicField() gamma = self.gamma_array() q = p ** f m = {r: beta.count(QQ((r, q - 1))) for r in range(q - 1)} D = -min(self.zigzag(x, flip_beta=True) for x in alpha + beta) # also: D = (self.weight() + 1 - m[0]) // 2 M = self.M_value() Fq = GF(q) gen = Fq.multiplicative_generator() zeta_q = ring.zeta(q - 1) tM = Fq(M / t) for k in range(q - 1): if gen ** k == tM: teich = zeta_q ** k break gauss_table = [gauss_sum(zeta_q ** r, Fq) for r in range(q - 1)] sigma = sum(q**(D + m[0] - m[r]) * prod(gauss_table[(-v * r) % (q - 1)]**gv for v, gv in gamma.items()) * teich ** r for r in range(q - 1)) resu = ZZ(-1) ** m[0] / (1 - q) * sigma if not ring.is_exact(): resu = resu.real_part().round() return resu
def fundamental_group(f, simplified=True, projective=False): r""" Return a presentation of the fundamental group of the complement of the algebraic set defined by the polynomial ``f``. INPUT: - ``f`` -- a polynomial in two variables, with coefficients in either the rationals or a number field with a fixed embedding in `\QQbar` - ``simplified`` -- boolean (default: ``True``); if set to ``True`` the presentation will be simplified (see below) - ``projective`` -- boolean (default: ``False``); if set to ``True``, the fundamental group of the complement of the projective completion of the curve will be computed, otherwise, the fundamental group of the complement in the affine plane will be computed If ``simplified`` is ``False``, the returned presentation has as many generators as degree of the polynomial times the points in the base used to create the segments that surround the discriminant. In this case, the generators are granted to be meridians of the curve. OUTPUT: A presentation of the fundamental group of the complement of the curve defined by ``f``. EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import fundamental_group # optional - sirocco sage: R.<x,y> = QQ[] sage: f = x^2 + y^3 sage: fundamental_group(f) # optional - sirocco Finitely presented group < ... > sage: fundamental_group(f, simplified=False) # optional - sirocco Finitely presented group < ... > :: sage: from sage.schemes.curves.zariski_vankampen import fundamental_group # optional - sirocco sage: R.<x,y> = QQ[] sage: f = y^3 + x^3 sage: fundamental_group(f) # optional - sirocco Finitely presented group < ... > It is also possible to have coefficients in a number field with a fixed embedding in `\QQbar`:: sage: from sage.schemes.curves.zariski_vankampen import fundamental_group # optional - sirocco sage: zeta = QQbar['x']('x^2+x+1').roots(multiplicities=False)[0] sage: zeta -0.50000000000000000? - 0.866025403784439?*I sage: F = NumberField(zeta.minpoly(), 'zeta', embedding=zeta) sage: F.inject_variables() Defining zeta sage: R.<x,y> = F[] sage: f = y^3 + x^3 +zeta *x + 1 sage: fundamental_group(f) # optional - sirocco Finitely presented group < x0 | > """ (x, y) = f.variables() F = f.base_ring() g = f.factor().radical().prod() d = g.degree(y) while not g.coefficient(y**d) in F or (projective and g.total_degree() > d): g = g.subs({x: x + y}) d = g.degree(y) disc = discrim(g) segs = segments(disc) vertices = list(set(flatten(segs))) Faux = FreeGroup(d) F = FreeGroup(d * len(vertices)) rels = [] if projective: rels.append(prod(F.gen(i) for i in range(d))) braidscomputed = braid_in_segment([(g, seg[0], seg[1]) for seg in segs]) for braidcomputed in braidscomputed: seg = (braidcomputed[0][0][1], braidcomputed[0][0][2]) b = braidcomputed[1] i = vertices.index(seg[0]) j = vertices.index(seg[1]) for k in range(d): el1 = Faux([k + 1]) * b.inverse() el2 = k + 1 w1 = F([sign(a)*d*i + a for a in el1.Tietze()]) w2 = F([d*j + el2]) rels.append(w1/w2) G = F / rels if simplified: return G.simplified() else: return G
def demazure_character(self, w, f=None): r""" Returns the Demazure character associated to ``w``. INPUT: - ``w`` -- an element of the ambient weight lattice realization of the crystal, or a reduced word, or an element in the associated Weyl group OPTIONAL: - ``f`` -- a function from the crystal to a module This is currently only supported for crystals whose underlying weight space is the ambient space. The Demazure character is obtained by applying the Demazure operator `D_w` (see :meth:`sage.categories.regular_crystals.RegularCrystals.ParentMethods.demazure_operator`) to the highest weight element of the classical crystal. The simple Demazure operators `D_i` (see :meth:`sage.categories.regular_crystals.RegularCrystals.ElementMethods.demazure_operator_simple`) do not braid on the level of crystals, but on the level of characters they do. That is why it makes sense to input ``w`` either as a weight, a reduced word, or as an element of the underlying Weyl group. EXAMPLES:: sage: T = crystals.Tableaux(['A',2], shape = [2,1]) sage: e = T.weight_lattice_realization().basis() sage: weight = e[0] + 2*e[2] sage: weight.reduced_word() [2, 1] sage: T.demazure_character(weight) x1^2*x2 + x1*x2^2 + x1^2*x3 + x1*x2*x3 + x1*x3^2 sage: T = crystals.Tableaux(['A',3],shape=[2,1]) sage: T.demazure_character([1,2,3]) x1^2*x2 + x1*x2^2 + x1^2*x3 + x1*x2*x3 + x2^2*x3 sage: W = WeylGroup(['A',3]) sage: w = W.from_reduced_word([1,2,3]) sage: T.demazure_character(w) x1^2*x2 + x1*x2^2 + x1^2*x3 + x1*x2*x3 + x2^2*x3 sage: T = crystals.Tableaux(['B',2], shape = [2]) sage: e = T.weight_lattice_realization().basis() sage: weight = -2*e[1] sage: T.demazure_character(weight) x1^2 + x1*x2 + x2^2 + x1 + x2 + x1/x2 + 1/x2 + 1/x2^2 + 1 sage: T = crystals.Tableaux("B2",shape=[1/2,1/2]) sage: b2=WeylCharacterRing("B2",base_ring=QQ).ambient() sage: T.demazure_character([1,2],f=lambda x:b2(x.weight())) b2(-1/2,1/2) + b2(1/2,-1/2) + b2(1/2,1/2) REFERENCES: .. [D1974] \M. Demazure, Desingularisation des varietes de Schubert, Ann. E. N. S., Vol. 6, (1974), p. 163-172 .. [M2009] Sarah Mason, An Explicit Construction of Type A Demazure Atoms, Journal of Algebraic Combinatorics, Vol. 29, (2009), No. 3, p.295-313. :arXiv:`0707.4267` """ from sage.misc.misc_c import prod from sage.rings.integer_ring import ZZ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing if hasattr(w, 'reduced_word'): word = w.reduced_word() else: word = w n = self.weight_lattice_realization().n u = self.algebra(ZZ).sum_of_monomials(self.module_generators) u = self.demazure_operator(u, word) if f is None: x = ['x%s' % i for i in range(1, n + 1)] P = PolynomialRing(ZZ, x) # TODO: use P.linear_combination when PolynomialRing will be a ModulesWithBasis return sum((coeff * prod((x[i]**(c.weight()[i]) for i in range(n)), P.one()) for c, coeff in u), P.zero()) else: return sum((coeff * f(c)) for c, coeff in u)