Example #1
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
Example #2
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
Example #3
0
def AffineGeometryDesign(n, d, F, point_coordinates=True, check=True):
    r"""
    Return an affine geometry design.

    The affine geometry design `AG_d(n,q)` is the 2-design whose blocks are the
    `d`-vector subspaces in `\GF{q}^n`. It has parameters

    .. MATH::

        v = q^n,\ k = q^d,\ \lambda = \binom{n-1}{d-1}_q

    where the `q`-binomial coefficient `\binom{m}{r}_q` is defined by

    .. MATH::

        \binom{m}{r}_q = \frac{(q^m - 1)(q^{m-1} - 1) \cdots (q^{m-r+1}-1)}
              {(q^r-1)(q^{r-1}-1)\cdots (q-1)}

    .. SEEALSO::

        :func:`ProjectiveGeometryDesign`

    INPUT:

    - ``n`` (integer) -- the Euclidean dimension. The number of points of the
      design is `v=|\GF{q}^n|`.

    - ``d`` (integer) -- the dimension of the (affine) subspaces of `\GF{q}^n`
      which make up the blocks.

    - ``F`` -- a finite field or a prime power.

    - ``point_coordinates`` -- (optional, default ``True``) whether we use
      coordinates in `\GF{q}^n` or plain integers for the points of the design.

    - ``check`` -- (optional, default ``True``) whether to check the output.

    EXAMPLES::

        sage: BD = designs.AffineGeometryDesign(3, 1, GF(2))
        sage: BD.is_t_design(return_parameters=True)
        (True, (2, 8, 2, 1))
        sage: BD = designs.AffineGeometryDesign(3, 2, GF(4))
        sage: BD.is_t_design(return_parameters=True)
        (True, (2, 64, 16, 5))
        sage: BD = designs.AffineGeometryDesign(4, 2, GF(3))
        sage: BD.is_t_design(return_parameters=True)
        (True, (2, 81, 9, 13))

    With ``F`` an integer instead of a finite field::

        sage: BD = designs.AffineGeometryDesign(3, 2, 4)
        sage: BD.is_t_design(return_parameters=True)
        (True, (2, 64, 16, 5))

    Testing the option ``point_coordinates``::

        sage: designs.AffineGeometryDesign(3, 1, GF(2), point_coordinates=True).blocks()[0]
        [(0, 0, 0), (0, 0, 1)]
        sage: designs.AffineGeometryDesign(3, 1, GF(2), point_coordinates=False).blocks()[0]
        [0, 1]
    """
    try:
        q = int(F)
    except TypeError:
        q = F.cardinality()
    else:
        from sage.rings.finite_rings.finite_field_constructor import GF
        F = GF(q)

    n = int(n)
    d = int(d)

    from itertools import islice
    from sage.combinat.q_analogues import q_binomial
    from sage.matrix.echelon_matrix import reduced_echelon_matrix_iterator

    points = {
        p: i
        for i, p in enumerate(
            reduced_echelon_matrix_iterator(
                F, 1, n + 1, copy=True, set_immutable=True)) if p[0, 0]
    }

    blocks = []
    l1 = int(q_binomial(n + 1, d + 1, q) - q_binomial(n, d + 1, q))
    l2 = q**d
    for m1 in islice(
            reduced_echelon_matrix_iterator(F, d + 1, n + 1, copy=False),
            int(l1)):
        b = []
        for m2 in islice(
                reduced_echelon_matrix_iterator(F, 1, d + 1, copy=False),
                int(l2)):
            m = m2 * m1
            m.echelonize()
            m.set_immutable()
            b.append(points[m])
        blocks.append(b)

    B = BlockDesign(len(points),
                    blocks,
                    name="AffineGeometryDesign",
                    check=check)

    if point_coordinates:
        rd = {i: p[0][1:] for p, i in points.items()}
        for v in rd.values():
            v.set_immutable()
        B.relabel(rd)

    if check:
        if not B.is_t_design(
                t=2, v=q**n, k=q**d, l=q_binomial(n - 1, d - 1, q)):
            raise RuntimeError(
                "error in AffineGeometryDesign "
                "construction. Please e-mail [email protected]")
    return B
