示例#1
0
    def normal_cone(self):
        r"""
        Return the (closure of the) normal cone of the triangulation.

        Recall that a regular triangulation is one that equals the
        "crease lines" of a convex piecewise-linear function. This
        support function is not unique, for example, you can scale it
        by a positive constant. The set of all piecewise-linear
        functions with fixed creases forms an open cone. This cone can
        be interpreted as the cone of normal vectors at a point of the
        secondary polytope, which is why we call it normal cone. See
        [GKZ1994]_ Section 7.1 for details.

        OUTPUT:

        The closure of the normal cone. The `i`-th entry equals the
        value of the piecewise-linear function at the `i`-th point of
        the configuration.

        For an irregular triangulation, the normal cone is empty. In
        this case, a single point (the origin) is returned.

        EXAMPLES::

            sage: triangulation = polytopes.hypercube(2).triangulate(engine='internal')
            sage: triangulation
            (<0,1,3>, <1,2,3>)
            sage: N = triangulation.normal_cone();  N
            4-d cone in 4-d lattice
            sage: N.rays()
            ( 0,  0,  0, -1),
            ( 0,  0,  1,  1),
            ( 0,  0, -1, -1),
            ( 1,  0,  0,  1),
            (-1,  0,  0, -1),
            ( 0,  1,  0, -1),
            ( 0, -1,  0,  1)
            in Ambient free module of rank 4
            over the principal ideal domain Integer Ring
            sage: N.dual().rays()
            (1, -1, 1, -1)
            in Ambient free module of rank 4
            over the principal ideal domain Integer Ring

        TESTS::

            sage: polytopes.simplex(2).triangulate().normal_cone()
            3-d cone in 3-d lattice
            sage: _.dual().is_trivial()
            True
        """
        if not self.point_configuration().base_ring().is_subring(QQ):
            raise NotImplementedError(
                'Only base rings ZZ and QQ are supported')
        from ppl import Constraint_System, Linear_Expression, C_Polyhedron
        from sage.matrix.constructor import matrix
        from sage.arith.all import lcm
        pc = self.point_configuration()
        cs = Constraint_System()
        for facet in self.interior_facets():
            s0, s1 = self._boundary_simplex_dictionary()[facet]
            p = set(s0).difference(facet).pop()
            q = set(s1).difference(facet).pop()
            origin = pc.point(p).reduced_affine_vector()
            base_indices = [i for i in s0 if i != p]
            base = matrix([
                pc.point(i).reduced_affine_vector() - origin
                for i in base_indices
            ])
            sol = base.solve_left(pc.point(q).reduced_affine_vector() - origin)
            relation = [0] * pc.n_points()
            relation[p] = sum(sol) - 1
            relation[q] = 1
            for i, base_i in enumerate(base_indices):
                relation[base_i] = -sol[i]
            rel_denom = lcm([QQ(r).denominator() for r in relation])
            relation = [ZZ(r * rel_denom) for r in relation]
            ex = Linear_Expression(relation, 0)
            cs.insert(ex >= 0)
        from sage.modules.free_module import FreeModule
        ambient = FreeModule(ZZ, self.point_configuration().n_points())
        if cs.empty():
            cone = C_Polyhedron(ambient.dimension(), 'universe')
        else:
            cone = C_Polyhedron(cs)
        from sage.geometry.cone import _Cone_from_PPL
        return _Cone_from_PPL(cone, lattice=ambient)
def has_equivalent_Jordan_decomposition_at_prime(self, other, p):
    """
    Determines if the given quadratic form has a Jordan decomposition
    equivalent to that of self.

    INPUT:

    a QuadraticForm

    OUTPUT:

    boolean

    EXAMPLES::

        sage: Q1 = QuadraticForm(ZZ, 3, [1, 0, -1, 1, 0, 3])
        sage: Q2 = QuadraticForm(ZZ, 3, [1, 0, 0, 2, -2, 6])
        sage: Q3 = QuadraticForm(ZZ, 3, [1, 0, 0, 1, 0, 11])
        sage: [Q1.level(), Q2.level(), Q3.level()]
        [44, 44, 44]
        sage: Q1.has_equivalent_Jordan_decomposition_at_prime(Q2,2)
        False
        sage: Q1.has_equivalent_Jordan_decomposition_at_prime(Q2,11)
        False
        sage: Q1.has_equivalent_Jordan_decomposition_at_prime(Q3,2)
        False
        sage: Q1.has_equivalent_Jordan_decomposition_at_prime(Q3,11)
        True
        sage: Q2.has_equivalent_Jordan_decomposition_at_prime(Q3,2)
        True
        sage: Q2.has_equivalent_Jordan_decomposition_at_prime(Q3,11)
        False

    """
    ## Sanity Checks
    #if not isinstance(other, QuadraticForm):
    if not isinstance(other, type(self)):
        raise TypeError(
            "Oops!  The first argument must be of type QuadraticForm.")
    if not is_prime(p):
        raise TypeError("Oops!  The second argument must be a prime number.")

    ## Get the relevant local normal forms quickly
    self_jordan = self.jordan_blocks_by_scale_and_unimodular(p,
                                                             safe_flag=False)
    other_jordan = other.jordan_blocks_by_scale_and_unimodular(p,
                                                               safe_flag=False)

    ## DIAGNOSTIC
    #print "self_jordan = ", self_jordan
    #print "other_jordan = ", other_jordan

    ## Check for the same number of Jordan components
    if len(self_jordan) != len(other_jordan):
        return False

    ## Deal with odd primes:  Check that the Jordan component scales, dimensions, and discriminants are the same
    if p != 2:
        for i in range(len(self_jordan)):
            if (self_jordan[i][0] != other_jordan[i][0]) \
               or (self_jordan[i][1].dim() != other_jordan[i][1].dim()) \
               or (legendre_symbol(self_jordan[i][1].det() * other_jordan[i][1].det(), p) != 1):
                return False

        ## All tests passed for an odd prime.
        return True

    ## For p = 2:  Check that all Jordan Invariants are the same.
    elif p == 2:

        ## Useful definition
        t = len(self_jordan)  ## Define t = Number of Jordan components

        ## Check that all Jordan Invariants are the same (scale, dim, and norm)
        for i in range(t):
            if (self_jordan[i][0] != other_jordan[i][0]) \
               or (self_jordan[i][1].dim() != other_jordan[i][1].dim()) \
               or (valuation(GCD(self_jordan[i][1].coefficients()), p) != valuation(GCD(other_jordan[i][1].coefficients()), p)):
                return False

        ## DIAGNOSTIC
        #print "Passed the Jordan invariant test."

        ## Use O'Meara's isometry test 93:29 on p277.
        ## ------------------------------------------

        ## List of norms, scales, and dimensions for each i
        scale_list = [ZZ(2)**self_jordan[i][0] for i in range(t)]
        norm_list = [
            ZZ(2)**(self_jordan[i][0] +
                    valuation(GCD(self_jordan[i][1].coefficients()), 2))
            for i in range(t)
        ]
        dim_list = [(self_jordan[i][1].dim()) for i in range(t)]

        ## List of Hessian determinants and Hasse invariants for each Jordan (sub)chain
        ## (Note: This is not the same as O'Meara's Gram determinants, but ratios are the same!)  -- NOT SO GOOD...
        ## But it matters in condition (ii), so we multiply all by 2 (instead of dividing by 2 since only square-factors matter, and it's easier.)
        j = 0
        self_chain_det_list = [
            self_jordan[j][1].Gram_det() * (scale_list[j]**dim_list[j])
        ]
        other_chain_det_list = [
            other_jordan[j][1].Gram_det() * (scale_list[j]**dim_list[j])
        ]
        self_hasse_chain_list = [
            self_jordan[j][1].scale_by_factor(
                ZZ(2)**self_jordan[j][0]).hasse_invariant__OMeara(2)
        ]
        other_hasse_chain_list = [
            other_jordan[j][1].scale_by_factor(
                ZZ(2)**other_jordan[j][0]).hasse_invariant__OMeara(2)
        ]

        for j in range(1, t):
            self_chain_det_list.append(self_chain_det_list[j - 1] *
                                       self_jordan[j][1].Gram_det() *
                                       (scale_list[j]**dim_list[j]))
            other_chain_det_list.append(other_chain_det_list[j - 1] *
                                        other_jordan[j][1].Gram_det() *
                                        (scale_list[j]**dim_list[j]))
            self_hasse_chain_list.append(self_hasse_chain_list[j-1] \
                                         * hilbert_symbol(self_chain_det_list[j-1], self_jordan[j][1].Gram_det(), 2) \
                                         * self_jordan[j][1].hasse_invariant__OMeara(2))
            other_hasse_chain_list.append(other_hasse_chain_list[j-1] \
                                          * hilbert_symbol(other_chain_det_list[j-1], other_jordan[j][1].Gram_det(), 2) \
                                          * other_jordan[j][1].hasse_invariant__OMeara(2))

        ## SANITY CHECK -- check that the scale powers are strictly increasing
        for i in range(1, len(scale_list)):
            if scale_list[i - 1] >= scale_list[i]:
                raise RuntimeError(
                    "Oops!  There is something wrong with the Jordan Decomposition -- the given scales are not strictly increasing!"
                )

        ## DIAGNOSTIC
        #print "scale_list = ", scale_list
        #print "norm_list = ", norm_list
        #print "dim_list = ", dim_list
        #print
        #print "self_chain_det_list = ", self_chain_det_list
        #print "other_chain_det_list = ", other_chain_det_list
        #print "self_hasse_chain_list = ", self_hasse_chain_list
        #print "other_hasse_chain_det_list = ", other_hasse_chain_list

        ## Test O'Meara's two conditions
        for i in range(t - 1):

            ## Condition (i): Check that their (unit) ratio is a square (but it suffices to check at most mod 8).
            modulus = norm_list[i] * norm_list[i + 1] / (scale_list[i]**2)
            if modulus > 8:
                modulus = 8
            if (modulus > 1) and ((
                (self_chain_det_list[i] / other_chain_det_list[i]) % modulus)
                                  != 1):
                #print "Failed when i =", i, " in condition 1."
                return False

            ## Check O'Meara's condition (ii) when appropriate
            if norm_list[i + 1] % (4 * norm_list[i]) == 0:
                if self_hasse_chain_list[i] * hilbert_symbol(norm_list[i] * other_chain_det_list[i], -self_chain_det_list[i], 2) \
                       != other_hasse_chain_list[i] * hilbert_symbol(norm_list[i], -other_chain_det_list[i], 2):      ## Nipp conditions
                    #print "Failed when i =", i, " in condition 2."
                    return False

        ## All tests passed for the prime 2.
        return True

    else:
        raise TypeError("Oops!  This should not have happened.")
