Beispiel #1
0
    def __init__(self, B):
        r"""
        Initialize the ComputeMinimalPolynomials class.

        INPUT:

        - ``B`` -- a square matrix

        TESTS::

            sage: from sage.matrix.compute_J_ideal import ComputeMinimalPolynomials
            sage: ComputeMinimalPolynomials(matrix([[1, 2]]))
            Traceback (most recent call last):
            ...
            TypeError: square matrix required
        """
        from sage.rings.polynomial.polynomial_ring import polygen

        super(ComputeMinimalPolynomials, self).__init__()
        if not B.is_square():
            raise TypeError("square matrix required")

        self._B = B
        self._D = B.base_ring()
        X = polygen(self._D)
        adjoint = (X - B).adjoint()
        d = B.nrows()**2
        b = matrix(d, 1, adjoint.list())
        self.chi_B = B.charpoly(X)
        self.mu_B = B.minimal_polynomial()
        self._A = matrix.block([[b , -self.chi_B*matrix.identity(d)]])
        self._DX = X.parent()
        self._cache = {}
Beispiel #2
0
    def braid(self, n, K=QQ, names=None):
        r"""
        The braid arrangement.

        INPUT:

        - ``n`` -- integer

        - ``K`` -- field (default: ``QQ``)

        - ``names`` -- tuple of strings or ``None`` (default); the
          variable names for the ambient space

        OUTPUT:

        The hyperplane arrangement consisting of the `n(n-1)/2`
        hyperplanes `\{ x_i - x_j = 0 : 1 \leq i \leq j \leq n \}`.

        EXAMPLES::

            sage: hyperplane_arrangements.braid(4)
            Arrangement of 6 hyperplanes of dimension 4 and rank 3
        """
        x = polygen(QQ, 'x')
        A = self.graphical(graphs.CompleteGraph(n), K, names=names)
        charpoly = prod(x-i for i in range(n))
        A.characteristic_polynomial.set_cache(charpoly)
        return A
Beispiel #3
0
    def hodge_polynomial(self):
        """
        Return the Hodge polynomial.

        .. SEEALSO::

            :meth:`hodge_numbers`, :meth:`hodge_polygon_vertices`, :meth:`hodge_function`

        EXAMPLES::

            sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
            sage: H = Hyp(cyclotomic=([6,10],[3,12]))
            sage: H.hodge_polynomial()
            (T^3 + 2*T^2 + 2*T + 1)/T^2
            sage: H = Hyp(cyclotomic=([2,2,2,2,3,3,3,6,6],[1,1,4,5,9]))
            sage: H.hodge_polynomial()
            (T^5 + 3*T^4 + 3*T^3 + 3*T^2 + 3*T + 1)/T^2
        """
        alpha = self._alpha

        def z(x):
            return alpha.count(x)

        T = polygen(ZZ, 'T')
        return sum(T ** (self.zigzag(a, flip_beta=True) - z(a)) *
                   (T**z(a) - 1) // (T - 1)
                   for a in set(alpha))
Beispiel #4
0
    def braid(self, n, K=QQ, names=None):
        r"""
        The braid arrangement.

        INPUT:

        - ``n`` -- integer

        - ``K`` -- field (default: ``QQ``)

        - ``names`` -- tuple of strings or ``None`` (default); the
          variable names for the ambient space

        OUTPUT:

        The hyperplane arrangement consisting of the `n(n-1)/2`
        hyperplanes `\{ x_i - x_j = 0 : 1 \leq i \leq j \leq n \}`.

        EXAMPLES::

            sage: hyperplane_arrangements.braid(4)
            Arrangement of 6 hyperplanes of dimension 4 and rank 3
        """
        x = polygen(QQ, 'x')
        A = self.graphical(graphs.CompleteGraph(n), K, names=names)
        charpoly = prod(x - i for i in range(n))
        A.characteristic_polynomial.set_cache(charpoly)
        return A
    def hodge_polynomial(self):
        """
        Return the Hodge polynomial.

        .. SEEALSO::

            :meth:`hodge_numbers`

        EXAMPLES::

            sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp
            sage: H = Hyp(cyclotomic=([6,10],[3,12]))
            sage: H.hodge_polynomial()
            (T^3 + 2*T^2 + 2*T + 1)/T^2
            sage: H = Hyp(cyclotomic=([2,2,2,2,3,3,3,6,6],[1,1,4,5,9]))
            sage: H.hodge_polynomial()
            (T^5 + 3*T^4 + 3*T^3 + 3*T^2 + 3*T + 1)/T^2
        """
        alpha = self._alpha

        def z(x):
            return alpha.count(x)

        T = polygen(ZZ, 'T')
        return sum(T ** (self.zigzag(a, flip_beta=True) - z(a)) *
                   (T**z(a) - 1) // (T - 1)
                   for a in set(alpha))
Beispiel #6
0
    def __init__(self, B):
        r"""
        Initialize the ComputeMinimalPolynomials class.

        INPUT:

        - ``B`` -- a square matrix

        TESTS::

            sage: from sage.matrix.compute_J_ideal import ComputeMinimalPolynomials
            sage: ComputeMinimalPolynomials(matrix([[1, 2]]))
            Traceback (most recent call last):
            ...
            TypeError: square matrix required
        """
        from sage.rings.polynomial.polynomial_ring import polygen

        super(ComputeMinimalPolynomials, self).__init__()
        if not B.is_square():
            raise TypeError("square matrix required")

        self._B = B
        self._D = B.base_ring()
        X = polygen(self._D)
        adjoint = (X - B).adjoint()
        d = B.nrows()**2
        b = matrix(d, 1, adjoint.list())
        self.chi_B = B.charpoly(X)
        self.mu_B = B.minimal_polynomial()
        self._A = matrix.block([[b, -self.chi_B * matrix.identity(d)]])
        self._DX = X.parent()
        self._cache = {}
Beispiel #7
0
    def Ish(self, n, K=QQ, names=None):
        r"""
        Return the Ish arrangement.

        INPUT:

        - ``n`` -- integer

        - ``K`` -- field (default:``QQ``)

        - ``names`` -- tuple of strings or ``None`` (default); the
          variable names for the ambient space

        OUTPUT:

        The Ish arrangement, which is the set of `n(n-1)` hyperplanes.

        .. MATH::

            \{ x_i - x_j = 0 : 1 \leq i \leq j \leq n \} 
            \cup 
            \{ x_1 - x_j = i : 1 \leq i \leq j \leq n \}.

        EXAMPLES::

            sage: a = hyperplane_arrangements.Ish(3);  a
            Arrangement of 6 hyperplanes of dimension 3 and rank 2
            sage: a.characteristic_polynomial()
            x^3 - 6*x^2 + 9*x
            sage: b = hyperplane_arrangements.Shi(3)
            sage: b.characteristic_polynomial()
            x^3 - 6*x^2 + 9*x

        TESTS::

            sage: a.characteristic_polynomial.clear_cache()  # long time
            sage: a.characteristic_polynomial()              # long time
            x^3 - 6*x^2 + 9*x

        REFERENCES:

        ..  [AR] D. Armstrong, B. Rhoades
            "The Shi arrangement and the Ish arrangement"
            :arxiv:`1009.1655`
        """
        H = make_parent(K, n, names)
        x = H.gens()
        hyperplanes = []
        for i in range(n):
            for j in range(i + 1, n):
                hyperplanes.append(x[i] - x[j])
                hyperplanes.append(x[0] - x[j] - (i + 1))
        A = H(*hyperplanes)
        x = polygen(QQ, 'x')
        charpoly = x * sum([(-1)**k * stirling_number2(n, n - k) *
                            prod([(x - 1 - j) for j in range(k, n - 1)])
                            for k in range(0, n)])
        A.characteristic_polynomial.set_cache(charpoly)
        return A
def semi_stable_frobenius_polynomial(E: EllipticCurve_number_field,
                                     q: NumberFieldFractionalIdeal,
                                     t=1):
    """
    Input:
        E - an elliptic curve over a number field K
        q - a prime of the number field K
        t - an element of t coprime to q, used to change the purely
            ramified extension that is chosen

    Output:
        A frobenius polymial of an elliptic curve E' over O_K/q that
        is obtained by computing a semistable model of E over a purely
        ramified extension of K_q.
        Note that in the case of multiplicative reduction we return:
        (x-1)*(x-Norm(q)) in the split case and
        (x+1)*(x+Norm(q)) in the nonsplit case

    Note that the output is slightly random, since it is dependent ony the
    purely ramified extension that is chosen. So E' is and hence the frobenius
    polynomial is only defined up to twists.
    """
    K = E.base_ring()
    assert not K(t) in q

    local_data = E.local_data(q)

    if local_data.has_good_reduction():
        Ebar = E.reduction(q)
    elif E.j_invariant().valuation(q) >= 0:
        x = polygen(K)
        u = uniformizer(q)
        e = semistable_ramification(local_data)
        L = K.extension(x**e - t * u, "a")
        EL = E.change_ring(L)
        qL = L.primes_above(q)[0]
        Ebar = reduction(EL, qL)
    else:
        # we have potentially multiplicative reduction
        x = polygen(ZZ)
        if local_data.has_nonsplit_multiplicative_reduction():
            return (x + 1) * (x + q.norm())
        else:
            return (x - 1) * (x - q.norm())

    return Ebar.frobenius_polynomial()
Beispiel #9
0
    def Ish(self, n, K=QQ, names=None):
        r"""
        Return the Ish arrangement.

        INPUT:

        - ``n`` -- integer

        - ``K`` -- field (default:``QQ``)

        - ``names`` -- tuple of strings or ``None`` (default); the
          variable names for the ambient space

        OUTPUT:

        The Ish arrangement, which is the set of `n(n-1)` hyperplanes.

        .. MATH::

            \{ x_i - x_j = 0 : 1 \leq i \leq j \leq n \} 
            \cup 
            \{ x_1 - x_j = i : 1 \leq i \leq j \leq n \}.

        EXAMPLES::

            sage: a = hyperplane_arrangements.Ish(3);  a
            Arrangement of 6 hyperplanes of dimension 3 and rank 2
            sage: a.characteristic_polynomial()
            x^3 - 6*x^2 + 9*x
            sage: b = hyperplane_arrangements.Shi(3)
            sage: b.characteristic_polynomial()
            x^3 - 6*x^2 + 9*x

        TESTS::

            sage: a.characteristic_polynomial.clear_cache()  # long time
            sage: a.characteristic_polynomial()              # long time
            x^3 - 6*x^2 + 9*x

        REFERENCES:

        ..  [AR] D. Armstrong, B. Rhoades
            "The Shi arrangement and the Ish arrangement"
            :arxiv:`1009.1655`
        """
        H = make_parent(K, n, names)
        x = H.gens()
        hyperplanes = []
        for i in range(n):
            for j in range(i+1, n):
                hyperplanes.append(x[i] - x[j])
                hyperplanes.append(x[0] - x[j] - (i+1))
        A = H(*hyperplanes)
        x = polygen(QQ, 'x')
        charpoly = x * sum([(-1)**k * stirling_number2(n, n-k) *
                            prod([(x - 1 - j) for j in range(k, n-1)]) for k in range(0, n)])
        A.characteristic_polynomial.set_cache(charpoly)
        return A
Beispiel #10
0
def test_semi_stable_frobenius_polynomial_t():
    # an example where the frobenius polynomial depends on the purely ramified extension
    # we make
    x = polygen(QQ)
    K = QQ.extension(x - 1, "one")
    E = EllipticCurve(K, [49, 343])
    assert E.discriminant() == -(2**4) * 31 * 7**6
    assert E.j_invariant() == K(2**8 * 3**3) / 31
    f1 = semi_stable_frobenius_polynomial(E, K * 7, 1)
    f2 = semi_stable_frobenius_polynomial(E, K * 7, -1)(x=-x)
    assert f1 == f2
Beispiel #11
0
    def polynomial(self):
        r"""Return a defining polynomial of `\QQ`, as for other number fields.

        This is is also aliased to :meth:`self.defining_polynomial()`
        and :meth:`self.absolute_polynomial()`.

        EXAMPLES::

            sage: QQ.polynomial()
            x

        """
        from sage.rings.polynomial.polynomial_ring import polygen
        return polygen(self)