Example #4
0
def ProjectiveGeometryDesign(n,
                             d,
                             F,
                             algorithm=None,
                             point_coordinates=True,
                             check=True):
    r"""
    Return a projective geometry design.

    The projective geometry design `PG_d(n,q)` has for points the lines of
    `\GF{q}^{n+1}`, and for blocks the `d+1`-dimensional subspaces of
    `\GF{q}^{n+1}`, each of which contains `\frac {|\GF{q}|^{d+1}-1} {|\GF{q}|-1}` lines.
    It is a `2`-design with parameters

    .. MATH::

        v = \binom{n+1}{1}_q,\ k = \binom{d+1}{1}_q,\ \lambda =
        \binom{n-1}{d-1}_q

    where the `q`-binomial coefficient `\binom{m}{r}_q` is defined by

    .. MATH::

        \binom{m}{r}_q = \frac{(q^m - 1)(q^{m-1} - 1) \cdots (q^{m-r+1}-1)}
              {(q^r-1)(q^{r-1}-1)\cdots (q-1)}

    .. SEEALSO::

        :func:`AffineGeometryDesign`

    INPUT:

    - ``n`` is the projective dimension

    - ``d`` is the dimension of the subspaces which make up the blocks.

    - ``F`` -- a finite field or a prime power.

    - ``algorithm`` -- set to ``None`` by default, which results in using Sage's
      own implementation. In order to use GAP's implementation instead (i.e. its
      ``PGPointFlatBlockDesign`` function) set ``algorithm="gap"``. Note that
      GAP's "design" package must be available in this case, and that it can be
      installed with the ``gap_packages`` spkg.

    - ``point_coordinates`` -- ``True`` by default. Ignored and assumed to be ``False`` if
      ``algorithm="gap"``. If ``True``, the ground set is indexed by coordinates
      in `\GF{q}^{n+1}`.  Otherwise the ground set is indexed by integers.

    - ``check`` -- (optional, default to ``True``) whether to check the output.

    EXAMPLES:

    The set of `d`-dimensional subspaces in a `n`-dimensional projective space
    forms `2`-designs (or balanced incomplete block designs)::

        sage: PG = designs.ProjectiveGeometryDesign(4, 2, GF(2))
        sage: PG
        Incidence structure with 31 points and 155 blocks
        sage: PG.is_t_design(return_parameters=True)
        (True, (2, 31, 7, 7))

        sage: PG = designs.ProjectiveGeometryDesign(3, 1, GF(4))
        sage: PG.is_t_design(return_parameters=True)
        (True, (2, 85, 5, 1))

    Check with ``F`` being a prime power::

        sage: PG = designs.ProjectiveGeometryDesign(3, 2, 4)
        sage: PG
        Incidence structure with 85 points and 85 blocks

    Use coordinates::

        sage: PG = designs.ProjectiveGeometryDesign(2, 1, GF(3))
        sage: PG.blocks()[0]
        [(1, 0, 0), (1, 0, 1), (1, 0, 2), (0, 0, 1)]

    Use indexing by integers::

        sage: PG = designs.ProjectiveGeometryDesign(2,1,GF(3),point_coordinates=0)
        sage: PG.blocks()[0]
        [0, 1, 2, 12]

    Check that the constructor using gap also works::

        sage: BD = designs.ProjectiveGeometryDesign(2, 1, GF(2), algorithm="gap") # optional - gap_packages (design package)
        sage: BD.is_t_design(return_parameters=True)                              # optional - gap_packages (design package)
        (True, (2, 7, 3, 1))
    """
    try:
        q = int(F)
    except TypeError:
        q = F.cardinality()
    else:
        from sage.rings.finite_rings.finite_field_constructor import GF
        F = GF(q)

    if algorithm is None:
        from sage.matrix.echelon_matrix import reduced_echelon_matrix_iterator

        points = {
            p: i
            for i, p in enumerate(
                reduced_echelon_matrix_iterator(
                    F, 1, n + 1, copy=True, set_immutable=True))
        }
        blocks = []
        for m1 in reduced_echelon_matrix_iterator(F, d + 1, n + 1, copy=False):
            b = []
            for m2 in reduced_echelon_matrix_iterator(F, 1, d + 1, copy=False):
                m = m2 * m1
                m.echelonize()
                m.set_immutable()
                b.append(points[m])
            blocks.append(b)
        B = BlockDesign(len(points),
                        blocks,
                        name="ProjectiveGeometryDesign",
                        check=check)
        if point_coordinates:
            B.relabel({i: p[0] for p, i in points.items()})

    elif algorithm == "gap":  # Requires GAP's Design
        libgap.load_package("design")
        D = libgap.PGPointFlatBlockDesign(n, F.order(), d)
        v = D['v'].sage()
        gblcks = D['blocks'].sage()
        gB = []
        for b in gblcks:
            gB.append([x - 1 for x in b])
        B = BlockDesign(v, gB, name="ProjectiveGeometryDesign", check=check)

    if check:
        from sage.combinat.q_analogues import q_binomial
        q = F.cardinality()
        if not B.is_t_design(t=2,
                             v=q_binomial(n + 1, 1, q),
                             k=q_binomial(d + 1, 1, q),
                             l=q_binomial(n - 1, d - 1, q)):
            raise RuntimeError(
                "error in ProjectiveGeometryDesign "
                "construction. Please e-mail [email protected]")
    return B