示例#3
0
    def _frob_sparse(self, i, j, N0):
        r"""
        Compute `Frob(x^i y^(-j) dx ) / dx` for y^r = f(x) with N0 terms

        INPUT:

        -   ``i`` - The power of x in the expression `Frob(x^i dx/y^j) / dx`

        -   ``j`` - The (negative) power of y in the expression
                    `Frob(x^i dx/y^j) / dx`

        OUTPUT:

        ``frobij`` - a Matrix of size  (d * (N0 - 1) + ) x (N0)
                     that represents the Frobenius expansion of
                     x^i dx/y^j modulo p^(N0 + 1)

                    the entry (l, s) corresponds to the coefficient associated
                    to the monomial x**(p * (i + 1 + l) -1) * y**(p * -(j + r*s))
                    (l, s) --> (p * (i + 1 + l) -1, p * -(j + r*s))

        ALGORITHM:

        Compute:

        Frob(x^i dx/y^j) / dx = p * x ** (p * (i+1) - 1) * y ** (-j*p) * Sigma

        where:

        .. MATH::

            Sigma = \sum_{k = 0} ^{N0-1}
                            \sum_{s = 0} ^k
                                (-1) ** (k-s) * binomial(k, s)
                                * binomial(-j/r, k)
                                * self._frobpow[s]
                                * self._y ** (-self._r * self._p * s)
                    = \sum_{s = 0} ^{N0 - 1}
                        \sum_{k = s} ^N0
                            (-1) ** (k-s) * binomial(k, s)
                            * binomial(-j/self._r, k)
                            * self._frobpow[s]
                            * self._y ** (-self._r*self._p*s)
                    = \sum_{s = 0} ^{N0-1}
                            D_{j, s}
                            * self._frobpow[s]
                            * self._y ** (-self._r * self._p * s)
                    = \sum_{s = 0} ^N0
                        \sum_{l = 0} ^(d*s)
                            D_{j, s} * self._frobpow[s][l]
                            * x ** (self._p ** l)
                            * y ** (-self._r * self._p ** s)

        and:

        .. MATH::

            D_{j, s} = \sum_{k = s} ^N0 (-1) ** (k-s) * binomial(k, s) * binomial(-j/self._r, k) )

        TESTS::

            sage: p = 499
            sage: x = PolynomialRing(GF(p),"x").gen()
            sage: C = CyclicCover(3, x^4 + 4*x^3 + 9*x^2 + 3*x + 1)
            sage: C._init_frob()
            sage: C._frob_sparse(2, 0, 1)
            [499]
            sage: C._frob_sparse(2, 0, 2)
            [499   0]
            [  0   0]
            [  0   0]
            [  0   0]
            [  0   0]
            sage: C._frob_sparse(2, 1, 1)
            [499]
            sage: C._frob_sparse(2, 1, 2)
            [ 82834998  41417000]
            [        0 124251000]
            [        0 124250002]
            [        0  41416501]
            [        0  41417000]
            sage: C._frob_sparse(2, 2, 1)
            [499]
        """
        def _extend_frobpow(power):
            if power < len(self._frobpow_list):
                pass
            else:
                frobpow = self._Zqx(self._frobpow_list[-1])
                for k in range(len(self._frobpow_list), power + 1):
                    frobpow *= self._frobf
                self._frobpow_list.extend([frobpow.list()])
            assert power < len(self._frobpow_list)

        _extend_frobpow(N0)
        r = self._r
        Dj = [
            self._Zq(
                sum([(-1)**(k - l) * binomial(k, l) * binomial(-ZZ(j) / r, k)
                     for k in range(l, N0)])) for l in range(N0)
        ]
        frobij = matrix(self._Zq, self._d * (N0 - 1) + 1, N0)
        for s in range(N0):
            for l in range(self._d * s + 1):
                frobij[l, s] = self._p * Dj[s] * self._frobpow_list[s][l]
        return frobij
示例#4
0
def logpp_gam(p, p_prec):
    """returns the (integral) power series log_p(1+p*z)*(1/log_p(1+p)) where the denominator is computed with some accuracy"""
    L = logpp(p, p_prec)
    ZZp = Zp(p, 2 * p_prec)
    loggam = ZZ(ZZp(1 + p).log(0))
    return ps_normalize(L / loggam, p, p_prec)