Beispiel #12
0
def weight(rg, t=None):
    r"""
    Return the weight of a rigging.

    INPUT:

    - ``rg`` -- a rigging, a list of partitions
    - ``t`` -- an optional parameter, (default: the generator from `\ZZ['t']`)

    OUTPUT:

    - a polynomial in the parameter ``t``

    EXAMPLES::

        sage: from sage.combinat.sf.kfpoly import weight
        sage: weight([[2,1], [1]])
        1
        sage: weight([[3], [1]])
        t^2 + t
        sage: weight([[2,1], [3]])
        t^4
        sage: weight([[2, 2], [1, 1]])
        1
        sage: weight([[3, 1], [1, 1]])
        t
        sage: weight([[4], [1, 1]], 2)
        16
        sage: weight([[4], [2]], t=2)
        4
    """
    from sage.combinat.q_analogues import q_binomial
    if t is None:
        t = polygen(ZZ, 't')

    nu = rg + [[]]
    l = 1 + max(map(len, nu))
    nu = [list(mu) + [0] * l for mu in nu]
    res = t**int(sum(i * (i - 1) // 2 for i in rg[-1]))
    for k in range(1, len(nu) - 1):
        sa = 0
        mid = nu[k]
        for i in range(max(len(rg[k]), len(rg[k - 1]))):
            sa += nu[k - 1][i] - 2 * mid[i] + nu[k + 1][i]
            if mid[i] - mid[i + 1] + sa >= 0:
                res *= q_binomial(mid[i] - mid[i + 1] + sa, sa, t)
            mu = nu[k - 1][i] - mid[i]
            res *= t**int(mu * (mu - 1) // 2)
    return res
Beispiel #13
0
def weight(rg, t=None):
    r"""
    Return the weight of a rigging.

    INPUT:

    - ``rg`` -- a rigging, a list of partitions
    - ``t`` -- an optional parameter, (default: the generator from `\ZZ['t']`)

    OUTPUT:

    - a polynomial in the parameter ``t``

    EXAMPLES::

        sage: from sage.combinat.sf.kfpoly import weight
        sage: weight([[2,1], [1]])
        1
        sage: weight([[3], [1]])
        t^2 + t
        sage: weight([[2,1], [3]])
        t^4
        sage: weight([[2, 2], [1, 1]])
        1
        sage: weight([[3, 1], [1, 1]])
        t
        sage: weight([[4], [1, 1]], 2)
        16
        sage: weight([[4], [2]], t=2)
        4
    """
    from sage.combinat.q_analogues import q_binomial
    if t is None:
        t = polygen(ZZ, 't')

    nu = rg + [ [] ]
    l = 1 + max( map(len, nu) )
    nu = [ list(mu) + [0]*l for mu in nu ]
    res = t**int(sum(i * (i-1) // 2 for i in rg[-1]))
    for k in range(1, len(nu)-1):
        sa = 0
        mid = nu[k]
        for i in range( max(len(rg[k]), len(rg[k-1])) ):
            sa += nu[k-1][i] - 2*mid[i] + nu[k+1][i]
            if mid[i] - mid[i+1] + sa >= 0:
                res *= q_binomial(mid[i]-mid[i+1]+sa, sa, t)
            mu = nu[k-1][i] - mid[i]
            res *= t**int(mu * (mu-1) // 2)
    return res
def from_mathematica(a):
    try:
        return QQ(a.sage())
    except Exception:
        pass
    try:
        return AA(a.sage())
    except Exception:
        coefficients = mathematica.CoefficientList(
            mathematica.MinimalPolynomial(a, 'x'), 'x').sage()
        x = polygen(QQ)
        minpoly = x.parent()(coefficients)
        interval = mathematica.IsolatingInterval(a).sage()
        rif_interval = RIF(interval)
        return AA.polynomial_root(minpoly, rif_interval)
Beispiel #15
0
    def semiorder(self, n, K=QQ, names=None):
        r"""
        Return the semiorder arrangement.

        INPUT:

        - ``n`` -- integer

        - ``K`` -- field (default: `\QQ`)

        - ``names`` -- tuple of strings or ``None`` (default); the
          variable names for the ambient space

        OUTPUT:

        The semiorder arrangement, which is the set of `n(n-1)`
        hyperplanes `\{ x_i - x_j = -1,1 : 1 \leq i \leq j \leq n\}`.

        EXAMPLES::

            sage: hyperplane_arrangements.semiorder(4)
            Arrangement of 12 hyperplanes of dimension 4 and rank 3

        TESTS::

            sage: h = hyperplane_arrangements.semiorder(5)
            sage: h.characteristic_polynomial()
            x^5 - 20*x^4 + 180*x^3 - 790*x^2 + 1380*x
            sage: h.characteristic_polynomial.clear_cache()  # long time
            sage: h.characteristic_polynomial()              # long time
            x^5 - 20*x^4 + 180*x^3 - 790*x^2 + 1380*x
        """
        H = make_parent(K, n, names)
        x = H.gens()
        hyperplanes = []
        for i in range(n):
            for j in range(i + 1, n):
                for k in [-1, 1]:
                    hyperplanes.append(x[i] - x[j] - k)
        A = H(*hyperplanes)
        x = polygen(QQ, 'x')
        charpoly = x * sum([
            stirling_number2(n, k) * prod([x - k - i for i in range(1, k)])
            for k in range(1, n + 1)
        ])
        A.characteristic_polynomial.set_cache(charpoly)
        return A
Beispiel #16
0
    def linial(self, n, K=QQ, names=None):
        r"""
        Return the linial hyperplane arrangement.

        INPUT:

        - ``n`` -- integer

        - ``K`` -- field (default: `\QQ`)

        - ``names`` -- tuple of strings or ``None`` (default); the
          variable names for the ambient space

        OUTPUT:

        The linial hyperplane arrangement is the set of hyperplanes
        `\{x_i - x_j = 1 : 1\leq i < j \leq n\}`.

        EXAMPLES::

            sage: a = hyperplane_arrangements.linial(4);  a
            Arrangement of 6 hyperplanes of dimension 4 and rank 3
            sage: a.characteristic_polynomial()
            x^4 - 6*x^3 + 15*x^2 - 14*x

        TESTS::

            sage: h = hyperplane_arrangements.linial(5)
            sage: h.characteristic_polynomial()
            x^5 - 10*x^4 + 45*x^3 - 100*x^2 + 90*x
            sage: h.characteristic_polynomial.clear_cache()  # long time
            sage: h.characteristic_polynomial()              # long time
            x^5 - 10*x^4 + 45*x^3 - 100*x^2 + 90*x
        """
        H = make_parent(K, n, names)
        x = H.gens()
        hyperplanes = []
        for i in range(n):
            for j in range(i + 1, n):
                hyperplanes.append(x[i] - x[j] - 1)
        A = H(*hyperplanes)
        x = polygen(QQ, 'x')
        charpoly = x * sum(
            binomial(n, k) * (x - k)**(n - 1) for k in range(n + 1)) / 2**n
        A.characteristic_polynomial.set_cache(charpoly)
        return A
Beispiel #17
0
    def Shi(self, n, K=QQ, names=None):
        r"""
        Return the Shi arrangement.

        INPUT:

        - ``n`` -- integer

        - ``K`` -- field (default:``QQ``)

        - ``names`` -- tuple of strings or ``None`` (default); the
          variable names for the ambient space

        OUTPUT:

        The Shi arrangement is the set of `n(n-1)` hyperplanes: `\{ x_i - x_j
        = 0,1 : 1 \leq i \leq j \leq n \}`.

        EXAMPLES::

            sage: hyperplane_arrangements.Shi(4)
            Arrangement of 12 hyperplanes of dimension 4 and rank 3

        TESTS::

            sage: h = hyperplane_arrangements.Shi(4)
            sage: h.characteristic_polynomial()
            x^4 - 12*x^3 + 48*x^2 - 64*x
            sage: h.characteristic_polynomial.clear_cache()  # long time
            sage: h.characteristic_polynomial()              # long time
            x^4 - 12*x^3 + 48*x^2 - 64*x
        """
        H = make_parent(K, n, names)
        x = H.gens()
        hyperplanes = []
        for i in range(n):
            for j in range(i + 1, n):
                for const in [0, 1]:
                    hyperplanes.append(x[i] - x[j] - const)
        A = H(*hyperplanes)
        x = polygen(QQ, 'x')
        charpoly = x * sum([(-1)**k * stirling_number2(n, n - k) *
                            prod([(x - 1 - j) for j in range(k, n - 1)])
                            for k in range(0, n)])
        A.characteristic_polynomial.set_cache(charpoly)
        return A
Beispiel #18
0
    def linial(self, n, K=QQ, names=None):
        r"""
        Return the linial hyperplane arrangement.

        INPUT:

        - ``n`` -- integer

        - ``K`` -- field (default: `\QQ`)

        - ``names`` -- tuple of strings or ``None`` (default); the
          variable names for the ambient space

        OUTPUT:

        The linial hyperplane arrangement is the set of hyperplanes
        `\{x_i - x_j = 1 : 1\leq i < j \leq n\}`.

        EXAMPLES::

            sage: a = hyperplane_arrangements.linial(4);  a
            Arrangement of 6 hyperplanes of dimension 4 and rank 3
            sage: a.characteristic_polynomial()
            x^4 - 6*x^3 + 15*x^2 - 14*x

        TESTS::

            sage: h = hyperplane_arrangements.linial(5)
            sage: h.characteristic_polynomial()
            x^5 - 10*x^4 + 45*x^3 - 100*x^2 + 90*x
            sage: h.characteristic_polynomial.clear_cache()  # long time
            sage: h.characteristic_polynomial()              # long time
            x^5 - 10*x^4 + 45*x^3 - 100*x^2 + 90*x
        """
        H = make_parent(K, n, names)
        x = H.gens()
        hyperplanes = []
        for i in range(n):
            for j in range(i+1, n):
                hyperplanes.append(x[i] - x[j] - 1)
        A = H(*hyperplanes)
        x = polygen(QQ, 'x')
        charpoly = x * sum(binomial(n, k)*(x - k)**(n - 1) for k in range(n + 1)) / 2**n
        A.characteristic_polynomial.set_cache(charpoly)
        return A
Beispiel #19
0
    def semiorder(self, n, K=QQ, names=None):
        r"""
        Return the semiorder arrangement.

        INPUT:

        - ``n`` -- integer

        - ``K`` -- field (default: `\QQ`)

        - ``names`` -- tuple of strings or ``None`` (default); the
          variable names for the ambient space

        OUTPUT:

        The semiorder arrangement, which is the set of `n(n-1)`
        hyperplanes `\{ x_i - x_j = -1,1 : 1 \leq i \leq j \leq n\}`.

        EXAMPLES::

            sage: hyperplane_arrangements.semiorder(4)
            Arrangement of 12 hyperplanes of dimension 4 and rank 3

        TESTS::

            sage: h = hyperplane_arrangements.semiorder(5)
            sage: h.characteristic_polynomial()
            x^5 - 20*x^4 + 180*x^3 - 790*x^2 + 1380*x
            sage: h.characteristic_polynomial.clear_cache()  # long time
            sage: h.characteristic_polynomial()              # long time 
            x^5 - 20*x^4 + 180*x^3 - 790*x^2 + 1380*x
        """
        H = make_parent(K, n, names)
        x = H.gens()
        hyperplanes = []
        for i in range(n):
            for j in range(i+1, n):
                for k in [-1, 1]:
                    hyperplanes.append(x[i] - x[j] - k)
        A = H(*hyperplanes)
        x = polygen(QQ, 'x')
        charpoly = x * sum([stirling_number2(n, k) * prod([x - k - i for i in range(1, k)]) 
                            for k in range(1, n+1)])
        A.characteristic_polynomial.set_cache(charpoly)
        return A
Beispiel #20
0
    def Shi(self, n, K=QQ, names=None):
        r"""
        Return the Shi arrangement.

        INPUT:

        - ``n`` -- integer

        - ``K`` -- field (default:``QQ``)

        - ``names`` -- tuple of strings or ``None`` (default); the
          variable names for the ambient space

        OUTPUT:

        The Shi arrangement is the set of `n(n-1)` hyperplanes: `\{ x_i - x_j
        = 0,1 : 1 \leq i \leq j \leq n \}`.

        EXAMPLES::

            sage: hyperplane_arrangements.Shi(4)
            Arrangement of 12 hyperplanes of dimension 4 and rank 3

        TESTS::

            sage: h = hyperplane_arrangements.Shi(4)
            sage: h.characteristic_polynomial()
            x^4 - 12*x^3 + 48*x^2 - 64*x
            sage: h.characteristic_polynomial.clear_cache()  # long time
            sage: h.characteristic_polynomial()              # long time
            x^4 - 12*x^3 + 48*x^2 - 64*x
        """
        H = make_parent(K, n, names)
        x = H.gens()
        hyperplanes = []
        for i in range(n):
            for j in range(i+1, n):
                for const in [0, 1]:
                    hyperplanes.append(x[i] - x[j] - const)
        A = H(*hyperplanes)
        x = polygen(QQ, 'x')
        charpoly = x * sum([(-1)**k * stirling_number2(n, n-k) *
                            prod([(x - 1 - j) for j in range(k, n-1)]) for k in range(0, n)])
        A.characteristic_polynomial.set_cache(charpoly)
        return A
Beispiel #21
0
def kfpoly(mu, nu, t=None):
    r"""
    Return the Kostka-Foulkes polynomial `K_{\mu, \nu}(t)`
    by generating all rigging sequences for the shape `\mu`, and then
    selecting those of content `\nu`.

    INPUT:

    - ``mu``, ``nu`` -- partitions
    - ``t`` -- an optional parameter (default: ``None``)

    OUTPUT:

    - the Koskta-Foulkes polynomial indexed by partitions ``mu`` and ``nu`` and
      evaluated at the parameter ``t``.  If ``t`` is ``None`` the resulting polynomial
      is in the polynomial ring `\ZZ['t']`.

    EXAMPLES::

        sage: from sage.combinat.sf.kfpoly import kfpoly
        sage: kfpoly([2,2], [2,1,1])
        t
        sage: kfpoly([4], [2,1,1])
        t^3
        sage: kfpoly([4], [2,2])
        t^2
        sage: kfpoly([1,1,1,1], [2,2])
        0

    TESTS::

        sage: kfpoly([], [])
        1
    """
    if mu == nu:
        return 1

    if t is None:
        t = polygen(ZZ, 't')

    nuc = _Partitions(nu).conjugate()

    f = lambda x: weight(x, t) if x[0] == nuc else 0

    return sum(f(rg) for rg in riggings(mu))
Beispiel #22
0
    def Catalan(self, n, K=QQ, names=None):
        r"""
        Return the Catalan arrangement.

        INPUT:

        - ``n`` -- integer

        - ``K`` -- field (default: `\QQ`)

        - ``names`` -- tuple of strings or ``None`` (default); the
          variable names for the ambient space

        OUTPUT:

        The arrangement of `3n(n-1)/2` hyperplanes `\{ x_i - x_j =
        -1,0,1 : 1 \leq i \leq j \leq n \}`.

        EXAMPLES::

            sage: hyperplane_arrangements.Catalan(5)
            Arrangement of 30 hyperplanes of dimension 5 and rank 4

        TESTS::

            sage: h = hyperplane_arrangements.Catalan(5)
            sage: h.characteristic_polynomial()
            x^5 - 30*x^4 + 335*x^3 - 1650*x^2 + 3024*x
            sage: h.characteristic_polynomial.clear_cache()  # long time
            sage: h.characteristic_polynomial()              # long time
            x^5 - 30*x^4 + 335*x^3 - 1650*x^2 + 3024*x
        """
        H = make_parent(K, n, names)
        x = H.gens()
        hyperplanes = []
        for i in range(n):
            for j in range(i + 1, n):
                for k in [-1, 0, 1]:
                    hyperplanes.append(x[i] - x[j] - k)
        Cn = H(*hyperplanes)
        x = polygen(QQ, 'x')
        charpoly = x * prod([x - n - i for i in range(1, n)])
        Cn.characteristic_polynomial.set_cache(charpoly)
        return Cn
Beispiel #23
0
    def Catalan(self, n, K=QQ, names=None):
        r"""
        Return the Catalan arrangement.

        INPUT:

        - ``n`` -- integer

        - ``K`` -- field (default: `\QQ`)

        - ``names`` -- tuple of strings or ``None`` (default); the
          variable names for the ambient space

        OUTPUT:

        The arrangement of `3n(n-1)/2` hyperplanes `\{ x_i - x_j =
        -1,0,1 : 1 \leq i \leq j \leq n \}`.

        EXAMPLES::

            sage: hyperplane_arrangements.Catalan(5)
            Arrangement of 30 hyperplanes of dimension 5 and rank 4

        TESTS::

            sage: h = hyperplane_arrangements.Catalan(5)
            sage: h.characteristic_polynomial()
            x^5 - 30*x^4 + 335*x^3 - 1650*x^2 + 3024*x
            sage: h.characteristic_polynomial.clear_cache()  # long time
            sage: h.characteristic_polynomial()              # long time
            x^5 - 30*x^4 + 335*x^3 - 1650*x^2 + 3024*x
        """
        H = make_parent(K, n, names)
        x = H.gens()
        hyperplanes = []
        for i in range(n):
            for j in range(i+1, n):
                for k in [-1, 0, 1]:
                    hyperplanes.append(x[i] - x[j] - k)
        Cn = H(*hyperplanes)
        x = polygen(QQ, 'x')
        charpoly = x*prod([x-n-i for i in range(1, n)])
        Cn.characteristic_polynomial.set_cache(charpoly)
        return Cn
Beispiel #24
0
def kfpoly(mu, nu, t=None):
    r"""
    Returns the Kostka-Foulkes polynomial `K_{\mu, \nu}(t)`
    by generating all rigging sequences for the shape `\mu`, and then
    selecting those of content `\nu`.

    INPUT:

    - ``mu``, ``nu`` -- partitions
    - ``t`` -- an optional parameter (default: ``None``)

    OUTPUT:

    - the Koskta-Foulkes polynomial indexed by partitions ``mu`` and ``nu`` and
      evaluated at the parameter ``t``.  If ``t`` is ``None`` the resulting polynomial
      is in the polynomial ring `\mathbb{Z}['t']`.

    EXAMPLES::

        sage: from sage.combinat.sf.kfpoly import kfpoly
        sage: kfpoly([2,2], [2,1,1])
        t
        sage: kfpoly([4], [2,1,1])
        t^3
        sage: kfpoly([4], [2,2])
        t^2
        sage: kfpoly([1,1,1,1], [2,2])
        0
    """
    if mu == nu:
        return 1
    elif mu == []:
        return 0

    if t is None:
        t = polygen(ZZ, 't')

    nuc = sage.combinat.partition.Partition(nu).conjugate()

    f = lambda x: weight(x, t) if x[0] == nuc else 0

    res = sum([f(rg) for rg in riggings(mu)])
    return res
Beispiel #25
0
def c8_c9_func(SUK, v, A, prec=106):
    r"""
    Return the constants `c_8` and `c_9` from Smart's TCDF paper, [Sma1995]_

    INPUT:

    - ``SUK`` -- a group of `S`-units
    - ``v`` -- a finite place of ``K`` (a fractional ideal)
    - ``A`` -- the set of the product of the coefficients of the `S`-unit equation with each root of unity of ``K``
    - ``prec`` -- (default: 106) the precision of the real field

    OUTPUT:

    The constants ``c8`` and ``c9``, as real numbers

    EXAMPLES::

        sage: from sage.rings.number_field.S_unit_solver import c8_c9_func
        sage: K.<xi> = NumberField(x^3-3)
        sage: SUK = UnitGroup(K, S=tuple(K.primes_above(3)))
        sage: v_fin = K.primes_above(3)[0]
        sage: A = K.roots_of_unity()

        sage: c8_c9_func(SUK, v_fin,A) # abs tol 1e-29
        (4.524941291354698258804956696127e15, 1.621521281297160786545580368612e16)

    REFERENCES:

    - [Sma1995]_ p. 825
    - [Sma1998]_ p. 226, Theorem A.2 for the local constants

    """
    R = RealField(prec)
    num_mus = len(mus(SUK, v))+1
    p = v.smallest_integer()
    f_p = v.residue_class_degree()
    d = SUK.number_field().degree()
    if p == 2:
        local_c2 = Integer(197142) * Integer(36)**num_mus
    elif p%4 == 1:
        local_c2 = Integer(35009) * (Integer(45)/Integer(2))**num_mus
    else:
        local_c2 = Integer(30760) * Integer(25)**num_mus
    x = polygen(SUK.number_field())
    if (p > 2 and not ((x**2+1).is_irreducible())) or (p==2 and not ((x**2+3).is_irreducible())):
        D = d
    else:
        D = 2*d
    l_c3 = (num_mus+1)**(2*num_mus+4) * p**(D * f_p/d) * (f_p*R(p).log())**(-num_mus-1) * D**(num_mus+2)

    def modified_height(SUK, v, D, b):
        #[Sma1998]_ p. 226
        max_log_b = max([phi(b).log().abs() for phi in SUK.number_field().places(prec)])
        return R(max([b.global_height(), max_log_b/(2*R.pi()*D), f_p*R(p).log()/d]))

    mus_prod = prod([modified_height(SUK,v,D,b) for b in mus(SUK,v)])
    local_c3 = R(max([mus_prod*modified_height(SUK, v, D, mu0) for mu0 in possible_mu0s(SUK, v)]))

    l_c3 *= local_c3
    H = max([modified_height(SUK, v, D, alpha) for alpha in mus(SUK, v)+possible_mu0s(SUK, v)])
    if p == 2:
        local_c4 = R(3 * 2**10 * (num_mus+1)**2 * D**2 * H).log()
    else:
        local_c4 = R(2**11 * (num_mus+1)**2 * D**2 * H).log()
    local_c5 = 2 * R(D).log()
    return R(local_c2*l_c3*local_c4), R(local_c2*l_c3*local_c4*local_c5)
Beispiel #26
0
def conjugacy_test(jlist, verbose=False):
    r"""
    Test whether a list of algebraic numbers contains a complete
    conjugacy class of 2-power degree.

    INPUT:

    - `jlist` (list): a list of algebraic numbers in the same field

    - `verbose` (boolean, default ``False``): verbosity flag

    OUTPUT:

    A possibly empty list of irreducible polynomials over `\QQ` of
    2-power degree all of whose roots are in the list.

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.Qcurves import conjugacy_test
        sage: conjugacy_test([3])
        [x - 3]
        sage: K.<a> = QuadraticField(2)
        sage: conjugacy_test([K(3), a])
        [x - 3]
        sage: conjugacy_test([K(3), 3+a])
        [x - 3]
        sage: conjugacy_test([3+a])
        []
        sage: conjugacy_test([3+a, 3-a])
        [x^2 - 6*x + 7]
        sage: x = polygen(QQ)
        sage: f = x^3-3
        sage: K.<a> = f.splitting_field()
        sage: js = f.roots(K, multiplicities=False)
        sage: conjugacy_test(js)
        []
        sage: f = x^4-3
        sage: K.<a> = NumberField(f)
        sage: js = f.roots(K, multiplicities=False)
        sage: conjugacy_test(js)
        []
        sage: K.<a> = f.splitting_field()
        sage: js = f.roots(K, multiplicities=False)
        sage: conjugacy_test(js)
        [x^4 - 3]

    """
    from sage.sets.set import Set

    # First test to see if the list contains a rational

    jQ = next((j for j in jlist if j in QQ), None)
    if jQ:
        if verbose:
            print("Yes: an isogenous curve has rational j-invariant {}".format(
                jQ))
        x = polygen(QQ)
        return [x - jQ]

    # If the degree d is odd then we know that none of the
    # j-invariants in the class have 2-power degree, so we can exit.

    K = jlist[0].parent()
    if K.degree() % 2:
        if verbose:
            print("Odd-degree case: no rational j-invariant in the class {}".
                  format(jlist))
        return []

    # If K has no quadratic subfields we can similarly conclude right
    # away.  This is one way of determining this.

    if K(1).descend_mod_power(QQ, 2) == [1]:
        if verbose:
            print(
                "No-quadratic-subfield case: no rational j-invariant in the class {}"
                .format(jlist))
        return []

    # compute the minimum polynomials of the j-invariants in the class
    pols = [j.minpoly() for j in jlist]

    # pick out those of 2-power degree
    pols = [f for f in pols if f.degree().prime_to_m_part(2) == 1]

    # If none, there is nothing to do
    if not pols:
        return []

    # see if there's a poly of degree d appearing d times.  NB There
    # may be more than one of these, possibly including some conjugacy
    # classes defined over the core field but not central, so we
    # return all those with the minimal degree.

    mindeg = min([f.degree() for f in pols])
    minpols = [f for f in pols if f.degree() == mindeg]
    centrepols = list(Set([f for f in pols if f.degree() == minpols.count(f)]))
    if centrepols:
        if verbose:
            print(
                "Yes: the isogeny class contains all j-invariants with min poly {}"
                .format(centrepols))
        return centrepols
    if verbose:
        print("No complete conjugacy class of 2-power size found in {}".format(
            jlist))
    return []
Beispiel #27
0
def schur_to_hl(mu, t=None):
    r"""
    Returns a dictionary corresponding to `s_\mu` in Hall-Littlewood `P` basis.

    INPUT:

    - ``mu`` -- a partition
    - ``t`` -- an optional parameter (default : the generator from `\mathbb{Z}['t']` )

    OUTPUT:

    - a dictionary with the coefficients `K_{\mu\nu}(t)` for `\nu` smaller
      in dominance order than `\mu`

    EXAMPLES::

        sage: from sage.combinat.sf.kfpoly import *
        sage: schur_to_hl([1,1,1])
        {[1, 1, 1]: 1}
        sage: a = schur_to_hl([2,1])
        sage: for m,c in sorted(a.iteritems()): print m, c
        [1, 1, 1] t^2 + t
        [2, 1] 1
        sage: a = schur_to_hl([3])
        sage: for m,c in sorted(a.iteritems()): print m, c
        [1, 1, 1] t^3
        [2, 1] t
        [3] 1
        sage: a = schur_to_hl([4])
        sage: for m,c in sorted(a.iteritems()): print m, c
        [1, 1, 1, 1] t^6
        [2, 1, 1] t^3
        [2, 2] t^2
        [3, 1] t
        [4] 1
        sage: a = schur_to_hl([3,1])
        sage: for m,c in sorted(a.iteritems()): print m, c
        [1, 1, 1, 1] t^5 + t^4 + t^3
        [2, 1, 1] t^2 + t
        [2, 2] t
        [3, 1] 1
        sage: a = schur_to_hl([2,2])
        sage: for m,c in sorted(a.iteritems()): print m, c
        [1, 1, 1, 1] t^4 + t^2
        [2, 1, 1] t
        [2, 2] 1
        sage: a = schur_to_hl([2,1,1])
        sage: for m,c in sorted(a.iteritems()): print m, c
        [1, 1, 1, 1] t^3 + t^2 + t
        [2, 1, 1] 1
        sage: a = schur_to_hl([1,1,1,1])
        sage: for m,c in sorted(a.iteritems()): print m, c
        [1, 1, 1, 1] 1
        sage: a = schur_to_hl([2,2,2])
        sage: for m,c in sorted(a.iteritems()): print m, c
        [1, 1, 1, 1, 1, 1] t^9 + t^7 + t^6 + t^5 + t^3
        [2, 1, 1, 1, 1] t^4 + t^2
        [2, 2, 1, 1] t
        [2, 2, 2] 1
    """
    if mu == []:
        return {mu: 1}
    if t is None:
        t = polygen(ZZ, 't')

    res = {}
    for rg in riggings(mu):
        res[ rg[0] ] = res.get(rg[0], 0) + weight(rg, t)

    d = {}
    for key in res:
        d[ key.conjugate() ] = res[key]
    return d
Beispiel #28
0
def schur_to_hl(mu, t=None):
    r"""
    Returns a dictionary corresponding to `s_\mu` in Hall-Littlewood `P` basis.

    INPUT:

    - ``mu`` -- a partition
    - ``t`` -- an optional parameter (default : the generator from `\mathbb{Z}['t']` )

    OUTPUT:

    - a dictionary with the coefficients `K_{\mu\nu}(t)` for `\nu` smaller
      in dominance order than `\mu`

    EXAMPLES::

        sage: from sage.combinat.sf.kfpoly import *
        sage: schur_to_hl([1,1,1])
        {[1, 1, 1]: 1}
        sage: a = schur_to_hl([2,1])
        sage: for m,c in sorted(a.iteritems()): print m, c
        [1, 1, 1] t^2 + t
        [2, 1] 1
        sage: a = schur_to_hl([3])
        sage: for m,c in sorted(a.iteritems()): print m, c
        [1, 1, 1] t^3
        [2, 1] t
        [3] 1
        sage: a = schur_to_hl([4])
        sage: for m,c in sorted(a.iteritems()): print m, c
        [1, 1, 1, 1] t^6
        [2, 1, 1] t^3
        [2, 2] t^2
        [3, 1] t
        [4] 1
        sage: a = schur_to_hl([3,1])
        sage: for m,c in sorted(a.iteritems()): print m, c
        [1, 1, 1, 1] t^5 + t^4 + t^3
        [2, 1, 1] t^2 + t
        [2, 2] t
        [3, 1] 1
        sage: a = schur_to_hl([2,2])
        sage: for m,c in sorted(a.iteritems()): print m, c
        [1, 1, 1, 1] t^4 + t^2
        [2, 1, 1] t
        [2, 2] 1
        sage: a = schur_to_hl([2,1,1])
        sage: for m,c in sorted(a.iteritems()): print m, c
        [1, 1, 1, 1] t^3 + t^2 + t
        [2, 1, 1] 1
        sage: a = schur_to_hl([1,1,1,1])
        sage: for m,c in sorted(a.iteritems()): print m, c
        [1, 1, 1, 1] 1
        sage: a = schur_to_hl([2,2,2])
        sage: for m,c in sorted(a.iteritems()): print m, c
        [1, 1, 1, 1, 1, 1] t^9 + t^7 + t^6 + t^5 + t^3
        [2, 1, 1, 1, 1] t^4 + t^2
        [2, 2, 1, 1] t
        [2, 2, 2] 1
    """
    if mu == []:
        return {mu: 1}
    if t is None:
        t = polygen(ZZ, 't')

    res = {}
    for rg in riggings(mu):
        res[rg[0]] = res.get(rg[0], 0) + weight(rg, t)

    d = {}
    for key in res:
        d[key.conjugate()] = res[key]
    return d
Beispiel #29
0
def is_Q_curve(E, maxp=100, certificate=False, verbose=False):
    r"""
    Return whether ``E`` is a `\QQ`-curve, with optional certificate.

    INPUT:

    - ``E`` (elliptic curve) -- an elliptic curve over a number field.

    - ``maxp`` (int, default 100): bound on primes used for checking
      necessary local conditions.  The result will not depend on this,
      but using a larger value may return ``False`` faster.

    - ``certificate`` (bool, default ``False``): if ``True`` then a
      second value is returned giving a certificate for the
      `\QQ`-curve property.

    OUTPUT:

    If ``certificate`` is ``False``: either ``True`` (if `E` is a
    `\QQ`-curve), or ``False``.

    If ``certificate`` is ``True``: a tuple consisting of a boolean
    flag as before and a certificate, defined as follows:

    - when the flag is ``True``, so `E` is a `\QQ`-curve:

        - either {'CM':`D`} where `D` is a negative discriminant, when
          `E` has potential CM with discriminant `D`;

        - otherwise {'CM': `0`, 'core_poly': `f`, 'rho': `\rho`, 'r':
          `r`, 'N': `N`}, when `E` is a non-CM `\QQ`-curve, where the
          core polynomial `f` is an irreducible monic polynomial over
          `QQ` of degree `2^\rho`, all of whose roots are
          `j`-invariants of curves isogenous to `E`, the core level
          `N` is a square-free integer with `r` prime factors which is
          the LCM of the degrees of the isogenies between these
          conjugates.  For example, if there exists a curve `E'`
          isogenous to `E` with `j(E')=j\in\QQ`, then the certificate
          is {'CM':0, 'r':0, 'rho':0, 'core_poly': x-j, 'N':1}.

    - when the flag is ``False``, so `E` is not a `\QQ`-curve, the
      certificate is a prime `p` such that the reductions of `E` at
      the primes dividing `p` are inconsistent with the property of
      being a `\QQ`-curve.  See the ALGORITHM section for details.

    ALGORITHM:

    See [CrNa2020]_ for details.

    1. If `E` has rational `j`-invariant, or has CM, then return
    ``True``.

    2. Replace `E` by a curve defined over `K=\QQ(j(E))`. Let `N` be
    the conductor norm.

    3. For all primes `p\mid N` check that the valuations of `j` at
    all `P\mid p` are either all negative or all non-negative; if not,
    return ``False``.

    4. For `p\le maxp`, `p\not\mid N`, check that either `E` is
    ordinary mod `P` for all `P\mid p`, or `E` is supersingular mod
    `P` for all `P\mid p`; if neither, return ``False``.  If all are
    ordinary, check that the integers `a_P(E)^2-4N(P)` have the same
    square-free part; if not, return ``False``.

    5. Compute the `K`-isogeny class of `E` using the "heuristic"
    option (which is faster, but not guaranteed to be complete).
    Check whether the set of `j`-invariants of curves in the class of
    `2`-power degree contains a complete Galois orbit.  If so, return
    ``True``.

    6. Otherwise repeat step 4 for more primes, and if still
    undecided, repeat Step 5 without the "heuristic" option, to get
    the complete `K`-isogeny class (which will probably be no bigger
    than before).  Now return ``True`` if the set of `j`-invariants of
    curves in the class contains a complete Galois orbit, otherwise
    return ``False``.

    EXAMPLES:

    A non-CM curve over `\QQ` and a CM curve over `\QQ` are both
    trivially `\QQ`-curves::

        sage: from sage.schemes.elliptic_curves.Qcurves import is_Q_curve
        sage: E = EllipticCurve([1,2,3,4,5])
        sage: flag, cert = is_Q_curve(E, certificate=True)
        sage: flag
        True
        sage: cert
        {'CM': 0, 'N': 1, 'core_poly': x, 'r': 0, 'rho': 0}

        sage: E = EllipticCurve(j=8000)
        sage: flag, cert = is_Q_curve(E, certificate=True)
        sage: flag
        True
        sage: cert
        {'CM': -8}

    A non-`\QQ`-curve over a quartic field.  The local data at bad
    primes above `3` is inconsistent::

        sage: from sage.schemes.elliptic_curves.Qcurves import is_Q_curve
        sage: R.<x> = PolynomialRing(QQ)
        sage: K.<a> = NumberField(R([3, 0, -5, 0, 1]))
        sage: E = EllipticCurve([K([-3,-4,1,1]),K([4,-1,-1,0]),K([-2,0,1,0]),K([-621,778,138,-178]),K([9509,2046,-24728,10380])])
        sage: is_Q_curve(E, certificate=True, verbose=True)
        Checking whether Elliptic Curve defined by y^2 + (a^3+a^2-4*a-3)*x*y + (a^2-2)*y = x^3 + (-a^2-a+4)*x^2 + (-178*a^3+138*a^2+778*a-621)*x + (10380*a^3-24728*a^2+2046*a+9509) over Number Field in a with defining polynomial x^4 - 5*x^2 + 3 is a Q-curve
        No: inconsistency at the 2 primes dividing 3
        - potentially multiplicative: [True, False]
        (False, 3)

    A non-`\QQ`-curve over a quadratic field.  The local data at bad
    primes is consistent, but the local test at good primes above `13`
    is not::

        sage: K.<a> = NumberField(R([-10, 0, 1]))
        sage: E = EllipticCurve([K([0,1]),K([-1,-1]),K([0,0]),K([-236,40]),K([-1840,464])])
        sage: is_Q_curve(E, certificate=True, verbose=True)
        Checking whether Elliptic Curve defined by y^2 + a*x*y = x^3 + (-a-1)*x^2 + (40*a-236)*x + (464*a-1840) over Number Field in a with defining polynomial x^2 - 10 is a Q-curve
        Applying local tests at good primes above p<=100
        No: inconsistency at the 2 ordinary primes dividing 13
        - Frobenius discriminants mod squares: [-1, -3]
        No: local test at p=13 failed
        (False, 13)

    A quadratic `\QQ`-curve with CM discriminant `-15` (`j`-invariant not in `\QQ`)::

        sage: from sage.schemes.elliptic_curves.Qcurves import is_Q_curve
        sage: R.<x> = PolynomialRing(QQ)
        sage: K.<a> = NumberField(R([-1, -1, 1]))
        sage: E = EllipticCurve([K([1,0]),K([-1,0]),K([0,1]),K([0,-2]),K([0,1])])
        sage: is_Q_curve(E, certificate=True, verbose=True)
        Checking whether Elliptic Curve defined by y^2 + x*y + a*y = x^3 + (-1)*x^2 + (-2*a)*x + a over Number Field in a with defining polynomial x^2 - x - 1 is a Q-curve
        Yes: E is CM (discriminant -15)
        (True, {'CM': -15})

    An example over `\QQ(\sqrt{2},\sqrt{3})`.  The `j`-invariant is in
    `\QQ(\sqrt{6})`, so computations will be done over that field, and
    in fact there is an isogenous curve with rational `j`, so we have
    a so-called rational `\QQ`-curve::

        sage: K.<a> = NumberField(R([1, 0, -4, 0, 1]))
        sage: E = EllipticCurve([K([-2,-4,1,1]),K([0,1,0,0]),K([0,1,0,0]),K([-4780,9170,1265,-2463]),K([163923,-316598,-43876,84852])])
        sage: flag, cert = is_Q_curve(E, certificate=True)
        sage: flag
        True
        sage: cert
        {'CM': 0, 'N': 1, 'core_degs': [1], 'core_poly': x - 85184/3, 'r': 0, 'rho': 0}

    Over the same field, a so-called strict `\QQ`-curve which is not
    isogenous to one with rational `j`, but whose core field is
    quadratic. In fact the isogeny class over `K` consists of `6`
    curves, four with conjugate quartic `j`-invariants and `2` with
    quadratic conjugate `j`-invariants in `\QQ(\sqrt{3})` (but which
    are not base-changes from the quadratic subfield)::

        sage: E = EllipticCurve([K([0,-3,0,1]),K([1,4,0,-1]),K([0,0,0,0]),K([-2,-16,0,4]),K([-19,-32,4,8])])
        sage: flag, cert = is_Q_curve(E, certificate=True)
        sage: flag
        True
        sage: cert
        {'CM': 0,
        'N': 2,
        'core_degs': [1, 2],
        'core_poly': x^2 - 840064*x + 1593413632,
        'r': 1,
        'rho': 1}

    """
    from sage.rings.number_field.number_field_base import is_NumberField

    if verbose:
        print("Checking whether {} is a Q-curve".format(E))

    try:
        assert is_NumberField(E.base_field())
    except (AttributeError, AssertionError):
        raise TypeError(
            "{} must be an elliptic curve defined over a number field in is_Q_curve()"
        )

    from sage.rings.integer_ring import ZZ
    from sage.arith.functions import lcm
    from sage.libs.pari import pari
    from sage.rings.number_field.number_field import NumberField
    from sage.schemes.elliptic_curves.constructor import EllipticCurve
    from sage.schemes.elliptic_curves.cm import cm_j_invariants_and_orders, is_cm_j_invariant

    # Step 1

    # all curves with rational j-invariant are Q-curves:
    jE = E.j_invariant()
    if jE in QQ:
        if verbose:
            print("Yes: j(E) is in QQ")
        if certificate:
            # test for CM
            for d, f, j in cm_j_invariants_and_orders(QQ):
                if jE == j:
                    return True, {'CM': d * f**2}
            # else not CM
            return True, {
                'CM': ZZ(0),
                'r': ZZ(0),
                'rho': ZZ(0),
                'N': ZZ(1),
                'core_poly': polygen(QQ)
            }
        else:
            return True

    # CM curves are Q-curves:
    flag, df = is_cm_j_invariant(jE)
    if flag:
        d, f = df
        D = d * f**2
        if verbose:
            print("Yes: E is CM (discriminant {})".format(D))
        if certificate:
            return True, {'CM': D}
        else:
            return True

    # Step 2: replace E by a curve defined over Q(j(E)):

    K = E.base_field()
    jpoly = jE.minpoly()
    if jpoly.degree() < K.degree():
        if verbose:
            print("switching to smaller base field: j's minpoly is {}".format(
                jpoly))
        f = pari(jpoly).polredbest().sage({'x': jpoly.parent().gen()})
        K2 = NumberField(f, 'b')
        jE = jpoly.roots(K2)[0][0]
        if verbose:
            print("New j is {} over {}, with minpoly {}".format(
                jE, K2, jE.minpoly()))
        #assert jE.minpoly()==jpoly
        E = EllipticCurve(j=jE)
        K = K2
        if verbose:
            print("New test curve is {}".format(E))

    # Step 3: check primes of bad reduction

    NN = E.conductor().norm()
    for p in NN.support():
        Plist = K.primes_above(p)
        if len(Plist) < 2:
            continue
        # pot_mult = potential multiplicative reduction
        pot_mult = [jE.valuation(P) < 0 for P in Plist]
        consistent = all(pot_mult) or not any(pot_mult)
        if not consistent:
            if verbose:
                print("No: inconsistency at the {} primes dividing {}".format(
                    len(Plist), p))
                print("  - potentially multiplicative: {}".format(pot_mult))
            if certificate:
                return False, p
            else:
                return False

    # Step 4 check: primes P of good reduction above p<=B:

    if verbose:
        print("Applying local tests at good primes above p<={}".format(maxp))

    res4, p = Step4Test(E, B=maxp, oldB=0, verbose=verbose)
    if not res4:
        if verbose:
            print("No: local test at p={} failed".format(p))
        if certificate:
            return False, p
        else:
            return False

    if verbose:
        print("...all local tests pass for p<={}".format(maxp))

    # Step 5: compute the (partial) K-isogeny class of E and test the
    # set of j-invariants in the class:

    C = E.isogeny_class(algorithm='heuristic', minimal_models=False)
    jC = [E2.j_invariant() for E2 in C]
    centrejpols = conjugacy_test(jC, verbose=verbose)
    if centrejpols:
        if verbose:
            print(
                "Yes: the isogeny class contains a complete conjugacy class of j-invariants"
            )
        if certificate:
            for f in centrejpols:
                rho = f.degree().valuation(2)
                centre_indices = [i for i, j in enumerate(jC) if f(j) == 0]
                M = C.matrix()
                core_degs = [M[centre_indices[0], i] for i in centre_indices]
                level = lcm(core_degs)
                if level.is_squarefree():
                    r = len(level.prime_divisors())
                    cert = {
                        'CM': ZZ(0),
                        'core_poly': f,
                        'rho': rho,
                        'r': r,
                        'N': level,
                        'core_degs': core_degs
                    }
                    return True, cert
            print("No central curve found")
        else:
            return True

    # Now we are undecided.  This can happen if either (1) E is not a
    # Q-curve but we did not use enough primes in Step 4 to detect
    # this, or (2) E is a Q-curve but in Step 5 we did not compute the
    # complete isogeny class.  Case (2) is most unlikely since the
    # heuristic bound used in computing isogeny classes means that we
    # have all isogenous curves linked to E by an isogeny of degree
    # supported on primes<1000.

    # We first rerun Step 4 with a larger bound.

    xmaxp = 10 * maxp
    if verbose:
        print(
            "Undecided after first round, so we apply more local tests, up to {}"
            .format(xmaxp))

    res4, p = Step4Test(E, B=xmaxp, oldB=maxp, verbose=verbose)
    if not res4:
        if verbose:
            print("No: local test at p={} failed".format(p))
        if certificate:
            return False, p
        else:
            return False

    # Now we rerun Step 5 using a rigorous computation of the complete
    # isogeny class.  This will probably contain no more curves than
    # before, in which case -- since we already tested that the set of
    # j-invariants does not contain a complete Galois conjugacy class
    # -- we can deduce that E is not a Q-curve.

    if verbose:
        print("...all local tests pass for p<={}".format(xmaxp))
        print("We now compute the complete isogeny class...")

    Cfull = E.isogeny_class(minimal_models=False)
    jCfull = [E2.j_invariant() for E2 in Cfull]

    if len(jC) == len(jCfull):
        if verbose:
            print("...and find that we already had the complete class:so No")
        if certificate:
            return False, 0
        else:
            return False
    if verbose:
        print(
            "...and find that the class contains {} curves, not just the {} we computed originally"
            .format(len(jCfull), len(jC)))
    centrejpols = conjugacy_test(jCfull, verbose=verbose)
    if cert:
        if verbose:
            print(
                "Yes: the isogeny class contains a complete conjugacy class of j-invariants"
            )
        if certificate:
            return True, centrejpols
        else:
            return True
    if verbose:
        print(
            "No: the isogeny class does *not* contain a complete conjugacy class of j-invariants"
        )
    if certificate:
        return False, 0
    else:
        return False
Beispiel #30
0
def q_binomial(n, k, q=None, algorithm='auto'):
    r"""
    Return the `q`-binomial coefficient.

    This is also known as the Gaussian binomial coefficient, and is defined by

    .. MATH::

        \binom{n}{k}_q = \frac{(1-q^n)(1-q^{n-1}) \cdots (1-q^{n-k+1})}
        {(1-q)(1-q^2)\cdots (1-q^k)}.

    See :wikipedia:`Gaussian_binomial_coefficient`

    If `q` is unspecified, then the variable is the generator `q` for
    a univariate polynomial ring over the integers.

    INPUT:

    - ``n, k`` -- The values, `n` and `k` defined above.

    - ``q`` -- (Default: ``None``) The variable `q`; if ``None``, then use a
      default variable in `\ZZ[q]`.

    - ``algorithm`` -- (Default: ``'auto'``) The algorithm to use and can be
      one of the following:

      - ``'auto'`` -- Automatically choose the algorithm; see the algorithm
        section below
      - ``'naive'`` -- Use the naive algorithm
      - ``'cyclotomic'`` -- Use cyclotomic algorithm

    ALGORITHM:

    The naive algorithm uses the product formula. The cyclotomic
    algorithm uses a product of cyclotomic polynomials
    (cf. [CH2006]_).

    When the algorithm is set to ``'auto'``, we choose according to
    the following rules:

    - If ``q`` is a polynomial:

      When ``n`` is small or ``k`` is small with respect to ``n``, one
      uses the naive algorithm. When both ``n`` and ``k`` are big, one
      uses the cyclotomic algorithm.

    - If ``q`` is in the symbolic ring, one uses the cyclotomic algorithm.

    - Otherwise one uses the naive algorithm, unless ``q`` is a root of
      unity, then one uses the cyclotomic algorithm.

    EXAMPLES:

    By default, the variable is the generator of `\ZZ[q]`::

        sage: from sage.combinat.q_analogues import q_binomial
        sage: g = q_binomial(5,1) ; g
        q^4 + q^3 + q^2 + q + 1
        sage: g.parent()
        Univariate Polynomial Ring in q over Integer Ring

    The `q`-binomial coefficient vanishes unless `0 \leq k \leq n`::

        sage: q_binomial(4,5)
        0
        sage: q_binomial(5,-1)
        0

    Other variables can be used, given as third parameter::

        sage: p = ZZ['p'].gen()
        sage: q_binomial(4,2,p)
        p^4 + p^3 + 2*p^2 + p + 1

    The third parameter can also be arbitrary values::

        sage: q_binomial(5,1,2) == g.subs(q=2)
        True
        sage: q_binomial(5,1,1)
        5
        sage: q_binomial(4,2,-1)
        2
        sage: q_binomial(4,2,3.14)
        152.030056160000
        sage: R = GF(25, 't')
        sage: t = R.gen(0)
        sage: q_binomial(6, 3, t)
        2*t + 3

    We can also do this for more complicated objects such as matrices or
    symmetric functions::

        sage: q_binomial(4,2,matrix([[2,1],[-1,3]]))
        [ -6  84]
        [-84  78]
        sage: Sym = SymmetricFunctions(QQ)
        sage: s = Sym.schur()
        sage: q_binomial(4,1, s[2]+s[1])
        s[] + s[1] + s[1, 1] + s[1, 1, 1] + 2*s[2] + 4*s[2, 1] + 3*s[2, 1, 1]
         + 4*s[2, 2] + 3*s[2, 2, 1] + s[2, 2, 2] + 3*s[3] + 7*s[3, 1] + 3*s[3, 1, 1]
         + 6*s[3, 2] + 2*s[3, 2, 1] + s[3, 3] + 4*s[4] + 6*s[4, 1] + s[4, 1, 1]
         + 3*s[4, 2] + 3*s[5] + 2*s[5, 1] + s[6]

    TESTS:

    One checks that the first two arguments are integers::

        sage: q_binomial(1/2,1)
        Traceback (most recent call last):
        ...
        ValueError: arguments (1/2, 1) must be integers

    One checks that `n` is nonnegative::

        sage: q_binomial(-4,1)
        Traceback (most recent call last):
        ...
        ValueError: n must be nonnegative

    This also works for variables in the symbolic ring::

        sage: z = var('z')
        sage: factor(q_binomial(4,2,z))
        (z^2 + z + 1)*(z^2 + 1)

    This also works for complex roots of unity::

        sage: q_binomial(6,1,I)
        1 + I

    Check that the algorithm does not matter::

        sage: q_binomial(6,3, algorithm='naive') == q_binomial(6,3, algorithm='cyclotomic')
        True

    One more test::

        sage: q_binomial(4, 2, Zmod(6)(2), algorithm='naive')
        5

    REFERENCES:

    .. [CH2006] William Y.C. Chen and Qing-Hu Hou, "Factors of the Gaussian
       coefficients", Discrete Mathematics 306 (2006), 1446-1449.
       :doi:`10.1016/j.disc.2006.03.031`

    AUTHORS:

    - Frederic Chapoton, David Joyner and William Stein
    """
    # sanity checks
    if not (n in ZZ and k in ZZ):
        raise ValueError("arguments (%s, %s) must be integers" % (n, k))
    if n < 0:
        raise ValueError('n must be nonnegative')
    if not (0 <= k and k <= n):
        return 0

    k = min(n - k, k)  # Pick the smallest k

    # polynomiality test
    if q is None:
        from sage.rings.polynomial.polynomial_ring import polygen
        q = polygen(ZZ, name='q')
        is_polynomial = True
    else:
        from sage.rings.polynomial.polynomial_element import Polynomial
        is_polynomial = isinstance(q, Polynomial)
    from sage.symbolic.ring import SR

    # heuristic choice of the fastest algorithm
    if algorithm == 'auto':
        if is_polynomial:
            if n <= 70 or k <= n / 4:
                algorithm = 'naive'
            else:
                algorithm = 'cyclo_polynomial'
        elif q in SR:
            algorithm = 'cyclo_generic'
        else:
            algorithm = 'naive'
    elif algorithm == 'cyclotomic':
        if is_polynomial:
            algorithm = 'cyclo_polynomial'
        else:
            algorithm = 'cyclo_generic'
    elif algorithm != 'naive':
        raise ValueError("invalid algorithm choice")

    # the algorithms
    try:
        if algorithm == 'naive':
            denomin = prod([1 - q**i for i in range(1, k + 1)])
            if denomin == 0:  # q is a root of unity, use the cyclotomic algorithm
                algorithm = 'cyclo_generic'
            else:
                numerat = prod([1 - q**i for i in range(n - k + 1, n + 1)])
                try:
                    return numerat // denomin
                except TypeError:
                    return numerat / denomin
        from sage.functions.all import floor
        if algorithm == 'cyclo_generic':
            from sage.rings.polynomial.cyclotomic import cyclotomic_value
            return prod(
                cyclotomic_value(d, q) for d in range(2, n + 1)
                if floor(n / d) != floor(k / d) + floor((n - k) / d))
        if algorithm == 'cyclo_polynomial':
            R = q.parent()
            return prod(
                R.cyclotomic_polynomial(d) for d in range(2, n + 1)
                if floor(n / d) != floor(k / d) + floor((n - k) / d))
    except (ZeroDivisionError, TypeError):
        # As a last attempt, do the computation formally and then substitute
        return q_binomial(n, k)(q)
Beispiel #31
0
POWM2T2 = 2**(m2+1)
POWM2M2D3 = ZZ((2**m2-2)/3)
POWM2P1D3 = ZZ((2**m2+1)/3)
K1MASK = 2**m1 - 1
K1HIGHBIT = 2**(m1-1)
K0MASK = 2**m0 - 1
K0HIGHBIT = 2**(m0-1)
NECKLACES = 1/m1*sum(euler_phi(d)*2**(ZZ(m1/d)) for d in m1.divisors())
# 9*2*2*2 > 64
if m2 > 8:
    USE_DWORD = 1
else:
    USE_DWORD = 0

GF2 = GF(2)
x = polygen(GF2)
K2 = GF(2**m2, name='g2', modulus=modulus)
g2 = K2.gen()
z2 = K2.multiplicative_generator()
K1 = GF(2**m1, name='g1', modulus=modulus)
g1 = K1.gen()
z1 = K1.multiplicative_generator()
Z1 = z1.polynomial().change_ring(ZZ)(2)
K0 = GF(2**m0, name='g0', modulus=modulus)
g0 = K0.gen()
z0 = K0.multiplicative_generator()
L = [K0, K1, K2]
moduli = [k.modulus() for k in L]
F = [f.change_ring(ZZ)(2) for f in moduli]
weights = [f.hamming_weight()-2 for f in moduli]
degrees = [(f-x**f.degree()-1).exponents() for f in moduli]
    def arnoux_yoccoz(genus):
        r"""
        Construct the Arnoux-Yoccoz surface of genus 3 or greater.
        
        This presentation of the surface follows Section 2.3 of 
        Joshua P. Bowman's paper "The Complete Family of Arnoux-Yoccoz 
        Surfaces."
        
        EXAMPLES::

            sage: from flatsurf import *
            sage: s = translation_surfaces.arnoux_yoccoz(4)
            sage: TestSuite(s).run()
            sage: s.is_delaunay_decomposed()
            True
            sage: s = s.canonicalize()
            sage: field=s.base_ring()
            sage: a = field.gen()
            sage: from sage.matrix.constructor import Matrix
            sage: m = Matrix([[a,0],[0,~a]])
            sage: ss = m*s
            sage: ss = ss.canonicalize()
            sage: s.cmp_translation_surface(ss)==0
            True

        The Arnoux-Yoccoz pseudo-Anosov are known to have (minimal) invariant
        foliations with SAF=0::

            sage: S3 = translation_surfaces.arnoux_yoccoz(3)
            sage: Jxx, Jyy, Jxy = S3.j_invariant()
            sage: Jxx.is_zero() and Jyy.is_zero()
            True
            sage: Jxy
            [ 0  2  0]
            [ 2 -2  0]
            [ 0  0  2]

            sage: S4 = translation_surfaces.arnoux_yoccoz(4)
            sage: Jxx, Jyy, Jxy = S4.j_invariant()
            sage: Jxx.is_zero() and Jyy.is_zero()
            True
            sage: Jxy
            [ 0  2  0  0]
            [ 2 -2  0  0]
            [ 0  0  2  2]
            [ 0  0  2  0]
        """
        g=ZZ(genus)
        assert g>=3
        from sage.rings.polynomial.polynomial_ring import polygen
        x = polygen(AA)
        p=sum([x**i for i in xrange(1,g+1)])-1
        cp = AA.common_polynomial(p)
        alpha_AA = AA.polynomial_root(cp, RIF(1/2, 1))
        field=NumberField(alpha_AA.minpoly(),'alpha',embedding=alpha_AA)
        a=field.gen()
        from sage.modules.free_module import VectorSpace
        V=VectorSpace(field,2)
        p=[None for i in xrange(g+1)]
        q=[None for i in xrange(g+1)]
        p[0]=V(( (1-a**g)/2, a**2/(1-a) ))
        q[0]=V(( -a**g/2, a ))
        p[1]=V(( -(a**(g-1)+a**g)/2, (a-a**2+a**3)/(1-a) ))
        p[g]=V(( 1+(a-a**g)/2, (3*a-1-a**2)/(1-a) ))
        for i in xrange(2,g):
            p[i]=V(( (a-a**i)/(1-a) , a/(1-a) ))
        for i in xrange(1,g+1):
            q[i]=V(( (2*a-a**i-a**(i+1))/(2*(1-a)), (a-a**(g-i+2))/(1-a) ))
        from flatsurf.geometry.polygon import Polygons
        P=Polygons(field)
        s = Surface_list(field)
        T = [None] * (2*g+1)
        Tp = [None] * (2*g+1)
        from sage.matrix.constructor import Matrix
        m=Matrix([[1,0],[0,-1]])
        for i in xrange(1,g+1):
            # T_i is (P_0,Q_i,Q_{i-1})
            T[i]=s.add_polygon(P(edges=[ q[i]-p[0], q[i-1]-q[i], p[0]-q[i-1] ]))
            # T_{g+i} is (P_i,Q_{i-1},Q_{i})
            T[g+i]=s.add_polygon(P(edges=[ q[i-1]-p[i], q[i]-q[i-1], p[i]-q[i] ]))
            # T'_i is (P'_0,Q'_{i-1},Q'_i)
            Tp[i]=s.add_polygon(m*s.polygon(T[i]))
            # T'_{g+i} is (P'_i,Q'_i, Q'_{i-1})
            Tp[g+i]=s.add_polygon(m*s.polygon(T[g+i]))
        for i in xrange(1,g):
            s.change_edge_gluing(T[i],0,T[i+1],2)
            s.change_edge_gluing(Tp[i],2,Tp[i+1],0)
        for i in xrange(1,g+1):
            s.change_edge_gluing(T[i],1,T[g+i],1)
            s.change_edge_gluing(Tp[i],1,Tp[g+i],1)
        #P 0 Q 0 is paired with P' 0 Q' 0, ...
        s.change_edge_gluing(T[1],2,Tp[g],2)
        s.change_edge_gluing(Tp[1],0,T[g],0)
        # P1Q1 is paired with P'_g Q_{g-1}
        s.change_edge_gluing(T[g+1],2,Tp[2*g],2)
        s.change_edge_gluing(Tp[g+1],0,T[2*g],0)
        # P1Q0 is paired with P_{g-1} Q_{g-1}
        s.change_edge_gluing(T[g+1],0,T[2*g-1],2)
        s.change_edge_gluing(Tp[g+1],2,Tp[2*g-1],0)
        # PgQg is paired with Q1P2
        s.change_edge_gluing(T[2*g],2,T[g+2],0)
        s.change_edge_gluing(Tp[2*g],0,Tp[g+2],2)
        for i in xrange(2,g-1):
            # PiQi is paired with Q'_i P'_{i+1}
            s.change_edge_gluing(T[g+i],2,Tp[g+i+1],2)
            s.change_edge_gluing(Tp[g+i],0,T[g+i+1],0)
        s.set_immutable()
        return TranslationSurface(s)
Beispiel #33
0
def isomorphisms(E, F, JustOne=False):
    r"""
    Return one or all isomorphisms between two elliptic curves.

    INPUT:

    - ``E``, ``F`` (EllipticCurve) -- Two elliptic curves.

    - ``JustOne`` (bool) If ``True``, returns one isomorphism, or ``None`` if
      the curves are not isomorphic.  If ``False``, returns a (possibly
      empty) list of isomorphisms.

    OUTPUT:

    Either ``None``, or a 4-tuple `(u,r,s,t)` representing an isomorphism,
    or a list of these.

    .. note::

        This function is not intended for users, who should use the
        interface provided by ``ell_generic``.

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.weierstrass_morphism import *
        sage: isomorphisms(EllipticCurve_from_j(0),EllipticCurve('27a3'))
        [(-1, 0, 0, -1), (1, 0, 0, 0)]
        sage: isomorphisms(EllipticCurve_from_j(0),EllipticCurve('27a3'),JustOne=True)
        (1, 0, 0, 0)
        sage: isomorphisms(EllipticCurve_from_j(0),EllipticCurve('27a1'))
        []
        sage: isomorphisms(EllipticCurve_from_j(0),EllipticCurve('27a1'),JustOne=True)
    """
    from .ell_generic import is_EllipticCurve
    if not is_EllipticCurve(E) or not is_EllipticCurve(F):
        raise ValueError("arguments are not elliptic curves")
    K = E.base_ring()

    j = E.j_invariant()
    if j != F.j_invariant():
        if JustOne:
            return None
        return []

    from sage.rings.polynomial.polynomial_ring import polygen
    x = polygen(K, 'x')

    a1E, a2E, a3E, a4E, a6E = E.ainvs()
    a1F, a2F, a3F, a4F, a6F = F.ainvs()

    char = K.characteristic()

    if char == 2:
        if j == 0:
            ulist = (x**3 - (a3E / a3F)).roots(multiplicities=False)
            ans = []
            for u in ulist:
                slist = (x**4 + a3E * x + (a2F**2 + a4F) * u**4 + a2E**2 +
                         a4E).roots(multiplicities=False)
                for s in slist:
                    r = s**2 + a2E + a2F * u**2
                    tlist = (x**2 + a3E * x + r**3 + a2E * r**2 + a4E * r +
                             a6E + a6F * u**6).roots(multiplicities=False)
                    for t in tlist:
                        if JustOne:
                            return (u, r, s, t)
                        ans.append((u, r, s, t))
            if JustOne:
                return None
            ans.sort()
            return ans
        else:
            ans = []
            u = a1E / a1F
            r = (a3E + a3F * u**3) / a1E
            slist = [
                s[0]
                for s in (x**2 + a1E * x + (r + a2E + a2F * u**2)).roots()
            ]
            for s in slist:
                t = (a4E + a4F * u**4 + s * a3E + r * s * a1E + r**2)
                if JustOne:
                    return (u, r, s, t)
                ans.append((u, r, s, t))
            if JustOne:
                return None
            ans.sort()
            return ans

    b2E, b4E, b6E, b8E = E.b_invariants()
    b2F, b4F, b6F, b8F = F.b_invariants()

    if char == 3:
        if j == 0:
            ulist = (x**4 - (b4E / b4F)).roots(multiplicities=False)
            ans = []
            for u in ulist:
                s = a1E - a1F * u
                t = a3E - a3F * u**3
                rlist = (x**3 - b4E * x +
                         (b6E - b6F * u**6)).roots(multiplicities=False)
                for r in rlist:
                    if JustOne:
                        return (u, r, s, t + r * a1E)
                    ans.append((u, r, s, t + r * a1E))
            if JustOne:
                return None
            ans.sort()
            return ans
        else:
            ulist = (x**2 - b2E / b2F).roots(multiplicities=False)
            ans = []
            for u in ulist:
                r = (b4F * u**4 - b4E) / b2E
                s = (a1E - a1F * u)
                t = (a3E - a3F * u**3 + a1E * r)
                if JustOne:
                    return (u, r, s, t)
                ans.append((u, r, s, t))
            if JustOne:
                return None
            ans.sort()
            return ans


# now char!=2,3:
    c4E, c6E = E.c_invariants()
    c4F, c6F = F.c_invariants()

    if j == 0:
        m, um = 6, c6E / c6F
    elif j == 1728:
        m, um = 4, c4E / c4F
    else:
        m, um = 2, (c6E * c4F) / (c6F * c4E)
    ulist = (x**m - um).roots(multiplicities=False)
    ans = []
    for u in ulist:
        s = (a1F * u - a1E) / 2
        r = (a2F * u**2 + a1E * s + s**2 - a2E) / 3
        t = (a3F * u**3 - a1E * r - a3E) / 2
        if JustOne:
            return (u, r, s, t)
        ans.append((u, r, s, t))
    if JustOne:
        return None
    ans.sort()
    return ans
    ====================================================================

"""

import pytest
from sage_code.type_three_not_momose import type_three_not_momose
from sage_code.generic import get_strong_type_3_epsilons
from sage.all import QQ
from sage.rings.number_field.number_field import NumberField
from sage.rings.polynomial.polynomial_ring import polygen

TEST_SETTINGS = {
    "bound": 1000,
}

x = polygen(QQ)
test_cases = [
    (x**2 + 1, 1, 1),
    (x**3 + 3 * x**2 + 1, 1, 0),
    (x**4 - 9 * x**2 - 10 * x + 50, 5, 1),
    (x**4 - x**3 - x**2 - 2 * x + 4, 1, 2),
]


@pytest.mark.parametrize("f, class_number, strong_L_count", test_cases)
def test_type_three_not_momose(f, class_number, strong_L_count):
    K = NumberField(f, "a")
    Kgal = K.galois_closure("b")
    embeddings = K.embeddings(Kgal)

    strong_type_3_epsilons = get_strong_type_3_epsilons(K, embeddings)
Beispiel #35
0
def isomorphisms(E, F, JustOne=False):
    r"""
    Return one or all isomorphisms between two elliptic curves.

    INPUT:

    - ``E``, ``F`` (EllipticCurve) -- Two elliptic curves.

    - ``JustOne`` (bool) If True, returns one isomorphism, or None if
      the curves are not isomorphic.  If False, returns a (possibly
      empty) list of isomorphisms.

    OUTPUT:

    Either None, or a 4-tuple `(u,r,s,t)` representing an isomorphism,
    or a list of these.

    .. note::

       This function is not intended for users, who should use the
       interface provided by ``ell_generic``.

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.weierstrass_morphism import *
        sage: isomorphisms(EllipticCurve_from_j(0),EllipticCurve('27a3'))
        [(-1, 0, 0, -1), (1, 0, 0, 0)]
        sage: isomorphisms(EllipticCurve_from_j(0),EllipticCurve('27a3'),JustOne=True)
        (1, 0, 0, 0)
        sage: isomorphisms(EllipticCurve_from_j(0),EllipticCurve('27a1'))
        []
        sage: isomorphisms(EllipticCurve_from_j(0),EllipticCurve('27a1'),JustOne=True)
    """
    from .ell_generic import is_EllipticCurve
    if not is_EllipticCurve(E) or not is_EllipticCurve(F):
        raise ValueError("arguments are not elliptic curves")
    K = E.base_ring()

    j = E.j_invariant()
    if j != F.j_invariant():
        if JustOne:
            return None
        return []

    from sage.rings.polynomial.polynomial_ring import polygen
    x = polygen(K, 'x')

    a1E, a2E, a3E, a4E, a6E = E.ainvs()
    a1F, a2F, a3F, a4F, a6F = F.ainvs()

    char = K.characteristic()

    if char == 2:
        if j == 0:
            ulist = (x**3-(a3E/a3F)).roots(multiplicities=False)
            ans = []
            for u in ulist:
                slist = (x**4+a3E*x+(a2F**2+a4F)*u**4+a2E**2+a4E).roots(multiplicities=False)
                for s in slist:
                    r = s**2+a2E+a2F*u**2
                    tlist = (x**2 + a3E*x + r**3 + a2E*r**2 + a4E*r + a6E + a6F*u**6).roots(multiplicities=False)
                    for t in tlist:
                        if JustOne:
                            return (u, r, s, t)
                        ans.append((u, r, s, t))
            if JustOne:
                return None
            ans.sort()
            return ans
        else:
            ans = []
            u = a1E/a1F
            r = (a3E+a3F*u**3)/a1E
            slist = [s[0] for s in (x**2+a1E*x+(r+a2E+a2F*u**2)).roots()]
            for s in slist:
                t = (a4E+a4F*u**4 + s*a3E + r*s*a1E + r**2)
                if JustOne:
                    return (u, r, s, t)
                ans.append((u, r, s, t))
            if JustOne:
                return None
            ans.sort()
            return ans

    b2E, b4E, b6E, b8E = E.b_invariants()
    b2F, b4F, b6F, b8F = F.b_invariants()

    if char == 3:
        if j == 0:
            ulist = (x**4-(b4E/b4F)).roots(multiplicities=False)
            ans = []
            for u in ulist:
                s = a1E-a1F*u
                t = a3E-a3F*u**3
                rlist = (x**3-b4E*x+(b6E-b6F*u**6)).roots(multiplicities=False)
                for r in rlist:
                    if JustOne:
                        return (u, r, s, t+r*a1E)
                    ans.append((u, r, s, t+r*a1E))
            if JustOne:
                return None
            ans.sort()
            return ans
        else:
            ulist = (x**2 - b2E / b2F).roots(multiplicities=False)
            ans = []
            for u in ulist:
                r = (b4F * u**4 - b4E) / b2E
                s = (a1E - a1F * u)
                t = (a3E - a3F * u**3 + a1E * r)
                if JustOne:
                    return (u, r, s, t)
                ans.append((u, r, s, t))
            if JustOne:
                return None
            ans.sort()
            return ans

# now char!=2,3:
    c4E, c6E = E.c_invariants()
    c4F, c6F = F.c_invariants()

    if j == 0:
        m, um = 6, c6E/c6F
    elif j == 1728:
        m, um = 4, c4E/c4F
    else:
        m, um = 2, (c6E*c4F)/(c6F*c4E)
    ulist = (x**m-um).roots(multiplicities=False)
    ans = []
    for u in ulist:
        s = (a1F*u - a1E)/2
        r = (a2F*u**2 + a1E*s + s**2 - a2E)/3
        t = (a3F*u**3 - a1E*r - a3E)/2
        if JustOne:
            return (u, r, s, t)
        ans.append((u, r, s, t))
    if JustOne:
        return None
    ans.sort()
    return ans
Beispiel #36
0
    def Shi(self, data, K=QQ, names=None, m=1):
        r"""
        Return the Shi arrangement.

        INPUT:

        - ``data`` -- either an integer or a Cartan type (or coercible
          into; see "CartanType")

        - ``K`` -- field (default:``QQ``)

        - ``names`` -- tuple of strings or ``None`` (default); the
          variable names for the ambient space

        - ``m`` -- integer (default: 1)

        OUTPUT:

        - If ``data`` is an integer `n`, return the Shi arrangement in
          dimension `n`, i.e. the set of `n(n-1)` hyperplanes:
          `\{ x_i - x_j = 0,1 : 1 \leq i \leq j \leq n \}`. This corresponds
          to the Shi arrangement of Cartan type `A_{n-1}`.

        - If ``data`` is a Cartan type, return the Shi arrangement of given
          type.

        - If `m > 1`, return the `m`-extended Shi arrangement of given type.

        The `m`-extended Shi arrangement of a given crystallographic
        Cartan type is defined by the inner product
        `\langle a,x \rangle = k` for `-m < k \leq m` and
        `a \in \Phi^+` is a positive root of the root system `\Phi`.

        EXAMPLES::

            sage: hyperplane_arrangements.Shi(4)
            Arrangement of 12 hyperplanes of dimension 4 and rank 3
            sage: hyperplane_arrangements.Shi("A3")
            Arrangement of 12 hyperplanes of dimension 4 and rank 3
            sage: hyperplane_arrangements.Shi("A3",m=2)
            Arrangement of 24 hyperplanes of dimension 4 and rank 3
            sage: hyperplane_arrangements.Shi("B4")
            Arrangement of 32 hyperplanes of dimension 4 and rank 4
            sage: hyperplane_arrangements.Shi("B4",m=3)
            Arrangement of 96 hyperplanes of dimension 4 and rank 4
            sage: hyperplane_arrangements.Shi("C3")
            Arrangement of 18 hyperplanes of dimension 3 and rank 3
            sage: hyperplane_arrangements.Shi("D4",m=3)
            Arrangement of 72 hyperplanes of dimension 4 and rank 4
            sage: hyperplane_arrangements.Shi("E6")
            Arrangement of 72 hyperplanes of dimension 8 and rank 6
            sage: hyperplane_arrangements.Shi("E6",m=2)
            Arrangement of 144 hyperplanes of dimension 8 and rank 6

        If the Cartan type is not crystallographic, the Shi arrangement
        is not defined::

            sage: hyperplane_arrangements.Shi("H4")
            Traceback (most recent call last):
            ...
            NotImplementedError: Shi arrangements are not defined for non crystallographic Cartan types

        The characteristic polynomial is pre-computed using the results
        of [Ath1996]_::

            sage: hyperplane_arrangements.Shi("A3").characteristic_polynomial()
            x^4 - 12*x^3 + 48*x^2 - 64*x
            sage: hyperplane_arrangements.Shi("A3",m=2).characteristic_polynomial()
            x^4 - 24*x^3 + 192*x^2 - 512*x
            sage: hyperplane_arrangements.Shi("C3").characteristic_polynomial()
            x^3 - 18*x^2 + 108*x - 216
            sage: hyperplane_arrangements.Shi("E6").characteristic_polynomial()
            x^8 - 72*x^7 + 2160*x^6 - 34560*x^5 + 311040*x^4 - 1492992*x^3 + 2985984*x^2
            sage: hyperplane_arrangements.Shi("B4",m=3).characteristic_polynomial()
            x^4 - 96*x^3 + 3456*x^2 - 55296*x + 331776

        TESTS::

            sage: h = hyperplane_arrangements.Shi(4)
            sage: h.characteristic_polynomial()
            x^4 - 12*x^3 + 48*x^2 - 64*x
            sage: h.characteristic_polynomial.clear_cache()  # long time
            sage: h.characteristic_polynomial()              # long time
            x^4 - 12*x^3 + 48*x^2 - 64*x
            sage: h = hyperplane_arrangements.Shi("A3",m=2)
            sage: h.characteristic_polynomial()
            x^4 - 24*x^3 + 192*x^2 - 512*x
            sage: h.characteristic_polynomial.clear_cache()
            sage: h.characteristic_polynomial()
            x^4 - 24*x^3 + 192*x^2 - 512*x
            sage: h = hyperplane_arrangements.Shi("B3",m=3)
            sage: h.characteristic_polynomial()
            x^3 - 54*x^2 + 972*x - 5832
            sage: h.characteristic_polynomial.clear_cache()
            sage: h.characteristic_polynomial()
            x^3 - 54*x^2 + 972*x - 5832
        """
        if data in NN:
            cartan_type = CartanType(["A", data - 1])
        else:
            cartan_type = CartanType(data)
        if not cartan_type.is_crystallographic():
            raise NotImplementedError(
                "Shi arrangements are not defined for non crystallographic Cartan types"
            )
        n = cartan_type.rank()
        h = cartan_type.coxeter_number()
        Ra = RootSystem(cartan_type).ambient_space()
        PR = Ra.positive_roots()
        d = Ra.dimension()
        H = make_parent(K, d, names)
        x = H.gens()
        hyperplanes = []

        for a in PR:
            for const in range(-m + 1, m + 1):
                hyperplanes.append(sum(a[j] * x[j] for j in range(d)) - const)
        A = H(*hyperplanes)
        x = polygen(QQ, 'x')
        charpoly = x**(d - n) * (x - m * h)**n
        A.characteristic_polynomial.set_cache(charpoly)
        return A
Beispiel #37
0
def q_binomial(n, k, q=None, algorithm='auto'):
    r"""
    Return the `q`-binomial coefficient.

    This is also known as the Gaussian binomial coefficient, and is defined by

    .. MATH::

        \binom{n}{k}_q = \frac{(1-q^n)(1-q^{n-1}) \cdots (1-q^{n-k+1})}
        {(1-q)(1-q^2)\cdots (1-q^k)}.

    See :wikipedia:`Gaussian_binomial_coefficient`.

    If `q` is unspecified, then the variable is the generator `q` for
    a univariate polynomial ring over the integers.

    INPUT:

    - ``n, k`` -- the values `n` and `k` defined above

    - ``q`` -- (default: ``None``) the variable `q`; if ``None``, then use a
      default variable in `\ZZ[q]`

    - ``algorithm`` -- (default: ``'auto'``) the algorithm to use and can be
      one of the following:

      - ``'auto'`` -- automatically choose the algorithm; see the algorithm
        section below
      - ``'naive'`` -- use the naive algorithm
      - ``'cyclotomic'`` -- use cyclotomic algorithm

    ALGORITHM:

    The naive algorithm uses the product formula. The cyclotomic
    algorithm uses a product of cyclotomic polynomials
    (cf. [CH2006]_).

    When the algorithm is set to ``'auto'``, we choose according to
    the following rules:

    - If ``q`` is a polynomial:

      When ``n`` is small or ``k`` is small with respect to ``n``, one
      uses the naive algorithm. When both ``n`` and ``k`` are big, one
      uses the cyclotomic algorithm.

    - If ``q`` is in the symbolic ring, one uses the cyclotomic algorithm.

    - Otherwise one uses the naive algorithm, unless ``q`` is a root of
      unity, then one uses the cyclotomic algorithm.

    EXAMPLES:

    By default, the variable is the generator of `\ZZ[q]`::

        sage: from sage.combinat.q_analogues import q_binomial
        sage: g = q_binomial(5,1) ; g
        q^4 + q^3 + q^2 + q + 1
        sage: g.parent()
        Univariate Polynomial Ring in q over Integer Ring

    The `q`-binomial coefficient vanishes unless `0 \leq k \leq n`::

        sage: q_binomial(4,5)
        0
        sage: q_binomial(5,-1)
        0

    Other variables can be used, given as third parameter::

        sage: p = ZZ['p'].gen()
        sage: q_binomial(4,2,p)
        p^4 + p^3 + 2*p^2 + p + 1

    The third parameter can also be arbitrary values::

        sage: q_binomial(5,1,2) == g.subs(q=2)
        True
        sage: q_binomial(5,1,1)
        5
        sage: q_binomial(4,2,-1)
        2
        sage: q_binomial(4,2,3.14)
        152.030056160000
        sage: R = GF(25, 't')
        sage: t = R.gen(0)
        sage: q_binomial(6, 3, t)
        2*t + 3

    We can also do this for more complicated objects such as matrices or
    symmetric functions::

        sage: q_binomial(4,2,matrix([[2,1],[-1,3]]))
        [ -6  84]
        [-84  78]
        sage: Sym = SymmetricFunctions(QQ)
        sage: s = Sym.schur()
        sage: q_binomial(4,1, s[2]+s[1])
        s[] + s[1] + s[1, 1] + s[1, 1, 1] + 2*s[2] + 4*s[2, 1] + 3*s[2, 1, 1]
         + 4*s[2, 2] + 3*s[2, 2, 1] + s[2, 2, 2] + 3*s[3] + 7*s[3, 1] + 3*s[3, 1, 1]
         + 6*s[3, 2] + 2*s[3, 2, 1] + s[3, 3] + 4*s[4] + 6*s[4, 1] + s[4, 1, 1]
         + 3*s[4, 2] + 3*s[5] + 2*s[5, 1] + s[6]

    TESTS:

    One checks that the first two arguments are integers::

        sage: q_binomial(1/2,1)
        Traceback (most recent call last):
        ...
        TypeError: no conversion of this rational to integer

    One checks that `n` is nonnegative::

        sage: q_binomial(-4,1)
        Traceback (most recent call last):
        ...
        ValueError: n must be nonnegative

    This also works for variables in the symbolic ring::

        sage: z = var('z')
        sage: factor(q_binomial(4,2,z))
        (z^2 + z + 1)*(z^2 + 1)

    This also works for complex roots of unity::

        sage: q_binomial(6, 1, QQbar(I))
        I + 1

    Note that the symbolic computation works (see :trac:`14982`)::

        sage: q_binomial(6, 1, I)
        I + 1

    Check that the algorithm does not matter::

        sage: q_binomial(6,3, algorithm='naive') == q_binomial(6,3, algorithm='cyclotomic')
        True

    One more test::

        sage: q_binomial(4, 2, Zmod(6)(2), algorithm='naive')
        5

    REFERENCES:

    .. [CH2006] William Y.C. Chen and Qing-Hu Hou, *Factors of the Gaussian
       coefficients*, Discrete Mathematics 306 (2006), 1446-1449.
       :doi:`10.1016/j.disc.2006.03.031`

    AUTHORS:

    - Frederic Chapoton, David Joyner and William Stein
    """
    # sanity checks
    n = ZZ(n)
    k = ZZ(k)
    if n < 0:
        raise ValueError('n must be nonnegative')

    # polynomiality test
    if q is None:
        from sage.rings.polynomial.polynomial_ring import polygen
        q = polygen(ZZ, name='q')
        is_polynomial = True
    else:
        from sage.rings.polynomial.polynomial_element import Polynomial
        is_polynomial = isinstance(q, Polynomial)

    R = parent(q)
    zero = R.zero()
    one = R.one()

    if not(0 <= k and k <= n):
        return zero

    k = min(n-k,k) # Pick the smallest k

    # heuristic choice of the fastest algorithm
    if algorithm == 'auto':
        from sage.symbolic.ring import SR
        if is_polynomial:
            if n <= 70 or k <= n // 4:
                algorithm = 'naive'
            else:
                algorithm = 'cyclo_polynomial'
        elif q in SR:
            algorithm = 'cyclo_generic'
        else:
            algorithm = 'naive'
    elif algorithm == 'cyclotomic':
        if is_polynomial:
            algorithm = 'cyclo_polynomial'
        else:
            algorithm = 'cyclo_generic'
    elif algorithm != 'naive':
        raise ValueError("invalid algorithm choice")

    # the algorithms
    if algorithm == 'naive':
        denom = prod(one - q**i for i in range(1, k+1))
        if not denom: # q is a root of unity, use the cyclotomic algorithm
            return cyclotomic_value(n, k, q, algorithm='cyclotomic')
        else:
            num = prod(one - q**i for i in range(n-k+1, n+1))
            try:
                try:
                    return num // denom
                except TypeError:
                    return num / denom
            except (TypeError, ZeroDivisionError):
                # use substitution instead
                return q_binomial(n,k)(q)
    elif algorithm == 'cyclo_generic':
        from sage.rings.polynomial.cyclotomic import cyclotomic_value
        return prod(cyclotomic_value(d,q)
                    for d in range(2,n+1)
                    if (n//d) != (k//d) + ((n-k)//d))
    elif algorithm == 'cyclo_polynomial':
        return prod(R.cyclotomic_polynomial(d)
                    for d in range(2,n+1)
                    if (n//d) != (k//d) + ((n-k)//d))
Beispiel #38
0
def q_binomial(n, k, q=None, algorithm='auto'):
    r"""
    Return the `q`-binomial coefficient.

    This is also known as the Gaussian binomial coefficient, and is defined by

    .. MATH::

        \binom{n}{k}_q = \frac{(1-q^n)(1-q^{n-1}) \cdots (1-q^{n-k+1})}
        {(1-q)(1-q^2)\cdots (1-q^k)}.

    See :wikipedia:`Gaussian_binomial_coefficient`.

    If `q` is unspecified, then the variable is the generator `q` for
    a univariate polynomial ring over the integers.

    INPUT:

    - ``n, k`` -- the values `n` and `k` defined above

    - ``q`` -- (default: ``None``) the variable `q`; if ``None``, then use a
      default variable in `\ZZ[q]`

    - ``algorithm`` -- (default: ``'auto'``) the algorithm to use and can be
      one of the following:

      - ``'auto'`` -- automatically choose the algorithm; see the algorithm
        section below
      - ``'naive'`` -- use the naive algorithm
      - ``'cyclotomic'`` -- use cyclotomic algorithm

    ALGORITHM:

    The naive algorithm uses the product formula. The cyclotomic
    algorithm uses a product of cyclotomic polynomials
    (cf. [CH2006]_).

    When the algorithm is set to ``'auto'``, we choose according to
    the following rules:

    - If ``q`` is a polynomial:

      When ``n`` is small or ``k`` is small with respect to ``n``, one
      uses the naive algorithm. When both ``n`` and ``k`` are big, one
      uses the cyclotomic algorithm.

    - If ``q`` is in the symbolic ring, one uses the cyclotomic algorithm.

    - Otherwise one uses the naive algorithm, unless ``q`` is a root of
      unity, then one uses the cyclotomic algorithm.

    EXAMPLES:

    By default, the variable is the generator of `\ZZ[q]`::

        sage: from sage.combinat.q_analogues import q_binomial
        sage: g = q_binomial(5,1) ; g
        q^4 + q^3 + q^2 + q + 1
        sage: g.parent()
        Univariate Polynomial Ring in q over Integer Ring

    The `q`-binomial coefficient vanishes unless `0 \leq k \leq n`::

        sage: q_binomial(4,5)
        0
        sage: q_binomial(5,-1)
        0

    Other variables can be used, given as third parameter::

        sage: p = ZZ['p'].gen()
        sage: q_binomial(4,2,p)
        p^4 + p^3 + 2*p^2 + p + 1

    The third parameter can also be arbitrary values::

        sage: q_binomial(5,1,2) == g.subs(q=2)
        True
        sage: q_binomial(5,1,1)
        5
        sage: q_binomial(4,2,-1)
        2
        sage: q_binomial(4,2,3.14)
        152.030056160000
        sage: R = GF(25, 't')
        sage: t = R.gen(0)
        sage: q_binomial(6, 3, t)
        2*t + 3

    We can also do this for more complicated objects such as matrices or
    symmetric functions::

        sage: q_binomial(4,2,matrix([[2,1],[-1,3]]))
        [ -6  84]
        [-84  78]
        sage: Sym = SymmetricFunctions(QQ)
        sage: s = Sym.schur()
        sage: q_binomial(4,1, s[2]+s[1])
        s[] + s[1] + s[1, 1] + s[1, 1, 1] + 2*s[2] + 4*s[2, 1] + 3*s[2, 1, 1]
         + 4*s[2, 2] + 3*s[2, 2, 1] + s[2, 2, 2] + 3*s[3] + 7*s[3, 1] + 3*s[3, 1, 1]
         + 6*s[3, 2] + 2*s[3, 2, 1] + s[3, 3] + 4*s[4] + 6*s[4, 1] + s[4, 1, 1]
         + 3*s[4, 2] + 3*s[5] + 2*s[5, 1] + s[6]

    TESTS:

    One checks that the first two arguments are integers::

        sage: q_binomial(1/2,1)
        Traceback (most recent call last):
        ...
        TypeError: no conversion of this rational to integer

    One checks that `n` is nonnegative::

        sage: q_binomial(-4,1)
        Traceback (most recent call last):
        ...
        ValueError: n must be nonnegative

    This also works for variables in the symbolic ring::

        sage: z = var('z')
        sage: factor(q_binomial(4,2,z))
        (z^2 + z + 1)*(z^2 + 1)

    This also works for complex roots of unity::

        sage: q_binomial(10, 4, QQbar(I))
        2

    Note that the symbolic computation works (see :trac:`14982`)::

        sage: q_binomial(10, 4, I)
        2

    Check that the algorithm does not matter::

        sage: q_binomial(6, 3, algorithm='naive') == q_binomial(6, 3, algorithm='cyclotomic')
        True

    One more test::

        sage: q_binomial(4, 2, Zmod(6)(2), algorithm='naive')
        5

    Check that it works with Python integers::

        sage: r = q_binomial(3r, 2r, 1r); r
        3
        sage: type(r)
        <type 'int'>

    Check that arbitrary polynomials work::

        sage: R.<x> = ZZ[]
        sage: q_binomial(2, 1, x^2 - 1, algorithm="naive")
        x^2
        sage: q_binomial(2, 1, x^2 - 1, algorithm="cyclotomic")
        x^2

    Check that the parent is always the parent of ``q``::

        sage: R.<q> = CyclotomicField(3)
        sage: for algo in ["naive", "cyclotomic"]:
        ....:     for n in range(4):
        ....:         for k in range(4):
        ....:             a = q_binomial(n, k, q, algorithm=algo)
        ....:             assert a.parent() is R

    ::

        sage: q_binomial(2, 1, x^2 - 1, algorithm="quantum")
        Traceback (most recent call last):
        ...
        ValueError: unknown algorithm 'quantum'

    REFERENCES:

    .. [CH2006] William Y.C. Chen and Qing-Hu Hou, *Factors of the Gaussian
       coefficients*, Discrete Mathematics 306 (2006), 1446-1449.
       :doi:`10.1016/j.disc.2006.03.031`

    AUTHORS:

    - Frederic Chapoton, David Joyner and William Stein
    """
    # sanity checks
    n = ZZ(n)
    k = ZZ(k)
    if n < 0:
        raise ValueError('n must be nonnegative')

    k = min(n - k, k)  # Pick the smallest k

    # polynomiality test
    if q is None:
        from sage.rings.polynomial.polynomial_ring import polygen
        q = polygen(ZZ, name='q')
        is_polynomial = True
    else:
        from sage.rings.polynomial.polynomial_element import Polynomial
        is_polynomial = isinstance(q, Polynomial)

    # We support non-Sage Elements too, where parent(q) is really
    # type(q). The calls R(0) and R(1) should work in all cases to
    # generate the correct 0 and 1 elements.
    R = parent(q)
    zero = R(0)
    one = R(1)

    if k <= 0:
        return one if k == 0 else zero

    # heuristic choice of the fastest algorithm
    if algorithm == 'auto':
        if n <= 70 or k <= n // 4:
            algorithm = 'naive'
        elif is_polynomial:
            algorithm = 'cyclotomic'
        else:
            from sage.symbolic.ring import SR
            if R is SR:
                algorithm = 'cyclotomic'
            else:
                algorithm = 'naive'

    # the algorithms
    while algorithm == 'naive':
        denom = prod(one - q**i for i in range(1, k + 1))
        if not denom:  # q is a root of unity, use the cyclotomic algorithm
            algorithm = 'cyclotomic'
            break
        else:
            num = prod(one - q**i for i in range(n - k + 1, n + 1))
            try:
                try:
                    return num // denom
                except TypeError:
                    return num / denom
            except (TypeError, ZeroDivisionError):
                # use substitution instead
                return q_binomial(n, k)(q)
    if algorithm == 'cyclotomic':
        from sage.rings.polynomial.cyclotomic import cyclotomic_value
        return prod(
            cyclotomic_value(d, q) for d in range(2, n + 1)
            if (n // d) != (k // d) + ((n - k) // d))
    else:
        raise ValueError("unknown algorithm {!r}".format(algorithm))