Example #5
0
        def principal_specialization(self, n=infinity, q=None):
            r"""
            Return the principal specialization of a symmetric function.

            The *principal specialization* of order `n` at `q`
            is the ring homomorphism `ps_{n,q}` from the ring of
            symmetric functions to another commutative ring `R`
            given by `x_i \mapsto q^{i-1}` for `i \in \{1,\dots,n\}`
            and `x_i \mapsto 0` for `i > n`.
            Here, `q` is a given element of `R`, and we assume that
            the variables of our symmetric functions are
            `x_1, x_2, x_3, \ldots`.
            (To be more precise, `ps_{n,q}` is a `K`-algebra
            homomorphism, where `K` is the base ring.)
            See Section 7.8 of [EnumComb2]_.

            The *stable principal specialization* at `q` is the ring
            homomorphism `ps_q` from the ring of symmetric functions
            to another commutative ring `R` given by
            `x_i \mapsto q^{i-1}` for all `i`.
            This is well-defined only if the resulting infinite sums
            converge; thus, in particular, setting `q = 1` in the
            stable principal specialization is an invalid operation.

            INPUT:

            - ``n`` (default: ``infinity``) -- a nonnegative integer or
              ``infinity``, specifying whether to compute the principal
              specialization of order ``n`` or the stable principal
              specialization.

            - ``q`` (default: ``None``) -- the value to use for `q`; the
              default is to create a ring of polynomials in ``q``
              (or a field of rational functions in ``q``) over the
              given coefficient ring.

            We use the formulas from Proposition 7.8.3 of [EnumComb2]_
            (using Gaussian binomial coefficients `\binom{u}{v}_q`):

            .. MATH::

                ps_{n,q}(h_\lambda) = \prod_i \binom{n+\lambda_i-1}{\lambda_i}_q,

                ps_{n,1}(h_\lambda) = \prod_i \binom{n+\lambda_i-1}{\lambda_i},

                ps_q(h_\lambda) = 1 / \prod_i \prod_{j=1}^{\lambda_i} (1-q^j).

            EXAMPLES::

                sage: h = SymmetricFunctions(QQ).h()
                sage: x = h[2,1]
                sage: x.principal_specialization(3)
                q^6 + 2*q^5 + 4*q^4 + 4*q^3 + 4*q^2 + 2*q + 1
                sage: x = 3*h[2] + 2*h[1] + 1
                sage: x.principal_specialization(3, q=var("q"))
                2*(q^3 - 1)/(q - 1) + 3*(q^4 - 1)*(q^3 - 1)/((q^2 - 1)*(q - 1)) + 1

            TESTS::

                sage: x = h.zero()
                sage: s = x.principal_specialization(3); s
                0

            """
            from sage.combinat.q_analogues import q_binomial

            def get_variable(ring, name):
                try:
                    ring(name)
                except TypeError:
                    from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
                    return PolynomialRing(ring, name).gen()
                else:
                    raise ValueError(
                        "the variable %s is in the base ring, pass it explicitly"
                        % name)

            if q is None:
                q = get_variable(self.base_ring(), 'q')

            if q == 1:
                if n == infinity:
                    raise ValueError(
                        "the stable principal specialization at q=1 is not defined"
                    )
                f = lambda partition: prod(
                    binomial(n + part - 1, part) for part in partition)
            elif n == infinity:
                f = lambda partition: prod(1 / prod(
                    (1 - q**i) for i in range(1, part + 1))
                                           for part in partition)
            else:
                f = lambda partition: prod(
                    q_binomial(n + part - 1, part, q=q) for part in partition)

            return self.parent()._apply_module_morphism(self, f, q.parent())
