def __init__(self, data, V=None): if isinstance(data, (tuple, list)): self._dict = {} for mon, mult in data: mon = V(mon) if mon.is_zero(): raise ValueError('zero in denominator') mult = ZZ(mult) if mult <= 0: raise ValueError('non-positive multiplicity in denominator') mon.set_immutable() self._dict[mon] = mult elif isinstance(data, dict): self._dict = data elif isinstance(data, FactoredDenominator): self._dict = data._dict self._tuple = data._tuple return elif isinstance(data, Vector_integer_dense): data = V(data) data.set_immutable() self._dict = {data: ZZ.one()} self._tuple = ((data, ZZ.one()),) return else: raise TypeError('invalid data of type {} to initialized a FactoredDenominator'.format(type(data))) self._tuple = tuple(sorted(self._dict.items()))
def __init__(self, k, p=None, prec_cap=20, base=None, character=None, tuplegen=None, act_on_left=False): """ - ``character`` -- - None (default) - (chi, None) - (None, n) (n integral) - (chi, n) - lambda (for n half-integral use this form) """ if p is not None: p = ZZ(p) if base is None: if p is None: raise ValueError("specify p or a base") base = ZpCA(p,prec_cap) elif isinstance(base, pAdicGeneric): if base.prime() != p: raise ValueError("p must be the same as the prime of base") if base.precision_cap() != prec_cap: raise ValueError("prec_cap must match the precision cap of base") elif prec_cap > k+1: # non-classical if p is None or not p.is_prime(): raise ValueError("p must be prime for non-classical weight") from sage.rings.padics.pow_computer import PowComputer_long # should eventually be the PowComputer on ZpCA once that uses longs. Dist, WeightKAction = get_dist_classes(p, prec_cap, base) self.Element = Dist if Dist is Dist_long: self.prime_pow = PowComputer_long(p, prec_cap, prec_cap, prec_cap, 0) Parent.__init__(self, base) self._k = k self._p = p self._prec_cap = prec_cap act = WeightKAction(self, character, tuplegen, act_on_left) self._act = act self._populate_coercion_lists_(action_list=[act])
def random_element(self, bound=100, *args, **kwds): r""" Return a random element of `{\rm SL}_2(\ZZ)` with entries whose absolute value is strictly less than bound (default 100). Additional arguments and keywords are passed to the random_element method of ZZ. (Algorithm: Generate a random pair of integers at most bound. If they are not coprime, throw them away and start again. If they are, find an element of `{\rm SL}_2(\ZZ)` whose bottom row is that, and left-multiply it by `\begin{pmatrix} 1 & w \\ 0 & 1\end{pmatrix}` for an integer `w` randomly chosen from a small enough range that the answer still has entries at most bound.) It is, unfortunately, not true that all elements of SL2Z with entries < bound appear with equal probability; those with larger bottom rows are favoured, because there are fewer valid possibilities for w. EXAMPLES:: sage: SL2Z.random_element() [60 13] [83 18] sage: SL2Z.random_element(5) [-1 3] [ 1 -4] Passes extra positional or keyword arguments through:: sage: SL2Z.random_element(5, distribution='1/n') [ 1 -4] [ 0 1] """ if bound <= 1: raise ValueError("bound must be greater than 1") c = ZZ.random_element(1-bound, bound, *args, **kwds) d = ZZ.random_element(1-bound, bound, *args, **kwds) if gcd(c,d) != 1: # try again return self.random_element(bound, *args, **kwds) else: a,b,c,d = lift_to_sl2z(c,d,0) whi = bound wlo = bound if c > 0: whi = min(whi, ((bound - a)/ZZ(c)).ceil()) wlo = min(wlo, ((bound + a)/ZZ(c)).ceil()) elif c < 0: whi = min(whi, ((bound + a)/ZZ(-c)).ceil()) wlo = min(wlo, ((bound - a)/ZZ(-c)).ceil()) if d > 0: whi = min(whi, ((bound - b)/ZZ(d)).ceil()) wlo = min(wlo, ((bound + b)/ZZ(d)).ceil()) elif d < 0: whi = min(whi, ((bound + b)/ZZ(-d)).ceil()) wlo = min(wlo, ((bound - b)/ZZ(-d)).ceil()) w = ZZ.random_element(1-wlo, whi, *args, **kwds) a += c*w b += d*w return self([a,b,c,d])
def random_element(self, algorithm='default'): r""" Returns a random element of self, optionally using the algorithm argument to decide how it generates the element. Algorithms currently implemented: - default: Choose `a_i`, `i >= 0`, randomly between `0` and `p-1` until a nonzero choice is made. Then continue choosing `a_i` randomly between `0` and `p-1` until we reach precision_cap, and return `\sum a_i p^i`. EXAMPLES:: sage: Zp(5,6).random_element() 3 + 3*5 + 2*5^2 + 3*5^3 + 2*5^4 + 5^5 + O(5^6) sage: ZpCA(5,6).random_element() 4*5^2 + 5^3 + O(5^6) sage: ZpFM(5,6).random_element() 2 + 4*5^2 + 2*5^4 + 5^5 + O(5^6) """ if (algorithm == 'default'): if self.is_capped_relative(): i = 0 a_i = ZZ.random_element(self.prime()) while a_i.is_zero(): i += 1 a_i = ZZ.random_element(self.prime()) return self((self.prime()**i)*(a_i + self.prime()*ZZ.random_element(self.prime_pow.pow_Integer_Integer(self.precision_cap()-1)))) else: return self(ZZ.random_element(self.prime_pow.pow_Integer_Integer(self.precision_cap()))) else: raise NotImplementedError("Don't know %s algorithm"%algorithm)
def _validate(self, n): """ Verify that n is positive and has at most 4095 digits. INPUT: - ``n`` -- integer. OUTPUT: The integer as a Sage integer. This function raises a ValueError if the two conditions listed above are not both satisfied. It is here because GMP-ECM silently ignores all digits of input after the 4095th! EXAMPLES:: sage: ecm = ECM() sage: ecm._validate(3) 3 sage: ecm._validate(0) Traceback (most recent call last): ... ValueError: n must be positive sage: ecm._validate(10^5000) Traceback (most recent call last): ... ValueError: n must have at most 4095 digits """ n = ZZ(n) if n <= 0: raise ValueError("n must be positive") if n.ndigits() > 4095: raise ValueError("n must have at most 4095 digits") return n
def lift(self, P, prec=20): r""" Given a point `P` in the formal group of the elliptic curve `E` with split multiplicative reduction, this produces an element `u` in `\QQ_p^{\times}` mapped to the point `P` by the Tate parametrisation. The algorithm return the unique such element in `1+p\ZZ_p`. INPUT: - ``P`` - a point on the elliptic curve. - ``prec`` - the `p`-adic precision, default is 20. EXAMPLES:: sage: e = EllipticCurve('130a1') sage: eq = e.tate_curve(5) sage: P = e([-6,10]) sage: l = eq.lift(12*P, prec=10); l 1 + 4*5 + 5^3 + 5^4 + 4*5^5 + 5^6 + 5^7 + 4*5^8 + 5^9 + O(5^10) Now we map the lift l back and check that it is indeed right.:: sage: eq.parametrisation_onto_original_curve(l) (4*5^-2 + 2*5^-1 + 4*5 + 3*5^3 + 5^4 + 2*5^5 + 4*5^6 + O(5^7) : 2*5^-3 + 5^-1 + 4 + 4*5 + 5^2 + 3*5^3 + 4*5^4 + O(5^6) : 1 + O(5^20)) sage: e5 = e.change_ring(Qp(5,9)) sage: e5(12*P) (4*5^-2 + 2*5^-1 + 4*5 + 3*5^3 + 5^4 + 2*5^5 + 4*5^6 + O(5^7) : 2*5^-3 + 5^-1 + 4 + 4*5 + 5^2 + 3*5^3 + 4*5^4 + O(5^6) : 1 + O(5^9)) """ p = self._p R = Qp(self._p, prec) if not self._E == P.curve(): raise ValueError("The point must lie on the original curve.") if not self.is_split(): raise ValueError("The curve must have split multiplicative reduction.") if P.is_zero(): return R.one() if P[0].valuation(p) >= 0: raise ValueError("The point must lie in the formal group.") Eq = self.curve(prec=prec) C, r, s, t = self._isomorphism(prec=prec) xx = r + C ** 2 * P[0] yy = t + s * C ** 2 * P[0] + C ** 3 * P[1] try: Eq([xx, yy]) except Exception: raise RuntimeError("Bug : Point %s does not lie on the curve " % (xx, yy)) tt = -xx / yy eqhat = Eq.formal() eqlog = eqhat.log(prec + 3) z = eqlog(tt) u = ZZ.one() fac = ZZ.one() for i in range(1, 2 * prec + 1): fac *= i u += z ** i / fac return u
def is_hyperbolic(self, p): r""" Check if the quadratic form is a sum of hyperbolic planes over the `p`-adic numbers `\QQ_p` or over the real numbers `\RR`. REFERENCES: This criteria follows from Cassels's "Rational Quadratic Forms": - local invariants for hyperbolic plane (Lemma 2.4, p58) - direct sum formulas (Lemma 2.3, p58) INPUT: - `p` -- a prime number > 0 or `-1` for the infinite place OUTPUT: boolean EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,1]) sage: Q.is_hyperbolic(-1) False sage: Q.is_hyperbolic(2) False sage: Q.is_hyperbolic(3) False sage: Q.is_hyperbolic(5) ## Here -1 is a square, so it's true. True sage: Q.is_hyperbolic(7) False sage: Q.is_hyperbolic(13) ## Here -1 is a square, so it's true. True """ ## False for odd-dim'l forms if self.dim() % 2: return False ## True for the zero form if not self.dim(): return True ## Compare local invariants ## Note: since the dimension is even, the extra powers of 2 in ## self.det() := Det(2*Q) don't affect the answer! m = ZZ(self.dim() // 2) if p == -1: return self.signature() == 0 if p == 2: return (QQ(self.det() * (-1) ** m).is_padic_square(p) and self.hasse_invariant(p) == (-1) ** m.binomial(2)) # here -1 is hilbert_symbol(-1,-1,2) return (QQ(self.det() * (-1) ** m).is_padic_square(p) and self.hasse_invariant(p) == 1)
def iterator_fast(n, l): """ Iterate over all ``l`` weighted integer vectors with total weight ``n``. INPUT: - ``n`` -- an integer - ``l`` -- the weights in weakly decreasing order EXAMPLES:: sage: from sage.combinat.integer_vector_weighted import iterator_fast sage: list(iterator_fast(3, [2,1,1])) [[1, 1, 0], [1, 0, 1], [0, 3, 0], [0, 2, 1], [0, 1, 2], [0, 0, 3]] sage: list(iterator_fast(2, [2])) [[1]] Test that :trac:`20491` is fixed:: sage: type(list(iterator_fast(2, [2]))[0][0]) <type 'sage.rings.integer.Integer'> """ if n < 0: return zero = ZZ.zero() one = ZZ.one() if not l: if n == 0: yield [] return if len(l) == 1: if n % l[0] == 0: yield [n // l[0]] return k = 0 cur = [n // l[k] + one] rem = n - cur[-1] * l[k] # Amount remaining while cur: cur[-1] -= one rem += l[k] if rem == zero: yield cur + [zero] * (len(l) - len(cur)) elif cur[-1] < zero or rem < zero: rem += cur.pop() * l[k] k -= 1 elif len(l) == len(cur) + 1: if rem % l[-1] == zero: yield cur + [rem // l[-1]] else: k += 1 cur.append(rem // l[k] + one) rem -= cur[-1] * l[k]
def rank(self, x): r""" Return the rank of an element of this Cartesian product. The *rank* of ``x`` is its position in the enumeration. It is an integer between ``0`` and ``n-1`` where ``n`` is the cardinality of this set. .. SEEALSO:: - :meth:`EnumeratedSets.ParentMethods.rank` - :meth:`unrank` EXAMPLES:: sage: C = cartesian_product([GF(2), GF(11), GF(7)]) sage: C.rank(C((1,2,5))) 96 sage: C.rank(C((0,0,0))) 0 sage: for c in C: print(C.rank(c)) 0 1 2 3 4 5 ... 150 151 152 153 sage: F1 = FiniteEnumeratedSet('abcdefgh') sage: F2 = IntegerRange(250) sage: F3 = Partitions(20) sage: C = cartesian_product([F1, F2, F3]) sage: c = C(('a', 86, [7,5,4,4])) sage: C.rank(c) 54213 sage: C.unrank(54213) ('a', 86, [7, 5, 4, 4]) """ from builtins import zip from sage.rings.integer_ring import ZZ x = self(x) b = ZZ.one() rank = ZZ.zero() for f, c in zip(reversed(x.cartesian_factors()), reversed(self.cartesian_factors())): rank += b * c.rank(f) b *= c.cardinality() return rank
def getcoeff(self, key, **kwds) : (ch, k) = key if ch != self.__ch : return ZZ.zero() if self.__series is None : self.__series = \ self.__factory.by_taylor_expansion( self.__fs, self.__weight ) try : return self.__series[k] except KeyError : return ZZ.zero()
def Heisenberg(p): r""" Return the Heisenberg origami. EXAMPLES:: sage: from surface_dynamics.all import * sage: h2 = origamis.Heisenberg(2) sage: h2.stratum_component() H_3(1^4)^c sage: h2.veech_group().index() 3 sage: h2.sum_of_lyapunov_exponents() 2 sage: h3 = origamis.Heisenberg(3) sage: h3.stratum_component() H_10(2^9)^even sage: h3.veech_group().index() 1 sage: h3.sum_of_lyapunov_exponents() 5 sage: h5 = origamis.Heisenberg(5) sage: h5.stratum_component() H_51(4^25)^even sage: h5.veech_group().index() 1 sage: h5.sum_of_lyapunov_exponents() 15 """ p = ZZ(p) if not p.is_prime(): raise ValueError("p (={}) must be prime".format(p)) N = p**3 # map (a,b,d) -> a*p**2 + b*p + d pp = p*p r = [None]*N u = [None]*N for a in xrange(p): for b in xrange(p): for d in xrange(p): n = a*pp + b*p + d r[n] = ((a+1)%p)*pp + b*p + d u[n] = a*pp + ((b+1)%p)*p + ((a+d)%p) return Origami(r,u, as_tuple=True, name="Heisenberg origami on GF(%d)"%p)
def number_of_reflection_hyperplanes(self): r""" Return the number of reflection hyperplanes of ``self``. This is also the number of distinguished reflections. For real groups, this coincides with the number of reflections. This implementation uses that it is given by the sum of the codegrees of ``self`` plus its rank. .. SEEALSO:: :meth:`number_of_reflections` EXAMPLES:: sage: W = ColoredPermutations(1,3) sage: W.number_of_reflection_hyperplanes() 3 sage: W = ColoredPermutations(2,3) sage: W.number_of_reflection_hyperplanes() 9 sage: W = ColoredPermutations(4,3) sage: W.number_of_reflection_hyperplanes() 15 sage: W = ReflectionGroup((4,2,3)) # optional - gap3 sage: W.number_of_reflection_hyperplanes() # optional - gap3 15 """ from sage.rings.all import ZZ return ZZ.sum(codeg + 1 for codeg in self.codegrees())
def number_of_reflections(self): r""" Return the number of reflections of ``self``. For real groups, this coincides with the number of reflection hyperplanes. This implementation uses that it is given by the sum of the degrees of ``self`` minus its rank. .. SEEALSO:: :meth:`number_of_reflection_hyperplanes` EXAMPLES:: sage: [SymmetricGroup(i).number_of_reflections() for i in range(int(8))] [0, 0, 1, 3, 6, 10, 15, 21] sage: W = ColoredPermutations(1,3) sage: W.number_of_reflections() 3 sage: W = ColoredPermutations(2,3) sage: W.number_of_reflections() 9 sage: W = ColoredPermutations(4,3) sage: W.number_of_reflections() 21 sage: W = ReflectionGroup((4,2,3)) # optional - gap3 sage: W.number_of_reflections() # optional - gap3 15 """ from sage.rings.all import ZZ return ZZ.sum(deg - 1 for deg in self.degrees())
def character_ring(self, modules_base_ring = None, base_ring = None, side = 'right', q = None): """ Returns the character ring of ``self`` EXAMPLES:: sage: S = Semigroups().Finite().example() sage: S.rename("S") sage: A = S.character_ring(); A The right-character ring of S over Rational Field sage: A.category() Category of character rings of finite semigroups over Integer Ring with realizations sage: QQ.<q> = QQ[] sage: A = S.character_ring(q = q); A The right-character ring of S over Rational Field """ from sage.rings.rational_field import QQ if modules_base_ring is None: modules_base_ring = QQ from sage.rings.integer_ring import ZZ if q is None: q = ZZ.one() if base_ring is None: base_ring = q.parent() from sage.categories.rings import Rings assert base_ring in Rings() assert q in base_ring from sage_semigroups.monoids.character_ring import AbstractCharacterRing return AbstractCharacterRing(self, modules_base_ring, base_ring, side = side, q = q)
def degree(self): r""" EXAMPLES:: sage: from surface_dynamics.misc.factored_denominator import FactoredDenominator sage: V = ZZ**3 sage: FactoredDenominator([], None).degree() 0 sage: FactoredDenominator([((1,0,0), 2)], V).degree() 2 sage: FactoredDenominator([((0,1,2), 3), ((1,1,1), 1)], V).degree() 4 sage: FactoredDenominator([((0,-1,2), 1), ((1,0,0), 1), ((0,0,2), 1)], V).degree() 3 TESTS:: sage: parent(FactoredDenominator([], None).degree()) Integer Ring sage: parent(FactoredDenominator([((1,0,0), 2)], V).degree()) Integer Ring """ if not self._tuple: return ZZ.zero() return sum(m for _,m in self._tuple)
def build_Lambdalist_from_AB(A,B,T, scaling): # T is the matrix of Hecke acting on Homology try: x,y,z,t = T.list() except AttributeError: x,y,z,t = T alpha = ZZ(z)/ZZ(y) beta = ZZ(t-x)/ZZ(y) d = alpha.denominator().lcm(beta.denominator()) alpha, beta = ZZ(d*alpha), ZZ(d*beta) ans = [] K = A.parent() for A0, B0 in product(our_nroot(A,scaling,return_all=True),our_nroot(B,scaling,return_all=True)): for B1 in our_nroot(B0, d,return_all=True): ans.append(Matrix(K,2,2,[A0,B0,B1**alpha,A0*B1**beta])) return ans
def phi(self, i): r""" Return the value of `\varphi_i` on ``self``. INPUT: - ``i`` -- an element of the index set EXAMPLES:: sage: M = crystals.infinity.NakajimaMonomials(['D',4,3]) sage: m = M.module_generators[0].f(1) sage: [m.phi(i) for i in M.index_set()] [1, -1, 1] sage: M = crystals.infinity.NakajimaMonomials(['C',4,1]) sage: m = M.module_generators[0].f_string([4,2,3]) sage: [m.phi(i) for i in M.index_set()] [0, 1, -1, 2, -1] """ if i not in self.parent().index_set(): raise ValueError("i must be an element of the index set") if not self._Y or all(x[0] != i for x in self._Y): return ZZ.zero() d = copy(self._Y) K = max(x[1] for x in d if x[0] == i) for a in range(K): if (i,a) in d: continue else: d[(i,a)] = 0 S = sorted((x for x in six.iteritems(d) if x[0][0] == i), key=lambda x: x[0][1]) return max(sum(S[k][1] for k in range(s)) for s in range(1,len(S)+1))
def number_of_rooted_trees(n): r""" Return the number of rooted trees with `n` nodes. Compute the number `a(n)` of rooted trees with `n` nodes using the recursive formula ([SL000081]_): .. MATH:: a(n+1) = \frac{1}{n} \sum_{k=1}^{n} \left( \sum_{d|k} d a(d) \right) a(n-k+1) EXAMPLES:: sage: from sage.combinat.rooted_tree import number_of_rooted_trees sage: [number_of_rooted_trees(i) for i in range(10)] [0, 1, 1, 2, 4, 9, 20, 48, 115, 286] REFERENCES: .. [SL000081] Sloane's :oeis:`A000081` """ if n == 0: return Integer(0) if n == 1: return Integer(1) n = Integer(n) return sum(sum(d * number_of_rooted_trees(d) for d in k.divisors()) * number_of_rooted_trees(n - k) for k in ZZ.range(1, n)) // (n - 1)
def _kf(self, i): r""" Return the value `k_f` with respect to ``i`` and ``self``. INPUT: - ``i`` -- an element of the index set EXAMPLES:: sage: M = crystals.infinity.NakajimaMonomials(['F',4,1]) sage: m = M.module_generators[0].f_string([0,1,4,3]) sage: [m._kf(i) for i in M.index_set()] [0, 0, 2, 0, 0] """ if all(i != x[0] for x in self._Y): return ZZ.zero() d = copy(self._Y) K = max(key[1] for key in d if key[0] == i) for a in range(K): if (i,a) in d: continue else: d[(i,a)] = 0 S = sorted((x for x in six.iteritems(d) if x[0][0] == i), key=lambda x: x[0][1]) sum = 0 phi = self.phi(i) for var,exp in S: sum += exp if sum == phi: return var[1]
def __contains__(self, x): r""" Tests if ``x`` is an element of ``self``. INPUT: - ``x`` -- matrix EXAMPLES:: sage: from sage.combinat.integer_matrices import IntegerMatrices sage: IM = IntegerMatrices([4], [1,2,1]) sage: matrix([[1, 2, 1]]) in IM True sage: matrix(QQ, [[1, 2, 1]]) in IM True sage: matrix([[2, 1, 1]]) in IM False TESTS:: sage: from sage.combinat.integer_matrices import IntegerMatrices sage: IM = IntegerMatrices([4], [1,2,1]) sage: [1, 2, 1] in IM False sage: matrix([[-1, 3, 1]]) in IM False """ from sage.matrix.matrix import Matrix if not isinstance(x, Matrix): return False row_sums = [ZZ.zero()] * x.nrows() col_sums = [ZZ.zero()] * x.ncols() for i in range(x.nrows()): for j in range(x.ncols()): x_ij = x[i, j] if x_ij not in ZZ or x_ij < 0: return False row_sums[i] += x_ij col_sums[j] += x_ij if row_sums[i] != self._row_sums[i]: return False if col_sums != self._col_sums: return False return True
def cardinality(self): r""" Return the number of integer necklaces with the evaluation ``content``. The formula for the number of necklaces of content `\alpha` a composition of `n` is: .. MATH:: \sum_{d|gcd(\alpha)} \phi(d) \binom{n/d}{\alpha_1/d, \ldots, \alpha_\ell/d}, where `\phi(d)` is the Euler `\phi` function. EXAMPLES:: sage: Necklaces([]).cardinality() 0 sage: Necklaces([2,2]).cardinality() 2 sage: Necklaces([2,3,2]).cardinality() 30 sage: Necklaces([0,3,2]).cardinality() 2 Check to make sure that the count matches up with the number of necklace words generated. :: sage: comps = [[],[2,2],[3,2,7],[4,2],[0,4,2],[2,0,4]]+Compositions(4).list() sage: ns = [Necklaces(comp) for comp in comps] sage: all(n.cardinality() == len(n.list()) for n in ns) True """ evaluation = self._content le = list(evaluation) if not le: return ZZ.zero() n = sum(le) return ZZ.sum(euler_phi(j) * factorial(n // j) // prod(factorial(ni // j) for ni in evaluation) for j in divisors(gcd(le))) // n
def _rank_dict(self): r""" Builds the rank dictionnary of the poset, if it exists, i.e. a dictionary ``d`` where ``d[object] = self.rank_function()(object) A *rank function* of a poset `P` is a function `r` that maps elements of `P` to integers and satisfies: `r(x) = r(y) + 1` if `x` covers `y`. The function `r` is normalized such that its smallest value is `0`. When `P` has several components, this is done for each component separately. EXAMPLES:: sage: H = Poset()._hasse_diagram sage: H._rank_dict {} sage: H = Poset([[1,3,2],[4],[4,5,6],[6],[7],[7],[7],[]])._hasse_diagram sage: H._rank_dict {0: 0, 1: 1, 2: 1, 3: 2, 4: 2, 5: 1, 6: 2, 7: 3} sage: H = Poset(([1,2,3,4,5],[[1,2],[2,3],[3,4],[1,5],[5,4]]))._hasse_diagram sage: H._rank_dict is None True """ rank_fcn = {} # rank_fcn will be the dictionary whose i-th entry # is the rank of vertex i for every i. not_found = set(self.vertices()) while not_found: y = not_found.pop() rank_fcn[y] = ZZ.zero() # We set some vertex to have rank 0 component = set([y]) queue = set([y]) while queue: # look at the neighbors of y and set the ranks; # then look at the neighbors of the neighbors ... y = queue.pop() for x in self.neighbors_out(y): if x not in rank_fcn: rank_fcn[x] = rank_fcn[y] + 1 queue.add(x) component.add(x) for x in self.neighbors_in(y): if x not in rank_fcn: rank_fcn[x] = rank_fcn[y] - 1 queue.add(x) component.add(x) elif rank_fcn[x] != rank_fcn[y] - 1: return None # Normalize the ranks of vertices in the connected component # so that smallest is 0: m = min(rank_fcn[j] for j in component) for j in component: rank_fcn[j] -= m not_found.difference_update(component) #now, all ranks are set. return rank_fcn
def _coerce_map_from_(self, S): """ This is called implicitly by arithmetic methods. EXAMPLES:: sage: k = GF(7) sage: e = k(6) sage: e * 2 # indirect doctest 5 sage: 12 % 7 5 sage: ZZ.residue_field(7).hom(GF(7))(1) # See trac 11319 1 sage: K.<w> = QuadraticField(337) # See trac 11319 sage: pp = K.ideal(13).factor()[0][0] sage: RF13 = K.residue_field(pp) sage: RF13.hom([GF(13)(1)]) Ring morphism: From: Residue field of Fractional ideal (w + 18) To: Finite Field of size 13 Defn: 1 |--> 1 Check that :trac:`19573` is resolved:: sage: Integers(9).hom(GF(3)) Natural morphism: From: Ring of integers modulo 9 To: Finite Field of size 3 sage: Integers(9).hom(GF(5)) Traceback (most recent call last): ... TypeError: natural coercion morphism from Ring of integers modulo 9 to Finite Field of size 5 not defined There is no coercion from a `p`-adic ring to its residue field:: sage: GF(3).has_coerce_map_from(Zp(3)) False """ if S is int: return integer_mod.Int_to_IntegerMod(self) elif S is ZZ: return integer_mod.Integer_to_IntegerMod(self) elif isinstance(S, IntegerModRing_generic): from .residue_field import ResidueField_generic if (S.characteristic() % self.characteristic() == 0 and (not isinstance(S, ResidueField_generic) or S.degree() == 1)): try: return integer_mod.IntegerMod_to_IntegerMod(S, self) except TypeError: pass to_ZZ = ZZ._internal_coerce_map_from(S) if to_ZZ is not None: return integer_mod.Integer_to_IntegerMod(self) * to_ZZ
def random_element(self, prec=None, integral=False): """ Return a random element of this ring. INPUT: - ``prec`` -- an integer or ``None`` (the default): the absolute precision of the generated random element - ``integral`` -- a boolean (default: ``False``); if true return an element in the ring of integers EXAMPLES:: sage: K = QpLC(2) sage: K.random_element() # random 2^-8 + 2^-7 + 2^-6 + 2^-5 + 2^-3 + 1 + 2^2 + 2^3 + 2^5 + O(2^12) sage: K.random_element(integral=True) # random 2^3 + 2^4 + 2^5 + 2^6 + 2^7 + 2^10 + 2^11 + 2^14 + 2^15 + 2^16 + 2^17 + 2^18 + 2^19 + O(2^20) sage: K.random_element(prec=10) # random 2^(-3) + 1 + 2 + 2^4 + 2^8 + O(2^10) If the given precision is higher than the internal cap of the parent, then the cap is used:: sage: K.precision_cap_relative() 20 sage: K.random_element(prec=100) # random 2^5 + 2^8 + 2^11 + 2^12 + 2^14 + 2^18 + 2^20 + 2^24 + O(2^25) """ if integral: val = 0 else: val = ZZ.random_element() if prec is None: prec = self._prec_cap_absolute - val p = self.prime() x = ZZ.random_element(p**prec) relcap = x.valuation(p) + self._prec_cap_relative if relcap < prec: prec = relcap return self._element_class(self, x*(p**val), prec=prec)
def gcd(self,other): """ Greatest common divisor. NOTE: Since we are in a field and the greatest common divisor is only determined up to a unit, it is correct to either return zero or one. Note that fraction fields of unique factorization domains provide a more sophisticated gcd. EXAMPLES:: sage: GF(5)(1).gcd(GF(5)(1)) 1 sage: GF(5)(1).gcd(GF(5)(0)) 1 sage: GF(5)(0).gcd(GF(5)(0)) 0 For fields of characteristic zero (i.e., containing the integers as a sub-ring), evaluation in the integer ring is attempted. This is for backwards compatibility:: sage: gcd(6.0,8); gcd(6.0,8).parent() 2 Integer Ring If this fails, we resort to the default we see above:: sage: gcd(6.0*CC.0,8*CC.0); gcd(6.0*CC.0,8*CC.0).parent() 1.00000000000000 Complex Field with 53 bits of precision AUTHOR: - Simon King (2011-02): Trac ticket #10771 """ P = self.parent() try: other = P(other) except (TypeError, ValueError): raise ArithmeticError, "The second argument can not be interpreted in the parent of the first argument. Can't compute the gcd" from sage.rings.integer_ring import ZZ if ZZ.is_subring(P): try: return ZZ(self).gcd(ZZ(other)) except TypeError: pass # there is no custom gcd, so, we resort to something that always exists # (that's new behaviour) if self==0 and other==0: return P.zero() return P.one()
def init_dir_char(self, chi): """ Initiate with a Web Dirichlet character. """ self.original_object = [chi] chi = chi.chi.primitive_character() self.object_type = "dirichletcharacter" self.dim = 1 self.motivic_weight = 0 self.conductor = ZZ(chi.conductor()) self.bad_semistable_primes = [] self.bad_pot_good = self.conductor.prime_factors() if chi.is_odd(): aa = 1 bb = I else: aa = 0 bb = 1 self.sign = chi.gauss_sum_numerical() / (bb * float(sqrt(chi.modulus())) ) # this has now type python complex. later we need a gp complex self.sign = ComplexField()(self.sign) self.mu_fe = [aa] self.nu_fe = [] self.gammaV = [aa] self.langlands = True self.selfdual = (chi.multiplicative_order() <= 2) # rather than all( abs(chi(m).imag) < 0.0001 for m in range(chi.modulus() ) ) self.primitive = True self.set_dokchitser_Lfunction() self.set_number_of_coefficients() self.dirichlet_coefficients = [ chi(m) for m in range(self.numcoeff + 1) ] if self.selfdual: self.coefficient_type = 2 else: self.coefficient_type = 3 self.coefficient_period = chi.modulus() self.besancon_bound = 10000 def eu(p): """ local euler factor """ if self.selfdual: K = QQ else: K = ComplexField() R = PolynomialRing(K, "T") T = R.gens()[0] if self.conductor % p != 0: return 1 - ComplexField()(chi(p)) * T else: return R(1) self.local_euler_factor = eu self.ld.gp().quit()
def find_primitive_p_divisible_vector__random(self, p): """ Finds a random `p`-primitive vector in `L/pL` whose value is `p`-divisible. .. note:: Since there are about `p^{(n-2)}` of these lines, we have a `1/p` chance of randomly finding an appropriate vector. .. warning:: If there are local obstructions for this to happen, then this algorithm will never terminate... =( We should check for this too! EXAMPLES:: sage: Q = QuadraticForm(ZZ, 2, [10,1,4]) sage: Q.find_primitive_p_divisible_vector__random(5) # random (1, 1) sage: Q.find_primitive_p_divisible_vector__random(5) # random (1, 0) sage: Q.find_primitive_p_divisible_vector__random(5) # random (2, 0) sage: Q.find_primitive_p_divisible_vector__random(5) # random (2, 2) sage: Q.find_primitive_p_divisible_vector__random(5) # random (3, 3) sage: Q.find_primitive_p_divisible_vector__random(5) # random (3, 3) sage: Q.find_primitive_p_divisible_vector__random(5) # random (2, 0) """ n = self.dim() v = vector([ZZ.random_element(p) for i in range(n)]) ## Repeatedly choose random vectors, and evaluate until the value is p-divisible. while True: if (self(v) % p == 0) and (v != 0): return v else: v[ZZ.random_element(n)] = ZZ.random_element(p) ## Replace a random entry and try again.
def random_element(self,prec=Infinity,precrandom=None): from ZZp_element import ZZp_element if precrandom is None: if prec is not Infinity: precrandom = prec else: precrandom = self._prec modulo = self._p ** precrandom approximation = ZZ.random_element(modulo) precision = self.precision()(prec) return ZZp_element(self, approximation, precision)
def one(self): r""" Return the identity element of the fundamental group. EXAMPLES:: sage: from sage.combinat.root_system.fundamental_group import FundamentalGroupOfExtendedAffineWeylGroup sage: FundamentalGroupOfExtendedAffineWeylGroup(['A',2,1], general_linear=True).one() pi[0] """ return self(ZZ.zero())
def group_generators(self): r""" Return group generators for ``self``. EXAMPLES:: sage: from sage.combinat.root_system.fundamental_group import FundamentalGroupOfExtendedAffineWeylGroup sage: FundamentalGroupOfExtendedAffineWeylGroup(['A',2,1], general_linear=True).group_generators() (pi[1],) """ return (self(ZZ.one()),)
def __init__(self, *args, **kwargs): """ Construct a substitution box (S-box) for a given lookup table `S`. INPUT: - ``S`` - a finite iterable defining the S-box with integer or finite field elements - ``big_endian`` - controls whether bits shall be ordered in big endian order (default: ``True``) EXAMPLE: We construct a 3-bit S-box where e.g. the bits (0,0,1) are mapped to (1,1,1).:: sage: S = mq.SBox(7,6,0,4,2,5,1,3); S (7, 6, 0, 4, 2, 5, 1, 3) sage: S(0) 7 TESTS:: sage: S = mq.SBox() Traceback (most recent call last): ... TypeError: No lookup table provided. sage: S = mq.SBox(1, 2, 3) Traceback (most recent call last): ... TypeError: Lookup table length is not a power of 2. sage: S = mq.SBox(5, 6, 0, 3, 4, 2, 1, 2) sage: S.n 3 """ if "S" in kwargs: S = kwargs["S"] elif len(args) == 1: S = args[0] elif len(args) > 1: S = args else: raise TypeError("No lookup table provided.") _S = [] for e in S: if is_FiniteFieldElement(e): e = e.polynomial().change_ring(ZZ).subs( e.parent().characteristic()) _S.append(e) S = _S if not ZZ(len(S)).is_power_of(2): raise TypeError("Lookup table length is not a power of 2.") self._S = S self.m = ZZ(len(S)).exact_log(2) self.n = ZZ(max(S)).nbits() self._F = GF(2) self._big_endian = kwargs.get("big_endian", True)
def __call__(self, X): """ Apply substitution to ``X``. If ``X`` is a list, it is interpreted as a sequence of bits depending on the bit order of this S-box. INPUT: - ``X`` - either an integer, a tuple of `\GF{2}` elements of length ``len(self)`` or a finite field element in `\GF{2^n}`. As a last resort this function tries to convert ``X`` to an integer. EXAMPLE:: sage: S = mq.SBox([7,6,0,4,2,5,1,3]) sage: S(7) 3 sage: S((0,2,3)) [0, 1, 1] sage: S[0] 7 sage: S[(0,0,1)] [1, 1, 0] sage: k.<a> = GF(2^3) sage: S(a^2) a sage: S(QQ(3)) 4 sage: S([1]*10^6) Traceback (most recent call last): ... TypeError: Cannot apply SBox to provided element. sage: S(1/2) Traceback (most recent call last): ... TypeError: Cannot apply SBox to 1/2. sage: S = mq.SBox(3, 0, 1, 3, 1, 0, 2, 2) sage: S(0) 3 sage: S([0,0,0]) [1, 1] """ if isinstance(X, (int, long, Integer)): return self._S[ZZ(X)] try: from sage.modules.free_module_element import vector K = X.parent() if K.order() == 2**self.n: X = vector(X) else: raise TypeError if not self._big_endian: X = list(reversed(X)) else: X = list(X) X = ZZ(map(ZZ, X), 2) out = self.to_bits(self._S[X], self.n) if self._big_endian: out = list(reversed(out)) return K(vector(GF(2), out)) except (AttributeError, TypeError): pass try: if len(X) == self.m: if self._big_endian: X = list(reversed(X)) X = ZZ(map(ZZ, X), 2) out = self._S[X] return self.to_bits(out, self.n) except TypeError: pass try: return self._S[ZZ(X)] except TypeError: pass if len(str(X)) > 50: raise TypeError, "Cannot apply SBox to provided element." else: raise TypeError, "Cannot apply SBox to %s." % (X, )
def __init__(self, number_field, proof=True): """ Create a unit group of a number field. INPUT: - ``number_field`` - a number field - ``proof`` - boolean (default True): proof flag The proof flag is passed to pari via the ``pari_bnf()`` function which computes the unit group. See the documentation for the number_field module. EXAMPLES:: sage: x = polygen(QQ) sage: K.<a> = NumberField(x^2-38) sage: UK = K.unit_group(); UK Unit group with structure C2 x Z of Number Field in a with defining polynomial x^2 - 38 sage: UK.gens() (u0, u1) sage: UK.gens_values() [-1, 6*a - 37] sage: K.<a> = QuadraticField(-3) sage: UK = K.unit_group(); UK Unit group with structure C6 of Number Field in a with defining polynomial x^2 + 3 sage: UK.gens() (u,) sage: UK.gens_values() [-1/2*a + 1/2] sage: K.<z> = CyclotomicField(13) sage: UK = K.unit_group(); UK Unit group with structure C26 x Z x Z x Z x Z x Z of Cyclotomic Field of order 13 and degree 12 sage: UK.gens() (u0, u1, u2, u3, u4, u5) sage: UK.gens_values() # random [-z^11, z^5 + z^3, z^6 + z^5, z^9 + z^7 + z^5, z^9 + z^5 + z^4 + 1, z^5 + z] """ proof = get_flag(proof, "number_field") K = number_field pK = K.pari_bnf(proof) self.__number_field = K # compute the units via pari: fu = [K(u) for u in pK.bnfunit()] # compute a torsion generator and pick the 'simplest' one: n, z = pK.nfrootsof1() n = ZZ(n) self.__ntu = n z = K(z) # If we replaced z by another torsion generator we would need # to allow for this in the dlog function! So we do not. # Store the actual generators (torsion first): gens = [z] + fu values = Sequence(gens, immutable=True, universe=self, check=False) # Construct the abtract group: gens_orders = tuple([ZZ(n)] + [ZZ(0)] * len(fu)) AbelianGroupWithValues_class.__init__(self, gens_orders, 'u', values, number_field)
def vectors_by_length(self, bound): """ Returns a list of short vectors together with their values. This is a naive algorithm which uses the Cholesky decomposition, but does not use the LLL-reduction algorithm. INPUT: bound -- an integer >= 0 OUTPUT: A list L of length (bound + 1) whose entry L `[i]` is a list of all vectors of length `i`. Reference: This is a slightly modified version of Cohn's Algorithm 2.7.5 in "A Course in Computational Number Theory", with the increment step moved around and slightly re-indexed to allow clean looping. Note: We could speed this up for very skew matrices by using LLL first, and then changing coordinates back, but for our purposes the simpler method is efficient enough. =) EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,1]) sage: Q.vectors_by_length(5) [[[0, 0]], [[0, -1], [-1, 0]], [[-1, -1], [1, -1]], [], [[0, -2], [-2, 0]], [[-1, -2], [1, -2], [-2, -1], [2, -1]]] :: sage: Q1 = DiagonalQuadraticForm(ZZ, [1,3,5,7]) sage: Q1.vectors_by_length(5) [[[0, 0, 0, 0]], [[-1, 0, 0, 0]], [], [[0, -1, 0, 0]], [[-1, -1, 0, 0], [1, -1, 0, 0], [-2, 0, 0, 0]], [[0, 0, -1, 0]]] :: sage: Q = QuadraticForm(ZZ, 4, [1,1,1,1, 1,0,0, 1,0, 1]) sage: map(len, Q.vectors_by_length(2)) [1, 12, 12] :: sage: Q = QuadraticForm(ZZ, 4, [1,-1,-1,-1, 1,0,0, 4,-3, 4]) sage: map(len, Q.vectors_by_length(3)) [1, 3, 0, 3] """ # pari uses eps = 1e-6 ; nothing bad should happen if eps is too big # but if eps is too small, roundoff errors may knock off some # vectors of norm = bound (see #7100) eps = RDF(1e-6) bound = ZZ(floor(max(bound, 0))) Theta_Precision = bound + eps n = self.dim() ## Make the vector of vectors which have a given value ## (So theta_vec[i] will have all vectors v with Q(v) = i.) theta_vec = [[] for i in range(bound + 1)] ## Initialize Q with zeros and Copy the Cholesky array into Q Q = self.cholesky_decomposition() ## 1. Initialize T = n * [RDF(0)] ## Note: We index the entries as 0 --> n-1 U = n * [RDF(0)] i = n - 1 T[i] = RDF(Theta_Precision) U[i] = RDF(0) L = n * [0] x = n * [0] Z = RDF(0) ## 2. Compute bounds Z = (T[i] / Q[i][i]).sqrt(extend=False) L[i] = (Z - U[i]).floor() x[i] = (-Z - U[i]).ceil() done_flag = False Q_val_double = RDF(0) Q_val = 0 ## WARNING: Still need a good way of checking overflow for this value... ## Big loop which runs through all vectors while not done_flag: ## 3b. Main loop -- try to generate a complete vector x (when i=0) while (i > 0): #print " i = ", i #print " T[i] = ", T[i] #print " Q[i][i] = ", Q[i][i] #print " x[i] = ", x[i] #print " U[i] = ", U[i] #print " x[i] + U[i] = ", (x[i] + U[i]) #print " T[i-1] = ", T[i-1] T[i - 1] = T[i] - Q[i][i] * (x[i] + U[i]) * (x[i] + U[i]) #print " T[i-1] = ", T[i-1] #print " x = ", x #print i = i - 1 U[i] = 0 for j in range(i + 1, n): U[i] = U[i] + Q[i][j] * x[j] ## Now go back and compute the bounds... ## 2. Compute bounds Z = (T[i] / Q[i][i]).sqrt(extend=False) L[i] = (Z - U[i]).floor() x[i] = (-Z - U[i]).ceil() # carry if we go out of bounds -- when Z is so small that # there aren't any integral vectors between the bounds # Note: this ensures T[i-1] >= 0 in the next iteration while (x[i] > L[i]): i += 1 x[i] += 1 ## 4. Solution found (This happens when i = 0) #print "-- Solution found! --" #print " x = ", x #print " Q_val = Q(x) = ", Q_val Q_val_double = Theta_Precision - T[0] + Q[0][0] * (x[0] + U[0]) * ( x[0] + U[0]) Q_val = Q_val_double.round() ## SANITY CHECK: Roundoff Error is < 0.001 if abs(Q_val_double - Q_val) > 0.001: print " x = ", x print " Float = ", Q_val_double, " Long = ", Q_val raise RuntimeError, "The roundoff error is bigger than 0.001, so we should use more precision somewhere..." #print " Float = ", Q_val_double, " Long = ", Q_val, " XX " #print " The float value is ", Q_val_double #print " The associated long value is ", Q_val if (Q_val <= bound): #print " Have vector ", x, " with value ", Q_val theta_vec[Q_val].append(deepcopy(x)) ## 5. Check if x = 0, for exit condition. =) j = 0 done_flag = True while (j < n): if (x[j] != 0): done_flag = False j += 1 ## 3a. Increment (and carry if we go out of bounds) x[i] += 1 while (x[i] > L[i]) and (i < n - 1): i += 1 x[i] += 1 #print " Leaving ThetaVectors()" return theta_vec
def zeta__exact(n): r""" Returns the exact value of the Riemann Zeta function The argument must be a critical value, namely either positive even or negative odd. See for example [Iwa1972]_, p13, Special value of `\zeta(2k)` EXAMPLES: Let us test the accuracy for negative special values:: sage: RR = RealField(100) sage: for i in range(1,10): ....: print("zeta({}): {}".format(1-2*i, RR(zeta__exact(1-2*i)) - zeta(RR(1-2*i)))) zeta(-1): 0.00000000000000000000000000000 zeta(-3): 0.00000000000000000000000000000 zeta(-5): 0.00000000000000000000000000000 zeta(-7): 0.00000000000000000000000000000 zeta(-9): 0.00000000000000000000000000000 zeta(-11): 0.00000000000000000000000000000 zeta(-13): 0.00000000000000000000000000000 zeta(-15): 0.00000000000000000000000000000 zeta(-17): 0.00000000000000000000000000000 Let us test the accuracy for positive special values:: sage: all(abs(RR(zeta__exact(2*i))-zeta(RR(2*i))) < 10**(-28) for i in range(1,10)) True TESTS:: sage: zeta__exact(4) 1/90*pi^4 sage: zeta__exact(-3) 1/120 sage: zeta__exact(0) -1/2 sage: zeta__exact(5) Traceback (most recent call last): ... TypeError: n must be a critical value (i.e. even > 0 or odd < 0) REFERENCES: - [Iwa1972]_ - [IR1990]_ - [Was1997]_ """ if n < 0: return bernoulli(1-n)/(n-1) elif n > 1: if (n % 2 == 0): return ZZ(-1)**(n//2 + 1) * ZZ(2)**(n-1) * pi**n * bernoulli(n) / factorial(n) else: raise TypeError("n must be a critical value (i.e. even > 0 or odd < 0)") elif n == 1: return infinity elif n == 0: return QQ((-1, 2))
def block_design_checker(self, t, v, k, lmbda, type=None): """ This is *not* a wrapper for GAP Design's IsBlockDesign. The GAP Design function IsBlockDesign http://www.gap-system.org/Manuals/pkg/design/htm/CHAP004.htm apparently simply checks the record structure and no mathematical properties. Instead, the function below checks some necessary (but not sufficient) "easy" identities arising from the identity. INPUT: - ``t`` - the t as in "t-design" - ``v`` - the number of points - ``k`` - the number of blocks incident to a point - ``lmbda`` - each t-tuple of points should be incident with lmbda blocks - ``type`` - can be 'simple' or 'binary' or 'connected' Depending on the option, this wraps IsBinaryBlockDesign, IsSimpleBlockDesign, or IsConnectedBlockDesign. - Binary: no block has a repeated element. - Simple: no block is repeated. - Connected: its incidence graph is a connected graph. WARNING: This is very fast but can return false positives. EXAMPLES:: sage: from sage.combinat.designs.block_design import BlockDesign sage: BD = BlockDesign(7,[[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) sage: BD.is_block_design() (True, [2, 7, 3, 1]) sage: BD.block_design_checker(2, 7, 3, 1) True sage: BD.block_design_checker(2, 7, 3, 1,"binary") True sage: BD.block_design_checker(2, 7, 3, 1,"connected") True sage: BD.block_design_checker(2, 7, 3, 1,"simple") True """ from sage.sets.set import Set if not(v == len(self.points())): return False b = lmbda*binomial(v, t)/binomial(k, t) r = int(b*k/v) if not(b == len(self.blocks())): return False if not(ZZ(v).divides(b*k)): return False A = self.incidence_matrix() #k = sum(A.columns()[0]) #r = sum(A.rows()[0]) for i in range(b): if not(sum(A.columns()[i]) == k): return False for i in range(v): if not(sum(A.rows()[i]) == r): return False if type is None: return True if type == "binary": for b in self.blocks(): if len(b) != len(Set(b)): return False return True if type == "simple": B = self.blocks() for b in B: if B.count(b) > 1: return False return True if type == "connected": Gamma = self.incidence_graph() if Gamma.is_connected(): return True else: return False
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 jordan_blocks_by_scale_and_unimodular(self, p, safe_flag=True): r""" Return a list of pairs `(s_i, L_i)` where `L_i` is a maximal `p^{s_i}`-unimodular Jordan component which is further decomposed into block diagonals of block size `\le 2`. For each `L_i` the 2x2 blocks are listed after the 1x1 blocks (which follows from the convention of the :meth:`local_normal_form` method). .. NOTE:: The decomposition of each `L_i` into smaller blocks is not unique! The ``safe_flag`` argument allows us to select whether we want a copy of the output, or the original output. By default ``safe_flag = True``, so we return a copy of the cached information. If this is set to ``False``, then the routine is much faster but the return values are vulnerable to being corrupted by the user. INPUT: - `p` -- a prime number > 0. OUTPUT: A list of pairs `(s_i, L_i)` where: - `s_i` is an integer, - `L_i` is a block-diagonal unimodular quadratic form over `\ZZ_p`. .. note:: These forms `L_i` are defined over the `p`-adic integers, but by a matrix over `\ZZ` (or `\QQ`?). EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,9,5,7]) sage: Q.jordan_blocks_by_scale_and_unimodular(3) [(0, Quadratic form in 3 variables over Integer Ring with coefficients: [ 1 0 0 ] [ * 5 0 ] [ * * 7 ]), (2, Quadratic form in 1 variables over Integer Ring with coefficients: [ 1 ])] :: sage: Q2 = QuadraticForm(ZZ, 2, [1,1,1]) sage: Q2.jordan_blocks_by_scale_and_unimodular(2) [(-1, Quadratic form in 2 variables over Integer Ring with coefficients: [ 2 2 ] [ * 2 ])] sage: Q = Q2 + Q2.scale_by_factor(2) sage: Q.jordan_blocks_by_scale_and_unimodular(2) [(-1, Quadratic form in 2 variables over Integer Ring with coefficients: [ 2 2 ] [ * 2 ]), (0, Quadratic form in 2 variables over Integer Ring with coefficients: [ 2 2 ] [ * 2 ])] """ # Try to use the cached result try: if safe_flag: return copy.deepcopy( self.__jordan_blocks_by_scale_and_unimodular_dict[p]) else: return self.__jordan_blocks_by_scale_and_unimodular_dict[p] except Exception: # Initialize the global dictionary if it doesn't exist if not hasattr(self, '__jordan_blocks_by_scale_and_unimodular_dict'): self.__jordan_blocks_by_scale_and_unimodular_dict = {} # Deal with zero dim'l forms if self.dim() == 0: return [] # Find the Local Normal form of Q at p Q1 = self.local_normal_form(p) # Parse this into Jordan Blocks n = Q1.dim() tmp_Jordan_list = [] i = 0 start_ind = 0 if (n >= 2) and (Q1[0, 1] != 0): start_scale = valuation(Q1[0, 1], p) - 1 else: start_scale = valuation(Q1[0, 0], p) while (i < n): # Determine the size of the current block if (i == n - 1) or (Q1[i, i + 1] == 0): block_size = 1 else: block_size = 2 # Determine the valuation of the current block if block_size == 1: block_scale = valuation(Q1[i, i], p) else: block_scale = valuation(Q1[i, i + 1], p) - 1 # Process the previous block if the valuation increased if block_scale > start_scale: tmp_Jordan_list += [ (start_scale, Q1.extract_variables( range(start_ind, i)).scale_by_factor(ZZ(1) / (QQ(p)**(start_scale)))) ] start_ind = i start_scale = block_scale # Increment the index i += block_size # Add the last block tmp_Jordan_list += [(start_scale, Q1.extract_variables(range( start_ind, n)).scale_by_factor(ZZ(1) / QQ(p)**(start_scale)))] # Cache the result self.__jordan_blocks_by_scale_and_unimodular_dict[p] = tmp_Jordan_list # Return the result return tmp_Jordan_list
def local_normal_form(self, p): r""" Return a locally integrally equivalent quadratic form over the `p`-adic integers `\ZZ_p` which gives the Jordan decomposition. The Jordan components are written as sums of blocks of size <= 2 and are arranged by increasing scale, and then by increasing norm. This is equivalent to saying that we put the 1x1 blocks before the 2x2 blocks in each Jordan component. INPUT: - `p` -- a positive prime number. OUTPUT: a quadratic form over `\ZZ` .. WARNING:: Currently this only works for quadratic forms defined over `\ZZ`. EXAMPLES:: sage: Q = QuadraticForm(ZZ, 2, [10,4,1]) sage: Q.local_normal_form(5) Quadratic form in 2 variables over Integer Ring with coefficients: [ 1 0 ] [ * 6 ] :: sage: Q.local_normal_form(3) Quadratic form in 2 variables over Integer Ring with coefficients: [ 10 0 ] [ * 15 ] sage: Q.local_normal_form(2) Quadratic form in 2 variables over Integer Ring with coefficients: [ 1 0 ] [ * 6 ] """ # Sanity Checks if (self.base_ring() != IntegerRing()): raise NotImplementedError( "Oops! This currently only works for quadratic forms defined over IntegerRing(). =(" ) if not ((p >= 2) and is_prime(p)): raise TypeError("Oops! p is not a positive prime number. =(") # Some useful local variables Q = self.parent()(self.base_ring(), self.dim(), self.coefficients()) # Prepare the final form to return Q_Jordan = copy.deepcopy(self) Q_Jordan.__init__(self.base_ring(), 0) while Q.dim() > 0: n = Q.dim() # Step 1: Find the minimally p-divisible matrix entry, preferring diagonals # ------------------------------------------------------------------------- (min_i, min_j) = Q.find_entry_with_minimal_scale_at_prime(p) if min_i == min_j: min_val = valuation(2 * Q[min_i, min_j], p) else: min_val = valuation(Q[min_i, min_j], p) # Error if we still haven't seen non-zero coefficients! if (min_val == Infinity): raise RuntimeError("Oops! The original matrix is degenerate. =(") # Step 2: Arrange for the upper leftmost entry to have minimal valuation # ---------------------------------------------------------------------- if (min_i == min_j): block_size = 1 Q.swap_variables(0, min_i, in_place=True) else: # Work in the upper-left 2x2 block, and replace it by its 2-adic equivalent form Q.swap_variables(0, min_i, in_place=True) Q.swap_variables(1, min_j, in_place=True) # 1x1 => make upper left the smallest if (p != 2): block_size = 1 Q.add_symmetric(1, 0, 1, in_place=True) # 2x2 => replace it with the appropriate 2x2 matrix else: block_size = 2 # Step 3: Clear out the remaining entries # --------------------------------------- min_scale = p**min_val # This is the minimal valuation of the Hessian matrix entries. # Perform cancellation over Z by ensuring divisibility if (block_size == 1): a = 2 * Q[0, 0] for j in range(block_size, n): b = Q[0, j] g = GCD(a, b) # Sanity Check: a/g is a p-unit if valuation(g, p) != valuation(a, p): raise RuntimeError( "Oops! We have a problem with our rescaling not preserving p-integrality!" ) Q.multiply_variable( ZZ(a / g), j, in_place=True ) # Ensures that the new b entry is divisible by a Q.add_symmetric(ZZ(-b / g), j, 0, in_place=True) # Performs the cancellation elif (block_size == 2): a1 = 2 * Q[0, 0] a2 = Q[0, 1] b1 = Q[1, 0] # This is the same as a2 b2 = 2 * Q[1, 1] big_det = (a1 * b2 - a2 * b1) small_det = big_det / (min_scale * min_scale) # Cancels out the rows/columns of the 2x2 block for j in range(block_size, n): a = Q[0, j] b = Q[1, j] # Ensures an integral result (scale jth row/column by big_det) Q.multiply_variable(big_det, j, in_place=True) # Performs the cancellation (by producing -big_det * jth row/column) Q.add_symmetric(ZZ(-(a * b2 - b * a2)), j, 0, in_place=True) Q.add_symmetric(ZZ(-(-a * b1 + b * a1)), j, 1, in_place=True) # Now remove the extra factor (non p-unit factor) in big_det we introduced above Q.divide_variable(ZZ(min_scale * min_scale), j, in_place=True) # Uses Cassels's proof to replace the remaining 2 x 2 block if (((1 + small_det) % 8) == 0): Q[0, 0] = 0 Q[1, 1] = 0 Q[0, 1] = min_scale elif (((5 + small_det) % 8) == 0): Q[0, 0] = min_scale Q[1, 1] = min_scale Q[0, 1] = min_scale else: raise RuntimeError( "Error in LocalNormal: Impossible behavior for a 2x2 block! \n" ) # Check that the cancellation worked, extract the upper-left block, and trim Q to handle the next block. for i in range(block_size): for j in range(block_size, n): if Q[i, j] != 0: raise RuntimeError( "Oops! The cancellation didn't work properly at entry (" + str(i) + ", " + str(j) + ").") Q_Jordan = Q_Jordan + Q.extract_variables(range(block_size)) Q = Q.extract_variables(range(block_size, n)) return Q_Jordan
def __call__(self, cm, aut): self.count += ZZ(1) self.weighted_count += QQ((1, (1 if aut is None else aut.group_cardinality())))
def __init__(self): self.count = ZZ(0) self.weighted_count = QQ(0)
def basis_of_short_vectors(self, show_lengths=False): r""" Return a basis for `\ZZ^n` made of vectors with minimal lengths Q(`v`). OUTPUT: a tuple of vectors, and optionally a tuple of values for each vector. This uses :pari:`qfminim`. EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7]) sage: Q.basis_of_short_vectors() ((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)) sage: Q.basis_of_short_vectors(True) (((1, 0, 0, 0), (0, 1, 0, 0), (0, 0, 1, 0), (0, 0, 0, 1)), (1, 3, 5, 7)) The returned vectors are immutable:: sage: v = Q.basis_of_short_vectors()[0] sage: v (1, 0, 0, 0) sage: v[0] = 0 Traceback (most recent call last): ... ValueError: vector is immutable; please change a copy instead (use copy()) """ # Set an upper bound for the number of vectors to consider Max_number_of_vectors = 10000 # Generate a PARI matrix for the associated Hessian matrix M_pari = self.__pari__() # Run through all possible minimal lengths to find a spanning set of vectors n = self.dim() M1 = Matrix([[0]]) vec_len = 0 while M1.rank() < n: vec_len += 1 pari_mat = M_pari.qfminim(vec_len, Max_number_of_vectors)[2] number_of_vecs = ZZ(pari_mat.matsize()[1]) vector_list = [] for i in range(number_of_vecs): new_vec = vector([ZZ(x) for x in list(pari_mat[i])]) vector_list.append(new_vec) # Make a matrix from the short vectors if vector_list: M1 = Matrix(vector_list) # Organize these vectors by length (and also introduce their negatives) max_len = vec_len // 2 vector_list_by_length = [[] for _ in range(max_len + 1)] for v in vector_list: l = self(v) vector_list_by_length[l].append(v) vector_list_by_length[l].append(vector([-x for x in v])) # Make a matrix from the column vectors (in order of ascending length). sorted_list = [] for i in range(len(vector_list_by_length)): for v in vector_list_by_length[i]: sorted_list.append(v) sorted_matrix = Matrix(sorted_list).transpose() # Determine a basis of vectors of minimal length pivots = sorted_matrix.pivots() basis = tuple(sorted_matrix.column(i) for i in pivots) for v in basis: v.set_immutable() # Return the appropriate result if show_lengths: pivot_lengths = tuple(self(v) for v in basis) return basis, pivot_lengths else: return basis
def integral(self, var=None): """ Return the integral of this polynomial. By default, the integration variable is the variable of the polynomial. Otherwise, the integration variable is the optional parameter ``var`` .. NOTE:: The integral is always chosen so that the constant term is 0. EXAMPLES:: sage: R.<x> = PolynomialRing(ZZ, sparse=True) sage: (1 + 3*x^10 - 2*x^100).integral() -2/101*x^101 + 3/11*x^11 + x TESTS: Check that :trac:`18600` is fixed:: sage: R.<x> = PolynomialRing(ZZ, sparse=True) sage: (x^2^100).integral() 1/1267650600228229401496703205377*x^1267650600228229401496703205377 Check the correctness when the base ring is a polynomial ring:: sage: R.<x> = PolynomialRing(ZZ, sparse=True) sage: S.<t> = PolynomialRing(R, sparse=True) sage: (x*t+1).integral() 1/2*x*t^2 + t sage: (x*t+1).integral(x) 1/2*x^2*t + x Check the correctness when the base ring is not an integral domain:: sage: R.<x> = PolynomialRing(Zmod(4), sparse=True) sage: (x^4 + 2*x^2 + 3).integral() x^5 + 2*x^3 + 3*x sage: x.integral() Traceback (most recent call last): ... ZeroDivisionError: Inverse does not exist. """ R = self.parent() # TODO: # calling the coercion model bin_op is much more accurate than using the # true division (which is bypassed by polynomials). But it does not work # in all cases!! from sage.structure.element import coercion_model as cm import operator try: Q = cm.bin_op(R.one(), ZZ.one(), operator.div).parent() except TypeError: F = (R.base_ring().one() / ZZ.one()).parent() Q = R.change_ring(F) if var is not None and var != R.gen(): return Q( {k: v.integral(var) for k, v in six.iteritems(self.__coeffs)}, check=False) return Q({k + 1: v / (k + 1) for k, v in six.iteritems(self.__coeffs)}, check=False)
def theta_series_degree_2(Q, prec): r""" Compute the theta series of degree 2 for the quadratic form Q. INPUT: - ``prec`` -- an integer. OUTPUT: dictionary, where: - keys are `{\rm GL}_2(\ZZ)`-reduced binary quadratic forms (given as triples of coefficients) - values are coefficients EXAMPLES:: sage: Q2 = QuadraticForm(ZZ, 4, [1,1,1,1, 1,0,0, 1,0, 1]) sage: S = Q2.theta_series_degree_2(10) sage: S[(0,0,2)] 24 sage: S[(1,0,1)] 144 sage: S[(1,1,1)] 192 AUTHORS: - Gonzalo Tornaria (2010-03-23) REFERENCE: - Raum, Ryan, Skoruppa, Tornaria, 'On Formal Siegel Modular Forms' (preprint) """ if Q.base_ring() != ZZ: raise TypeError, "The quadratic form must be integral" if not Q.is_positive_definite(): raise ValueError, "The quadratic form must be positive definite" try: X = ZZ(prec - 1) # maximum discriminant except TypeError: raise TypeError, "prec is not an integer" if X < -1: raise ValueError, "prec must be >= 0" if X == -1: return {} V = ZZ**Q.dim() H = Q.Hessian_matrix() t = cputime() max = int(floor((X + 1) / 4)) v_list = (Q.vectors_by_length(max)) # assume a>0 v_list = map(lambda (vs): map(V, vs), v_list) # coerce vectors into V verbose("Computed vectors_by_length", t) # Deal with the singular part coeffs = {(0, 0, 0): ZZ(1)} for i in range(1, max + 1): coeffs[(0, 0, i)] = ZZ(2) * len(v_list[i]) # Now deal with the non-singular part a_max = int(floor(sqrt(X / 3))) for a in range(1, a_max + 1): t = cputime() c_max = int(floor((a * a + X) / (4 * a))) for c in range(a, c_max + 1): for v1 in v_list[a]: v1_H = v1 * H def B_v1(v): return v1_H * v2 for v2 in v_list[c]: b = abs(B_v1(v2)) if b <= a and 4 * a * c - b * b <= X: qf = (a, b, c) count = ZZ(4) if b == 0 else ZZ(2) coeffs[qf] = coeffs.get(qf, ZZ(0)) + count verbose("done a = %d" % a, t) return coeffs
def _get_powers(self,g,emb = None): r""" Auxiliary function to compute the Sigma_0(p)^2 action on moments. The action sends a monomial x^i to (gx)^i, where gx = (b+dx)/(a+cx). The action on two-variable functions is simply the product of two copies of the one variable action. Input: - g : Sigma0SquaredElement object (in the relevant Sigma_0(p)^2 group) Returns: matrix of (g_x,g_y) acting on distributions in the basis given by monomials EXAMPLES:: sage: D = BianchiDistributions(11,2) sage: h = D.Sigma0Squared()([1,1,0,1],[1,1,0,1]) sage: D._get_powers(h) [1 0 0 0] [1 1 0 0] [1 0 1 0] [1 1 1 1] sage: h = D.Sigma0Squared()([2,3,11,1],[12,1,22,1]) sage: D._get_powers(h) [1 0 0 0] [7 6 0 0] [1 0 1 0] [7 6 7 6] """ ## We want to ultimately compute actions on distributions. The matrix describing the (left) ## action of g on distributions is the transpose of the action of adjoint(g) acting on the (left) ## of analytic functions, so we start by taking adjoints. Then put the matrix entries into lists ## NOTE: First apply the adjuster defined above; permutes a,b,c,d to allow for different conventions. abcdx = g.first_element() abcdy = g.second_element() ## Adjust for action: change of convention is encoded in our_adjuster class above adjuster = self._adjuster abcdx = adjuster(abcdx.matrix()) abcdy = adjuster(abcdy.matrix()) ## We want better keys; keys in Zp are not great. Store them instead in ZZ abcdxZZ = tuple(ZZ(t) for t in abcdx) abcdyZZ = tuple(ZZ(t) for t in abcdy) ## check to see if the action of (g,h) has already been computed and cached try: return self._cache_powers[(abcdxZZ,abcdyZZ)] except KeyError: pass ## Sanity check output verbose('The element [{},{}] has not been stored. Computing:'.format(abcdxZZ,abcdyZZ), level=2) R = self._PowerSeries ## Zp[[x,y] y = R.gen() x = R.base_ring().gen() ## get values of a,b,c,d for x and y if emb is None: a,b,c,d = abcdx A,B,C,D = abcdy else: gg = emb(abcdy) a,b,c,d = gg[0].list() A,B,C,D = gg[1].list() ## Initialise terms num_x = b + d * x ## b + az + O(11^depth)R denom_x = a + c * x ## d + cz + O(11^depth)R num_y = B+ D * x denom_y = A + C * x ## Ratios r = R.base_ring()(num_x / denom_x) ## after action on x s = num_y / denom_y ## after action on y r = r.change_ring(ZZ) s = s.change_ring(ZZ) RZ = self._PowerSeries_ZZ phi = s.parent().hom([RZ.gen()]) ## Constant term const = r.parent()(1) spows = [const] for n in range(self._depth): spows.append(s * spows[-1]) acted_monomials = {} for j in range(self._depth): acted_monomials[(0, j)] = phi(spows[j]) rpow = 1 for i in range(1, self._depth): rpow *= r rpow.add_bigoh(self._depth) for j in range(self._depth): acted_monomials[(i,j)] = rpow * phi(spows[j]) matrix_rows = [] for n in range(self._dimension): f = acted_monomials[tuple(self.ij_from_pos(n))] new_row = [] for polx in f.padded_list(self._depth): new_row.extend(polx.padded_list(self._depth)) matrix_rows.append(new_row) ## Build matrix . DO NOT TAKE TRANSPOSE, (built this in as a consequence of implementation) matrix_action = Matrix(ZZ, matrix_rows) #acted_monomials_list = Matrix(R.base_ring(),self._depth,self._depth,acted_monomials_list)#.apply_map(ZZ) self._cache_powers[(abcdxZZ,abcdyZZ)] = matrix_action return matrix_action
def _singularity_analysis_(self, var, zeta, precision): r""" Perform singularity analysis on this growth element. INPUT: - ``var`` -- a string denoting the variable - ``zeta`` -- a number - ``precision`` -- an integer OUTPUT: An asymptotic expansion for `[z^n] f` where `n` is ``var`` and `f` has this growth element as a singular expansion in `T=\frac{1}{1-\frac{z}{\zeta}}\to \infty` where this element is a growth element in `T`. EXAMPLES:: sage: from sage.rings.asymptotic.growth_group import GrowthGroup sage: G = GrowthGroup('exp(x)^QQ * x^QQ * log(x)^QQ') sage: G(x^(1/2))._singularity_analysis_('n', 2, precision=2) 1/sqrt(pi)*(1/2)^n*n^(-1/2) - 1/8/sqrt(pi)*(1/2)^n*n^(-3/2) + O((1/2)^n*n^(-5/2)) sage: G(log(x))._singularity_analysis_('n', 1, precision=5) n^(-1) + O(n^(-3)) sage: G(x*log(x))._singularity_analysis_('n', 1, precision=5) log(n) + euler_gamma + 1/2*n^(-1) + O(n^(-2)) TESTS:: sage: G('exp(x)*log(x)')._singularity_analysis_('n', 1, precision=5) Traceback (most recent call last): ... NotImplementedError: singularity analysis of exp(x)*log(x) not implemented sage: G('exp(x)*x*log(x)')._singularity_analysis_('n', 1, precision=5) Traceback (most recent call last): ... NotImplementedError: singularity analysis of exp(x)*x*log(x) not yet implemented since it has more than two factors sage: G(1)._singularity_analysis_('n', 2, precision=3) Traceback (most recent call last): ... NotImplementedOZero: The error term in the result is O(0) which means 0 for sufficiently large n. sage: G('exp(x)')._singularity_analysis_('n', 2, precision=3) Traceback (most recent call last): ... NotImplementedError: singularity analysis of exp(x) not implemented """ factors = self.factors() if len(factors) == 0: from .asymptotic_expansion_generators import asymptotic_expansions from .misc import NotImplementedOZero raise NotImplementedOZero(var=var) elif len(factors) == 1: return factors[0]._singularity_analysis_(var=var, zeta=zeta, precision=precision) elif len(factors) == 2: from .growth_group import MonomialGrowthGroup from sage.rings.integer_ring import ZZ a, b = factors if all(isinstance(f.parent(), MonomialGrowthGroup) for f in factors) \ and a.parent().gens_monomial() \ and b.parent().gens_logarithmic() \ and a.parent().variable_name() == \ b.parent().variable_name(): if b.exponent not in ZZ: raise NotImplementedError( 'singularity analysis of {} not implemented ' 'since exponent {} of {} is not an integer'.format( self, b.exponent, b.parent().gen())) from sage.rings.asymptotic.asymptotic_expansion_generators import \ asymptotic_expansions return asymptotic_expansions.SingularityAnalysis( var=var, zeta=zeta, alpha=a.exponent, beta=ZZ(b.exponent), delta=0, precision=precision, normalized=False) else: raise NotImplementedError( 'singularity analysis of {} not implemented'.format( self)) else: raise NotImplementedError( 'singularity analysis of {} not yet implemented ' 'since it has more than two factors'.format(self))
def krawtchouk(n, q, l, x, check=True): r""" Compute ``K^{n,q}_l(x)``, the Krawtchouk (a.k.a. Kravchuk) polynomial. See :wikipedia:`Kravchuk_polynomials`. It is defined by the generating function .. MATH:: (1+(q-1)z)^{n-x}(1-z)^x=\sum_{l} K^{n,q}_l(x)z^l and is equal to .. MATH:: K^{n,q}_l(x)=\sum_{j=0}^l (-1)^j (q-1)^{(l-j)} \binom{x}{j} \binom{n-x}{l-j}, INPUT: - ``n, q, x`` -- arbitrary numbers - ``l`` -- a nonnegative integer - ``check`` -- check the input for correctness. ``True`` by default. Otherwise, pass it as it is. Use ``check=False`` at your own risk. EXAMPLES:: sage: codes.bounds.krawtchouk(24,2,5,4) 2224 sage: codes.bounds.krawtchouk(12300,4,5,6) 567785569973042442072 TESTS: check that the bug reported on :trac:`19561` is fixed:: sage: codes.bounds.krawtchouk(3,2,3,3) -1 sage: codes.bounds.krawtchouk(int(3),int(2),int(3),int(3)) -1 sage: codes.bounds.krawtchouk(int(3),int(2),int(3),int(3),check=False) -1.0 sage: codes.bounds.krawtchouk(24,2,5,4) 2224 other unusual inputs :: sage: codes.bounds.krawtchouk(sqrt(5),1-I*sqrt(3),3,55.3).n() 211295.892797... + 1186.42763...*I sage: codes.bounds.krawtchouk(-5/2,7*I,3,-1/10) 480053/250*I - 357231/400 sage: codes.bounds.krawtchouk(1,1,-1,1) Traceback (most recent call last): ... ValueError: l must be a nonnegative integer sage: codes.bounds.krawtchouk(1,1,3/2,1) Traceback (most recent call last): ... TypeError: no conversion of this rational to integer """ from sage.arith.all import binomial from sage.arith.srange import srange # Use the expression in equation (55) of MacWilliams & Sloane, pg 151 # We write jth term = some_factor * (j-1)th term if check: from sage.rings.integer_ring import ZZ l0 = ZZ(l) if l0 != l or l0 < 0: raise ValueError('l must be a nonnegative integer') l = l0 kraw = jth_term = (q - 1)**l * binomial(n, l) # j=0 for j in srange(1, l + 1): jth_term *= -q * (l - j + 1) * (x - j + 1) / ((q - 1) * j * (n - j + 1)) kraw += jth_term return kraw
def _find_scaling_L_ratio(self): r""" This function is use to set ``_scaling``, the factor used to adjust the scalar multiple of the modular symbol. If `[0]`, the modular symbol evaluated at 0, is non-zero, we can just scale it with respect to the approximation of the L-value. It is known that the quotient is a rational number with small denominator. Otherwise we try to scale using quadratic twists. ``_scaling`` will be set to a rational non-zero multiple if we succeed and to 1 otherwise. Even if we fail we scale at least to make up the difference between the periods of the `X_0`-optimal curve and our given curve `E` in the isogeny class. EXAMPLES:: sage : m = EllipticCurve('11a1').modular_symbol(use_eclib=True) sage : m._scaling 1 sage: m = EllipticCurve('11a2').modular_symbol(use_eclib=True) sage: m._scaling 5/2 sage: m = EllipticCurve('11a3').modular_symbol(use_eclib=True) sage: m._scaling 1/10 sage: m = EllipticCurve('11a1').modular_symbol(use_eclib=False) sage: m._scaling 1/5 sage: m = EllipticCurve('11a2').modular_symbol(use_eclib=False) sage: m._scaling 1 sage: m = EllipticCurve('11a3').modular_symbol(use_eclib=False) sage: m._scaling 1/25 sage: m = EllipticCurve('37a1').modular_symbol(use_eclib=False) sage: m._scaling 1 sage: m = EllipticCurve('37a1').modular_symbol(use_eclib=True) sage: m._scaling -1 sage: m = EllipticCurve('389a1').modular_symbol(use_eclib=True) sage: m._scaling -1/2 sage: m = EllipticCurve('389a1').modular_symbol(use_eclib=False) sage: m._scaling 2 sage: m = EllipticCurve('196a1').modular_symbol(use_eclib=False) sage: m._scaling 1/2 Some harder cases fail:: sage: m = EllipticCurve('121b1').modular_symbol(use_eclib=False) Warning : Could not normalize the modular symbols, maybe all further results will be multiplied by -1, 2 or -2. sage: m._scaling 1 TESTS:: sage: rk0 = ['11a1', '11a2', '15a1', '27a1', '37b1'] sage: for la in rk0: # long time (3s on sage.math, 2011) ... E = EllipticCurve(la) ... me = E.modular_symbol(use_eclib = True) ... ms = E.modular_symbol(use_eclib = False) ... print E.lseries().L_ratio()*E.real_components(), me(0), ms(0) 1/5 1/5 1/5 1 1 1 1/4 1/4 1/4 1/3 1/3 1/3 2/3 2/3 2/3 sage: rk1 = ['37a1','43a1','53a1', '91b1','91b2','91b3'] sage: [EllipticCurve(la).modular_symbol(use_eclib=True)(0) for la in rk1] # long time (1s on sage.math, 2011) [0, 0, 0, 0, 0, 0] sage: for la in rk1: # long time (8s on sage.math, 2011) ... E = EllipticCurve(la) ... m = E.modular_symbol(use_eclib = True) ... lp = E.padic_lseries(5) ... for D in [5,17,12,8]: ... ED = E.quadratic_twist(D) ... md = sum([kronecker(D,u)*m(ZZ(u)/D) for u in range(D)]) ... etaa = lp._quotient_of_periods_to_twist(D) ... assert ED.lseries().L_ratio()*ED.real_components()*etaa == md """ E = self._E self._scaling = 1 # by now. self._failed_to_scale = False if self._sign == 1: at0 = self(0) # print 'modular symbol evaluates to ',at0,' at 0' if at0 != 0: l1 = self.__lalg__(1) if at0 != l1: verbose('scale modular symbols by %s' % (l1 / at0)) self._scaling = l1 / at0 else: # if [0] = 0, we can still hope to scale it correctly by considering twists of E Dlist = [ 5, 8, 12, 13, 17, 21, 24, 28, 29, 33, 37, 40, 41, 44, 53, 56, 57, 60, 61, 65, 69, 73, 76, 77, 85, 88, 89, 92, 93, 97 ] # a list of positive fundamental discriminants j = 0 at0 = 0 # computes [0]+ for the twist of E by D until one value is non-zero while j < 30 and at0 == 0: D = Dlist[j] # the following line checks if the twist of the newform of E by D is a newform # this is to avoid that we 'twist back' if all( valuation(E.conductor(), ell) <= valuation(D, ell) for ell in prime_divisors(D)): at0 = sum([ kronecker_symbol(D, u) * self(ZZ(u) / D) for u in range(1, abs(D)) ]) j += 1 if j == 30 and at0 == 0: # curves like "121b1", "225a1", "225e1", "256a1", "256b1", "289a1", "361a1", "400a1", "400c1", "400h1", "441b1", "441c1", "441d1", "441f1 .. will arrive here self._failed_to_scale = True self.__scale_by_periods_only__() else: l1 = self.__lalg__(D) if at0 != l1: verbose('scale modular symbols by %s found at D=%s ' % (l1 / at0, D), level=2) self._scaling = l1 / at0 else: # that is when sign = -1 Dlist = [ -3, -4, -7, -8, -11, -15, -19, -20, -23, -24, -31, -35, -39, -40, -43, -47, -51, -52, -55, -56, -59, -67, -68, -71, -79, -83, -84, -87, -88, -91 ] # a list of negative fundamental discriminants j = 0 at0 = 0 while j < 30 and at0 == 0: # computes [0]+ for the twist of E by D until one value is non-zero D = Dlist[j] if all( valuation(E.conductor(), ell) <= valuation(D, ell) for ell in prime_divisors(D)): at0 = -sum([ kronecker_symbol(D, u) * self(ZZ(u) / D) for u in range(1, abs(D)) ]) j += 1 if j == 30 and at0 == 0: # no more hope for a normalization # we do at least a scaling with the quotient of the periods self._failed_to_scale = True self.__scale_by_periods_only__() else: l1 = self.__lalg__(D) if at0 != l1: verbose('scale modular symbols by %s' % (l1 / at0)) self._scaling = l1 / at0
def __repr__(self): r""" Represent self's Fourier expansion as a power series c(v) q1^v q2^(v'), where v runs through totally-positive elements in the dual of the ring of integers. """ K = self.__base_field h = self.true_fourier_expansion() hprec = h.prec() d = self.scale() if h: D = K.discriminant() sqrtD = K(D).sqrt() if not D % 4: sqrtD /= 2 s = '' sign = False for i, p in enumerate(h.list()): i = ZZ(i) for n in p.exponents(): c = p[n] if c: q2exp = (i - n / sqrtD) / (d + d) q1exp = i / d - q2exp coef = True if sign: if c > 0 and c != 1: s += ' + ' + str(c) elif c + 1 and c != 1: s += ' - ' + str(-c) elif c + 1: s += ' + ' coef = False elif c - 1: s += ' - ' coef = False else: if abs(c) != 1 or not q1exp: s += str(c) else: coef = False if c == -1: s += '-' sign = True if q1exp: if coef: s += '*' if q1exp != q2exp or q1exp not in ZZ: s += 'q1^(%s)*q2^(%s)' % (q1exp, q2exp) elif q1exp != 1: s += 'q1^%s*q2^%s' % (q1exp, q2exp) else: s += 'q1*q2' sign = True if hprec % d: self.__string = s + ' + O(q1, q2)^(%s)' % (hprec / d) else: self.__string = s + ' + O(q1, q2)^%s' % (hprec / d) else: if hprec % d: self.__string = 'O(q1, q2)^(%s)' % (hprec / d) else: self.__string = 'O(q1, q2)^%s' % (hprec / d) return self.__string
def __init__(self, E, sign, normalize="L_ratio"): r""" Modular symbols attached to `E` using ``eclib``. INPUT: - ``E`` - an elliptic curve - ``sign`` - an integer, -1 or 1 - ``normalize`` - either 'L_ratio' (default) or 'none'; For 'L_ratio', the modular symbol is correctly normalized by comparing it to the quotient of `L(E,1)` by the least positive period for the curve and some small twists. For 'none', the modular symbol is almost certainly not correctly normalized, i.e. all values will be a fixed scalar multiple of what they should be. EXAMPLES:: sage: import sage.schemes.elliptic_curves.ell_modular_symbols sage: E=EllipticCurve('11a1') sage: M=sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolECLIB(E,+1) sage: M Modular symbol with sign 1 over Rational Field attached to Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field sage: M(0) 1/5 sage: E=EllipticCurve('11a2') sage: M=sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolECLIB(E,+1) sage: M(0) 1 This is a rank 1 case with vanishing positive twists. The modular symbol can not be adjusted:: sage: E=EllipticCurve('121b1') sage: M=sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolECLIB(E,+1) Warning : Could not normalize the modular symbols, maybe all further results will be multiplied by -1, 2 or -2. sage: M(0) 0 sage: M(1/7) -2 sage: M = EllipticCurve('121d1').modular_symbol(use_eclib=True) sage: M(0) 2 sage: M = EllipticCurve('121d1').modular_symbol(use_eclib=True,normalize='none') sage: M(0) 8 sage: E = EllipticCurve('15a1') sage: [C.modular_symbol(use_eclib=True,normalize='L_ratio')(0) for C in E.isogeny_class(use_tuple=False)] [1/4, 1/8, 1/4, 1/2, 1/8, 1/16, 1/2, 1] sage: [C.modular_symbol(use_eclib=True,normalize='none')(0) for C in E.isogeny_class(use_tuple=False)] [1/4, 1/4, 1/4, 1/4, 1/4, 1/4, 1/4, 1/4] Currently, the interface for negative modular symbols in eclib is not yet written:: sage: E.modular_symbol(use_eclib=True,sign=-1) Traceback (most recent call last): ... NotImplementedError: Despite that eclib has now -1 modular symbols the interface to them is not yet written. TESTS (for trac 10236):: sage: E = EllipticCurve('11a1') sage: m = E.modular_symbol(use_eclib=True) sage: m(1/7) 7/10 sage: m(0) 1/5 """ self._sign = ZZ(sign) if self._sign != sign: raise TypeError, 'sign must be an integer' if self._sign != -1 and self._sign != 1: raise TypeError, 'sign must -1 or 1' if self._sign == -1: raise NotImplementedError, "Despite that eclib has now -1 modular symbols the interface to them is not yet written." self._E = E self._use_eclib = True self._base_ring = QQ self._normalize = normalize self._modsym = ECModularSymbol(E) p = ZZ(2) while not E.is_good(p): p = p.next_prime() # this computes {0,oo} using the Hecke-operator at p self._atzero = sum([self._modsym(ZZ(a) / p) for a in range(p)]) / E.Np(p) if normalize == "L_ratio": self._find_scaling_L_ratio() elif normalize == "none": self._scaling = ZZ(1) else: raise ValueError, "no normalization '%s' known for modular symbols using John Cremona's eclib" % normalize
def siegel_product(self, u): """ Computes the infinite product of local densities of the quadratic form for the number `u`. EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1]) sage: Q.theta_series(11) 1 + 8*q + 24*q^2 + 32*q^3 + 24*q^4 + 48*q^5 + 96*q^6 + 64*q^7 + 24*q^8 + 104*q^9 + 144*q^10 + O(q^11) sage: Q.siegel_product(1) 8 sage: Q.siegel_product(2) ## This one is wrong -- expect 24, and the higher powers of 2 don't work... =( 24 sage: Q.siegel_product(3) 32 sage: Q.siegel_product(5) 48 sage: Q.siegel_product(6) 96 sage: Q.siegel_product(7) 64 sage: Q.siegel_product(9) 104 sage: Q.local_density(2,1) 1 sage: M = 4; len([v for v in mrange([M,M,M,M]) if Q(v) % M == 1]) / M^3 1 sage: M = 16; len([v for v in mrange([M,M,M,M]) if Q(v) % M == 1]) / M^3 # long time (41s on sage.math, 2011) 1 sage: Q.local_density(2,2) 3/2 sage: M = 4; len([v for v in mrange([M,M,M,M]) if Q(v) % M == 2]) / M^3 3/2 sage: M = 16; len([v for v in mrange([M,M,M,M]) if Q(v) % M == 2]) / M^3 # long time (41s on sage.math, 2011) 3/2 TESTS:: sage: [1] + [Q.siegel_product(ZZ(a)) for a in range(1,11)] == Q.theta_series(11).list() True """ ## Protect u (since it fails often if it's an just an int!) u = ZZ(u) n = self.dim() d = self.det( ) ## ??? Warning: This is a factor of 2^n larger than it should be! ## DIAGNOSTIC verbose("n = " + str(n)) verbose("d = " + str(d)) verbose("In siegel_product: d = ", d, "\n") ## Product of "bad" places to omit S = 2 * d * u ## DIAGNOSTIC verbose("siegel_product Break 1. \n") verbose(" u = ", u, "\n") ## Make the odd generic factors if ((n % 2) == 1): m = (n - 1) / 2 d1 = fundamental_discriminant( ((-1)**m) * 2 * d * u) ## Replaced d by 2d here to compensate for the determinant f = abs( d1) ## gaining an odd power of 2 by using the matrix of 2Q instead ## of the matrix of Q. ## --> Old d1 = CoreDiscriminant((mpz_class(-1)^m) * d * u); ## Make the ratio of factorials factor: [(2m)! / m!] * prod_{i=1}^m (2*i-1) factor1 = 1 for i in range(1, m + 1): factor1 *= 2 * i - 1 for i in range(m + 1, 2 * m + 1): factor1 *= i genericfactor = factor1 * ((u / f) ** m) \ * QQ(sqrt((2 ** n) * f) / (u * d)) \ * abs(QuadraticBernoulliNumber(m, d1) / bernoulli(2*m)) ## DIAGNOSTIC verbose("siegel_product Break 2. \n") ## Make the even generic factor if ((n % 2) == 0): m = n / 2 d1 = fundamental_discriminant(((-1)**m) * d) f = abs(d1) ## DIAGNOSTIC #cout << " mpz_class(-1)^m = " << (mpz_class(-1)^m) << " and d = " << d << endl; #cout << " f = " << f << " and d1 = " << d1 << endl; genericfactor = m / QQ(sqrt(f*d)) \ * ((u/2) ** (m-1)) * (f ** m) \ / abs(QuadraticBernoulliNumber(m, d1)) \ * (2 ** m) ## This last factor compensates for using the matrix of 2*Q ##return genericfactor ## Omit the generic factors in S and compute them separately omit = 1 include = 1 S_divisors = prime_divisors(S) ## DIAGNOSTIC #cout << "\n S is " << S << endl; #cout << " The Prime divisors of S are :"; #PrintV(S_divisors); for p in S_divisors: Q_normal = self.local_normal_form(p) ## DIAGNOSTIC verbose(" p = " + str(p) + " and its Kronecker symbol (d1/p) = (" + str(d1) + "/" + str(p) + ") is " + str(kronecker_symbol(d1, p)) + "\n") omit *= 1 / (1 - (kronecker_symbol(d1, p) / (p**m))) ## DIAGNOSTIC verbose(" omit = " + str(omit) + "\n") verbose(" Q_normal is \n" + str(Q_normal) + "\n") verbose(" Q_normal = \n" + str(Q_normal)) verbose(" p = " + str(p) + "\n") verbose(" u = " + str(u) + "\n") verbose(" include = " + str(include) + "\n") include *= Q_normal.local_density(p, u) ## DIAGNOSTIC #cout << " Including the p = " << p << " factor: " << local_density(Q_normal, p, u) << endl; ## DIAGNSOTIC verbose(" --- Exiting loop \n") #// **************** Important ******************* #// Additional fix (only included for n=4) to deal #// with the power of 2 introduced at the real place #// by working with Q instead of 2*Q. This needs to #// be done for all other n as well... #/* #if (n==4) # genericfactor = 4 * genericfactor; #*/ ## DIAGNSOTIC #cout << endl; #cout << " generic factor = " << genericfactor << endl; #cout << " omit = " << omit << endl; #cout << " include = " << include << endl; #cout << endl; ## DIAGNSOTIC #// cout << "siegel_product Break 3. " << endl; ## Return the final factor (and divide by 2 if n=2) if (n == 2): return (genericfactor * omit * include / 2) else: return (genericfactor * omit * include)
def padic_H_value(self, p, f, t, prec=None): """ Return the `p`-adic trace of Frobenius, computed using the Gross-Koblitz formula. If left unspecified, `prec` is set to the minimum `p`-adic precision needed to recover the Euler factor. INPUT: - `p` -- a prime number - `f` -- an integer such that `q = p^f` - `t` -- a rational parameter - ``prec`` -- precision (optional) OUTPUT: an integer EXAMPLES: From Benasque report [Benasque2009]_, page 8:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp sage: H = Hyp(alpha_beta=([1/2]*4,[0]*4)) sage: [H.padic_H_value(3,i,-1) for i in range(1,3)] [0, -12] sage: [H.padic_H_value(5,i,-1) for i in range(1,3)] [-4, 276] sage: [H.padic_H_value(7,i,-1) for i in range(1,3)] [0, -476] sage: [H.padic_H_value(11,i,-1) for i in range(1,3)] [0, -4972] From [Roberts2015]_ (but note conventions regarding `t`):: sage: H = Hyp(gamma_list=[-6,-1,4,3]) sage: t = 189/125 sage: H.padic_H_value(13,1,1/t) 0 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.padic_H_value(5,1,i) for i in range(2,5)] [1, -4, -4] sage: [H2.padic_H_value(5,1,i) for i in range(2,5)] [-4, 1, -4] REFERENCES: - [MagmaHGM]_ """ alpha = self._alpha beta = self._beta t = QQ(t) if 0 in alpha: return self._swap.padic_H_value(p, f, ~t, prec) gamma = self.gamma_array() q = p**f # m = {r: beta.count(QQ((r, q - 1))) for r in range(q - 1)} m = array.array('i', [0] * (q - 1)) for b in beta: u = b * (q - 1) if u.is_integer(): m[u] += 1 M = self.M_value() D = -min(self.zigzag(x, flip_beta=True) for x in alpha + beta) # also: D = (self.weight() + 1 - m[0]) // 2 if prec is None: prec = (self.weight() * f) // 2 + ceil(log(self.degree(), p)) + 1 # For some reason, working in Qp instead of Zp is much faster; # it appears to avoid some costly conversions. p_ring = Qp(p, prec=prec) teich = p_ring.teichmuller(M / t) gauss_table = [None] * (q - 1) for r in range(q - 1): if gauss_table[r] is None: gauss_table[r] = padic_gauss_sum(r, p, f, prec, factored=True, algorithm='sage', parent=p_ring) r1 = (r * p) % (q - 1) while r1 != r: gauss_table[r1] = gauss_table[r] r1 = (r1 * p) % (q - 1) sigma = p_ring.zero() u1 = p_ring.one() for r in range(q - 1): i = int(0) u = u1 u1 *= teich for v, gv in gamma.items(): r1 = (v * r) % (q - 1) i += gauss_table[r1][0] * gv u *= gauss_table[r1][1]**gv sigma += (-p)**(i // (p - 1)) * u << (f * (D + m[0] - m[r])) resu = ZZ(-1)**m[0] / (1 - q) * sigma return IntegerModRing(p**prec)(resu).lift_centered()
def _compute_sw_spherical_harm(s, l, m, theta, phi, condon_shortley=True, numerical=None): r""" Compute the spin-weighted spherical harmonic of spin weight ``s`` and indices ``(l,m)`` as a callable symbolic expression in (theta,phi) INPUT: - ``s`` -- integer; the spin weight - ``l`` -- non-negative integer; the harmonic degree - ``m`` -- integer within the range ``[-l, l]``; the azimuthal number - ``theta`` -- colatitude angle - ``phi`` -- azimuthal angle - ``condon_shortley`` -- (default: ``True``) determines whether the Condon-Shortley phase of `(-1)^m` is taken into account (see below) - ``numerical`` -- (default: ``None``) determines whether a symbolic or a numerical computation of a given type is performed; allowed values are - ``None``: a symbolic computation is performed - ``RDF``: Sage's machine double precision floating-point numbers (``RealDoubleField``) - ``RealField(n)``, where ``n`` is a number of bits: Sage's floating-point numbers with an arbitrary precision; note that ``RR`` is a shortcut for ``RealField(53)``. - ``float``: Python's floating-point numbers OUTPUT: - `{}_s Y_l^m(\theta,\phi)` either - as a symbolic expression if ``numerical`` is ``None`` - or a pair of floating-point numbers, each of them being of the type corresponding to ``numerical`` and representing respectively the real and imaginary parts of `{}_s Y_l^m(\theta,\phi)` ALGORITHM: The spin-weighted spherical harmonic is evaluated according to Eq. (3.1) of J. N. Golberg et al., J. Math. Phys. **8**, 2155 (1967) [:doi:`10.1063/1.1705135`], with an extra `(-1)^m` factor (the so-called *Condon-Shortley phase*) if ``condon_shortley`` is ``True``, the actual formula being then the one given in :wikipedia:`Spin-weighted_spherical_harmonics#Calculating` TESTS:: sage: from kerrgeodesic_gw.spin_weighted_spherical_harm import _compute_sw_spherical_harm sage: theta, phi = var("theta phi") sage: _compute_sw_spherical_harm(-2, 2, 1, theta, phi) 1/4*(sqrt(5)*cos(theta) + sqrt(5))*e^(I*phi)*sin(theta)/sqrt(pi) """ if abs(s)>l: return ZZ(0) if abs(theta) < 1.e-6: # TODO: fix the treatment of small theta values if theta < 0: # possibly with exact formula for theta=0 theta = -1.e-6 # else: # theta = 1.e-6 # cott2 = cos(theta/2)/sin(theta/2) res = 0 for r in range(l-s+1): res += (-1)**(l-r-s) * (binomial(l-s, r) * binomial(l+s, r+s-m) * cott2**(2*r+s-m)) res *= sin(theta/2)**(2*l) ff = factorial(l+m)*factorial(l-m)*(2*l+1) / (factorial(l+s)*factorial(l-s)) if numerical: pre = sqrt(numerical(ff)/numerical(pi))/2 else: pre = sqrt(ff)/(2*sqrt(pi)) res *= pre if condon_shortley: res *= (-1)**m if numerical: return (numerical(res*cos(m*phi)), numerical(res*sin(m*phi))) # Symbolic case: res = res.simplify_full() res = res.reduce_trig() # get rid of cos(theta/2) and sin(theta/2) res = res.simplify_trig() # further trigonometric simplifications res *= exp(I*m*phi) return res
def permutahedron(self, point=None, base_ring=None): r""" Return the permutahedron of ``self``, This is the convex hull of the point ``point`` in the weight basis under the action of ``self`` on the underlying vector space `V`. .. SEEALSO:: :meth:`~sage.combinat.root_system.reflection_group_real.permutahedron` INPUT: - ``point`` -- optional, a point given by its coordinates in the weight basis (default is `(1, 1, 1, \ldots)`) - ``base_ring`` -- optional, the base ring of the polytope .. NOTE:: The result is expressed in the root basis coordinates. .. NOTE:: If function is too slow, switching the base ring to :class:`RDF` will almost certainly speed things up. EXAMPLES:: sage: W = CoxeterGroup(['H',3], base_ring=RDF) sage: W.permutahedron() A 3-dimensional polyhedron in RDF^3 defined as the convex hull of 120 vertices sage: W = CoxeterGroup(['I',7]) sage: W.permutahedron() A 2-dimensional polyhedron in AA^2 defined as the convex hull of 14 vertices sage: W.permutahedron(base_ring=RDF) A 2-dimensional polyhedron in RDF^2 defined as the convex hull of 14 vertices sage: W = ReflectionGroup(['A',3]) # optional - gap3 sage: W.permutahedron() # optional - gap3 A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 24 vertices sage: W = ReflectionGroup(['A',3],['B',2]) # optional - gap3 sage: W.permutahedron() # optional - gap3 A 5-dimensional polyhedron in QQ^5 defined as the convex hull of 192 vertices TESTS:: sage: W = ReflectionGroup(['A',3]) # optional - gap3 sage: W.permutahedron([3,5,8]) # optional - gap3 A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 24 vertices .. PLOT:: :width: 300 px W = CoxeterGroup(['I',7]) p = W.permutahedron() sphinx_plot(p) """ n = self.one().canonical_matrix().rank() weights = self.fundamental_weights() if point is None: point = [ZZ.one()] * n v = sum(point[i-1] * weights[i] for i in weights.keys()) from sage.geometry.polyhedron.constructor import Polyhedron vertices = [v*w for w in self] if base_ring is None and v.base_ring() in [UniversalCyclotomicField(), QQbar]: vertices = [v.change_ring(AA) for v in vertices] base_ring = AA return Polyhedron(vertices=vertices, base_ring=base_ring)
def __init__(self, E, sign, normalize="L_ratio"): """ Modular symbols attached to `E` using ``sage``. INPUT: - ``E`` -- an elliptic curve - ``sign`` -- an integer, -1 or 1 - ``normalize`` -- either 'L_ratio' (default), 'period', or 'none'; For 'L_ratio', the modular symbol is correctly normalized by comparing it to the quotient of `L(E,1)` by the least positive period for the curve and some small twists. The normalization 'period' uses the integral_period_map for modular symbols and is known to be equal to the above normalization up to the sign and a possible power of 2. For 'none', the modular symbol is almost certainly not correctly normalized, i.e. all values will be a fixed scalar multiple of what they should be. But the initial computation of the modular symbol is much faster, though evaluation of it after computing it won't be any faster. EXAMPLES:: sage: E=EllipticCurve('11a1') sage: import sage.schemes.elliptic_curves.ell_modular_symbols sage: M=sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,+1) sage: M Modular symbol with sign 1 over Rational Field attached to Elliptic Curve defined by y^2 + y = x^3 - x^2 - 10*x - 20 over Rational Field sage: M(0) 1/5 sage: E=EllipticCurve('11a2') sage: M=sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,+1) sage: M(0) 1 sage: M=sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,-1) sage: M(1/3) 1 This is a rank 1 case with vanishing positive twists. The modular symbol is adjusted by -2:: sage: E=EllipticCurve('121b1') sage: M=sage.schemes.elliptic_curves.ell_modular_symbols.ModularSymbolSage(E,-1,normalize='L_ratio') sage: M(1/3) 2 sage: M._scaling -2 sage: M = EllipticCurve('121d1').modular_symbol(use_eclib=False) sage: M(0) 2 sage: M = EllipticCurve('121d1').modular_symbol(use_eclib=False,normalize='none') sage: M(0) 1 sage: E = EllipticCurve('15a1') sage: [C.modular_symbol(use_eclib=False, normalize='L_ratio')(0) for C in E.isogeny_class(use_tuple=False)] [1/4, 1/8, 1/4, 1/2, 1/8, 1/16, 1/2, 1] sage: [C.modular_symbol(use_eclib=False, normalize='period')(0) for C in E.isogeny_class(use_tuple=False)] [1/8, 1/16, 1/8, 1/4, 1/16, 1/32, 1/4, 1/2] sage: [C.modular_symbol(use_eclib=False, normalize='none')(0) for C in E.isogeny_class(use_tuple=False)] [1, 1, 1, 1, 1, 1, 1, 1] """ self._sign = ZZ(sign) if self._sign != sign: raise TypeError, 'sign must be an integer' if self._sign != -1 and self._sign != 1: raise TypeError, 'sign must -1 or 1' self._E = E self._use_eclib = False self._normalize = normalize self._modsym = E.modular_symbol_space(sign=self._sign) self._base_ring = self._modsym.base_ring() self._ambient_modsym = self._modsym.ambient_module() if normalize == "L_ratio": self._e = self._modsym.dual_eigenvector() self._find_scaling_L_ratio() if self._failed_to_scale: self._find_scaling_period() # will reset _e and _scaling else: self._e *= self._scaling elif normalize == "period": self._find_scaling_period() # this will set _e and _scaling elif normalize == "none": self._scaling = 1 self._e = self._modsym.dual_eigenvector() else: raise ValueError, "no normalization %s known for modular symbols" % normalize
def shift_factor(p, ram=ZZ.one(), q=1): """ Returns the roots of p in an appropriate extension of the base ring, sorted according to shift equivalence classes. INPUT: - ``p`` -- a univariate polynomial over QQ or a number field - ``ram`` (optional) -- positive integer - ``q`` (optional) -- if set to a quantity different from 1 or 0, the factorization will be made according to the q-shift instead of the ordinary shift. The value must not be a root of unity. OUTPUT: A list of pairs (q, e) where - q is an irreducible factor of p - e is a tuple of pairs (a, b) of nonnegative integers - p = c*prod( sigma^(a/ram)(q)^b for (q, e) in output list for (a, b) in e ) for some nonzero constant c (in the q-case, a possible power of x is also omitted) - e[0][0] == 0, and e[i][0] < e[i+1][0] for all i - any two distinct q have no roots at integer distance. The constant domain must have characteristic zero. In the q-case, ramification greater than 1 requires that q^(1/ram) exists in the constant domain. Note that rootof(q) is the largest root of every class. The other roots are given by rootof(q) - e[i][0]/ram. EXAMPLES:: sage: from ore_algebra.tools import shift_factor sage: x = ZZ['x'].gen() sage: shift_factor((x-2)*(x-4)*(x-8)*(2*x+3)*(2*x+15)) [[x - 8, [(0, 1), (4, 1), (6, 1)]], [2*x + 3, [(0, 1), (6, 1)]]] sage: shift_factor((x-2)*(x-4)*(x-8)*(2*x+3)*(2*x+15), q=2) [[-1/8*x + 1, [(0, 1), (1, 1), (2, 1)]], [2/3*x + 1, [(0, 1)]], [2/15*x + 1, [(0, 1)]]] """ classes = [] x = p.parent().gen() qq = q assert (x.parent().characteristic() == 0) if qq == 1: def sigma(u, n=1): return u(x + n) def candidate(u, v): d = u.degree() return ram * (u[d] * v[d - 1] - u[d - 1] * v[d]) / (u[d] * v[d] * d) else: def sigma(u, n=1): return u(x * qq**n) def candidate(u, v): d = u.degree() try: return -q_log(qq, (u[d] / v[d])**ram) / d except: return None for (q, b) in make_factor_iterator(p.parent())(p): if q.degree() < 1: continue if qq != 1: if q[0].is_zero(): continue else: q /= q[0] # have we already seen a member of the shift equivalence class of q? new = True for i in xrange(len(classes)): u = classes[i][0] if u.degree() != q.degree(): continue a = candidate(q, u) if a not in ZZ or sigma(q, a / ram) != u: continue # yes, we have: q(x+a) == u(x); u(x-a) == q(x) # register it and stop searching a = ZZ(a) new = False if a < 0: classes[i][1].append((-a, b)) elif a > 0: classes[i][0] = q classes[i][1] = [(n + a, m) for (n, m) in classes[i][1]] classes[i][1].append((0, b)) break # no, we haven't. this is the first. if new: classes.append([q, [(0, b)]]) for c in classes: c[1].sort(key=lambda e: e[0]) return classes
def __init__(self, number_field, proof=True, S=None): """ Create a unit group of a number field. INPUT: - ``number_field`` - a number field - ``proof`` - boolean (default True): proof flag - ``S`` - tuple of prime ideals, or an ideal, or a single ideal or element from which an ideal can be constructed, in which case the support is used. If None, the global unit group is constructed; otherwise, the S-unit group is constructed. The proof flag is passed to pari via the ``pari_bnf()`` function which computes the unit group. See the documentation for the number_field module. EXAMPLES:: sage: x = polygen(QQ) sage: K.<a> = NumberField(x^2-38) sage: UK = K.unit_group(); UK Unit group with structure C2 x Z of Number Field in a with defining polynomial x^2 - 38 sage: UK.gens() (u0, u1) sage: UK.gens_values() [-1, -6*a + 37] sage: K.<a> = QuadraticField(-3) sage: UK = K.unit_group(); UK Unit group with structure C6 of Number Field in a with defining polynomial x^2 + 3 with a = 1.732050807568878?*I sage: UK.gens() (u,) sage: UK.gens_values() [-1/2*a + 1/2] sage: K.<z> = CyclotomicField(13) sage: UK = K.unit_group(); UK Unit group with structure C26 x Z x Z x Z x Z x Z of Cyclotomic Field of order 13 and degree 12 sage: UK.gens() (u0, u1, u2, u3, u4, u5) sage: UK.gens_values() # random [-z^11, z^5 + z^3, z^6 + z^5, z^9 + z^7 + z^5, z^9 + z^5 + z^4 + 1, z^5 + z] sage: SUK = UnitGroup(K,S=2); SUK S-unit group with structure C26 x Z x Z x Z x Z x Z x Z of Cyclotomic Field of order 13 and degree 12 with S = (Fractional ideal (2),) TESTS: Number fields defined by non-monic and non-integral polynomials are supported (:trac:`252`); the representation depends on the PARI version:: sage: K.<a> = NumberField(7/9*x^3 + 7/3*x^2 - 56*x + 123) sage: K.unit_group() Unit group with structure C2 x Z x Z of Number Field in a with defining polynomial 7/9*x^3 + 7/3*x^2 - 56*x + 123 sage: UnitGroup(K, S=tuple(K.primes_above(7))) S-unit group with structure C2 x Z x Z x Z of Number Field in a with defining polynomial 7/9*x^3 + 7/3*x^2 - 56*x + 123 with S = (Fractional ideal (...),) sage: K.primes_above(7)[0] in (7/225*a^2 - 7/75*a - 42/25, 28/225*a^2 + 77/75*a - 133/25) True Conversion from unit group to a number field and back gives the right results (:trac:`25874`):: sage: K = QuadraticField(-3).composite_fields(QuadraticField(2))[0] sage: U = K.unit_group() sage: tuple(U(K(u)) for u in U.gens()) == U.gens() True sage: US = K.S_unit_group(3) sage: tuple(US(K(u)) for u in US.gens()) == US.gens() True """ proof = get_flag(proof, "number_field") K = number_field pK = K.pari_bnf(proof) self.__number_field = K self.__pari_number_field = pK # process the parameter S: if not S: S = self.__S = () else: if isinstance(S, list): S = tuple(S) if not isinstance(S, tuple): try: S = tuple(K.ideal(S).prime_factors()) except (NameError, TypeError, ValueError): raise ValueError("Cannot make a set of primes from %s" % (S, )) else: try: S = tuple(K.ideal(P) for P in S) except (NameError, TypeError, ValueError): raise ValueError("Cannot make a set of primes from %s" % (S, )) if not all(P.is_prime() for P in S): raise ValueError( "Not all elements of %s are prime ideals" % (S, )) self.__S = S self.__pS = pS = [P.pari_prime() for P in S] # compute the fundamental units via pari: fu = [K(u, check=False) for u in pK.bnf_get_fu()] self.__nfu = len(fu) # compute the additional S-unit generators: if S: self.__S_unit_data = pK.bnfunits(pS) else: self.__S_unit_data = pK.bnfunits() # TODO: converting the factored matrix representation of bnfunits into polynomial # form is a *big* waste of time su_fu_tu = [ pK.nfbasistoalg(pK.nffactorback(z)) for z in self.__S_unit_data[0] ] self.__nfu = len(pK.bnf_get_fu()) # number of fundamental units self.__nsu = len(su_fu_tu) - self.__nfu - 1 # number of S-units self.__ntu = pK.bnf_get_tu()[0] # order of torsion self.__rank = self.__nfu + self.__nsu # Move the torsion unit first, then fundamental units then S-units gens = [K(u, check=False) for u in su_fu_tu] gens = [gens[-1]] + gens[self.__nsu:-1] + gens[:self.__nsu] # Construct the abstract group: gens_orders = tuple([ZZ(self.__ntu)] + [ZZ(0)] * (self.__rank)) AbelianGroupWithValues_class.__init__(self, gens_orders, 'u', gens, number_field)
def zeta(self, n=2, all=False): """ Return one, or a list of all, primitive n-th root of unity in this unit group. EXAMPLES:: sage: x = polygen(QQ) sage: K.<z> = NumberField(x^2 + 3) sage: U = UnitGroup(K) sage: U.zeta(1) 1 sage: U.zeta(2) -1 sage: U.zeta(2, all=True) [-1] sage: U.zeta(3) -1/2*z - 1/2 sage: U.zeta(3, all=True) [-1/2*z - 1/2, 1/2*z - 1/2] sage: U.zeta(4) Traceback (most recent call last): ... ValueError: n (=4) does not divide order of generator sage: r.<x> = QQ[] sage: K.<b> = NumberField(x^2+1) sage: U = UnitGroup(K) sage: U.zeta(4) b sage: U.zeta(4,all=True) [b, -b] sage: U.zeta(3) Traceback (most recent call last): ... ValueError: n (=3) does not divide order of generator sage: U.zeta(3,all=True) [] """ N = self.__ntu K = self.number_field() n = ZZ(n) if n <= 0: raise ValueError("n (=%s) must be positive" % n) if n == 1: if all: return [K(1)] else: return K(1) elif n == 2: if all: return [K(-1)] else: return K(-1) if n.divides(N): z = self.torsion_generator().value()**(N // n) if all: return [z**i for i in n.coprime_integers(n)] else: return z else: if all: return [] else: raise ValueError("n (=%s) does not divide order of generator" % n)
def normalize_args_vectorspace(*args, **kwds): """ Normalize the arguments that relate to a vector space. INPUT: Something that defines an affine space. For example * An affine space itself: - ``A`` -- affine space * A vector space: - ``V`` -- a vector space * Degree and base ring: - ``degree`` -- integer. The degree of the affine group, that is, the dimension of the affine space the group is acting on. - ``ring`` -- a ring or an integer. The base ring of the affine space. If an integer is given, it must be a prime power and the corresponding finite field is constructed. - ``var='a'`` -- optional keyword argument to specify the finite field generator name in the case where ``ring`` is a prime power. OUTPUT: A pair ``(degree, ring)``. TESTS:: sage: from sage.groups.matrix_gps.named_group import normalize_args_vectorspace sage: A = AffineSpace(2, GF(4,'a')); A Affine Space of dimension 2 over Finite Field in a of size 2^2 sage: normalize_args_vectorspace(A) (2, Finite Field in a of size 2^2) sage: normalize_args_vectorspace(2,4) # shorthand (2, Finite Field in a of size 2^2) sage: V = ZZ^3; V Ambient free module of rank 3 over the principal ideal domain Integer Ring sage: normalize_args_vectorspace(V) (3, Integer Ring) sage: normalize_args_vectorspace(2, QQ) (2, Rational Field) """ from sage.rings.integer_ring import ZZ if len(args) == 1: V = args[0] try: degree = V.dimension_relative() except AttributeError: degree = V.dimension() ring = V.base_ring() if len(args) == 2: degree, ring = args try: ring = ZZ(ring) from sage.rings.finite_rings.finite_field_constructor import FiniteField var = kwds.get('var', 'a') ring = FiniteField(ring, var) except (ValueError, TypeError): pass return (ZZ(degree), ring)
def __call__(self, key): r""" Return all round keys in a list. INPUT: - ``key`` -- integer or bit list-like; the 64-bit key OUTPUT: - A list containing the round keys. If ``key`` is an integer the elements of the output list will be too. If ``key`` is list-like the element of the output list will be bit vectors. EXAMPLES: This implementation is using bit vectors for all internal representations. So you can invoke the key schedule with a bit vector:: sage: from sage.crypto.block_cipher.des import DES_KS sage: K = vector(GF(2),[0,0,0,1,0,0,1,1,0,0,1,1,0,1,0,0,0,1,0,1,0, ....: 1,1,1,0,1,1,1,1,0,0,1,1,0,0,1,1,0,1,1,1,0, ....: 1,1,1,1,0,0,1,1,0,1,1,1,1,1,1,1,1,1,0,0,0,1]) sage: ks = DES_KS(16, K) sage: [k for k in ks] [(0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0), (0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1), ... (1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1)] But of course you can invoke it with hex representation as well:: sage: K = 0x133457799bbcdff1 sage: ks = DES_KS(16, K) sage: [k.hex() for k in ks] ['1b02effc7072', '79aed9dbc9e5', ... 'cb3d8b0e17f5'] .. NOTE:: If you want to use a DES_KS object as an iterable you have to pass a ``masterKey`` value on initialisation. Otherwise you can omit ``masterKey`` and pass a key when you call the object. """ if isinstance(key, (list, tuple, Vector_mod2_dense)): inputType = 'vector' elif isinstance(key, (Integer, int)): inputType = 'integer' key = convert_to_vector(key, self._keySize) roundKeys = [] C, D = self._pc1(key) for i in range(16): C, D = self._left_shift(C, i), self._left_shift(D, i) roundKeys.append(self._pc2(list(C) + list(D))) return roundKeys if inputType == 'vector' else [ ZZ(list(k)[::-1], 2) for k in roundKeys ]