示例#5
0
def Krawtchouk(n, q, l, x, check=True):
    """
    Compute ``K^{n,q}_l(x)``, the Krawtchouk (a.k.a. Kravchuk) polynomial.

    See :wikipedia:`Kravchuk_polynomials`; It is defined by the generating function
    `(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)}{x \choose j}{n-x \choose 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: Krawtchouk(24,2,5,4)
        2224
        sage: Krawtchouk(12300,4,5,6)
        567785569973042442072

    TESTS:

    check that the bug reported on :trac:`19561` is fixed::

        sage: Krawtchouk(3,2,3,3)
        -1
        sage: Krawtchouk(int(3),int(2),int(3),int(3))
        -1
        sage: Krawtchouk(int(3),int(2),int(3),int(3),check=False)
        -5
        sage: Kravchuk(24,2,5,4)
        2224

    other unusual inputs ::

        sage: Krawtchouk(sqrt(5),1-I*sqrt(3),3,55.3).n()
        211295.892797... + 1186.42763...*I
        sage: Krawtchouk(-5/2,7*I,3,-1/10)
        480053/250*I - 357231/400
        sage: Krawtchouk(1,1,-1,1)
        Traceback (most recent call last):
        ...
        ValueError: l must be a nonnegative integer
        sage: 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 __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()[0]]
            [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()[0]]
            [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()[0]]
            [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()
            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
示例#7
0
def variance(v, bias=False):
    """
    Returns the variance of the elements of `v`

    We define the variance of the empty list to be NaN,
    following the convention of MATLAB, Scipy, and R.

    INPUT:

        - `v` -- a list of numbers
        
        - ``bias`` -- bool (default: False); if False, divide by
                      len(v) - 1 instead of len(v)
                      to give a less biased estimator (sample) for the 
                      standard deviation. 

    OUTPUT:
        
        - a number


    EXAMPLES::

        sage: variance([1..6])
        7/2
        sage: variance([1..6], bias=True)
        35/12
        sage: variance([e, pi])                 
        1/2*(pi - e)^2
        sage: variance([])                      
        NaN
        sage: variance([I, sqrt(2), 3/5])       
        1/450*(-5*sqrt(2) - 5*I + 6)^2 + 1/450*(10*sqrt(2) - 5*I - 3)^2 + 1/450*(-5*sqrt(2) + 10*I - 3)^2
        sage: variance([RIF(1.0103, 1.0103), RIF(2)])
        0.4897530450000000?
        sage: import numpy
        sage: x = numpy.array([1,2,3,4,5])
        sage: variance(x, bias=False)
        2.5
        sage: x = finance.TimeSeries([1..100])
        sage: variance(x)
        841.6666666666666
        sage: variance(x, bias=True)
        833.25
        sage: class MyClass:
        ...     def variance(self, bias = False):
        ...        return 1    
        sage: stats.variance(MyClass())
        1
        sage: class SillyPythonList:
        ...     def __init__(self):
        ...         self.__list = [2L,4L]
        ...     def __len__(self):
        ...         return len(self.__list)
        ...     def __iter__(self):
        ...         return self.__list.__iter__()
        ...     def mean(self):
        ...         return 3L
        sage: R = SillyPythonList()
        sage: variance(R)
        2
        sage: variance(R, bias=True)
        1
        

    TESTS:

    The performance issue from #10019 is solved::

        sage: variance([1] * 2^18)
        0
    """
    if hasattr(v, 'variance'): return v.variance(bias=bias)
    import numpy

    x = 0
    if isinstance(v, numpy.ndarray):
        # accounts for numpy arrays
        if bias == True:
            return v.var()
        elif bias == False:
            return v.var(ddof=1)
    if len(v) == 0:
        # variance of empty set defined as NaN
        return NaN

    mu = mean(v)
    for vi in v:
        x += (vi - mu)**2
    if bias:
        # population variance
        if isinstance(x, (int, long)):
            return x / ZZ(len(v))
        return x / len(v)
    else:
        # sample variance
        if isinstance(x, (int, long)):
            return x / ZZ(len(v) - 1)
        return x / (len(v) - 1)
def spin_weighted_spherical_harmonic(s,
                                     l,
                                     m,
                                     theta,
                                     phi,
                                     condon_shortley=True,
                                     cached=True,
                                     numerical=None):
    r"""
    Return the spin-weighted spherical harmonic of spin weight ``s`` and
    indices ``(l,m)``.

    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)
    - ``cached`` -- (default: ``True``) determines whether the result shall be
      cached; setting ``cached`` to ``False`` forces a new computation, without
      caching the result
    - ``numerical`` -- (default: ``None``) determines whether a symbolic or
      a numerical computation of a given type is performed; allowed values are

      - ``None``: the type of computation is deduced from the type of ``theta``
      - ``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:

    - the value of `{}_s Y_l^m(\theta,\phi)`, either as a symbolic expression
      or as floating-point complex number of the type determined by
      ``numerical``

    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`

    EXAMPLES::

        sage: from kerrgeodesic_gw import spin_weighted_spherical_harmonic
        sage: theta, phi = var('theta phi')
        sage: spin_weighted_spherical_harmonic(-2, 2, 1, theta, phi)
        1/4*(sqrt(5)*cos(theta) + sqrt(5))*e^(I*phi)*sin(theta)/sqrt(pi)
        sage: spin_weighted_spherical_harmonic(-2, 2, 1, theta, phi,
        ....:                                  condon_shortley=False)
        -1/4*(sqrt(5)*cos(theta) + sqrt(5))*e^(I*phi)*sin(theta)/sqrt(pi)
        sage: spin_weighted_spherical_harmonic(-2, 2, 1, pi/3, pi/4)
        (3/32*I + 3/32)*sqrt(5)*sqrt(3)*sqrt(2)/sqrt(pi)


    Evaluation as floating-point numbers: the type of the output is deduced
    from the input::

        sage: spin_weighted_spherical_harmonic(-2, 2, 1, 1.0, 2.0)  # tol 1.0e-13
        -0.170114676286891 + 0.371707349012686*I
        sage: parent(_)
        Complex Field with 53 bits of precision
        sage: spin_weighted_spherical_harmonic(-2, 2, 1, RDF(2.0), RDF(3.0))  # tol 1.0e-13
        -0.16576451879564585 + 0.023629159118690464*I
        sage: parent(_)
        Complex Double Field
        sage: spin_weighted_spherical_harmonic(-2, 2, 1, float(3.0), float(4.0))  # tol 1.0e-13
        (-0.0002911423884400524-0.00033709085352998027j)
        sage: parent(_)
        <type 'complex'>

    Computation with arbitrary precision are possible (here 100 bits)::

        sage: R100 = RealField(100); R100
        Real Field with 100 bits of precision
        sage: spin_weighted_spherical_harmonic(-2, 2, 1, R100(1.5), R100(2.0))  # tol 1.0e-28
        -0.14018136537676185317636108802 + 0.30630187143465275236861476906*I

    Even when the entry is symbolic, numerical evaluation can be enforced via
    the argument ``numerical``. For instance, setting ``numerical`` to ``RDF``
    (SageMath's Real Double Field)::

        sage: spin_weighted_spherical_harmonic(-2, 2, 1, pi/3, pi/4, numerical=RDF)  # tol 1.0e-13
        0.2897056515173923 + 0.28970565151739225*I
        sage: parent(_)
        Complex Double Field

    One can also use ``numerical=RR`` (SageMath's Real Field with precision set
    to 53 bits)::

        sage: spin_weighted_spherical_harmonic(-2, 2, 1, pi/3, pi/4, numerical=RR)   # tol 1.0e-13
        0.289705651517392 + 0.289705651517392*I
        sage: parent(_)
        Complex Field with 53 bits of precision

    Another option is to use Python floats::

        sage: spin_weighted_spherical_harmonic(-2, 2, 1, pi/3, pi/4, numerical=float)  # tol 1.0e-13
        (0.28970565151739225+0.2897056515173922j)
        sage: parent(_)
        <type 'complex'>

    One can go beyond double precision, for instance using 100 bits of
    precision::

        sage: spin_weighted_spherical_harmonic(-2, 2, 1, pi/3, pi/4,
        ....:                                  numerical=RealField(100))  # tol 1.0e-28
        0.28970565151739218525664455148 + 0.28970565151739218525664455148*I
        sage: parent(_)
        Complex Field with 100 bits of precision

    """
    global _cached_functions, _cached_values
    s = ZZ(s)  # ensure that we are dealing with Sage integers
    l = ZZ(l)
    m = ZZ(m)
    if abs(s) > l:
        return ZZ(0)
    if (isinstance(theta, Expression) and theta.variables() == (theta, )
            and isinstance(phi, Expression) and phi.variables() == (phi, )):
        # Evaluation as a symbolic function
        if cached:
            param = (s, l, m, condon_shortley)
            if param not in _cached_functions:
                _cached_functions[param] = _compute_sw_spherical_harm(
                    s,
                    l,
                    m,
                    theta,
                    phi,
                    condon_shortley=condon_shortley,
                    numerical=None).function(theta, phi)
            return _cached_functions[param](theta, phi)
        return _compute_sw_spherical_harm(s,
                                          l,
                                          m,
                                          theta,
                                          phi,
                                          condon_shortley=condon_shortley,
                                          numerical=None)
    # Numerical or symbolic evaluation
    param = (s, l, m, theta, phi, condon_shortley, str(numerical))
    if not cached or param not in _cached_values:
        # a new computation is required
        if numerical:
            # theta and phi are enforced to be of the type defined by numerical
            theta = numerical(theta)
            phi = numerical(phi)
        else:
            # the type of computation is deduced from that of theta
            if type(theta) is float:
                numerical = float
            elif (theta.parent() is RDF
                  or isinstance(theta.parent(), RealField_class)):
                numerical = theta.parent()
        res = _compute_sw_spherical_harm(s,
                                         l,
                                         m,
                                         theta,
                                         phi,
                                         condon_shortley=condon_shortley,
                                         numerical=numerical)
        if numerical is RDF or isinstance(numerical, RealField_class):
            res = numerical.complex_field()(res)
        elif numerical is float:
            res = complex(*res)
        if cached:
            _cached_values[param] = res
        return res
    return _cached_values[param]
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
示例#10
0
    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
            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),)

            """
        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 type(S) == list:
                S = tuple(S)
            if not type(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) for u in pK.bnfunit()]
        self.__nfu = len(fu)

        # compute the additional S-unit generators:
        if S:
            self.__S_unit_data = pK.bnfsunit(pS)
            su = [K(u) for u in self.__S_unit_data[0]]
        else:
            su = []
        self.__nsu = len(su)
        self.__rank = self.__nfu + self.__nsu

        # 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 + su
        values = Sequence(gens, immutable=True, universe=self, check=False)
        # Construct the abtract group:
        gens_orders = tuple([ZZ(n)] + [ZZ(0)] * (self.__rank))
        AbelianGroupWithValues_class.__init__(self, gens_orders, 'u', values,
                                              number_field)
示例#11
0
    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
示例#12
0
    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)
示例#13
0
    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, ))
示例#14
0
    def guess(self, sequence, algorithm='sage'):
        """
        Return the minimal CFiniteSequence that generates the sequence.

        Assume the first value has index 0.

        INPUT:

        - ``sequence`` -- list of integers
        - ``algorithm`` -- string
            - 'sage' - the default is to use Sage's matrix kernel function
            - 'pari' - use Pari's implementation of LLL
            - 'bm' - use Sage's Berlekamp-Massey algorithm

        OUTPUT:

        - a CFiniteSequence, or 0 if none could be found

        With the default kernel method, trailing zeroes are chopped
        off before a guessing attempt. This may reduce the data
        below the accepted length of six values.

        EXAMPLES::

            sage: C.<x> = CFiniteSequences(QQ)
            sage: C.guess([1,2,4,8,16,32])
            C-finite sequence, generated by -1/2/(x - 1/2)
            sage: r = C.guess([1,2,3,4,5])
            Traceback (most recent call last):
            ...
            ValueError: Sequence too short for guessing.

        With Berlekamp-Massey, if an odd number of values is given, the last one is dropped.
        So with an odd number of values the result may not generate the last value::

            sage: r = C.guess([1,2,4,8,9], algorithm='bm'); r
            C-finite sequence, generated by -1/2/(x - 1/2)
            sage: r[0:5]
            [1, 2, 4, 8, 16]
        """
        S = self.polynomial_ring()
        if algorithm == 'bm':
            from sage.matrix.berlekamp_massey import berlekamp_massey
            if len(sequence) < 2:
                raise ValueError('Sequence too short for guessing.')
            R = PowerSeriesRing(QQ, 'x')
            if len(sequence) % 2 == 1:
                sequence = sequence[:-1]
            l = len(sequence) - 1
            denominator = S(berlekamp_massey(sequence).list()[::-1])
            numerator = R(S(sequence) * denominator, prec=l).truncate()

            return CFiniteSequence(numerator / denominator)
        elif algorithm == 'pari':
            global _gp
            if len(sequence) < 6:
                raise ValueError('Sequence too short for guessing.')
            if _gp is None:
                _gp = Gp()
                _gp("ggf(v)=local(l,m,p,q,B);l=length(v);B=floor(l/2);\
                if(B<3,return(0));m=matrix(B,B,x,y,v[x-y+B+1]);\
                q=qflll(m,4)[1];if(length(q)==0,return(0));\
                p=sum(k=1,B,x^(k-1)*q[k,1]);\
                q=Pol(Pol(vector(l,n,v[l-n+1]))*p+O(x^(B+1)));\
                if(polcoeff(p,0)<0,q=-q;p=-p);q=q/p;p=Ser(q+O(x^(l+1)));\
                for(m=1,l,if(polcoeff(p,m-1)!=v[m],return(0)));q")
            _gp.set('gf', sequence)
            _gp("gf=ggf(gf)")
            num = S(sage_eval(_gp.eval("Vec(numerator(gf))"))[::-1])
            den = S(sage_eval(_gp.eval("Vec(denominator(gf))"))[::-1])
            if num == 0:
                return 0
            else:
                return CFiniteSequence(num / den)
        else:
            from sage.matrix.constructor import matrix
            from sage.functions.other import floor, ceil
            from numpy import trim_zeros
            l = len(sequence)
            while l > 0 and sequence[l-1] == 0:
                l -= 1
            sequence = sequence[:l]
            if l == 0:
                return 0
            if l < 6:
                raise ValueError('Sequence too short for guessing.')

            hl = ceil(ZZ(l)/2)
            A = matrix([sequence[k:k+hl] for k in range(hl)])
            K = A.kernel()
            if K.dimension() == 0:
                return 0
            R = PolynomialRing(QQ, 'x')
            den = R(trim_zeros(K.basis()[-1].list()[::-1]))
            if den == 1:
                return 0
            offset = next((i for i, x in enumerate(sequence) if x!=0), None)
            S = PowerSeriesRing(QQ, 'x', default_prec=l-offset)
            num = S(R(sequence)*den).add_bigoh(floor(ZZ(l)/2+1)).truncate()
            if num == 0 or sequence != S(num/den).list():
                return 0
            else:
                return CFiniteSequence(num / den)
示例#15
0
    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.__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.__scale_by_periods_only__()
            else:
                l1 = self.__lalg__(D)
                if at0 != l1:
                    verbose('scale modular symbols by %s' % (l1 / at0))
                    self._scaling = l1 / at0
示例#16
0
        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))
示例#17
0
    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()[0]]
            [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()[0]]
            [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
示例#18
0
    def random_element(self, M=None):
        r"""
            EXAMPLES::
            
                sage: D = OverconvergentDistributions(0, 7, base=Qp(7,5))
                sage: MS = OverconvergentModularSymbols(14, coefficients=D)
                sage: Phi = MS.random_element()
                sage: Phi._consistency_check()
                This modular symbol satisfies the manin relations
                sage: D = OverconvergentDistributions(2, base=ZpCA(5,10))
                sage: MS = OverconvergentModularSymbols(3, coefficients=D)
                sage: Phi = MS.random_element()
                sage: Phi._consistency_check()
                This modular symbol satisfies the manin relations
        """
        if M is None:
            M = self.precision_cap()
        #if M == 1?
        p = self.prime()
        k = self.weight()
        k_val = ZZ(k).valuation(p) if k != 0 else ZZ.zero()
        manin = self.source()
        gens = manin.gens()
        Id = gens[0]
        gammas = manin.gammas
        gam_shift = 0
        if k != 0:
            ## We will now a pick a generator to later alter in order to solve the difference equation
            if len(gammas) > 1:
                verbose("There is a non-torsion generator")
                gam_keys = gammas.keys()
                gam_keys.remove(Id)
                g0 = gam_keys[0]
                gam0 = gammas[g0]
            else:
                verbose("All generators are torsion")
                g0 = gens[1]
                if g0 in manin.reps_with_two_torsion():
                    verbose("Using a two torsion generator")
                    gam0 = manin.two_torsion_matrix(g0)
                else:
                    verbose("Using a three torsion generator")
                    gam0 = manin.three_torsion_matrix(g0)
            a = gam0.matrix()[0, 0]
            c = gam0.matrix()[1, 0]
            if g0 in manin.reps_with_three_torsion():
                aa = (gam0**2).matrix()[0, 0]
                cc = (gam0**2).matrix()[1, 0]
                #                gam_shift = max(c.valuation(p),cc.valuation(p))
                gam_shift = (a**(k - 1) * c + aa**(k - 1) * cc).valuation(p)
            else:
                gam_shift = c.valuation(p)

        M_in = _prec_for_solve_diff_eqn(M, p) + k_val + gam_shift
        verbose(
            "Working with precision %s (M, p, k, gam_shift) = (%s, %s, %s, %s)"
            % (M_in, M, p, k, gam_shift))
        CM = self.coefficient_module().change_precision(M_in)
        R = CM.base_ring()
        verbose("M_in, new base ring R = %s, %s" % (M_in, R))

        ## this loop runs thru all of the generators (except (0)-(infty)) and randomly chooses a distribution
        ## to assign to this generator (in the 2,3-torsion cases care is taken to satisfy the relevant relation)
        D = {}
        t = CM(0)
        for g in gens[1:]:
            #print "CM._prec_cap", CM.precision_cap()
            D[g] = CM.random_element(M_in)
            #            print "pre:",D[g]
            if g in manin.reps_with_two_torsion():
                if g in manin.reps_with_three_torsion():
                    raise ValueError("Level 1 not implemented")
                gamg = manin.two_torsion_matrix(g)
                D[g] = D[g] - D[g] * gamg
                t -= D[g]
            else:
                if g in manin.reps_with_three_torsion():
                    gamg = manin.three_torsion_matrix(g)
                    D[g] = 2 * D[g] - D[g] * gamg - D[g] * gamg**2
                    t -= D[g]
                else:
                    t += D[g] * gammas[g] - D[g]

        verbose("t after first random choices: %s" % (t))

        ## If k = 0, then t has total measure zero.  However, this is not true when k != 0
        ## (unlike Prop 5.1 of [PS1] this is not a lift of classical symbol).
        ## So instead we simply add (const)*mu_1 to some (non-torsion) v[j] to fix this
        ## here since (mu_1 |_k ([a,b,c,d]-1))(trival char) = chi(a) k a^{k-1} c ,
        ## we take the constant to be minus the total measure of t divided by (chi(a) k a^{k-1} c)
        ## Something a little different is done in the two and three torsion case.

        shift = 0
        if k != 0:
            if CM._character != None:
                chara = CM._character(a)
            else:
                chara = 1
            if not g0 in manin.reps_with_three_torsion():
                err = -t.moment(0) / (chara * k * a**(k - 1) * c)
            else:
                err = -t.moment(0) / (chara * k *
                                      (a**(k - 1) * c + aa**(k - 1) * cc))
            err_val = err.valuation()
            if err_val < 0:
                shift -= err_val
                verbose("err_val: %s, shift: %s, err: %s" %
                        (err_val, shift, err))
                err = err << shift
                verbose("New err: %s" % (err))
                t.ordp += shift
            v = [R(0)] * M_in
            v[1] = R(err)
            err = CM(v)
            verbose("err is: %s" % (err))
            ## In the two and three torsion case, we now adjust err to make it satisfy the torsion Manin relations
            if g0 in manin.reps_with_two_torsion():
                err = err - err * gam0
                t -= err
            elif g0 in manin.reps_with_three_torsion():
                err = 2 * err - err * gam0 - err * gam0**2
                t -= err
            else:
                t += err * gam0 - err

        verbose("The parent of this dist is %s" % (t.parent()))
        verbose("Before adjusting: %s, %s" % (t.ordp, t._moments))
        #try:
        #    mu = t.solve_diff_eqn()
        #except PrecisionError:
        #    verbose("t"%(t.ordp, t._moments, t.precision_absolute()
        verbose("Shift before mu = %s" % (shift))
        #if shift > 0:
        #    t = t.reduce_precision(t.precision_relative() - k.valuation(p) - gam_shift)
        #        t = t.reduce_precision_absolute(t.precision_absolute() - k_val - gam_shift)
        t = t.reduce_precision_absolute(_prec_for_solve_diff_eqn(M, p))
        verbose("About to solve diff_eqn with %s, %s" % (t.ordp, t._moments))
        t.normalize()
        verbose("After normalize: About to solve diff_eqn with %s, %s" %
                (t.ordp, t._moments))
        mu = t.solve_diff_eqn()
        verbose("Check difference equation (right after): %s" %
                (mu * gammas[Id] - mu - t))
        mu_val = mu.valuation()
        verbose("mu_val, mu_ordp, mu_moments and mu: %s, %s, %s, %s" %
                (mu_val, mu.ordp, mu._moments, mu))
        if mu_val < 0:
            shift -= mu_val
            mu.ordp -= mu_val
            if k != 0:
                err.ordp -= mu_val
        verbose("Desired M, mu's M: %s, %s" % (M, mu.precision_relative()))
        verbose("mu.ordp, mu._moments and mu: %s, %s, %s" %
                (mu.ordp, mu._moments, mu))
        mu = mu.reduce_precision_absolute(M)
        mu.normalize()
        verbose("Desired M, mu's M: %s, %s" % (M, mu.precision_relative()))
        verbose("mu.ordp, mu._moments: %s, %s" % (mu.ordp, mu._moments))
        if mu.precision_absolute(
        ) < M:  #Eventually, should just remove this check
            raise ValueError(
                "Insufficient precision after solving the difference equation."
            )
        D[Id] = -mu
        if shift > 0:
            for h in gens[1:]:
                D[h].ordp += shift
        #Should the absolute precision of the other values be lowered as well?
        if k != 0:
            D[g0] += err
        ret = self(D)
        verbose("ret_mu.ordp, ret_mu._moments, ret_mu._prec_rel: %s, %s, %s" %
                (ret._map[Id].ordp, ret._map[Id]._moments,
                 ret._map[Id].precision_relative()))
        t.ordp -= mu_val  #only for verbose
        verbose("Check difference equation (at end): %s" %
                (mu * gammas[Id] - mu - t.reduce_precision(M).normalize()))
        ## NEED TO BE CAREFUL HERE WITH p=2 AND RANDOMNESS BECAUSE OF THE ISSUE OF PLUS/MINUS AND CHAR 2
        if self.sign() == 1:
            return ret.plus_part()
        if self.sign() == -1:
            return ret.minus_part()
        return ret
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
示例#20
0
    def linear_relation(self, List):
        r"""
        Finds a linear relation between the given list of OMSs.  If they are LI, returns a list of all 0's.
    
        INPUT:

        - ``List`` -- a list of OMSs

        OUTPUT:

        - A list of p-adic numbers describing the linear relation of the list of OMSs
        """
        for Phi in List:
            assert Phi.valuation() >= 0, "Symbols must be integral"

        R = self.base()
        ## NEED A COMMAND FOR RELATIVE PRECISION OF OMS
        M = List[0].precision_relative()
        p = self.prime()
        d = len(List)
        V = R**d

        if d == 1:
            if List[0].is_zero():
                return [R(1)]
            else:
                return [R(0)]
        # Would be better to use the library as follows. Unfortunately, matsolvemod
        # is not included in the gen class!!
        #from sage.libs.pari.gen import pari
        #cols = [List[c].list_of_total_measures() for c in range(len(List) - 1)]
        #A = pari(Matrix(ZZ, len(cols[0]), d - 1, lambda i, j : cols[j][i].lift()))
        #aug_col = pari([ZZ(a) for a in List[-1].list_of_total_measures()]).Col()
        #v = A.matsolvemod(p ** M, aug_col)

        s = '['
        for c in range(d - 1):
            v = List[c].list_of_total_measures()
            for r in range(len(v)):
                s += str(ZZ(v[r]))
                if r < len(v) - 1:
                    s += ','
            if c < d - 2:
                s += ';'
        s = s + ']'

        verbose("s = %s" % (s))

        A = gp(s)
        verbose("A = %s" % (A))
        if len(List) == 2:
            A = A.Mat()

        s = '['
        v = List[d - 1].list_of_total_measures()
        for r in range(len(v)):
            s += str(ZZ(v[r]))
            if r < len(v) - 1:
                s += ','
        s += ']~'

        verbose("s = %s" % (s))

        B = gp(s)

        verbose("B = %s" % (B))

        v = A.mattranspose().matsolvemod(p**M, B)

        verbose("v = %s" % (v))

        if v == 0:
            return [R(0) for a in range(len(v))]
        else:
            ## Move back to SAGE from Pari
            v = [R(v[a]) for a in range(1, len(v) + 1)]
            return v + [R(-1)]
示例#21
0
def find_primitive_p_divisible_vector__next(self, p, v=None):
    """
    Finds the next `p`-primitive vector (up to scaling) in `L/pL` whose
    value is `p`-divisible, where the last vector returned was `v`.  For
    an intial call, no `v` needs to be passed.

    Returns vectors whose last non-zero entry is normalized to 0 or 1 (so no
    lines are counted repeatedly).  The ordering is by increasing the
    first non-normalized entry.  If we have tested all (lines of)
    vectors, then return None.

    OUTPUT:
        vector or None

    EXAMPLES::
    
        sage: Q = QuadraticForm(ZZ, 2, [10,1,4])
        sage: v = Q.find_primitive_p_divisible_vector__next(5); v
        (1, 1)
        sage: v = Q.find_primitive_p_divisible_vector__next(5, v); v
        (1, 0)
        sage: v = Q.find_primitive_p_divisible_vector__next(5, v); v


    """
    ## Initialize
    n = self.dim()
    if v == None:
        w = vector([ZZ(0) for i in range(n - 1)] + [ZZ(1)])
    else:
        w = deepcopy(v)

    ## Handle n = 1 separately.
    if n <= 1:
        raise RuntimeError, "Sorry -- Not implemented yet!"

    ## Look for the last non-zero entry (which must be 1)
    nz = n - 1
    while w[nz] == 0:
        nz += -1

    ## Test that the last non-zero entry is 1 (to detect tampering).
    if w[nz] != 1:
        print "Warning: The input vector to QuadraticForm.find_primitive_p_divisible_vector__next() is not normalized properly."

    ## Look for the next vector, until w == 0
    while True:

        ## Look for the first non-maximal (non-normalized) entry
        ind = 0
        while (ind < nz) and (w[ind] == p - 1):
            ind += 1

        #print ind, nz, w

        ## Increment
        if (ind < nz):
            w[ind] += 1
            for j in range(ind):
                w[j] = 0
        else:
            for j in range(ind + 1):  ## Clear all entries
                w[j] = 0

            if nz != 0:  ## Move the non-zero normalized index over by one, or return the zero vector
                w[nz - 1] = 1
                nz += -1

        ## Test for zero vector
        if w == 0:
            return None

        ## Test for p-divisibility
        if (self(w) % p == 0):
            return w
示例#22
0
 def to_int(triple):
     return ZZ(list(reversed(triple)), base=2)
示例#23
0
def MacMahonOmega(var,
                  expression,
                  denominator=None,
                  op=operator.ge,
                  Factorization_sort=False,
                  Factorization_simplify=True):
    r"""
    Return `\Omega_{\mathrm{op}}` of ``expression`` with respect to ``var``.

    To be more precise, calculate

    .. MATH::

        \Omega_{\mathrm{op}} \frac{n}{d_1 \dots d_n}

    for the numerator `n` and the factors `d_1`, ..., `d_n` of
    the denominator, all of which are Laurent polynomials in ``var``
    and return a (partial) factorization of the result.

    INPUT:

    - ``var`` -- a variable or a representation string of a variable

    - ``expression`` -- a
      :class:`~sage.structure.factorization.Factorization`
      of Laurent polynomials or, if ``denominator`` is specified,
      a Laurent polynomial interpreted as the numerator of the
      expression

    - ``denominator`` -- a Laurent polynomial or a
      :class:`~sage.structure.factorization.Factorization` (consisting
      of Laurent polynomial factors) or a tuple/list of factors (Laurent
      polynomials)

    - ``op`` -- (default: ``operator.ge``) an operator

      At the moment only ``operator.ge`` is implemented.

    - ``Factorization_sort`` (default: ``False``) and
      ``Factorization_simplify`` (default: ``True``) -- are passed on to
      :class:`sage.structure.factorization.Factorization` when creating
      the result

    OUTPUT:

    A (partial) :class:`~sage.structure.factorization.Factorization`
    of the result whose factors are Laurent polynomials

    .. NOTE::

        The numerator of the result may not be factored.

    REFERENCES:

    - [Mac1915]_

    - [APR2001]_

    EXAMPLES::

        sage: L.<mu, x, y, z, w> = LaurentPolynomialRing(ZZ)

        sage: MacMahonOmega(mu, 1, [1 - x*mu, 1 - y/mu])
        1 * (-x + 1)^-1 * (-x*y + 1)^-1

        sage: MacMahonOmega(mu, 1, [1 - x*mu, 1 - y/mu, 1 - z/mu])
        1 * (-x + 1)^-1 * (-x*y + 1)^-1 * (-x*z + 1)^-1
        sage: MacMahonOmega(mu, 1, [1 - x*mu, 1 - y*mu, 1 - z/mu])
        (-x*y*z + 1) * (-x + 1)^-1 * (-y + 1)^-1 * (-x*z + 1)^-1 * (-y*z + 1)^-1
        sage: MacMahonOmega(mu, 1, [1 - x*mu, 1 - y/mu^2])
        1 * (-x + 1)^-1 * (-x^2*y + 1)^-1
        sage: MacMahonOmega(mu, 1, [1 - x*mu^2, 1 - y/mu])
        (x*y + 1) * (-x + 1)^-1 * (-x*y^2 + 1)^-1

        sage: MacMahonOmega(mu, 1, [1 - x*mu, 1 - y*mu, 1 - z/mu^2])
        (-x^2*y*z - x*y^2*z + x*y*z + 1) *
        (-x + 1)^-1 * (-y + 1)^-1 * (-x^2*z + 1)^-1 * (-y^2*z + 1)^-1

        sage: MacMahonOmega(mu, 1, [1 - x*mu, 1 - y/mu^3])
        1 * (-x + 1)^-1 * (-x^3*y + 1)^-1
        sage: MacMahonOmega(mu, 1, [1 - x*mu, 1 - y/mu^4])
        1 * (-x + 1)^-1 * (-x^4*y + 1)^-1
        sage: MacMahonOmega(mu, 1, [1 - x*mu^3, 1 - y/mu])
        (x*y^2 + x*y + 1) * (-x + 1)^-1 * (-x*y^3 + 1)^-1
        sage: MacMahonOmega(mu, 1, [1 - x*mu^4, 1 - y/mu])
        (x*y^3 + x*y^2 + x*y + 1) * (-x + 1)^-1 * (-x*y^4 + 1)^-1

        sage: MacMahonOmega(mu, 1, [1 - x*mu^2, 1 - y/mu, 1 - z/mu])
        (x*y*z + x*y + x*z + 1) *
        (-x + 1)^-1 * (-x*y^2 + 1)^-1 * (-x*z^2 + 1)^-1
        sage: MacMahonOmega(mu, 1, [1 - x*mu^2, 1 - y*mu, 1 - z/mu])
        (-x*y*z^2 - x*y*z + x*z + 1) *
        (-x + 1)^-1 * (-y + 1)^-1 * (-x*z^2 + 1)^-1 * (-y*z + 1)^-1

        sage: MacMahonOmega(mu, 1, [1 - x*mu, 1 - y*mu, 1 - z*mu, 1 - w/mu])
        (x*y*z*w^2 + x*y*z*w - x*y*w - x*z*w - y*z*w + 1) *
        (-x + 1)^-1 * (-y + 1)^-1 * (-z + 1)^-1 *
        (-x*w + 1)^-1 * (-y*w + 1)^-1 * (-z*w + 1)^-1
        sage: MacMahonOmega(mu, 1, [1 - x*mu, 1 - y*mu, 1 - z/mu, 1 - w/mu])
        (x^2*y*z*w + x*y^2*z*w - x*y*z*w - x*y*z - x*y*w + 1) *
        (-x + 1)^-1 * (-y + 1)^-1 *
        (-x*z + 1)^-1 * (-x*w + 1)^-1 * (-y*z + 1)^-1 * (-y*w + 1)^-1

        sage: MacMahonOmega(mu, mu^-2, [1 - x*mu, 1 - y/mu])
        x^2 * (-x + 1)^-1 * (-x*y + 1)^-1
        sage: MacMahonOmega(mu, mu^-1, [1 - x*mu, 1 - y/mu])
        x * (-x + 1)^-1 * (-x*y + 1)^-1
        sage: MacMahonOmega(mu, mu, [1 - x*mu, 1 - y/mu])
        (-x*y + y + 1) * (-x + 1)^-1 * (-x*y + 1)^-1
        sage: MacMahonOmega(mu, mu^2, [1 - x*mu, 1 - y/mu])
        (-x*y^2 - x*y + y^2 + y + 1) * (-x + 1)^-1 * (-x*y + 1)^-1

    We demonstrate the different allowed input variants::

        sage: MacMahonOmega(mu,
        ....:     Factorization([(mu, 2), (1 - x*mu, -1), (1 - y/mu, -1)]))
        (-x*y^2 - x*y + y^2 + y + 1) * (-x + 1)^-1 * (-x*y + 1)^-1

        sage: MacMahonOmega(mu, mu^2,
        ....:     Factorization([(1 - x*mu, 1), (1 - y/mu, 1)]))
        (-x*y^2 - x*y + y^2 + y + 1) * (-x + 1)^-1 * (-x*y + 1)^-1

        sage: MacMahonOmega(mu, mu^2, [1 - x*mu, 1 - y/mu])
        (-x*y^2 - x*y + y^2 + y + 1) * (-x + 1)^-1 * (-x*y + 1)^-1

        sage: MacMahonOmega(mu, mu^2, (1 - x*mu)*(1 - y/mu))  # not tested because not fully implemented
        (-x*y^2 - x*y + y^2 + y + 1) * (-x + 1)^-1 * (-x*y + 1)^-1

        sage: MacMahonOmega(mu, mu^2 / ((1 - x*mu)*(1 - y/mu)))  # not tested because not fully implemented
        (-x*y^2 - x*y + y^2 + y + 1) * (-x + 1)^-1 * (-x*y + 1)^-1

    TESTS::

        sage: MacMahonOmega(mu, 1, [1 - x*mu])
        1 * (-x + 1)^-1
        sage: MacMahonOmega(mu, 1, [1 - x/mu])
        1
        sage: MacMahonOmega(mu, 0, [1 - x*mu])
        0
        sage: MacMahonOmega(mu, L(1), [])
        1
        sage: MacMahonOmega(mu, L(0), [])
        0
        sage: MacMahonOmega(mu, 2, [])
        2
        sage: MacMahonOmega(mu, 2*mu, [])
        2
        sage: MacMahonOmega(mu, 2/mu, [])
        0

    ::

        sage: MacMahonOmega(mu, Factorization([(1/mu, 1), (1 - x*mu, -1),
        ....:                                  (1 - y/mu, -2)], unit=2))
        2*x * (-x + 1)^-1 * (-x*y + 1)^-2
        sage: MacMahonOmega(mu, Factorization([(mu, -1), (1 - x*mu, -1),
        ....:                                  (1 - y/mu, -2)], unit=2))
        2*x * (-x + 1)^-1 * (-x*y + 1)^-2
        sage: MacMahonOmega(mu, Factorization([(mu, -1), (1 - x, -1)]))
        0
        sage: MacMahonOmega(mu, Factorization([(2, -1)]))
        1 * 2^-1

    ::

        sage: MacMahonOmega(mu, 1, [1 - x*mu, 1 - z, 1 - y/mu])
        1 * (-z + 1)^-1 * (-x + 1)^-1 * (-x*y + 1)^-1

    ::

        sage: MacMahonOmega(mu, 1, [1 - x*mu], op=operator.lt)
        Traceback (most recent call last):
        ...
        NotImplementedError: At the moment, only Omega_ge is implemented.

        sage: MacMahonOmega(mu, 1, Factorization([(1 - x*mu, -1)]))
        Traceback (most recent call last):
        ...
        ValueError: Factorization (-mu*x + 1)^-1 of the denominator
        contains negative exponents.

        sage: MacMahonOmega(2*mu, 1, [1 - x*mu])
        Traceback (most recent call last):
        ...
        ValueError: 2*mu is not a variable.

        sage: MacMahonOmega(mu, 1, Factorization([(0, 2)]))
        Traceback (most recent call last):
        ...
        ZeroDivisionError: Denominator contains a factor 0.

        sage: MacMahonOmega(mu, 1, [2 - x*mu])
        Traceback (most recent call last):
        ...
        NotImplementedError: Factor 2 - x*mu is not normalized.

        sage: MacMahonOmega(mu, 1, [1 - x*mu - mu^2])
        Traceback (most recent call last):
        ...
        NotImplementedError: Cannot handle factor 1 - x*mu - mu^2.

    ::

        sage: L.<mu, x, y, z, w> = LaurentPolynomialRing(QQ)
        sage: MacMahonOmega(mu, 1/mu,
        ....:     Factorization([(1 - x*mu, 1), (1 - y/mu, 2)], unit=2))
        1/2*x * (-x + 1)^-1 * (-x*y + 1)^-2
    """
    from sage.arith.misc import factor
    from sage.misc.misc_c import prod
    from sage.rings.integer_ring import ZZ
    from sage.rings.polynomial.laurent_polynomial_ring \
        import LaurentPolynomialRing, LaurentPolynomialRing_univariate
    from sage.structure.factorization import Factorization

    if op != operator.ge:
        raise NotImplementedError(
            'At the moment, only Omega_ge is implemented.')

    if denominator is None:
        if isinstance(expression, Factorization):
            numerator = expression.unit() * \
                        prod(f**e for f, e in expression if e > 0)
            denominator = tuple(f for f, e in expression if e < 0
                                for _ in range(-e))
        else:
            numerator = expression.numerator()
            denominator = expression.denominator()
    else:
        numerator = expression
    # at this point we have numerator/denominator

    if isinstance(denominator, (list, tuple)):
        factors_denominator = denominator
    else:
        if not isinstance(denominator, Factorization):
            denominator = factor(denominator)
        if not denominator.is_integral():
            raise ValueError(
                'Factorization {} of the denominator '
                'contains negative exponents.'.format(denominator))
        numerator *= ZZ(1) / denominator.unit()
        factors_denominator = tuple(factor for factor, exponent in denominator
                                    for _ in range(exponent))
    # at this point we have numerator/factors_denominator

    P = var.parent()
    if isinstance(P, LaurentPolynomialRing_univariate) and P.gen() == var:
        L = P
        L0 = L.base_ring()
    elif var in P.gens():
        var = repr(var)
        L0 = LaurentPolynomialRing(
            P.base_ring(), tuple(v for v in P.variable_names() if v != var))
        L = LaurentPolynomialRing(L0, var)
        var = L.gen()
    else:
        raise ValueError('{} is not a variable.'.format(var))

    other_factors = []
    to_numerator = []
    decoded_factors = []
    for factor in factors_denominator:
        factor = L(factor)
        D = factor.dict()
        if not D:
            raise ZeroDivisionError('Denominator contains a factor 0.')
        elif len(D) == 1:
            exponent, coefficient = next(iteritems(D))
            if exponent == 0:
                other_factors.append(L0(factor))
            else:
                to_numerator.append(factor)
        elif len(D) == 2:
            if D.get(0, 0) != 1:
                raise NotImplementedError(
                    'Factor {} is not normalized.'.format(factor))
            D.pop(0)
            exponent, coefficient = next(iteritems(D))
            decoded_factors.append((-coefficient, exponent))
        else:
            raise NotImplementedError(
                'Cannot handle factor {}.'.format(factor))
    numerator = L(numerator) / prod(to_numerator)

    result_numerator, result_factors_denominator = \
        _Omega_(numerator.dict(), decoded_factors)
    if result_numerator == 0:
        return Factorization([], unit=result_numerator)

    return Factorization([(result_numerator, 1)] + list(
        (f, -1) for f in other_factors) + list(
            (1 - f, -1) for f in result_factors_denominator),
                         sort=Factorization_sort,
                         simplify=Factorization_simplify)
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 is_locally_equivalent_to(self,
                             other,
                             check_primes_only=False,
                             force_jordan_equivalence_test=False):
    """
    Determines if the current quadratic form (defined over ZZ) is
    locally equivalent to the given form over the real numbers and the
    `p`-adic integers for every prime p.

    This works by comparing the local Jordan decompositions at every
    prime, and the dimension and signature at the real place.

    INPUT:

    a QuadraticForm

    OUTPUT:

    boolean

    EXAMPLES::

        sage: Q1 = QuadraticForm(ZZ, 3, [1, 0, -1, 2, -1, 5])
        sage: Q2 = QuadraticForm(ZZ, 3, [2, 1, 2, 2, 1, 3])
        sage: Q1.is_globally_equivalent_to(Q2)
        False
        sage: Q1.is_locally_equivalent_to(Q2)
        True

    """
    ## TO IMPLEMENT:
    if self.det() == 0:
        raise NotImplementedError(
            "OOps!  We need to think about whether this still works for degenerate forms...  especially check the signature."
        )

    ## Check that both forms have the same dimension and base ring
    if (self.dim() != other.dim()) or (self.base_ring() != other.base_ring()):
        return False

    ## Check that the determinant and level agree
    if (self.det() != other.det()) or (self.level() != other.level()):
        return False

    ## -----------------------------------------------------

    ## Test equivalence over the real numbers
    if self.signature() != other.signature():
        return False

    ## Test equivalence over Z_p for all primes
    if (self.base_ring() == ZZ) and (force_jordan_equivalence_test == False):

        ## Test equivalence with Conway-Sloane genus symbols (default over ZZ)
        if self.CS_genus_symbol_list() != other.CS_genus_symbol_list():
            return False
    else:
        ## Test equivalence via the O'Meara criterion.
        for p in prime_divisors(ZZ(2) * self.det()):
            #print "checking the prime p = ", p
            if not self.has_equivalent_Jordan_decomposition_at_prime(other, p):
                return False

    ## All tests have passed!
    return True
def local_normal_form(self, p):
    """
    Returns the a locally integrally equivalent quadratic form over
    the p-adic integers Z_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 = copy.deepcopy(self)
    Q.__init__(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

        ## DIAGNOSTIC
        #print "\n Finished Step 2 \n";
        #print "\n Q is: \n" + str(Q)  + "\n";
        #print "  p is: " + str(p)
        #print "  min_val is: " + str( min_val)
        #print "  block_size is: " + str(block_size)
        #print "\n Starting Step 3 \n"

        ## Step 3: Clear out the remaining entries
        ##  ---------------------------------------
        min_scale = p**min_val  ## This is the minimal valuation of the Hessian matrix entries.

        ##DIAGNOSTIC
        #print "Starting Step 3:"
        #print "----------------"
        #print "  min_scale is: " + str(min_scale)

        ## 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)

                ## DIAGNSOTIC
                #print "Cancelling from a 1x1 block:"
                #print "----------------------------"
                #print "  Cancelling entry with index (" + str(upper_left) + ", " + str(j) + ")"
                #print "  entry = " + str(b)
                #print "  gcd = " + str(g)
                #print "  a = " + str(a)
                #print "  b = " + str(b)
                #print "  a/g = " + str(a/g) + "   (used for stretching)"
                #print "  -b/g = " + str(-b/g) + "   (used for cancelling)"

                ## 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)

            ## DIAGNOSTIC
            #print "Cancelling out a 2x2 block:"
            #print "---------------------------"
            #print "  a1 = " + str(a1)
            #print "  a2 = " + str(a2)
            #print "  b1 = " + str(b1)
            #print "  b2 = " + str(b2)
            #print "  big_det = " + str(big_det)
            #print "  min_scale = " + str(min_scale)
            #print "  small_det = " + str(small_det)
            #print "  Q = \n", Q

            ## 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
示例#27
0
 def _N0_RH():
     return ceil(
         log(2 * binomial(2 * self._genus, self._genus), self._p) +
         self._genus * self._n / ZZ(2))
示例#28
0
def regular_symmetric_hadamard_matrix_with_constant_diagonal(
        n, e, existence=False):
    r"""
    Return a Regular Symmetric Hadamard Matrix with Constant Diagonal.

    A Hadamard matrix is said to be *regular* if its rows all sum to the same
    value.

    For `\epsilon\in\{-1,+1\}`, we say that `M` is a `(n,\epsilon)-RSHCD` if
    `M` is a regular symmetric Hadamard matrix with constant diagonal
    `\delta\in\{-1,+1\}` and row sums all equal to `\delta \epsilon
    \sqrt(n)`. For more information, see [HX2010]_ or 10.5.1 in
    [BH2012]_. For the case `n=324`, see :func:`RSHCD_324` and [CP2016]_.

    INPUT:

    - ``n`` (integer) -- side of the matrix

    - ``e`` -- one of `-1` or `+1`, equal to the value of `\epsilon`

    EXAMPLES::

        sage: from sage.combinat.matrices.hadamard_matrix import regular_symmetric_hadamard_matrix_with_constant_diagonal
        sage: regular_symmetric_hadamard_matrix_with_constant_diagonal(4,1)
        [ 1  1  1 -1]
        [ 1  1 -1  1]
        [ 1 -1  1  1]
        [-1  1  1  1]
        sage: regular_symmetric_hadamard_matrix_with_constant_diagonal(4,-1)
        [ 1 -1 -1 -1]
        [-1  1 -1 -1]
        [-1 -1  1 -1]
        [-1 -1 -1  1]

    Other hardcoded values::

        sage: for n,e in [(36,1),(36,-1),(100,1),(100,-1),(196, 1)]:  # long time
        ....:     print(repr(regular_symmetric_hadamard_matrix_with_constant_diagonal(n,e)))
        36 x 36 dense matrix over Integer Ring
        36 x 36 dense matrix over Integer Ring
        100 x 100 dense matrix over Integer Ring
        100 x 100 dense matrix over Integer Ring
        196 x 196 dense matrix over Integer Ring

        sage: for n,e in [(324,1),(324,-1)]: # not tested - long time, tested in RSHCD_324
        ....:     print(repr(regular_symmetric_hadamard_matrix_with_constant_diagonal(n,e)))
        324 x 324 dense matrix over Integer Ring
        324 x 324 dense matrix over Integer Ring

    From two close prime powers::

        sage: regular_symmetric_hadamard_matrix_with_constant_diagonal(64,-1)
        64 x 64 dense matrix over Integer Ring (use the '.str()' method to see the entries)

    From a prime power and a conference matrix::

        sage: regular_symmetric_hadamard_matrix_with_constant_diagonal(676,1)  # long time
        676 x 676 dense matrix over Integer Ring (use the '.str()' method to see the entries)

    Recursive construction::

        sage: regular_symmetric_hadamard_matrix_with_constant_diagonal(144,-1)
        144 x 144 dense matrix over Integer Ring (use the '.str()' method to see the entries)

    REFERENCE:

    - [BH2012]_

    - [HX2010]_
    """
    if existence and (n, e) in _rshcd_cache:
        return _rshcd_cache[n, e]

    from sage.graphs.strongly_regular_db import strongly_regular_graph

    def true():
        _rshcd_cache[n, e] = True
        return True

    M = None
    if abs(e) != 1:
        raise ValueError
    sqn = None
    if is_square(n):
        sqn = int(sqrt(n))
    if n < 0:
        if existence:
            return False
        raise ValueError
    elif n == 4:
        if existence:
            return true()
        if e == 1:
            M = J(4) - 2 * matrix(4, [[int(i + j == 3) for i in range(4)]
                                      for j in range(4)])
        else:
            M = -J(4) + 2 * I(4)
    elif n == 36:
        if existence:
            return true()
        if e == 1:
            M = strongly_regular_graph(36, 15, 6, 6).adjacency_matrix()
            M = J(36) - 2 * M
        else:
            M = strongly_regular_graph(36, 14, 4, 6).adjacency_matrix()
            M = -J(36) + 2 * M + 2 * I(36)
    elif n == 100:
        if existence:
            return true()
        if e == -1:
            M = strongly_regular_graph(100, 44, 18, 20).adjacency_matrix()
            M = 2 * M - J(100) + 2 * I(100)
        else:
            M = strongly_regular_graph(100, 45, 20, 20).adjacency_matrix()
            M = J(100) - 2 * M
    elif n == 196 and e == 1:
        if existence:
            return true()
        M = strongly_regular_graph(196, 91, 42, 42).adjacency_matrix()
        M = J(196) - 2 * M
    elif n == 324:
        if existence:
            return true()
        M = RSHCD_324(e)
    elif (e == 1 and n % 16 == 0 and not sqn is None
          and is_prime_power(sqn - 1) and is_prime_power(sqn + 1)):
        if existence:
            return true()
        M = -rshcd_from_close_prime_powers(sqn)

    elif (e == 1 and not sqn is None and sqn % 4 == 2
          and strongly_regular_graph(sqn - 1, (sqn - 2) // 2, (sqn - 6) // 4,
                                     existence=True) is True
          and is_prime_power(ZZ(sqn + 1))):
        if existence:
            return true()
        M = rshcd_from_prime_power_and_conference_matrix(sqn + 1)

    # Recursive construction: the Kronecker product of two RSHCD is a RSHCD
    else:
        from itertools import product
        for n1, e1 in product(divisors(n)[1:-1], [-1, 1]):
            e2 = e1 * e
            n2 = n // n1
            if (regular_symmetric_hadamard_matrix_with_constant_diagonal(
                    n1, e1, existence=True) is True and
                    regular_symmetric_hadamard_matrix_with_constant_diagonal(
                        n2, e2, existence=True)) is True:
                if existence:
                    return true()
                M1 = regular_symmetric_hadamard_matrix_with_constant_diagonal(
                    n1, e1)
                M2 = regular_symmetric_hadamard_matrix_with_constant_diagonal(
                    n2, e2)
                M = M1.tensor_product(M2)
                break

    if M is None:
        from sage.misc.unknown import Unknown
        _rshcd_cache[n, e] = Unknown
        if existence:
            return Unknown
        raise ValueError("I do not know how to build a {}-RSHCD".format(
            (n, e)))

    assert M * M.transpose() == n * I(n)
    assert set(map(sum, M)) == {ZZ(e * sqn)}

    return M
示例#29
0
    def _reduce_vector_horizontal_BSGS(self, G, e, s):
        r"""
        INPUT:

        - a vector -- G \in W_{e, s}

        OUTPUT:

        - a vector -- H \in W_{e - p, s} such that
            G x^e y^{-s} dx \cong H x^{e - p} y^{-s} dx

        TESTS::

            sage: p = 4999
            sage: x = PolynomialRing(GF(p),"x").gen()
            sage: C = CyclicCover(3, x^4 + 4*x^3 + 9*x^2 + 3*x + 1)
            sage: C._init_frob()
            sage: C._initialize_fat_horizontal(p, 3)
            sage: C._reduce_vector_horizontal_BSGS((0, 0, 0, 0), 2*p  - 1, p)
            (0, 0, 0, 0)
            sage: C._reduce_vector_horizontal_BSGS((83283349998, 0, 0, 0), 2*p  - 1, p)
            (23734897071, 84632332850, 44254975407, 23684517017)
            sage: C._reduce_vector_horizontal_BSGS((98582524551, 3200841460, 6361495378, 98571346457), 2*p - 1, p)
            (96813533420, 61680190736, 123292559950, 96786566978)
        """
        if G == 0:
            return G
        if self._verbose > 2:
            print("_reduce_vector_horizontal_BSGS(self, %s, %s, %s)" %
                  (vector(self._Qq, G), e, s))
        assert (e + 1) % self._p == 0
        (m0, m1), (M0, M1) = self._horizontal_matrix_reduction(s)
        vect = vector(self._Zq, G)
        # we do the first d reductions carefully
        D = 1
        for i in reversed(range(e - self._d + 1, e + 1)):
            Mi = M0 + i * M1
            Di = m0 + i * m1

            vect = Mi * vect
            D *= Di
        assert Di % self._p == 0
        iD = 1 / self._Zq0(D.lift() / self._p)
        vect = vector(self._Zq0,
                      [iD * ZZ(elt.lift() / self._p) for elt in vect])
        # use BSGS

        iDH, MH = self._horizontal_fat_s[s][(e + 1) / self._p - 1]
        vect = iDH * (MH * vect.change_ring(self._Zq0))

        # last reduction
        i = e - self._p + 1
        Mi = M0 + i * M1
        Di = 1 / (m0 + i * m1)

        vect = Di * (Mi * vect.change_ring(self._Zq))

        if self._verbose > 2:
            print("done _reduce_vector_horizontal_BSGS(self, %s, %s, %s)" %
                  (vector(self._Qq, G), e, s))
            print("return %s\n" % (vector(self._Qq, vect), ))
        return vect
示例#30
0
def d_basis(F, strat=True):
    r"""
    Return the `d`-basis for the Ideal ``F`` as defined in [BW93]_.

    INPUT:

    - ``F`` - an ideal
    - ``strat`` - use update strategy (default: ``True``)

    EXAMPLE::

        sage: from sage.rings.polynomial.toy_d_basis import d_basis
        sage: A.<x,y> = PolynomialRing(ZZ, 2)
        sage: f = -y^2 - y + x^3 + 7*x + 1
        sage: fx = f.derivative(x)
        sage: fy = f.derivative(y)
        sage: I = A.ideal([f,fx,fy])
        sage: gb = d_basis(I); gb
        [x - 2020, y - 11313, 22627]
    """
    R = F.ring()
    K = R.base_ring()

    G = set(inter_reduction(F.gens()))
    B = set(
        filter(lambda x_y: x_y[0] != x_y[1],
               [(f1, f2) for f1 in G for f2 in G]))
    D = set()
    C = set(B)

    LCM = R.monomial_lcm
    divides = R.monomial_divides
    divides_ZZ = lambda x, y: ZZ(x).divides(ZZ(y))

    while B != set():
        while C != set():
            f1, f2 = select(C)
            C.remove((f1, f2))
            lcm_lmf1_lmf2 = LCM(LM(f1), LM(f2))
            if not any( divides(LM(g), lcm_lmf1_lmf2) and \
                        divides_ZZ( LC(g), LC(f1) ) and \
                        divides_ZZ( LC(g), LC(f2) ) \
                        for g in G):
                h = gpol(f1, f2)
                h0 = h.reduce(G)
                if h0.lc() < 0:
                    h0 *= -1
                if not strat:
                    D = D.union([(g, h0) for g in G])
                    G.add(h0)
                else:
                    G, D = update(G, D, h0)
                G = inter_reduction(G)

        f1, f2 = select(B)
        B.remove((f1, f2))
        h = spol(f1, f2)
        h0 = h.reduce(G)
        if h0 != 0:
            if h0.lc() < 0:
                h0 *= -1
            if not strat:
                D = D.union([(g, h0) for g in G])
                G.add(h0)
            else:
                G, D = update(G, D, h0)

        B = B.union(D)
        C = D
        D = set()

    return Sequence(sorted(inter_reduction(G), reverse=True))