Example #6
0
def AffineGeometryDesign(n, d, F, point_coordinates=True, check=True):
    r"""
    Return an affine geometry design.

    The affine geometry design `AG_d(n,q)` is the 2-design whose blocks are the
    `d`-vector subspaces in `\GF{q}^n`. It has parameters

    .. MATH::

        v = q^n,\ k = q^d,\ \lambda = \binom{n-1}{d-1}_q

    where the `q`-binomial coefficient `\binom{m}{r}_q` is defined by

    .. MATH::

        \binom{m}{r}_q = \frac{(q^m - 1)(q^{m-1} - 1) \cdots (q^{m-r+1}-1)}
              {(q^r-1)(q^{r-1}-1)\cdots (q-1)}

    .. SEEALSO::

        :func:`ProjectiveGeometryDesign`

    INPUT:

    - ``n`` (integer) -- the Euclidean dimension. The number of points of the
      design is `v=|\GF{q}^n|`.

    - ``d`` (integer) -- the dimension of the (affine) subspaces of `\GF(q)^n`
      which make up the blocks.

    - ``F`` -- a finite field or a prime power.

    - ``point_coordinates`` -- (optional, default ``True``) whether we use
      coordinates in `\GF(q)^n` or plain integers for the points of the design.

    - ``check`` -- (optional, default ``True``) whether to check the output.

    EXAMPLES::

        sage: BD = designs.AffineGeometryDesign(3, 1, GF(2))
        sage: BD.is_t_design(return_parameters=True)
        (True, (2, 8, 2, 1))
        sage: BD = designs.AffineGeometryDesign(3, 2, GF(4))
        sage: BD.is_t_design(return_parameters=True)
        (True, (2, 64, 16, 5))
        sage: BD = designs.AffineGeometryDesign(4, 2, GF(3))
        sage: BD.is_t_design(return_parameters=True)
        (True, (2, 81, 9, 13))

    With ``F`` an integer instead of a finite field::

        sage: BD = designs.AffineGeometryDesign(3, 2, 4)
        sage: BD.is_t_design(return_parameters=True)
        (True, (2, 64, 16, 5))

    Testing the option ``point_coordinates``::

        sage: designs.AffineGeometryDesign(3, 1, GF(2), point_coordinates=True).blocks()[0]
        [(0, 0, 0), (0, 0, 1)]
        sage: designs.AffineGeometryDesign(3, 1, GF(2), point_coordinates=False).blocks()[0]
        [0, 1]
    """
    try:
        q = int(F)
    except TypeError:
        q = F.cardinality()
    else:
        from sage.rings.finite_rings.finite_field_constructor import GF
        F = GF(q)

    n = int(n)
    d = int(d)

    from itertools import islice
    from sage.combinat.q_analogues import q_binomial
    from sage.matrix.echelon_matrix import reduced_echelon_matrix_iterator

    points = {p:i for i,p in enumerate(reduced_echelon_matrix_iterator(F,1,n+1,copy=True,set_immutable=True)) if p[0,0]}

    blocks = []
    l1 = q_binomial(n+1, d+1, q) - q_binomial(n, d+1, q)
    l2 = q**d
    for m1 in islice(reduced_echelon_matrix_iterator(F,d+1,n+1,copy=False), l1):
        b = []
        for m2 in islice(reduced_echelon_matrix_iterator(F,1,d+1,copy=False), l2):
            m = m2*m1
            m.echelonize()
            m.set_immutable()
            b.append(points[m])
        blocks.append(b)

    B = BlockDesign(len(points), blocks, name="AffineGeometryDesign", check=check)

    if point_coordinates:
        rd = {i:p[0][1:] for p,i in points.iteritems()}
        for v in rd.values(): v.set_immutable()
        B.relabel(rd)

    if check:
        if not B.is_t_design(t=2, v=q**n, k=q**d, l=q_binomial(n-1, d-1, q)):
            raise RuntimeError("error in AffineGeometryDesign "
                    "construction. Please e-mail [email protected]")
    return B
Example #7
0
def ProjectiveGeometryDesign(n, d, F, algorithm=None, point_coordinates=True, check=True):
    r"""
    Return a projective geometry design.

    The projective geometry design `PG_d(n,q)` has for points the lines of
    `\GF{q}^{n+1}`, and for blocks the `d+1`-dimensional subspaces of
    `\GF{q}^{n+1}`, each of which contains `\frac {|\GF{q}|^{d+1}-1} {|\GF{q}|-1}` lines.
    It is a `2`-design with parameters

    .. MATH::

        v = \binom{n+1}{1}_q,\ k = \binom{d+1}{1}_q,\ \lambda =
        \binom{n-1}{d-1}_q

    where the `q`-binomial coefficient `\binom{m}{r}_q` is defined by

    .. MATH::

        \binom{m}{r}_q = \frac{(q^m - 1)(q^{m-1} - 1) \cdots (q^{m-r+1}-1)}
              {(q^r-1)(q^{r-1}-1)\cdots (q-1)}

    .. SEEALSO::

        :func:`AffineGeometryDesign`

    INPUT:

    - ``n`` is the projective dimension

    - ``d`` is the dimension of the subspaces which make up the blocks.

    - ``F`` -- a finite field or a prime power.

    - ``algorithm`` -- set to ``None`` by default, which results in using Sage's
      own implementation. In order to use GAP's implementation instead (i.e. its
      ``PGPointFlatBlockDesign`` function) set ``algorithm="gap"``. Note that
      GAP's "design" package must be available in this case, and that it can be
      installed with the ``gap_packages`` spkg.

    - ``point_coordinates`` -- ``True`` by default. Ignored and assumed to be ``False`` if
      ``algorithm="gap"``. If ``True``, the ground set is indexed by coordinates
      in `\GF{q}^{n+1}`.  Otherwise the ground set is indexed by integers.

    - ``check`` -- (optional, default to ``True``) whether to check the output.

    EXAMPLES:

    The set of `d`-dimensional subspaces in a `n`-dimensional projective space
    forms `2`-designs (or balanced incomplete block designs)::

        sage: PG = designs.ProjectiveGeometryDesign(4, 2, GF(2))
        sage: PG
        Incidence structure with 31 points and 155 blocks
        sage: PG.is_t_design(return_parameters=True)
        (True, (2, 31, 7, 7))

        sage: PG = designs.ProjectiveGeometryDesign(3, 1, GF(4))
        sage: PG.is_t_design(return_parameters=True)
        (True, (2, 85, 5, 1))

    Check with ``F`` being a prime power::

        sage: PG = designs.ProjectiveGeometryDesign(3, 2, 4)
        sage: PG
        Incidence structure with 85 points and 85 blocks

    Use coordinates::

        sage: PG = designs.ProjectiveGeometryDesign(2, 1, GF(3))
        sage: PG.blocks()[0]
        [(1, 0, 0), (1, 0, 1), (1, 0, 2), (0, 0, 1)]

    Use indexing by integers::

        sage: PG = designs.ProjectiveGeometryDesign(2,1,GF(3),point_coordinates=0)
        sage: PG.blocks()[0]
        [0, 1, 2, 12]

    Check that the constructor using gap also works::

        sage: BD = designs.ProjectiveGeometryDesign(2, 1, GF(2), algorithm="gap") # optional - gap_packages (design package)
        sage: BD.is_t_design(return_parameters=True)                              # optional - gap_packages (design package)
        (True, (2, 7, 3, 1))
    """
    try:
        q = int(F)
    except TypeError:
        q = F.cardinality()
    else:
        from sage.rings.finite_rings.finite_field_constructor import GF
        F = GF(q)

    if algorithm is None:
        from sage.matrix.echelon_matrix import reduced_echelon_matrix_iterator

        points = {p:i for i,p in enumerate(reduced_echelon_matrix_iterator(F,1,n+1,copy=True,set_immutable=True))}
        blocks = []
        for m1 in reduced_echelon_matrix_iterator(F,d+1,n+1,copy=False):
            b = []
            for m2 in reduced_echelon_matrix_iterator(F,1,d+1,copy=False):
                m = m2*m1
                m.echelonize()
                m.set_immutable()
                b.append(points[m])
            blocks.append(b)
        B = BlockDesign(len(points), blocks, name="ProjectiveGeometryDesign", check=check)
        if point_coordinates:
            B.relabel({i:p[0] for p,i in points.iteritems()})

    elif algorithm == "gap":   # Requires GAP's Design
        from sage.interfaces.gap import gap
        gap.load_package("design")
        gap.eval("D := PGPointFlatBlockDesign( %s, %s, %d )"%(n,F.order(),d))
        v = eval(gap.eval("D.v"))
        gblcks = eval(gap.eval("D.blocks"))
        gB = []
        for b in gblcks:
            gB.append([x-1 for x in b])
        B = BlockDesign(v, gB, name="ProjectiveGeometryDesign", check=check)

    if check:
        from sage.combinat.q_analogues import q_binomial
        q = F.cardinality()
        if not B.is_t_design(t=2, v=q_binomial(n+1,1,q),
                                  k=q_binomial(d+1,1,q),
                                  l=q_binomial(n-1, d-1, q)):
            raise RuntimeError("error in ProjectiveGeometryDesign "
                    "construction. Please e-mail [email protected]")
    return B
Example #8
0
def hall_polynomial(nu, mu, la, q=None):
    r"""
    Return the (classical) Hall polynomial `P^{\nu}_{\mu,\lambda}`
    (where `\nu`, `\mu` and `\lambda` are the inputs ``nu``, ``mu``
    and ``la``).

    Let `\nu,\mu,\lambda` be partitions. The Hall polynomial
    `P^{\nu}_{\mu,\lambda}(q)` (in the indeterminate `q`) is defined
    as follows: Specialize `q` to a prime power, and consider the
    category of `\GF{q}`-vector spaces with a distinguished
    nilpotent endomorphism. The morphisms in this category shall be
    the linear maps commuting with the distinguished endomorphisms.
    The *type* of an object in the category will be the Jordan type
    of the distinguished endomorphism; this is a partition. Now, if
    `N` is any fixed object of type `\nu` in this category, then
    the polynomial `P^{\nu}_{\mu,\lambda}(q)` specialized at the
    prime power `q` counts the number of subobjects `L` of `N` having
    type `\lambda` such that the quotient object `N / L` has type
    `\mu`. This determines the values of the polynomial
    `P^{\nu}_{\mu,\lambda}` at infinitely many points (namely, at all
    prime powers), and hence `P^{\nu}_{\mu,\lambda}` is uniquely
    determined. The degree of this polynomial is at most
    `n(\nu) - n(\lambda) - n(\mu)`, where
    `n(\kappa) = \sum_i (i-1) \kappa_i` for every partition `\kappa`.
    (If this is negative, then the polynomial is zero.)

    These are the structure coefficients of the
    :class:`(classical) Hall algebra <HallAlgebra>`.

    If `\lvert \nu \rvert \neq \lvert \mu \rvert + \lvert \lambda \rvert`,
    then we have `P^{\nu}_{\mu,\lambda} = 0`. More generally, if the
    Littlewood-Richardson coefficient `c^{\nu}_{\mu,\lambda}`
    vanishes, then `P^{\nu}_{\mu,\lambda} = 0`.

    The Hall polynomials satisfy the symmetry property
    `P^{\nu}_{\mu,\lambda} = P^{\nu}_{\lambda,\mu}`.

    ALGORITHM:

    If `\lambda = (1^r)` and
    `\lvert \nu \rvert = \lvert \mu \rvert + \lvert \lambda \rvert`,
    then we compute `P^{\nu}_{\mu,\lambda}` as follows (cf. Example 2.4
    in [Sch2006]_):

    First, write `\nu = (1^{l_1}, 2^{l_2}, \ldots, n^{l_n})`, and
    define a sequence `r = r_0 \geq r_1 \geq \cdots \geq r_n` such that

    .. MATH::

        \mu = \left( 1^{l_1 - r_0 + 2r_1 - r_2}, 2^{l_2 - r_1 + 2r_2 - r_3},
        \ldots , (n-1)^{l_{n-1} - r_{n-2} + 2r_{n-1} - r_n},
        n^{l_n - r_{n-1} + r_n} \right).

    Thus if `\mu = (1^{m_1}, \ldots, n^{m_n})`, we have the following system
    of equations:

    .. MATH::

        \begin{aligned}
        m_1 & = l_1 - r + 2r_1 - r_2,
        \\ m_2 & = l_2 - r_1 + 2r_2 - r_3,
        \\ & \vdots ,
        \\ m_{n-1} & = l_{n-1} - r_{n-2} + 2r_{n-1} - r_n,
        \\ m_n & = l_n - r_{n-1} + r_n
        \end{aligned}

    and solving for `r_i` and back substituting we obtain the equations:

    .. MATH::

        \begin{aligned}
        r_n & = r_{n-1} + m_n - l_n,
        \\ r_{n-1} & = r_{n-2} + m_{n-1} - l_{n-1} + m_n - l_n,
        \\ & \vdots ,
        \\ r_1 & = r + \sum_{k=1}^n (m_k - l_k),
        \end{aligned}

    or in general we have the recursive equation:

    .. MATH::

        r_i = r_{i-1} + \sum_{k=i}^n (m_k - l_k).

    This, combined with the condition that `r_0 = r`, determines the
    `r_i` uniquely (recursively). Next we define

    .. MATH::

        t = (r_{n-2} - r_{n-1})(l_n - r_{n-1})
        + (r_{n-3} - r_{n-2})(l_{n-1} + l_n - r_{n-2}) + \cdots
        + (r_0 - r_1)(l_2 + \cdots + l_n - r_1),

    and with these notations we have

    .. MATH::

        P^{\nu}_{\mu,(1^r)} = q^t \binom{l_n}{r_{n-1}}_q
        \binom{l_{n-1}}{r_{n-2} - r_{n-1}}_q \cdots \binom{l_1}{r_0 - r_1}_q.

    To compute `P^{\nu}_{\mu,\lambda}` in general, we compute the product
    `I_{\mu} I_{\lambda}` in the Hall algebra and return the coefficient of
    `I_{\nu}`.

    EXAMPLES::

        sage: from sage.combinat.hall_polynomial import hall_polynomial
        sage: hall_polynomial([1,1],[1],[1])
        q + 1
        sage: hall_polynomial([2],[1],[1])
        1
        sage: hall_polynomial([2,1],[2],[1])
        q
        sage: hall_polynomial([2,2,1],[2,1],[1,1])
        q^2 + q
        sage: hall_polynomial([2,2,2,1],[2,2,1],[1,1])
        q^4 + q^3 + q^2
        sage: hall_polynomial([3,2,2,1], [3,2], [2,1])
        q^6 + q^5
        sage: hall_polynomial([4,2,1,1], [3,1,1], [2,1])
        2*q^3 + q^2 - q - 1
        sage: hall_polynomial([4,2], [2,1], [2,1], 0)
        1

    TESTS::

        sage: hall_polynomial([3], [1], [1], 0)
        0
    """
    if q is None:
        q = ZZ['q'].gen()
    R = q.parent()

    # Make sure they are partitions
    nu = Partition(nu)
    mu = Partition(mu)
    la = Partition(la)

    if sum(nu) != sum(mu) + sum(la):
        return R.zero()

    if all(x == 1 for x in la):
        r = [len(la)]  # r will be [r_0, r_1, ..., r_n].
        exp_nu = nu.to_exp()  # exp_nu == [l_1, l_2, ..., l_n].
        exp_mu = mu.to_exp()  # exp_mu == [m_1, m_2, ..., m_n].
        n = max(len(exp_nu), len(exp_mu))
        for k in range(n):
            r.append(r[-1] + sum(exp_mu[k:]) - sum(exp_nu[k:]))
        # Now, r is [r_0, r_1, ..., r_n].
        exp_nu += [0] * (n - len(exp_nu))  # Pad with 0's until it has length n
        # Note that all -1 for exp_nu is due to indexing
        t = sum((r[k - 2] - r[k - 1]) * (sum(exp_nu[k - 1:]) - r[k - 1])
                for k in range(2, n + 1))
        if t < 0:
            # This case needs short-circuiting, since otherwise q**-t
            # might throw an exception if q is non-invertible.
            return R.zero()
        return q**t * q_binomial(exp_nu[n-1], r[n-1], q) \
               * prod([q_binomial(exp_nu[k-1], r[k-1] - r[k], q)
                       for k in range(1, n)], R.one())

    from sage.algebras.hall_algebra import HallAlgebra
    H = HallAlgebra(R, q)
    return (H[mu] * H[la]).coefficient(nu)
Example #9
0
def hall_polynomial(nu, mu, la, q=None):
    r"""
    Return the (classical) Hall polynomial `P^{\nu}_{\mu,\lambda}`
    (where `\nu`, `\mu` and `\lambda` are the inputs ``nu``, ``mu``
    and ``la``).

    Let `\nu,\mu,\lambda` be partitions. The Hall polynomial
    `P^{\nu}_{\mu,\lambda}(q)` (in the indeterminate `q`) is defined
    as follows: Specialize `q` to a prime power, and consider the
    category of `\GF{q}`-vector spaces with a distinguished
    nilpotent endomorphism. The morphisms in this category shall be
    the linear maps commuting with the distinguished endomorphisms.
    The *type* of an object in the category will be the Jordan type
    of the distinguished endomorphism; this is a partition. Now, if
    `N` is any fixed object of type `\nu` in this category, then
    the polynomial `P^{\nu}_{\mu,\lambda}(q)` specialized at the
    prime power `q` counts the number of subobjects `L` of `N` having
    type `\lambda` such that the quotient object `N / L` has type
    `\mu`. This determines the values of the polynomial
    `P^{\nu}_{\mu,\lambda}` at infinitely many points (namely, at all
    prime powers), and hence `P^{\nu}_{\mu,\lambda}` is uniquely
    determined. The degree of this polynomial is at most
    `n(\nu) - n(\lambda) - n(\mu)`, where
    `n(\kappa) = \sum_i (i-1) \kappa_i` for every partition `\kappa`.
    (If this is negative, then the polynomial is zero.)

    These are the structure coefficients of the
    :class:`(classical) Hall algebra <HallAlgebra>`.

    If `\lvert \nu \rvert \neq \lvert \mu \rvert + \lvert \lambda \rvert`,
    then we have `P^{\nu}_{\mu,\lambda} = 0`. More generally, if the
    Littlewood-Richardson coefficient `c^{\nu}_{\mu,\lambda}`
    vanishes, then `P^{\nu}_{\mu,\lambda} = 0`.

    The Hall polynomials satisfy the symmetry property
    `P^{\nu}_{\mu,\lambda} = P^{\nu}_{\lambda,\mu}`.

    ALGORITHM:

    If `\lambda = (1^r)` and
    `\lvert \nu \rvert = \lvert \mu \rvert + \lvert \lambda \rvert`,
    then we compute `P^{\nu}_{\mu,\lambda}` as follows (cf. Example 2.4
    in [Schiffmann]_):

    First, write `\nu = (1^{l_1}, 2^{l_2}, \ldots, n^{l_n})`, and
    define a sequence `r = r_0 \geq r_1 \geq \cdots \geq r_n` such that

    .. MATH::

        \mu = \left( 1^{l_1 - r_0 + 2r_1 - r_2}, 2^{l_2 - r_1 + 2r_2 - r_3},
        \ldots , (n-1)^{l_{n-1} - r_{n-2} + 2r_{n-1} - r_n},
        n^{l_n - r_{n-1} + r_n} \right).

    Thus if `\mu = (1^{m_1}, \ldots, n^{m_n})`, we have the following system
    of equations:

    .. MATH::

        \begin{aligned}
        m_1 & = l_1 - r + 2r_1 - r_2,
        \\ m_2 & = l_2 - r_1 + 2r_2 - r_3,
        \\ & \vdots ,
        \\ m_{n-1} & = l_{n-1} - r_{n-2} + 2r_{n-1} - r_n,
        \\ m_n & = l_n - r_{n-1} + r_n
        \end{aligned}

    and solving for `r_i` and back substituting we obtain the equations:

    .. MATH::

        \begin{aligned}
        r_n & = r_{n-1} + m_n - l_n,
        \\ r_{n-1} & = r_{n-2} + m_{n-1} - l_{n-1} + m_n - l_n,
        \\ & \vdots ,
        \\ r_1 & = r + \sum_{k=1}^n (m_k - l_k),
        \end{aligned}

    or in general we have the recursive equation:

    .. MATH::

        r_i = r_{i-1} + \sum_{k=i}^n (m_k - l_k).

    This, combined with the condition that `r_0 = r`, determines the
    `r_i` uniquely (recursively). Next we define

    .. MATH::

        t = (r_{n-2} - r_{n-1})(l_n - r_{n-1})
        + (r_{n-3} - r_{n-2})(l_{n-1} + l_n - r_{n-2}) + \cdots
        + (r_0 - r_1)(l_2 + \cdots + l_n - r_1),

    and with these notations we have

    .. MATH::

        P^{\nu}_{\mu,(1^r)} = q^t \binom{l_n}{r_{n-1}}_q
        \binom{l_{n-1}}{r_{n-2} - r_{n-1}}_q \cdots \binom{l_1}{r_0 - r_1}_q.

    To compute `P^{\nu}_{\mu,\lambda}` in general, we compute the product
    `I_{\mu} I_{\lambda}` in the Hall algebra and return the coefficient of
    `I_{\nu}`.

    EXAMPLES::

        sage: from sage.combinat.hall_polynomial import hall_polynomial
        sage: hall_polynomial([1,1],[1],[1])
        q + 1
        sage: hall_polynomial([2],[1],[1])
        1
        sage: hall_polynomial([2,1],[2],[1])
        q
        sage: hall_polynomial([2,2,1],[2,1],[1,1])
        q^2 + q
        sage: hall_polynomial([2,2,2,1],[2,2,1],[1,1])
        q^4 + q^3 + q^2
        sage: hall_polynomial([3,2,2,1], [3,2], [2,1])
        q^6 + q^5
        sage: hall_polynomial([4,2,1,1], [3,1,1], [2,1])
        2*q^3 + q^2 - q - 1
        sage: hall_polynomial([4,2], [2,1], [2,1], 0)
        1
    """
    if q is None:
        q = ZZ['q'].gen()
    R = q.parent()

    # Make sure they are partitions
    nu = Partition(nu)
    mu = Partition(mu)
    la = Partition(la)

    if sum(nu) != sum(mu) + sum(la):
        return R.zero()

    if all(x == 1 for x in la):
        r = [len(la)]   # r will be [r_0, r_1, ..., r_n].
        exp_nu = nu.to_exp()  # exp_nu == [l_1, l_2, ..., l_n].
        exp_mu = mu.to_exp()  # exp_mu == [m_1, m_2, ..., m_n].
        n = max(len(exp_nu), len(exp_mu))
        for k in range(n):
            r.append(r[-1] + sum(exp_mu[k:]) - sum(exp_nu[k:]))
        # Now, r is [r_0, r_1, ..., r_n].
        exp_nu += [0]*(n - len(exp_nu)) # Pad with 0's until it has length n
        # Note that all -1 for exp_nu is due to indexing
        t = sum((r[k-2] - r[k-1])*(sum(exp_nu[k-1:]) - r[k-1]) for k in range(2,n+1))
        if t < 0:
            # This case needs short-circuiting, since otherwise q**-t
            # might throw an exception if q is non-invertible.
            return R.zero()
        return q**t * q_binomial(exp_nu[n-1], r[n-1], q) \
               * prod([q_binomial(exp_nu[k-1], r[k-1] - r[k], q)
                       for k in range(1, n)], R.one())

    from sage.algebras.hall_algebra import HallAlgebra
    H = HallAlgebra(R, q)
    return (H[mu]*H[la]).coefficient(nu)