Beispiel #1
0
class RibbonTableaux(UniqueRepresentation, Parent):
    r"""
    Ribbon tableaux.

    A ribbon tableau is a skew tableau whose skew shape ``shape`` is
    tiled by ribbons of length ``length``. The weight ``weight`` is
    calculated from the labels on the ribbons.

    .. NOTE::

        Here we impose the condition that the ribbon tableaux are semistandard.

    INPUT(Optional):

    - ``shape``  -- skew shape as a list of lists or an object of type
      SkewPartition

    - ``length`` -- integer, ``shape`` is partitioned into ribbons of
      length ``length``

    - ``weight`` -- list of integers, computed from the values of
      non-zero entries labeling the ribbons

    EXAMPLES::

        sage: RibbonTableaux([[2,1],[]], [1,1,1], 1)
        Ribbon tableaux of shape [2, 1] / [] and weight [1, 1, 1] with 1-ribbons

        sage: R = RibbonTableaux([[5,4,3],[2,1]], [2,1], 3)
        sage: for i in R: i.pp(); print("\n")
          .  .  0  0  0
          .  0  0  2
          1  0  1
        <BLANKLINE>
          .  .  1  0  0
          .  0  0  0
          1  0  2
        <BLANKLINE>
          .  .  0  0  0
          .  1  0  1
          2  0  0
        <BLANKLINE>

    REFERENCES:

    .. [vanLeeuwen91] Marc. A. A. van Leeuwen, *Edge sequences, ribbon tableaux,
       and an action of affine permutations*. Europe J. Combinatorics. **20**
       (1999). http://wwwmathlabo.univ-poitiers.fr/~maavl/pdf/edgeseqs.pdf
    """
    @staticmethod
    def __classcall_private__(cls, shape=None, weight=None, length=None):
        """
        Return the correct parent object.

        EXAMPLES::

            sage: R = RibbonTableaux([[2,1],[]],[1,1,1],1)
            sage: R2 = RibbonTableaux(SkewPartition([[2,1],[]]),(1,1,1),1)
            sage: R is R2
            True
        """
        if shape is None and weight is None and length is None:
            return super(RibbonTableaux, cls).__classcall__(cls)

        return RibbonTableaux_shape_weight_length(shape, weight, length)

    def __init__(self):
        """
        EXAMPLES::

            sage: R = RibbonTableaux()
            sage: TestSuite(R).run()
        """
        Parent.__init__(self, category=Sets())

    def _repr_(self):
        """
        Return a string representation of ``self``.

        EXAMPLES::

            sage: RibbonTableaux()
            Ribbon tableaux
        """
        return "Ribbon tableaux"

    def _element_constructor_(self, rt):
        """
        Construct an element of ``self`` from ``rt``.

        EXAMPLES::

            sage: R = RibbonTableaux()
            sage: elt = R([[0, 0, 3, 0], [1, 1, 0], [2, 0, 4]]); elt
            [[0, 0, 3, 0], [1, 1, 0], [2, 0, 4]]
            sage: elt.parent() is R
            True
        """
        return self.element_class(self, rt)

    def from_expr(self, l):
        """
        Return a :class:`RibbonTableau` from a MuPAD-Combinat expr for a skew
        tableau. The first list in ``expr`` is the inner shape of the skew
        tableau. The second list are the entries in the rows of the skew
        tableau from bottom to top.

        Provided primarily for compatibility with MuPAD-Combinat.

        EXAMPLES::

            sage: RibbonTableaux().from_expr([[1,1],[[5],[3,4],[1,2]]])
            [[None, 1, 2], [None, 3, 4], [5]]
        """
        return self.element_class(self, SkewTableaux().from_expr(l))

    Element = RibbonTableau
    options = Tableaux.options
    global_options = deprecated_function_alias(18555, options)
Beispiel #2
0
        """
        for p in descents_composition_list(self.shape):
            yield self.from_permutation(p)

class Ribbon_class(RibbonShapedTableau):
    """
    This exists solely for unpickling ``Ribbon_class`` objects.
    """
    def __setstate__(self, state):
        r"""
        Unpickle old ``Ribbon_class`` objects.

        EXAMPLES::

            sage: loads('x\x9ck`J.NLO\xd5K\xce\xcfM\xca\xccK,\xd1+\xcaLJ\xca\xcf\xe3\n\x02S\xf1\xc99\x89\xc5\xc5\\\x85\x8c\x9a\x8d\x85L\xb5\x85\xcc\x1a\xa1\xac\xf1\x19\x89\xc5\x19\x85,~@VNfqI!kl!\x9bFl!\xbb\x06\xc4\x9c\xa2\xcc\xbc\xf4b\xbd\xcc\xbc\x92\xd4\xf4\xd4"\xae\xdc\xc4\xec\xd4x\x18\xa7\x90#\x94\xd1\xb05\xa8\x903\x03\xc80\x022\xb8Rc\x0b\xb95@<c \x8f\x07\xc40\x012xSSK\x93\xf4\x00l\x811\x17')
            [[None, 1, 2], [3, 4]]
            sage: loads(dumps( RibbonShapedTableau([[3,2,1], [1,1]]) ))  # indirect doctest
            [[None, 3, 2, 1], [1, 1]]
        """
        self.__class__ = RibbonShapedTableau
        self.__init__(RibbonShapedTableaux(), state['_list'])

from sage.structure.sage_object import register_unpickle_override
register_unpickle_override('sage.combinat.ribbon', 'Ribbon_class', Ribbon_class)
register_unpickle_override('sage.combinat.ribbon', 'StandardRibbons_shape', StandardRibbonShapedTableaux)

# Deprecations from trac:18555. July 2016
from sage.misc.superseded import deprecated_function_alias
RibbonShapedTableaux.global_options = deprecated_function_alias(18555, RibbonShapedTableaux.options)
StandardRibbonShapedTableaux.global_options = deprecated_function_alias(18555, StandardRibbonShapedTableaux.options)
Beispiel #3
0
class WeightSpace_class(ParentWithBase):
    r"""
    The space of `p`-adic weight-characters `\mathcal{W} = {\rm
    Hom}(\ZZ_p^\times, \CC_p^\times)`. This isomorphic to a
    disjoint union of `(p-1)` open discs of radius 1 (or 2 such discs if `p =
    2`), with the parameter on the open disc corresponding to the image of `1 +
    p` (or 5 if `p = 2`)

    TESTS::

        sage: W = pAdicWeightSpace(3)
        sage: W is loads(dumps(W))
        True
    """
    def __init__(self, p, base_ring):
        r"""
        Initialisation function.

        EXAMPLES::

            sage: pAdicWeightSpace(17)
            Space of 17-adic weight-characters defined over '17-adic Field with capped relative precision 20'
        """
        ParentWithBase.__init__(self, base=base_ring)
        p = ZZ(p)
        if not p.is_prime():
            raise ValueError("p must be prime")
        self._p = p
        self._param = Qp(p)((p == 2 and 5) or (p + 1))

    def _repr_(self):
        r"""
        String representation of self.

        EXAMPLES::

            sage: pAdicWeightSpace(17)._repr_()
            "Space of 17-adic weight-characters defined over '17-adic Field with capped relative precision 20'"
        """
        return "Space of %s-adic weight-characters defined over '%s'" % (
            self.prime(), self.base_ring())

    def __reduce__(self):
        r"""
        Used for pickling.

        EXAMPLES::

            sage: pAdicWeightSpace(3).__reduce__()
            (<function WeightSpace_constructor at ...>, (3, 3-adic Field with capped relative precision 20))
        """
        return (WeightSpace_constructor, (self.prime(), self.base_ring()))

    def __call__(self, arg1, arg2=None, algebraic=True):
        r"""
        Create an element of this space.

        If ``algebraic = True`` (the default), create a locally algebraic
        character. The arguments should be `(k, \chi)` with `k \in \ZZ`
        and `\chi` a Dirichlet character of `p`-power conductor defined over a
        `p`-adic field; this corresponds to the weight-character `x \mapsto x^k
        \chi(x)`. If `\chi` is omitted, it defaults to the trivial character.

        If ``algebraic = False``, create a general character. The arguments are
        now (t, w) where `t \in \ZZ/(p-1)\ZZ` and `w \in
        \CC_p` with `|w - 1| < 1`. This corresponds to the character
        `\kappa` satisfying `\kappa(\mu) = \mu^t` where `\mu` is a `(p-1)`-st
        root of unity, and `\kappa(1 + p) = w`.

        EXAMPLES::

            sage: W = pAdicWeightSpace(17)
            sage: W(4)
            4
            sage: W(4, DirichletGroup(17, Qp(17)).0)
            (4, 17, [3 + 13*17 + ... + O(17^20)])
            sage: W(1 + O(17^5), 4, algebraic = False)
            [1 + O(17^5), 4]
        """

        if isinstance(arg1, WeightCharacter):
            if arg1.parent() is self:
                return arg1
            elif arg1.parent().prime() == self.prime():
                return self._coerce_in_wtchar(arg1)
            else:
                raise TypeError("Incompatible type!")

        if algebraic:
            return AlgebraicWeight(self, arg1, arg2)
        else:
            return ArbitraryWeight(self, arg1, arg2)

    @cached_method
    def zero(self):
        """
        Return the zero of this weight space.

        EXAMPLES::

            sage: W = pAdicWeightSpace(17)
            sage: W.zero()
            0
        """
        return self(0)

    zero_element = deprecated_function_alias(17694, zero)

    def prime(self):
        r"""
        Return the prime `p` such that this is a `p`-adic weight space.

        EXAMPLES::

            sage: pAdicWeightSpace(17).prime()
            17
        """
        return self._p

    def base_extend(self, R):
        r"""
        Extend scalars to the ring R. There must be a canonical coercion map
        from the present base ring to R.

        EXAMPLES::

            sage: W = pAdicWeightSpace(3, QQ)
            sage: W.base_extend(Qp(3))
            Space of 3-adic weight-characters defined over '3-adic Field with capped relative precision 20'
            sage: W.base_extend(IntegerModRing(12))
            Traceback (most recent call last):
            ...
            TypeError: No coercion map from 'Rational Field' to 'Ring of integers modulo 12' is defined
        """
        if R.has_coerce_map_from(self.base_ring()):
            return WeightSpace_constructor(self.prime(), R)
        else:
            raise TypeError("No coercion map from '%s' to '%s' is defined" %
                            (self.base_ring(), R))

    def _coerce_impl(self, x):
        r"""
        Canonical coercion of x into self.

        TESTS::

            sage: W1 = pAdicWeightSpace(23, QQ)
            sage: W2 = W1.base_extend(Qp(23))
            sage: w = W1(3)
            sage: W2.coerce(w) # indirect doctest
            3
        """
        if isinstance(x, WeightCharacter) \
            and x.parent().prime() == self.prime() \
            and self.base_ring().has_coerce_map_from(x.base_ring()):
            return self._coerce_in_wtchar(x)
        raise TypeError

    def _coerce_in_wtchar(self, x):
        r"""
        Convert in a weight-character whose parent is different from self (with
        has the prime, but possibly different base ring).

        EXAMPLES::

            sage: W1 = pAdicWeightSpace(23, Qp(3))
            sage: W2 = pAdicWeightSpace(23, QQ)
            sage: w = W1(3)
            sage: W2._coerce_in_wtchar(w)
            3
        """
        if isinstance(x, AlgebraicWeight):
            return AlgebraicWeight(self, x.k(),
                                   x.chi().change_ring(self.base_ring()))
        else:
            return ArbitraryWeight(self,
                                   self.base_ring()(x.w()),
                                   x.teichmuller_type())
Beispiel #4
0
class GroupMixinLibGAP(object):
    @cached_method
    def is_abelian(self):
        r"""
        Test whether the group is Abelian.

        OUTPUT:

        Boolean. ``True`` if this group is an Abelian group.

        EXAMPLES::

            sage: SL(1, 17).is_abelian()
            True
            sage: SL(2, 17).is_abelian()
            False
        """
        return self.gap().IsAbelian().sage()

    @cached_method
    def is_finite(self):
        """
        Test whether the matrix group is finite.

        OUTPUT:

        Boolean.

        EXAMPLES::

            sage: G = GL(2,GF(3))
            sage: G.is_finite()
            True
            sage: SL(2,ZZ).is_finite()
            False
        """
        return self.gap().IsFinite().sage()

    def cardinality(self):
        """
        Implements :meth:`EnumeratedSets.ParentMethods.cardinality`.

        EXAMPLES::

            sage: G = Sp(4,GF(3))
            sage: G.cardinality()
            51840

            sage: G = SL(4,GF(3))
            sage: G.cardinality()
            12130560

            sage: F = GF(5); MS = MatrixSpace(F,2,2)
            sage: gens = [MS([[1,2],[-1,1]]),MS([[1,1],[0,1]])]
            sage: G = MatrixGroup(gens)
            sage: G.cardinality()
            480

            sage: G = MatrixGroup([matrix(ZZ,2,[1,1,0,1])])
            sage: G.cardinality()
            +Infinity

            sage: G = Sp(4,GF(3))
            sage: G.cardinality()
            51840

            sage: G = SL(4,GF(3))
            sage: G.cardinality()
            12130560

            sage: F = GF(5); MS = MatrixSpace(F,2,2)
            sage: gens = [MS([[1,2],[-1,1]]),MS([[1,1],[0,1]])]
            sage: G = MatrixGroup(gens)
            sage: G.cardinality()
            480

            sage: G = MatrixGroup([matrix(ZZ,2,[1,1,0,1])])
            sage: G.cardinality()
            +Infinity
        """
        return self.gap().Size().sage()
        from sage.rings.infinity import Infinity
        return Infinity

    order = cardinality

    @cached_method
    def conjugacy_classes_representatives(self):
        """
        Return a set of representatives for each of the conjugacy classes
        of the group.

        EXAMPLES::

            sage: G = SU(3,GF(2))
            sage: len(G.conjugacy_classes_representatives())
            16

            sage: G = GL(2,GF(3))
            sage: G.conjugacy_classes_representatives()
            (
            [1 0]  [0 2]  [2 0]  [0 2]  [0 2]  [0 1]  [0 1]  [2 0]
            [0 1], [1 1], [0 2], [1 2], [1 0], [1 2], [1 1], [0 1]
            )

            sage: len(GU(2,GF(5)).conjugacy_classes_representatives())
            36

        ::

            sage: GL(2,ZZ).conjugacy_classes_representatives()
            Traceback (most recent call last):
            ...
            NotImplementedError: only implemented for finite groups

        """
        if not self.is_finite():
            raise NotImplementedError("only implemented for finite groups")
        G = self.gap()
        reps = [cc.Representative() for cc in G.ConjugacyClasses()]
        return tuple(self(g) for g in reps)

    conjugacy_class_representatives = deprecated_function_alias(
        22783, conjugacy_classes_representatives)

    def conjugacy_classes(self):
        r"""
        Return a list with all the conjugacy classes of ``self``.

        EXAMPLES::

            sage: G = SL(2, GF(2))
            sage: G.conjugacy_classes()
            (Conjugacy class of [1 0]
             [0 1] in Special Linear Group of degree 2 over Finite Field of size 2,
             Conjugacy class of [0 1]
             [1 0] in Special Linear Group of degree 2 over Finite Field of size 2,
             Conjugacy class of [0 1]
             [1 1] in Special Linear Group of degree 2 over Finite Field of size 2)

        ::

            sage: GL(2,ZZ).conjugacy_classes()
            Traceback (most recent call last):
            ...
            NotImplementedError: only implemented for finite groups
        """
        if not self.is_finite():
            raise NotImplementedError("only implemented for finite groups")
        from sage.groups.conjugacy_classes import ConjugacyClassGAP
        return tuple(
            ConjugacyClassGAP(self, self(g))
            for g in self.conjugacy_classes_representatives())

    def conjugacy_class(self, g):
        r"""
        Return the conjugacy class of ``g``.

        OUTPUT:

        The conjugacy class of ``g`` in the group ``self``. If ``self`` is the
        group denoted by `G`, this method computes the set
        `\{x^{-1}gx\ \vert\ x\in G\}`.

        EXAMPLES::

            sage: G = SL(2, QQ)
            sage: g = G([[1,1],[0,1]])
            sage: G.conjugacy_class(g)
            Conjugacy class of [1 1]
            [0 1] in Special Linear Group of degree 2 over Rational Field
        """
        from sage.groups.conjugacy_classes import ConjugacyClassGAP
        return ConjugacyClassGAP(self, self(g))

    def class_function(self, values):
        """
        Return the class function with given values.

        INPUT:

        - ``values`` -- list/tuple/iterable of numbers. The values of the
          class function on the conjugacy classes, in that order.

        EXAMPLES::

            sage: G = GL(2,GF(3))
            sage: chi = G.class_function(range(8))
            sage: list(chi)
            [0, 1, 2, 3, 4, 5, 6, 7]
        """
        from sage.groups.class_function import ClassFunction_libgap
        return ClassFunction_libgap(self, values)

    @cached_method
    def center(self):
        """
        Return the center of this linear group as a subgroup.

        OUTPUT:

        The center as a subgroup.

        EXAMPLES::

            sage: G = SU(3,GF(2))
            sage: G.center()
            Matrix group over Finite Field in a of size 2^2 with 1 generators (
            [a 0 0]
            [0 a 0]
            [0 0 a]
            )
            sage: GL(2,GF(3)).center()
            Matrix group over Finite Field of size 3 with 1 generators (
            [2 0]
            [0 2]
            )
            sage: GL(3,GF(3)).center()
            Matrix group over Finite Field of size 3 with 1 generators (
            [2 0 0]
            [0 2 0]
            [0 0 2]
            )
            sage: GU(3,GF(2)).center()
            Matrix group over Finite Field in a of size 2^2 with 1 generators (
            [a + 1     0     0]
            [    0 a + 1     0]
            [    0     0 a + 1]
            )

            sage: A = Matrix(FiniteField(5), [[2,0,0], [0,3,0], [0,0,1]])
            sage: B = Matrix(FiniteField(5), [[1,0,0], [0,1,0], [0,1,1]])
            sage: MatrixGroup([A,B]).center()
            Matrix group over Finite Field of size 5 with 1 generators (
            [1 0 0]
            [0 1 0]
            [0 0 1]
            )
        """
        G = self.gap()
        center = list(G.Center().GeneratorsOfGroup())
        if len(center) == 0:
            center = [G.One()]
        return self.subgroup(center)

    def intersection(self, other):
        """
        Return the intersection of two groups (if it makes sense) as a
        subgroup of the first group.

        EXAMPLES::

            sage: A = Matrix([(0, 1/2, 0), (2, 0, 0), (0, 0, 1)])
            sage: B = Matrix([(0, 1/2, 0), (-2, -1, 2), (0, 0, 1)])
            sage: G = MatrixGroup([A,B])
            sage: len(G)  # isomorphic to S_3
            6
            sage: G.intersection(GL(3,ZZ))
            Matrix group over Rational Field with 1 generators (
            [ 1  0  0]
            [-2 -1  2]
            [ 0  0  1]
            )
            sage: GL(3,ZZ).intersection(G)
            Matrix group over Integer Ring with 1 generators (
            [ 1  0  0]
            [-2 -1  2]
            [ 0  0  1]
            )
            sage: G.intersection(SL(3,ZZ))
            Matrix group over Rational Field with 0 generators ()
        """
        G = self.gap()
        H = other.gap()
        C = G.Intersection(H)
        return self.subgroup(C.GeneratorsOfGroup())

    @cached_method
    def irreducible_characters(self):
        """
        Return the irreducible characters of the group.

        OUTPUT:

        A tuple containing all irreducible characters.

        EXAMPLES::

            sage: G = GL(2,2)
            sage: G.irreducible_characters()
            (Character of General Linear Group of degree 2 over Finite Field of size 2,
             Character of General Linear Group of degree 2 over Finite Field of size 2,
             Character of General Linear Group of degree 2 over Finite Field of size 2)

        ::

            sage: GL(2,ZZ).irreducible_characters()
            Traceback (most recent call last):
            ...
            NotImplementedError: only implemented for finite groups
        """
        if not self.is_finite():
            raise NotImplementedError("only implemented for finite groups")
        Irr = self.gap().Irr()
        L = []
        for irr in Irr:
            L.append(ClassFunction_libgap(self, irr))
        return tuple(L)

    def character(self, values):
        r"""
        Returns a group character from ``values``, where ``values`` is
        a list of the values of the character evaluated on the conjugacy
        classes.

        INPUT:

        - ``values`` -- a list of values of the character

        OUTPUT: a group character

        EXAMPLES::

            sage: G = MatrixGroup(AlternatingGroup(4))
            sage: G.character([1]*len(G.conjugacy_classes_representatives()))
            Character of Matrix group over Integer Ring with 12 generators

        ::

            sage: G = GL(2,ZZ)
            sage: G.character([1,1,1,1])
            Traceback (most recent call last):
            ...
            NotImplementedError: only implemented for finite groups
        """
        if not self.is_finite():
            raise NotImplementedError("only implemented for finite groups")
        return ClassFunction_libgap(self, values)

    def trivial_character(self):
        r"""
        Returns the trivial character of this group.

        OUTPUT: a group character

        EXAMPLES::

            sage: MatrixGroup(SymmetricGroup(3)).trivial_character()
            Character of Matrix group over Integer Ring with 6 generators

        ::

            sage: GL(2,ZZ).trivial_character()
            Traceback (most recent call last):
            ...
            NotImplementedError: only implemented for finite groups
        """
        if not self.is_finite():
            raise NotImplementedError("only implemented for finite groups")
        values = [1] * self._gap_().NrConjugacyClasses().sage()
        return self.character(values)

    def character_table(self):
        r"""
        Returns the matrix of values of the irreducible characters of this
        group `G` at its conjugacy classes.

        The columns represent the conjugacy classes of
        `G` and the rows represent the different irreducible
        characters in the ordering given by GAP.

        OUTPUT: a matrix defined over a cyclotomic field

        EXAMPLES::

            sage: MatrixGroup(SymmetricGroup(2)).character_table()
            [ 1 -1]
            [ 1  1]
            sage: MatrixGroup(SymmetricGroup(3)).character_table()
            [ 1  1 -1]
            [ 2 -1  0]
            [ 1  1  1]
            sage: MatrixGroup(SymmetricGroup(5)).character_table()
            [ 1  1  1  1  1  1  1]
            [ 1 -1 -1  1 -1  1  1]
            [ 4  0  1 -1 -2  1  0]
            [ 4  0 -1 -1  2  1  0]
            [ 5 -1  1  0  1 -1  1]
            [ 5  1 -1  0 -1 -1  1]
            [ 6  0  0  1  0  0 -2]
        """
        #code from function in permgroup.py, but modified for
        #how gap handles these groups.
        G = self._gap_()
        cl = self.conjugacy_classes()
        from sage.rings.all import Integer
        n = Integer(len(cl))
        irrG = G.Irr()
        ct = [[irrG[i][j] for j in range(n)] for i in range(n)]

        from sage.rings.all import CyclotomicField
        e = irrG.Flat().Conductor()
        K = CyclotomicField(e)
        ct = [[K(x) for x in v] for v in ct]

        # Finally return the result as a matrix.
        from sage.matrix.all import MatrixSpace
        MS = MatrixSpace(K, n)
        return MS(ct)

    def random_element(self):
        """
        Return a random element of this group.

        OUTPUT:

        A group element.

        EXAMPLES::

            sage: G = Sp(4,GF(3))
            sage: G.random_element()  # random
            [2 1 1 1]
            [1 0 2 1]
            [0 1 1 0]
            [1 0 0 1]
            sage: G.random_element() in G
            True

            sage: F = GF(5); MS = MatrixSpace(F,2,2)
            sage: gens = [MS([[1,2],[-1,1]]),MS([[1,1],[0,1]])]
            sage: G = MatrixGroup(gens)
            sage: G.random_element()  # random
            [1 3]
            [0 3]
            sage: G.random_element() in G
            True
        """
        return self(self.gap().Random())

    def __iter__(self):
        """
        Iterate over the elements of the group.

        EXAMPLES::

            sage: F = GF(3)
            sage: gens = [matrix(F,2, [1,0, -1,1]), matrix(F, 2, [1,1,0,1])]
            sage: G = MatrixGroup(gens)
            sage: next(iter(G))
            [1 0]
            [0 1]
        """
        if self.list.cache is not None:
            for g in self.list():
                yield g
            return
        iterator = self.gap().Iterator()
        while not iterator.IsDoneIterator().sage():
            yield self(iterator.NextIterator(), check=False)

    def __len__(self):
        """
        Return the number of elements in ``self``.

        EXAMPLES::

            sage: F = GF(3)
            sage: gens = [matrix(F,2, [1,-1,0,1]), matrix(F, 2, [1,1,-1,1])]
            sage: G = MatrixGroup(gens)
            sage: len(G)
            48

        An error is raised if the group is not finite::

            sage: len(GL(2,ZZ))
            Traceback (most recent call last):
            ...
            NotImplementedError: group must be finite
        """
        if not self.is_finite():
            raise NotImplementedError('group must be finite')
        return int(self.cardinality())

    @cached_method
    def list(self):
        """
        List all elements of this group.

        OUTPUT:

        A tuple containing all group elements in a random but fixed
        order.

        EXAMPLES::

            sage: F = GF(3)
            sage: gens = [matrix(F,2, [1,0,-1,1]), matrix(F, 2, [1,1,0,1])]
            sage: G = MatrixGroup(gens)
            sage: G.cardinality()
            24
            sage: v = G.list()
            sage: len(v)
            24
            sage: v[:5]
            (
            [0 1]  [0 1]  [0 1]  [0 2]  [0 2]
            [2 0], [2 1], [2 2], [1 0], [1 1]
            )
            sage: all(g in G for g in G.list())
            True

        An example over a ring (see :trac:`5241`)::

            sage: M1 = matrix(ZZ,2,[[-1,0],[0,1]])
            sage: M2 = matrix(ZZ,2,[[1,0],[0,-1]])
            sage: M3 = matrix(ZZ,2,[[-1,0],[0,-1]])
            sage: MG = MatrixGroup([M1, M2, M3])
            sage: MG.list()
            (
            [-1  0]  [-1  0]  [ 1  0]  [1 0]
            [ 0 -1], [ 0  1], [ 0 -1], [0 1]
            )
            sage: MG.list()[1]
            [-1  0]
            [ 0  1]
            sage: MG.list()[1].parent()
            Matrix group over Integer Ring with 3 generators (
            [-1  0]  [ 1  0]  [-1  0]
            [ 0  1], [ 0 -1], [ 0 -1]
            )

        An example over a field (see :trac:`10515`)::

            sage: gens = [matrix(QQ,2,[1,0,0,1])]
            sage: MatrixGroup(gens).list()
            (
            [1 0]
            [0 1]
            )

        Another example over a ring (see :trac:`9437`)::

            sage: len(SL(2, Zmod(4)).list())
            48

        An error is raised if the group is not finite::

            sage: GL(2,ZZ).list()
            Traceback (most recent call last):
            ...
            NotImplementedError: group must be finite
        """
        if not self.is_finite():
            raise NotImplementedError('group must be finite')
        elements = self.gap().Elements()
        return tuple(self(x, check=False) for x in elements)

    def is_isomorphic(self, H):
        """
        Test whether ``self`` and ``H`` are isomorphic groups.

        INPUT:

        - ``H`` -- a group.

        OUTPUT:

        Boolean.

        EXAMPLES::

            sage: m1 = matrix(GF(3), [[1,1],[0,1]])
            sage: m2 = matrix(GF(3), [[1,2],[0,1]])
            sage: F = MatrixGroup(m1)
            sage: G = MatrixGroup(m1, m2)
            sage: H = MatrixGroup(m2)
            sage: F.is_isomorphic(G)
            True
            sage: G.is_isomorphic(H)
            True
            sage: F.is_isomorphic(H)
            True
            sage: F==G, G==H, F==H
            (False, False, False)
        """
        iso = self.gap().IsomorphismGroups(H.gap())
        if iso.is_bool():  # fail means not isomorphic
            try:
                iso.sage()
                assert False
            except ValueError:
                pass
            return False
        return True
Beispiel #5
0
        return f.find_local_minimum(a=a, b=b, tol=tol, maxfun=maxfun)
    except AttributeError:
        pass
    a = float(a)
    b = float(b)
    import scipy.optimize
    xmin, fval, iter, funcalls = scipy.optimize.fminbound(f,
                                                          a,
                                                          b,
                                                          full_output=1,
                                                          xtol=tol,
                                                          maxfun=maxfun)
    return fval, xmin


find_maximum_on_interval = deprecated_function_alias(2607, find_local_maximum)
find_minimum_on_interval = deprecated_function_alias(2607, find_local_minimum)


def minimize(func,
             x0,
             gradient=None,
             hessian=None,
             algorithm="default",
             **args):
    r"""
    This function is an interface to a variety of algorithms for computing
    the minimum of a function of several variables.


    INPUT:
Beispiel #6
0
    from sage.arith.srange import srange
    # Use the expression in equation (55) of MacWilliams & Sloane, pg 151
    # We write jth term = some_factor * (j-1)th term
    if check:
        from sage.rings.integer_ring import ZZ
        l0 = ZZ(l)
        if l0 != l or l0 < 0:
            raise ValueError('l must be a nonnegative integer')
        l = l0
    kraw = jth_term = (q-1)**l * binomial(n, l) # j=0
    for j in srange(1,l+1):
        jth_term *= -q*(l-j+1)*(x-j+1)/((q-1)*j*(n-j+1))
        kraw += jth_term
    return kraw

Krawtchouk = deprecated_function_alias(20908, krawtchouk)
Kravchuk   = deprecated_function_alias(20908, krawtchouk)

def _delsarte_LP_building(n, d, d_star, q, isinteger,  solver, maxc = 0):
    """
    LP builder - common for the two functions; not exported.

    EXAMPLES::

        sage: from sage.coding.delsarte_bounds import _delsarte_LP_building
        sage: _,p=_delsarte_LP_building(7, 3, 0, 2, False, "PPL")
        sage: p.show()
        Maximization:
          x_0 + x_1 + x_2 + x_3 + x_4 + x_5 + x_6 + x_7
        Constraints:
          constraint_0: 1 <= x_0 <= 1
Beispiel #7
0
            """
            Return the weight of ``self``.

            EXAMPLES::

                sage: vct = CartanType(['C', 3]).as_folding()
                sage: RC = crystals.infinity.RiggedConfigurations(vct)
                sage: elt = RC(partition_list=[[1],[1,1],[1]], rigging_list=[[0],[-1,-1],[0]])
                sage: elt.weight()
                (-1, -1, 0)

                sage: vct = CartanType(['F', 4, 1]).as_folding()
                sage: RC = crystals.infinity.RiggedConfigurations(vct)
                sage: mg = RC.highest_weight_vector()
                sage: elt = mg.f_string([1,0,3,4,2,2]); ascii_art(elt)
                -1[ ]-1  0[ ]1  -2[ ][ ]-2  0[ ]1  -1[ ]-1
                sage: wt = elt.weight(); wt
                -Lambda[0] + Lambda[1] - 2*Lambda[2] + 3*Lambda[3] - Lambda[4] - delta
                sage: al = RC.weight_lattice_realization().simple_roots()
                sage: wt == -(al[0] + al[1] + 2*al[2] + al[3] + al[4])
                True
            """
            P = self.parent().weight_lattice_realization()
            alpha = list(P.simple_roots())
            return -sum(sum(x) * alpha[i] for i,x in enumerate(self))

# deprecations from trac:18555
from sage.misc.superseded import deprecated_function_alias
InfinityCrystalOfRiggedConfigurations.global_options = deprecated_function_alias(18555, InfinityCrystalOfRiggedConfigurations.options)

Beispiel #8
0
class ReducedIncidenceAlgebra(CombinatorialFreeModule):
    r"""
    The reduced incidence algebra of a poset.

    The reduced incidence algebra `R_P` is a subalgebra of the
    incidence algebra `I_P` where `\alpha(x, y) = \alpha(x', y')` when
    `[x, y]` is isomorphic to `[x', y']` as posets. Thus the delta, Möbius,
    and zeta functions are all elements of `R_P`.
    """
    def __init__(self, I, prefix='R'):
        """
        Initialize ``self``.

        TESTS::

            sage: P = posets.BooleanLattice(3)
            sage: R = P.incidence_algebra(QQ).reduced_subalgebra()
            sage: TestSuite(R).run()  # long time
        """
        self._ambient = I
        EC = {}
        P = self._ambient._poset
        if not P.is_finite():
            raise NotImplementedError("only implemented for finite posets")
        for i in self._ambient.basis().keys():
            S = P.subposet(P.interval(*i))
            added = False
            for k in EC:
                if S._hasse_diagram.is_isomorphic(k._hasse_diagram):
                    EC[k].append(i)
                    added = True
                    break
            if not added:
                EC[S] = [i]
        self._equiv_classes = map(sorted, EC.values())
        self._equiv_classes = {cls[0]: cls for cls in self._equiv_classes}
        cat = Algebras(I.base_ring()).FiniteDimensional().WithBasis()
        CombinatorialFreeModule.__init__(self,
                                         I.base_ring(),
                                         sorted(self._equiv_classes.keys()),
                                         prefix=prefix,
                                         category=cat)

    def _repr_(self):
        r"""
        Return a string representation of ``self``.

        EXAMPLES::

            sage: P = posets.BooleanLattice(4)
            sage: P.incidence_algebra(QQ).reduced_subalgebra()
            Reduced incidence algebra of Finite lattice containing 16 elements
             over Rational Field
        """
        msg = "Reduced incidence algebra of {} over {}"
        return msg.format(self._ambient._poset, self.base_ring())

    def poset(self):
        """
        Return the defining poset of ``self``.

        EXAMPLES::

            sage: P = posets.BooleanLattice(4)
            sage: R = P.incidence_algebra(QQ).reduced_subalgebra()
            sage: R.poset()
            Finite lattice containing 16 elements
            sage: R.poset() == P
            True
        """
        return self._ambient._poset

    def some_elements(self):
        """
        Return a list of elements of ``self``.

        EXAMPLES::

            sage: P = posets.BooleanLattice(4)
            sage: R = P.incidence_algebra(QQ).reduced_subalgebra()
            sage: R.some_elements()
            [2*R[(0, 0)] + 2*R[(0, 1)] + 3*R[(0, 3)],
             R[(0, 0)] - R[(0, 1)] + R[(0, 3)] - R[(0, 7)] + R[(0, 15)],
             R[(0, 0)] + R[(0, 1)] + R[(0, 3)] + R[(0, 7)] + R[(0, 15)]]
        """
        return [self.an_element(), self.moebius(), self.zeta()]

    @cached_method
    def one_basis(self):
        """
        Return the index of the element `1` in ``self``.

        EXAMPLES::

            sage: P = posets.BooleanLattice(4)
            sage: R = P.incidence_algebra(QQ).reduced_subalgebra()
            sage: R.one_basis()
            (0, 0)
        """
        for A in self.basis().keys():
            if A[0] == A[1]:
                return A

    def delta(self):
        """
        Return the Kronecker delta function in ``self``.

        EXAMPLES::

            sage: P = posets.BooleanLattice(4)
            sage: R = P.incidence_algebra(QQ).reduced_subalgebra()
            sage: R.delta()
            R[(0, 0)]
        """
        return self.one()

    @cached_method
    def zeta(self):
        r"""
        Return the `\zeta` function in ``self``.

        The `\zeta` function on a poset `P` is given by

        .. MATH::

            \zeta(x, y) = \begin{cases}
            1 & x \leq y, \\
            0 & x \not\leq y.
            \end{cases}

        EXAMPLES::

            sage: P = posets.BooleanLattice(4)
            sage: R = P.incidence_algebra(QQ).reduced_subalgebra()
            sage: R.zeta()
            R[(0, 0)] + R[(0, 1)] + R[(0, 3)] + R[(0, 7)] + R[(0, 15)]
        """
        return self.sum(self.basis())

    from sage.misc.superseded import deprecated_function_alias

    @cached_method
    def moebius(self):
        """
        Return the Möbius function of ``self``.

        EXAMPLES::

            sage: P = posets.BooleanLattice(4)
            sage: R = P.incidence_algebra(QQ).reduced_subalgebra()
            sage: R.moebius()
            R[(0, 0)] - R[(0, 1)] + R[(0, 3)] - R[(0, 7)] + R[(0, 15)]
        """
        mu = self._ambient._poset.moebius_function
        R = self.base_ring()
        return self.sum_of_terms((A, R(mu(*A))) for A in self.basis().keys())

    mobius = deprecated_function_alias(19855, moebius)

    @cached_method
    def _lift_basis(self, x):
        """
        Lift the basis element indexed by ``x`` to the ambient incidence
        algebra of ``self``.

        EXAMPLES::

            sage: P = posets.BooleanLattice(4)
            sage: R = P.incidence_algebra(QQ).reduced_subalgebra()
            sage: R._lift_basis((0, 7))
            I[0, 7] + I[0, 11] + I[0, 13] + I[0, 14] + I[1, 15]
             + I[2, 15] + I[4, 15] + I[8, 15]
        """
        return self._ambient.sum_of_monomials(self._equiv_classes[x])

    @lazy_attribute
    def lift(self):
        """
        Return the lift morphism from ``self`` to the ambient space.

        EXAMPLES::

            sage: P = posets.BooleanLattice(2)
            sage: R = P.incidence_algebra(QQ).reduced_subalgebra()
            sage: R.lift
            Generic morphism:
              From: Reduced incidence algebra of Finite lattice containing 4 elements over Rational Field
              To:   Incidence algebra of Finite lattice containing 4 elements over Rational Field
            sage: R.an_element() - R.one()
            R[(0, 0)] + 2*R[(0, 1)] + 3*R[(0, 3)]
            sage: R.lift(R.an_element() - R.one())
            I[0, 0] + 2*I[0, 1] + 2*I[0, 2] + 3*I[0, 3] + I[1, 1]
             + 2*I[1, 3] + I[2, 2] + 2*I[2, 3] + I[3, 3]
        """
        return self.module_morphism(self._lift_basis, codomain=self._ambient)

    def __getitem__(self, A):
        """
        Return the basis element indexed by ``A``.

        EXAMPLES::

            sage: P = posets.BooleanLattice(4)
            sage: R = P.incidence_algebra(QQ).reduced_subalgebra()
            sage: R[0, 0]
            R[(0, 0)]
            sage: R[0, 1]
            R[(0, 1)]
            sage: R[0, 15]
            R[(0, 15)]
            sage: R[0]
            R[(0, 0)]

        This works for any representative of the equivalence class::

            sage: R[3, 3]
            R[(0, 0)]
            sage: R[3, 11]
            R[(0, 1)]
        """
        if not isinstance(A, (list, tuple)):
            if A not in self._ambient._poset.list():
                raise ValueError("not an element of the poset")
            return self.one()
        else:
            A = tuple(A)
            if len(A) != 2:
                raise ValueError("not an interval")
        for k in self._equiv_classes:
            if A in self._equiv_classes[k]:
                return self.monomial(k)
        raise ValueError("not an interval")

    def _retract(self, x):
        """
        Return the retract of ``x`` from the incidence algebra to ``self``.

        EXAMPLES::

            sage: P = posets.BooleanLattice(4)
            sage: I = P.incidence_algebra(QQ)
            sage: R = I.reduced_subalgebra()
            sage: all(R._retract(R.lift(x)) == x for x in R.basis())
            True
            sage: R._retract(I.zeta()) == R.zeta()
            True
            sage: R._retract(I.delta()) == R.delta()
            True
            sage: R._retract(I.moebius()) == R.moebius()
            True
        """
        return self.sum_of_terms((k, x[k]) for k in self.basis().keys())

    class Element(CombinatorialFreeModule.Element):
        """
        An element of a reduced incidence algebra.
        """
        def __call__(self, x, y):
            """
            Return ``self(x, y)``.

            EXAMPLES::

                sage: P = posets.BooleanLattice(4)
                sage: R = P.incidence_algebra(QQ).reduced_subalgebra()
                sage: x = R.an_element()
                sage: x(0, 7)
                0
                sage: x(7, 15)
                2
                sage: x(4, 15)
                0
            """
            return self.parent().lift(self)(x, y)

        def _mul_(self, other):
            """
            Return the product of ``self`` and ``other``.

            EXAMPLES::

                sage: P = posets.BooleanLattice(4)
                sage: R = P.incidence_algebra(QQ).reduced_subalgebra()
                sage: x = R.an_element()
                sage: x * R.zeta()
                2*R[(0, 0)] + 4*R[(0, 1)] + 9*R[(0, 3)] + 17*R[(0, 7)] + 28*R[(0, 15)]
                sage: x * R.moebius()
                2*R[(0, 0)] + R[(0, 3)] - 5*R[(0, 7)] + 12*R[(0, 15)]
                sage: x * R.moebius() * R.zeta() == x
                True
            """
            P = self.parent()
            return P._retract(P.lift(self) * P.lift(other))

        @cached_method
        def to_matrix(self):
            r"""
            Return ``self`` as a matrix.

            EXAMPLES::

                sage: P = posets.BooleanLattice(2)
                sage: R = P.incidence_algebra(QQ).reduced_subalgebra()
                sage: mu = R.moebius()
                sage: mu.to_matrix()
                [ 1 -1 -1  1]
                [ 0  1  0 -1]
                [ 0  0  1 -1]
                [ 0  0  0  1]
            """
            return self.parent().lift(self).to_matrix()

        def is_unit(self):
            """
            Return if ``self`` is a unit.

            EXAMPLES::

                sage: P = posets.BooleanLattice(4)
                sage: R = P.incidence_algebra(QQ).reduced_subalgebra()
                sage: x = R.an_element()
                sage: x.is_unit()
                True
            """
            return self[self.parent().one_basis()].is_unit()

        def __invert__(self):
            """
            Return the inverse of ``self``.

            EXAMPLES::

                sage: P = posets.BooleanLattice(4)
                sage: R = P.incidence_algebra(QQ).reduced_subalgebra()
                sage: x = R.an_element()
                sage: ~x
                1/2*R[(0, 0)] - 1/2*R[(0, 1)] + 1/4*R[(0, 3)]
                 + 3/2*R[(0, 7)] - 33/4*R[(0, 15)]
            """
            P = self.parent()
            return P._retract(~P.lift(self))

        def lift(self):
            """
            Return the lift of ``self`` to the ambient space.

            EXAMPLES::

                sage: P = posets.BooleanLattice(2)
                sage: I = P.incidence_algebra(QQ)
                sage: R = I.reduced_subalgebra()
                sage: x = R.an_element(); x
                2*R[(0, 0)] + 2*R[(0, 1)] + 3*R[(0, 3)]
                sage: x.lift()
                2*I[0, 0] + 2*I[0, 1] + 2*I[0, 2] + 3*I[0, 3] + 2*I[1, 1]
                 + 2*I[1, 3] + 2*I[2, 2] + 2*I[2, 3] + 2*I[3, 3]
            """
            return self.parent().lift(self)
Beispiel #9
0
class Polyhedron_ZZ(Polyhedron_QQ):
    """
    Base class for Polyhedra over `\ZZ`

    TESTS::

        sage: p = Polyhedron([(0,0)], base_ring=ZZ);  p
        A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex
        sage: TestSuite(p).run(skip='_test_pickling')
    """
    _base_ring = ZZ

    def is_lattice_polytope(self):
        r"""
        Return whether the polyhedron is a lattice polytope.

        OUTPUT:

        ``True`` if the polyhedron is compact and has only integral
        vertices, ``False`` otherwise.

        EXAMPLES::

            sage: polytopes.cross_polytope(3).is_lattice_polytope()
            True
            sage: polytopes.regular_polygon(5).is_lattice_polytope()
            False

        TESTS:

        Check :trac:`22622`::

            sage: P1 = Polyhedron(vertices = [[1, 0], [0, 1]], rays = [[1, 1]])
            sage: P1.is_lattice_polytope()
            False
        """
        return self.is_compact()

    def ehrhart_polynomial(self,
                           verbose=False,
                           dual=None,
                           irrational_primal=None,
                           irrational_all_primal=None,
                           maxdet=None,
                           no_decomposition=None,
                           compute_vertex_cones=None,
                           smith_form=None,
                           dualization=None,
                           triangulation=None,
                           triangulation_max_height=None,
                           **kwds):
        r"""
        Return the Ehrhart polynomial of this polyhedron.

        Let `P` be a lattice polytope in `\RR^d` and define `L(P,t) = \# (tP
        \cap \ZZ^d)`. Then E. Ehrhart proved in 1962 that `L` coincides with a
        rational polynomial of degree `d` for integer `t`. `L` is called the
        *Ehrhart polynomial* of `P`. For more information see the
        :wikipedia:`Ehrhart_polynomial`.

        INPUT:

        - ``verbose`` - (boolean, default to ``False``) if ``True``, print the
          whole output of the LattE command.

        The following options are passed to the LattE command, for details you
        should consult `the LattE documentation
        <https://www.math.ucdavis.edu/~latte/software/packages/latte_current/>`__:

        - ``dual`` - (boolean) triangulate and signed-decompose in the dual
          space

        - ``irrational_primal`` - (boolean) triangulate in the dual space,
          signed-decompose in the primal space using irrationalization.

        - ``irrational_all_primal`` - (boolean) Triangulate and signed-decompose
          in the primal space using irrationalization.

        - ``maxdet`` -- (integer) decompose down to an index (determinant) of
          ``maxdet`` instead of index 1 (unimodular cones).

        - ``no_decomposition`` -- (boolean) do not signed-decompose simplicial cones.

        - ``compute_vertex_cones`` -- (string) either 'cdd' or 'lrs' or '4ti2'

        - ``smith_form`` -- (string) either 'ilio' or 'lidia'

        - ``dualization`` -- (string) either 'cdd' or '4ti2'

        - ``triangulation`` - (string) 'cddlib', '4ti2' or 'topcom'

        - ``triangulation_max_height`` - (integer) use a uniform distribution of
          height from 1 to this number

        .. NOTE::

            Any additional argument is forwarded to LattE's executable
            ``count``. All occurrences of '_' will be replaced with a '-'.

        ALGORITHM:

        This method calls the program ``count`` from LattE integrale, a program
        for lattice point enumeration (see
        https://www.math.ucdavis.edu/~latte/).

        .. SEEALSO::

            :mod:`~sage.interfaces.latte` the interface to LattE integrale

        EXAMPLES::

            sage: P = Polyhedron(vertices=[(0,0,0),(3,3,3),(-3,2,1),(1,-1,-2)])
            sage: p = P.ehrhart_polynomial()    # optional - latte_int
            sage: p                             # optional - latte_int
            7/2*t^3 + 2*t^2 - 1/2*t + 1
            sage: p(1)                          # optional - latte_int
            6
            sage: len(P.integral_points())
            6
            sage: p(2)                          # optional - latte_int
            36
            sage: len((2*P).integral_points())
            36

        The unit hypercubes::

            sage: from itertools import product
            sage: def hypercube(d):
            ....:     return Polyhedron(vertices=list(product([0,1],repeat=d)))
            sage: hypercube(3).ehrhart_polynomial()   # optional - latte_int
            t^3 + 3*t^2 + 3*t + 1
            sage: hypercube(4).ehrhart_polynomial()   # optional - latte_int
            t^4 + 4*t^3 + 6*t^2 + 4*t + 1
            sage: hypercube(5).ehrhart_polynomial()   # optional - latte_int
            t^5 + 5*t^4 + 10*t^3 + 10*t^2 + 5*t + 1
            sage: hypercube(6).ehrhart_polynomial()   # optional - latte_int
            t^6 + 6*t^5 + 15*t^4 + 20*t^3 + 15*t^2 + 6*t + 1

        An empty polyhedron::

            sage: P = Polyhedron(ambient_dim=3, vertices=[])
            sage: P.ehrhart_polynomial()    # optional - latte_int
            0
            sage: parent(_)                 # optional - latte_int
            Univariate Polynomial Ring in t over Rational Field

        TESTS:

        Test options::

            sage: P = Polyhedron(ieqs=[[1,-1,1,0], [-1,2,-1,0], [1,1,-2,0]], eqns=[[-1,2,-1,-3]], base_ring=ZZ)

            sage: p = P.ehrhart_polynomial(maxdet=5, verbose=True)  # optional - latte_int
            This is LattE integrale ...
            ...
            Invocation: count --ehrhart-polynomial '--redundancy-check=none' --cdd '--maxdet=5' /dev/stdin
            ...
            sage: p    # optional - latte_int
            1/2*t^2 + 3/2*t + 1

            sage: p = P.ehrhart_polynomial(dual=True, verbose=True)  # optional - latte_int
            This is LattE integrale ...
            ...
            Invocation: count --ehrhart-polynomial '--redundancy-check=none' --cdd --dual /dev/stdin
            ...
            sage: p   # optional - latte_int
            1/2*t^2 + 3/2*t + 1

            sage: p = P.ehrhart_polynomial(irrational_primal=True, verbose=True)   # optional - latte_int
            This is LattE integrale ...
            ...
            Invocation: count --ehrhart-polynomial '--redundancy-check=none' --cdd --irrational-primal /dev/stdin
            ...
            sage: p   # optional - latte_int
            1/2*t^2 + 3/2*t + 1

            sage: p = P.ehrhart_polynomial(irrational_all_primal=True, verbose=True)  # optional - latte_int
            This is LattE integrale ...
            ...
            Invocation: count --ehrhart-polynomial '--redundancy-check=none' --cdd --irrational-all-primal /dev/stdin
            ...
            sage: p   # optional - latte_int
            1/2*t^2 + 3/2*t + 1

        Test bad options::

            sage: P.ehrhart_polynomial(bim_bam_boum=19)   # optional - latte_int
            Traceback (most recent call last):
            ...
            RuntimeError: LattE integrale program failed (exit code 1):
            ...
            Invocation: count --ehrhart-polynomial '--redundancy-check=none' --cdd '--bim-bam-boum=19' /dev/stdin
            Unknown command/option --bim-bam-boum=19
        """
        if self.is_empty():
            from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
            from sage.rings.rational_field import QQ
            R = PolynomialRing(QQ, 't')
            return R.zero()

        # note: the options below are explicitely written in the function
        # declaration in order to keep tab completion (see #18211).
        kwds.update({
            'dual': dual,
            'irrational_primal': irrational_primal,
            'irrational_all_primal': irrational_all_primal,
            'maxdet': maxdet,
            'no_decomposition': no_decomposition,
            'compute_vertex_cones': compute_vertex_cones,
            'smith_form': smith_form,
            'dualization': dualization,
            'triangulation': triangulation,
            'triangulation_max_height': triangulation_max_height
        })

        from sage.interfaces.latte import count
        ine = self.cdd_Hrepresentation()
        return count(ine,
                     cdd=True,
                     ehrhart_polynomial=True,
                     verbose=verbose,
                     **kwds)

    @cached_method
    def polar(self):
        """
        Return the polar (dual) polytope.

        The polytope must have the IP-property (see
        :meth:`has_IP_property`), that is, the origin must be an
        interior point. In particular, it must be full-dimensional.

        OUTPUT:

        The polytope whose vertices are the coefficient vectors of the
        inequalities of ``self`` with inhomogeneous term normalized to
        unity.

        EXAMPLES::

            sage: p = Polyhedron(vertices=[(1,0,0),(0,1,0),(0,0,1),(-1,-1,-1)], base_ring=ZZ)
            sage: p.polar()
            A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 4 vertices
            sage: type(_)
            <class 'sage.geometry.polyhedron.parent.Polyhedra_ZZ_ppl_with_category.element_class'>
            sage: p.polar().base_ring()
            Integer Ring
        """
        if not self.has_IP_property():
            raise ValueError('The polytope must have the IP property.')

        vertices = [ieq.A() / ieq.b() for ieq in self.inequality_generator()]
        if all(all(v_i in ZZ for v_i in v) for v in vertices):
            return Polyhedron(vertices=vertices, base_ring=ZZ)
        else:
            return Polyhedron(vertices=vertices, base_ring=QQ)

    @cached_method
    def is_reflexive(self):
        """
        EXAMPLES::

            sage: p = Polyhedron(vertices=[(1,0,0),(0,1,0),(0,0,1),(-1,-1,-1)], base_ring=ZZ)
            sage: p.is_reflexive()
            True
        """
        return self.polar().is_lattice_polytope()

    @cached_method
    def has_IP_property(self):
        """
        Test whether the polyhedron has the IP property.

        The IP (interior point) property means that

        * ``self`` is compact (a polytope).

        * ``self`` contains the origin as an interior point.

        This implies that

        * ``self`` is full-dimensional.

        * The dual polyhedron is again a polytope (that is, a compact
          polyhedron), though not necessarily a lattice polytope.

        EXAMPLES::

            sage: Polyhedron([(1,1),(1,0),(0,1)], base_ring=ZZ).has_IP_property()
            False
            sage: Polyhedron([(0,0),(1,0),(0,1)], base_ring=ZZ).has_IP_property()
            False
            sage: Polyhedron([(-1,-1),(1,0),(0,1)], base_ring=ZZ).has_IP_property()
            True

        REFERENCES:

        - [PALP]_
        """
        return self.is_compact() and self.interior_contains(
            self.ambient_space().zero())

    def fibration_generator(self, dim):
        """
        Generate the lattice polytope fibrations.

        For the purposes of this function, a lattice polytope fiber is
        a sub-lattice polytope. Projecting the plane spanned by the
        subpolytope to a point yields another lattice polytope, the
        base of the fibration.

        INPUT:

        - ``dim`` -- integer. The dimension of the lattice polytope
          fiber.

        OUTPUT:

        A generator yielding the distinct lattice polytope fibers of
        given dimension.

        EXAMPLES::

            sage: P = Polyhedron(toric_varieties.P4_11169().fan().rays(), base_ring=ZZ)
            sage: list( P.fibration_generator(2) )
            [A 2-dimensional polyhedron in ZZ^4 defined as the convex hull of 3 vertices]
        """
        from sage.combinat.combination import Combinations
        if not self.is_compact():
            raise ValueError('Only polytopes (compact polyhedra) are allowed.')

        nonzero_points = [p for p in self.integral_points() if not p.is_zero()]
        origin = [[0] * self.ambient_dim()]
        fibers = set()
        parent = self.parent()

        for points in Combinations(nonzero_points, dim):
            plane = parent.element_class(parent, [origin, [], points], None)
            if plane.dim() != dim:
                continue
            fiber = self.intersection(plane)
            if fiber.base_ring() is not ZZ:
                continue
            fiber_vertices = tuple(
                sorted(tuple(v) for v in fiber.vertex_generator()))
            if fiber_vertices not in fibers:
                yield fiber
                fibers.update([fiber_vertices])
            plane._delete()

    def find_translation(self, translated_polyhedron):
        """
        Return the translation vector to ``translated_polyhedron``.

        INPUT:

        - ``translated_polyhedron`` -- a polyhedron.

        OUTPUT:

        A `\ZZ`-vector that translates ``self`` to
        ``translated_polyhedron``. A ``ValueError`` is raised if
        ``translated_polyhedron`` is not a translation of ``self``,
        this can be used to check that two polyhedra are not
        translates of each other.

        EXAMPLES::

            sage: X = polytopes.cube()
            sage: X.find_translation(X + vector([2,3,5]))
            (2, 3, 5)
            sage: X.find_translation(2*X)
            Traceback (most recent call last):
            ...
            ValueError: polyhedron is not a translation of self
        """
        no_translation_exception = ValueError(
            'polyhedron is not a translation of self')
        if (set(self.rays()) != set(translated_polyhedron.rays())
                or set(self.lines()) != set(translated_polyhedron.lines())
                or self.n_vertices() != translated_polyhedron.n_vertices()):
            raise no_translation_exception
        sorted_vertices = sorted(map(vector, self.vertices()))
        sorted_translated_vertices = sorted(
            map(vector, translated_polyhedron.vertices()))
        v = sorted_translated_vertices[0] - sorted_vertices[0]
        if any(vertex + v != translated_vertex
               for vertex, translated_vertex in zip(
                   sorted_vertices, sorted_translated_vertices)):
            raise no_translation_exception
        return v

    def _subpoly_parallel_facets(self):
        """
        Generator for all lattice sub-polyhedra with parallel facets.

        In a sub-polyhedron `Y\subset X` not all edges of `Y` need to
        be parallel to `X`. This method iterates over all
        sub-polyhedra where they are parallel, up to an overall
        translation of the sub-polyhedron. Degenerate sub-polyhedra of
        dimension strictly smaller are included.

        OUTPUT:

        A generator yielding `\ZZ`-polyhedra. By construction, each
        facet of the returned polyhedron is parallel to one of the
        facets of ``self``.

        EXAMPLES::

            sage: X = Polyhedron(vertices=[(0,0), (0,1), (1,0), (1,1)])
            sage: X._subpoly_parallel_facets()
            <generator object _subpoly_parallel_facets at 0x...>
            sage: for p in X._subpoly_parallel_facets():
            ....:     print(p.Vrepresentation())
            (A vertex at (0, 0),)
            (A vertex at (0, -1), A vertex at (0, 0))
            (A vertex at (-1, 0), A vertex at (0, 0))
            (A vertex at (-1, -1), A vertex at (-1, 0), A vertex at (0, -1), A vertex at (0, 0))

        TESTS::

            sage: X = Polyhedron(vertices=[(0,), (3,)])
            sage: [ p.vertices() for p in X._subpoly_parallel_facets() ]
            [(A vertex at (0),),
             (A vertex at (-1), A vertex at (0)),
             (A vertex at (-2), A vertex at (0)),
             (A vertex at (-3), A vertex at (0))]
            sage: list( Polyhedron(vertices=[[0,0]])._subpoly_parallel_facets() )
            [A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex]
            sage: list( Polyhedron()._subpoly_parallel_facets() )
            [The empty polyhedron in ZZ^0]
        """
        if self.dim() > 2 or not self.is_compact():
            raise NotImplementedError('only implemented for bounded polygons')
        from sage.geometry.polyhedron.plot import cyclic_sort_vertices_2d
        vertices = cyclic_sort_vertices_2d(self.vertices())
        n = len(vertices)
        if n == 1:  # single point
            yield self
            return
        edge_vectors = []
        for i in range(0, n):
            v = vertices[(i + 1) % n].vector() - vertices[i].vector()
            d = gcd(list(v))
            v_prim = (v / d).change_ring(ZZ)
            edge_vectors.append([v_prim * i for i in range(d + 1)])
        origin = self.ambient_space().zero()
        parent = self.parent()
        from itertools import product
        for edges in product(*edge_vectors):
            v = []
            point = origin
            for e in edges:
                point += e
                v.append(point)
            if point != origin:  # does not close up, not a subpolygon
                continue
            yield parent([v, [], []], None)

    @cached_method
    def minkowski_decompositions(self):
        """
        Return all Minkowski sums that add up to the polyhedron.

        OUTPUT:

        A tuple consisting of pairs `(X,Y)` of `\ZZ`-polyhedra that
        add up to ``self``. All pairs up to exchange of the summands
        are returned, that is, `(Y,X)` is not included if `(X,Y)`
        already is.

        EXAMPLES::

            sage: square = Polyhedron(vertices=[(0,0),(1,0),(0,1),(1,1)])
            sage: square.minkowski_decompositions()
            ((A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex,
              A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices),
             (A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices,
              A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices))

        Example from http://cgi.di.uoa.gr/~amantzaf/geo/ ::

            sage: Q = Polyhedron(vertices=[(4,0), (6,0), (0,3), (4,3)])
            sage: R = Polyhedron(vertices=[(0,0), (5,0), (8,4), (3,2)])
            sage: (Q+R).minkowski_decompositions()
            ((A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex,
              A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 7 vertices),
             (A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices,
              A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices),
             (A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices,
              A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 7 vertices),
             (A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 5 vertices,
              A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices),
             (A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices,
              A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 7 vertices),
             (A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 5 vertices,
              A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 3 vertices),
             (A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices,
              A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 7 vertices),
             (A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices,
              A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 6 vertices))

           sage: [ len(square.dilation(i).minkowski_decompositions())
           ....:   for i in range(6) ]
           [1, 2, 5, 8, 13, 18]
           sage: [ ceil((i^2+2*i-1)/2)+1 for i in range(10) ]
           [1, 2, 5, 8, 13, 18, 25, 32, 41, 50]

        TESTS::

            sage: Q = Polyhedron(vertices=[(4,0), (6,0), (0,3), (4,3)])
            sage: D = Q.Minkowski_decompositions()
            doctest:warning...:
            DeprecationWarning: Minkowski_decompositions is deprecated. Please use minkowski_decompositions instead.
            See http://trac.sagemath.org/23685 for details.
        """
        if self.dim() > 2 or not self.is_compact():
            raise NotImplementedError('only implemented for bounded polygons')
        summands = []

        def is_known_summand(poly):
            for summand in summands:
                try:
                    poly.find_translation(summand)
                    return True
                except ValueError:
                    pass

        decompositions = []
        for X in self._subpoly_parallel_facets():
            if is_known_summand(X):
                continue
            Y = self - X
            if X + Y != self:
                continue
            decompositions.append((X, Y))
            summands += [X, Y]
        return tuple(decompositions)

    Minkowski_decompositions = deprecated_function_alias(
        23685, minkowski_decompositions)
Beispiel #10
0
    or complex numbers, but must be provably invertible::

        sage: a,b,c,d = var('a,b,c,d');
        sage: moebius_transform(matrix(2,[a,b,c,d]),I)
        (I*a + b)/(I*c + d)
        sage: moebius_transform(matrix(2,[1,b,c,b*c+1]),I)
        (b + I)/(b*c + I*c + 1)
        sage: moebius_transform(matrix(2,[0,0,0,0]),I)
        Traceback (most recent call last):
        ...
        TypeError: A must be an invertible 2x2 matrix over the complex numbers or a symbolic ring
    """
    if A.ncols() == 2 and A.nrows() == 2 and A.det() != 0:
        (a, b, c, d) = A.list()
        if z == infinity:
            if c == 0:
                return infinity
            return a / c
        if a * d - b * c < 0:
            w = z.conjugate()  # Reverses orientation
        else:
            w = z
        if c * z + d == 0:
            return infinity
        return (a * w + b) / (c * w + d)
    raise TypeError("A must be an invertible 2x2 matrix over the"
                    " complex numbers or a symbolic ring")


mobius_transform = deprecated_function_alias(19855, moebius_transform)
Beispiel #11
0
class Polytopes():
    """
    A class of constructors for commonly used, famous, or interesting
    polytopes.
    """

    flow_polytope = staticmethod(DiGraph.flow_polytope)

    def regular_polygon(self, n, exact=True, base_ring=None):
        """
        Return a regular polygon with `n` vertices.

        INPUT:

        - ``n`` -- a positive integer, the number of vertices.

        - ``exact`` -- (boolean, default ``True``) if ``False`` floating point
          numbers are used for coordinates.

        - ``base_ring`` -- a ring in which the coordinates will lie. It is
          ``None`` by default. If it is not provided and ``exact`` is ``True``
          then it will be the field of real algebraic number, if ``exact`` is
          ``False`` it will be the real double field.

        EXAMPLES::

            sage: octagon = polytopes.regular_polygon(8)
            sage: octagon
            A 2-dimensional polyhedron in AA^2 defined as the convex hull of 8 vertices
            sage: octagon.n_vertices()
            8
            sage: v = octagon.volume()
            sage: v
            2.828427124746190?
            sage: v == 2*QQbar(2).sqrt()
            True

        Its non exact version::

            sage: polytopes.regular_polygon(3, exact=False).vertices()
            (A vertex at (0.0, 1.0),
             A vertex at (0.8660254038, -0.5),
             A vertex at (-0.8660254038, -0.5))
            sage: polytopes.regular_polygon(25, exact=False).n_vertices()
            25
        """
        n = ZZ(n)
        if n <= 2:
            raise ValueError(
                "n (={}) must be an integer greater than 2".format(n))

        if base_ring is None:
            if exact:
                base_ring = AA
            else:
                base_ring = RDF

        try:
            omega = 2 * base_ring.pi() / n
            verts = [((i * omega).sin(), (i * omega).cos()) for i in range(n)]
        except AttributeError:
            z = QQbar.zeta(n)
            verts = [(base_ring((z**k).imag()), base_ring((z**k).real()))
                     for k in range(n)]

        return Polyhedron(vertices=verts, base_ring=base_ring)

    def Birkhoff_polytope(self, n):
        """
        Return the Birkhoff polytope with `n!` vertices.

        The vertices of this polyhedron are the (flattened) `n` by `n`
        permutation matrices. So the ambient vector space has dimension `n^2`
        but the dimension of the polyhedron is `(n-1)^2`.

        INPUT:

        - ``n`` -- a positive integer giving the size of the permutation matrices.

        .. SEEALSO::

            :meth:`sage.matrix.matrix2.Matrix.as_sum_of_permutations` -- return
            the current matrix as a sum of permutation matrices

        EXAMPLES::

            sage: b3 = polytopes.Birkhoff_polytope(3)
            sage: b3.f_vector()
            (1, 6, 15, 18, 9, 1)
            sage: print b3.ambient_dim(), b3.dim()
            9 4
            sage: b3.is_lattice_polytope()
            True
            sage: p3 = b3.ehrhart_polynomial()     # optional - latte_int
            sage: p3                               # optional - latte_int
            1/8*t^4 + 3/4*t^3 + 15/8*t^2 + 9/4*t + 1
            sage: [p3(i) for i in [1,2,3,4]]       # optional - latte_int
            [6, 21, 55, 120]
            sage: [len((i*b3).integral_points()) for i in [1,2,3,4]]
            [6, 21, 55, 120]

            sage: b4 = polytopes.Birkhoff_polytope(4)
            sage: print b4.n_vertices(), b4.ambient_dim(), b4.dim()
            24 16 9
        """
        from itertools import permutations
        verts = []
        for p in permutations(range(n)):
            verts.append([
                ZZ.one() if p[i] == j else ZZ.zero() for j in range(n)
                for i in range(n)
            ])
        return Polyhedron(vertices=verts, base_ring=ZZ)

    @rename_keyword(deprecation=18213, dim_n='dim')
    def simplex(self, dim=3, project=False):
        """
        Return the ``dim`` dimensional simplex.

        The `d`-simplex is the convex hull in `\RR^{d+1}` of the standard basis
        `(1,0,\ldots,0)`, `(0,1,\ldots,0)`, \ldots, `(0,0,\ldots,1)`. For more
        information, see the :wikipedia:`Simplex`.

        INPUT:

        - ``dim`` -- The dimension of the simplex, a positive
          integer.

        - ``project`` -- (boolean, default ``False``) if ``True``, the polytope
          is (isometrically) projected to a vector space of dimension ``dim-1``.
          This operation turns the coordinates into floating point
          approximations and corresponds to the projection given by the matrix
          from :func:`zero_sum_projection`.

        EXAMPLES::

            sage: s5 = polytopes.simplex(5)
            sage: s5
            A 5-dimensional polyhedron in ZZ^6 defined as the convex hull of 6 vertices
            sage: s5.f_vector()
            (1, 6, 15, 20, 15, 6, 1)

            sage: s5 = polytopes.simplex(5, project=True)
            sage: s5
            A 5-dimensional polyhedron in RDF^5 defined as the convex hull of 6 vertices

        Its volume is `\sqrt{d+1} / d!`::

            sage: s5 = polytopes.simplex(5, project=True)
            sage: s5.volume()      # abs tol 1e-10
            0.0204124145231931
            sage: sqrt(6.) / factorial(5)
            0.0204124145231931

            sage: s6 = polytopes.simplex(6, project=True)
            sage: s6.volume()      # abs tol 1e-10
            0.00367465459870082
            sage: sqrt(7.) / factorial(6)
            0.00367465459870082
        """
        verts = list((ZZ**(dim + 1)).basis())
        if project: verts = project_points(*verts)
        return Polyhedron(vertices=verts)

    n_simplex = deprecated_function_alias(18213, simplex)

    def icosahedron(self, exact=True, base_ring=None):
        """
        Return an icosahedron with edge length 1.

        The icosahedron is one of the Platonic sold. It has 20 faces and is dual
        to the :meth:`dodecahedron`.

        INPUT:

        - ``exact`` -- (boolean, default ``True``) If ``False`` use an
          approximate ring for the coordinates.

        - ``base_ring`` -- (optional) the ring in which the coordinates will
          belong to.  Note that this ring must contain `\sqrt(5)`. If it is not
          provided and ``exact=True`` it will be the number field
          `\QQ[\sqrt(5)]` and if ``exact=False`` it will be the real double
          field.

        EXAMPLES::

            sage: ico = polytopes.icosahedron()
            sage: ico.f_vector()
            (1, 12, 30, 20, 1)
            sage: ico.volume()
            5/12*sqrt5 + 5/4

        Its non exact version::

            sage: ico = polytopes.icosahedron(exact=False)
            sage: ico.base_ring()
            Real Double Field
            sage: ico.volume()
            2.1816949907715726

        A version using `AA <sage.rings.qqbar.AlgebraicRealField>`::

            sage: ico = polytopes.icosahedron(base_ring=AA)   # long time
            sage: ico.base_ring()                             # long time
            Algebraic Real Field
            sage: ico.volume()                                # long time
            2.181694990624913?

        Note that if base ring is provided it must contain the square root of
        `5`. Otherwise you will get an error::

            sage: polytopes.icosahedron(base_ring=QQ)
            Traceback (most recent call last):
            ...
            TypeError: unable to convert 1/4*sqrt(5) + 1/4 to a rational
        """
        if base_ring is None and exact:
            from sage.rings.number_field.number_field import QuadraticField
            K = QuadraticField(5, 'sqrt5')
            sqrt5 = K.gen()
            g = (1 + sqrt5) / 2
            base_ring = K
        else:
            if base_ring is None:
                base_ring = RDF
            g = (1 + base_ring(5).sqrt()) / 2

        r12 = base_ring.one() / 2
        z = base_ring.zero()
        pts = [[z, s1 * r12, s2 * g / 2]
               for s1, s2 in itertools.product([1, -1], repeat=2)]
        verts = [p(v) for p in AlternatingGroup(3) for v in pts]
        return Polyhedron(vertices=verts, base_ring=base_ring)

    def dodecahedron(self, exact=True, base_ring=None):
        """
        Return a dodecahedron.

        The dodecahedron is the Platonic solid dual to the :meth:`icosahedron`.

        INPUT:

        - ``exact`` -- (boolean, default ``True``) If ``False`` use an
          approximate ring for the coordinates.

        - ``base_ring`` -- (optional) the ring in which the coordinates will
          belong to.  Note that this ring must contain `\sqrt(5)`. If it is not
          provided and ``exact=True`` it will be the number field
          `\QQ[\sqrt(5)]` and if ``exact=False`` it will be the real double
          field.

        EXAMPLES::

            sage: d12 = polytopes.dodecahedron()
            sage: d12.f_vector()
            (1, 20, 30, 12, 1)
            sage: d12.volume()
            -176*sqrt5 + 400
            sage: numerical_approx(_)
            6.45203596003699

            sage: d12 = polytopes.dodecahedron(exact=False)
            sage: d12.base_ring()
            Real Double Field

        Here is an error with a field that does not contain `\sqrt(5)`::

            sage: polytopes.dodecahedron(base_ring=QQ)
            Traceback (most recent call last):
            ...
            TypeError: unable to convert 1/4*sqrt(5) + 1/4 to a rational
        """
        return self.icosahedron(exact=exact, base_ring=base_ring).polar()

    def small_rhombicuboctahedron(self, exact=True, base_ring=None):
        """
        Return the (small) rhombicuboctahedron.

        The rhombicuboctahedron is an Archimedean solid with 24 vertices and 26
        faces. See the :wikipedia:`Rhombicuboctahedron` for more information.

        INPUT:

        - ``exact`` -- (boolean, default ``True``) If ``False`` use an
          approximate ring for the coordinates.

        - ``base_ring`` -- the ring in which the coordinates will belong to. If
          it is not provided and ``exact=True`` it will be a the number field
          `\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False`` it
          will be the real double field.

        EXAMPLES::

            sage: sr = polytopes.small_rhombicuboctahedron()
            sage: sr.f_vector()
            (1, 24, 48, 26, 1)
            sage: sr.volume()
            80/3*sqrt2 + 32

        The faces are `8` equilateral triangles and `18` squares::

            sage: sum(1 for f in sr.faces(2) if len(f.vertices()) == 3)
            8
            sage: sum(1 for f in sr.faces(2) if len(f.vertices()) == 4)
            18

        Its non exact version::

            sage: sr = polytopes.small_rhombicuboctahedron(False)
            sage: sr
            A 3-dimensional polyhedron in RDF^3 defined as the convex hull of 24
            vertices
            sage: sr.f_vector()
            (1, 24, 48, 26, 1)
        """
        if base_ring is None and exact:
            from sage.rings.number_field.number_field import QuadraticField
            K = QuadraticField(2, 'sqrt2')
            sqrt2 = K.gen()
            base_ring = K
        else:
            if base_ring is None:
                base_ring = RDF
            sqrt2 = base_ring(2).sqrt()

        one = base_ring.one()
        a = sqrt2 + one
        verts = []
        verts.extend([s1 * one, s2 * one, s3 * a]
                     for s1, s2, s3 in itertools.product([1, -1], repeat=3))
        verts.extend([s1 * one, s3 * a, s2 * one]
                     for s1, s2, s3 in itertools.product([1, -1], repeat=3))
        verts.extend([s1 * a, s2 * one, s3 * one]
                     for s1, s2, s3 in itertools.product([1, -1], repeat=3))
        return Polyhedron(vertices=verts)

    def great_rhombicuboctahedron(self, exact=True, base_ring=None):
        """
        Return the great rhombicuboctahedron.

        The great rohombicuboctahedron (or truncated cuboctahedron) is an
        Archimedean solid with 48 vertices and 26 faces. For more information
        see the :wikipedia:`Truncated_cuboctahedron`.

        INPUT:

        - ``exact`` -- (boolean, default ``True``) If ``False`` use an
          approximate ring for the coordinates.

        - ``base_ring`` -- the ring in which the coordinates will belong to. If
          it is not provided and ``exact=True`` it will be a the number field
          `\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False`` it
          will be the real double field.

        EXAMPLES::

            sage: gr = polytopes.great_rhombicuboctahedron()  # long time ~ 3sec
            sage: gr.f_vector()                               # long time
            (1, 48, 72, 26, 1)

        A faster implementation is obtained by setting ``exact=False``::

            sage: gr = polytopes.great_rhombicuboctahedron(exact=False)
            sage: gr.f_vector()
            (1, 48, 72, 26, 1)

        Its faces are 4 squares, 8 regular hexagons and 6 regular octagons::

            sage: sum(1 for f in gr.faces(2) if len(f.vertices()) == 4)
            12
            sage: sum(1 for f in gr.faces(2) if len(f.vertices()) == 6)
            8
            sage: sum(1 for f in gr.faces(2) if len(f.vertices()) == 8)
            6
        """
        if base_ring is None and exact:
            from sage.rings.number_field.number_field import QuadraticField
            K = QuadraticField(2, 'sqrt2')
            sqrt2 = K.gen()
            base_ring = K
        else:
            if base_ring is None:
                base_ring = RDF
            sqrt2 = base_ring(2).sqrt()

        one = base_ring.one()
        v1 = sqrt2 + 1
        v2 = 2 * sqrt2 + 1
        verts = [[s1 * z1, s2 * z2, s3 * z3]
                 for z1, z2, z3 in itertools.permutations([one, v1, v2])
                 for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
        return Polyhedron(vertices=verts, base_ring=base_ring)

    def rhombic_dodecahedron(self):
        """
        Return the rhombic dodecahedron.

        The rhombic dodecahedron is a a polytope  dual to the cuboctahedron. It
        has 14 vertices and 12 faces. For more information see
        the :wikipedia:`Rhombic_dodecahedron`.

        .. SEEALSO::

            :meth:`cuboctahedron`

        EXAMPLES::

            sage: rd = polytopes.rhombic_dodecahedron()
            sage: rd.f_vector()
            (1, 14, 24, 12, 1)

        Its faces are 12 quadrilaterals (not all identical)::

            sage: sum(1 for f in rd.faces(2) if len(f.vertices()) == 4)
            12

        Some more computations::

            sage: p = rd.ehrhart_polynomial()    # optional - latte_int
            sage: p                              # optional - latte_int
            16*t^3 + 12*t^2 + 4*t + 1
            sage: [p(i) for i in [1,2,3,4]]      # optional - latte_int
            [33, 185, 553, 1233]
            sage: [len((i*rd).integral_points()) for i in [1,2,3,4]]
            [33, 185, 553, 1233]
        """
        v = [[2, 0, 0], [-2, 0, 0], [0, 2, 0], [0, -2, 0], [0, 0, 2],
             [0, 0, -2]]
        v.extend((itertools.product([1, -1], repeat=3)))
        return Polyhedron(vertices=v, base_ring=ZZ)

    def cuboctahedron(self):
        """
        Return the cuboctahedron.

        The cuboctahedron is an Archimedean solid with 12 vertices and 14 faces
        dual to the rhombic dodecahedron. It can be defined as the convex hull
        of the twelve vertices `(0, \pm 1, \pm 1)`, `(\pm 1, 0, \pm 1)` and
        `(\pm 1, \pm 1, 0)`. For more information, see the
        :wikipedia:`Cuboctahedron`.

        .. SEEALSO::

            :meth:`rhombic_dodecahedron`

        EXAMPLES::

            sage: co = polytopes.cuboctahedron()
            sage: co.f_vector()
            (1, 12, 24, 14, 1)

        Its faces are 8 triangles and 6 squares::

            sage: sum(1 for f in co.faces(2) if len(f.vertices()) == 3)
            8
            sage: sum(1 for f in co.faces(2) if len(f.vertices()) == 4)
            6

        Some more computation::

            sage: co.volume()
            20/3
            sage: co.ehrhart_polynomial()      # optional - latte_int
            20/3*t^3 + 8*t^2 + 10/3*t + 1
        """
        v = [[0, -1, -1], [0, 1, -1], [1, -1, 0], [1, 1, 0], [1, 0, 1],
             [1, 0, -1], [0, 1, 1], [0, -1, 1], [-1, 0, 1], [-1, 1, 0],
             [-1, 0, -1], [-1, -1, 0]]
        return Polyhedron(vertices=v, base_ring=ZZ)

    def buckyball(self, exact=True, base_ring=None):
        """
        Return the bucky ball.

        The bucky ball, also known as the truncated icosahedron is an Archimedean solid.
        It has 32 faces and 60 vertices.

        .. SEEALSO::

            :meth:`icosahedron`

        INPUT:

        - ``exact`` -- (boolean, default ``True``) If ``False`` use an
          approximate ring for the coordinates.

        - ``base_ring`` -- the ring in which the coordinates will belong to. If
          it is not provided and ``exact=True`` it will be a the number field
          `\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False`` it
          will be the real double field.

        EXAMPLES::

            sage: bb = polytopes.buckyball()   # long time - 6secs
            sage: bb.f_vector()                # long time
            (1, 60, 90, 32, 1)
            sage: bb.base_ring()               # long time
            Number Field in sqrt5 with defining polynomial x^2 - 5

        A much faster implementation using floating point approximations::

            sage: bb = polytopes.buckyball(exact=False)
            sage: bb.f_vector()
            (1, 60, 90, 32, 1)
            sage: bb.base_ring()
            Real Double Field

        Its faces are 5 regular pentagons and 6 regular hexagons::

            sage: sum(1 for f in bb.faces(2) if len(f.vertices()) == 5)
            12
            sage: sum(1 for f in bb.faces(2) if len(f.vertices()) == 6)
            20
        """
        return self.icosahedron(exact=exact,
                                base_ring=base_ring).edge_truncation()

    def pentakis_dodecahedron(self, exact=True, base_ring=None):
        """
        Return the pentakis dodecahedron.

        The pentakis dodecahedron (orkisdodecahedron) is a face-regular,
        vertex-uniform polytope dual to the truncated icosahedron.  It has 60
        faces and 32 vertices. See the :wikipedia:`Pentakis_dodecahedron` for more
        information.

        INPUT:

        - ``exact`` -- (boolean, default ``True``) If ``False`` use an
          approximate ring for the coordinates.

        - ``base_ring`` -- the ring in which the coordinates will belong to. If
          it is not provided and ``exact=True`` it will be a the number field
          `\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False`` it
          will be the real double field.

        EXAMPLES::

            sage: pd = polytopes.pentakis_dodecahedron()    # long time - ~10 sec
            sage: pd.n_vertices()                           # long time
            32
            sage: pd.n_inequalities()                       # long time
            60

        A much faster implementation is obtained when setting ``exact=False``::

            sage: pd = polytopes.pentakis_dodecahedron(exact=False)
            sage: pd.n_vertices()
            32
            sage: pd.n_inequalities()
            60

        The 60 are triangles::

            sage: all(len(f.vertices()) == 3 for f in pd.faces(2))
            True
        """
        return self.buckyball(exact=exact, base_ring=base_ring).polar()

    def Kirkman_icosahedron(self):
        """
        Return the Kirkman icosahedron.

        The Kirkman icosahedron is a 3-polytope with integer coordinates: `(\pm
        9, \pm 6, \pm 6)`, `(\pm 12, \pm 4, 0)`, `(0, \pm 12, \pm 8)`, `(\pm 6,
        0, \pm 12)`. See [Fetter2012]_ for more information.

        EXAMPLES::

            sage: ki = polytopes.Kirkman_icosahedron()
            sage: ki.f_vector()
            (1, 20, 38, 20, 1)

            sage: ki.volume()
            6528

            sage: vertices = ki.vertices()
            sage: edges = [[vector(edge[0]),vector(edge[1])] for edge in ki.bounded_edges()]
            sage: edge_lengths = [norm(edge[0]-edge[1]) for edge in edges]
            sage: union(edge_lengths)
            [7, 8, 9, 11, 12, 14, 16]
        """
        vertices = [[9, 6, 6], [-9, 6, 6], [9, -6, 6], [9, 6, -6], [-9, -6, 6],
                    [-9, 6, -6], [9, -6, -6], [-9, -6, -6], [12, 4, 0],
                    [-12, 4, 0], [12, -4, 0], [-12, -4, 0], [0, 12, 8],
                    [0, -12, 8], [0, 12, -8], [0, -12, -8], [6, 0, 12],
                    [-6, 0, 12], [6, 0, -12], [-6, 0, -12]]
        return Polyhedron(vertices=vertices, base_ring=ZZ)

    def twenty_four_cell(self):
        """
        Return the standard 24-cell polytope.

        The 24-cell polyhedron (also called icositetrachoron or octaplex) is a
        regular polyhedron in 4-dimension. For more information see
        the :wikipedia:`24-cell`.

        EXAMPLES::

            sage: p24 = polytopes.twenty_four_cell()
            sage: p24.f_vector()
            (1, 24, 96, 96, 24, 1)
            sage: v = next(p24.vertex_generator())
            sage: for adj in v.neighbors(): print adj
            A vertex at (-1/2, -1/2, -1/2, 1/2)
            A vertex at (-1/2, -1/2, 1/2, -1/2)
            A vertex at (-1, 0, 0, 0)
            A vertex at (-1/2, 1/2, -1/2, -1/2)
            A vertex at (0, -1, 0, 0)
            A vertex at (0, 0, -1, 0)
            A vertex at (0, 0, 0, -1)
            A vertex at (1/2, -1/2, -1/2, -1/2)

            sage: p24.volume()
            2
        """
        q12 = QQ((1, 2))
        verts = list(itertools.product([q12, -q12], repeat=4))
        B4 = (ZZ**4).basis()
        verts.extend(v for v in B4)
        verts.extend(-v for v in B4)
        return Polyhedron(vertices=verts)

    def six_hundred_cell(self, exact=False):
        """
        Return the standard 600-cell polytope.

        The 600-cell is a 4-dimensional regular polytope. In many ways this is
        an analogue of the icosahedron.

        .. WARNING::

            The coordinates are not exact by default. The computation with exact
            coordinates takes a huge amount of time.

        INPUT:

        - ``exact`` - (boolean, default ``False``) if ``True`` use exact
          coordinates instead of floating point approximations

        EXAMPLES::

            sage: p600 = polytopes.six_hundred_cell()
            sage: p600
            A 4-dimensional polyhedron in RDF^4 defined as the convex hull of 120 vertices
            sage: p600.f_vector()
            (1, 120, 720, 1200, 600, 1)

        Computation with exact coordinates is currently too long to be useful::

            sage: p600 = polytopes.six_hundred_cell(exact=True) # not tested - very long time
            sage: len(list(p600.bounded_edges()))               # not tested - very long time
            120
        """
        if exact:
            from sage.rings.number_field.number_field import QuadraticField
            K = QuadraticField(5, 'sqrt5')
            sqrt5 = K.gen()
            g = (1 + sqrt5) / 2
            base_ring = K
        else:
            g = (1 + RDF(5).sqrt()) / 2
            base_ring = RDF

        q12 = base_ring(1) / base_ring(2)
        z = base_ring.zero()
        verts = [[s1 * q12, s2 * q12, s3 * q12, s4 * q12]
                 for s1, s2, s3, s4 in itertools.product([1, -1], repeat=4)]
        V = (base_ring)**4
        verts.extend(V.basis())
        verts.extend(-v for v in V.basis())
        pts = [[s1 * q12, s2 * g / 2, s3 / (2 * g), z]
               for (s1, s2, s3) in itertools.product([1, -1], repeat=3)]
        for p in AlternatingGroup(4):
            verts.extend(p(x) for x in pts)
        return Polyhedron(vertices=verts, base_ring=base_ring)

    @rename_keyword(deprecation=18213, points_n='n', dim_n='dim')
    def cyclic_polytope(self, dim, n, base_ring=QQ):
        """
        Return a cyclic polytope.

        A cyclic polytope of dimension ``dim`` with ``n`` vertices is the convex
        hull of the points  ``(t,t^2,...,t^dim)`` with `t \in \{0,1,...,n-1\}` .
        For more information, see the :wikipedia:`Cyclic_polytope`.

        INPUT:

        - ``dim`` -- positive integer. the dimension of the polytope.

        - ``n`` -- positive integer. the number of vertices.

        - ``base_ring`` -- either ``QQ`` (default) or ``RDF``.

        EXAMPLES::

            sage: c = polytopes.cyclic_polytope(4,10)
            sage: c.f_vector()
            (1, 10, 45, 70, 35, 1)
        """
        verts = [[t**i for i in range(1, dim + 1)] for t in range(n)]
        return Polyhedron(vertices=verts, base_ring=base_ring)

    @rename_keyword(deprecation=18213, dim_n='dim')
    def hypersimplex(self, dim, k, project=False):
        """
        Return the hypersimplex in dimension ``dim`` and parameter ``k``.

        The hypersimplex `\Delta_{d,k}` is the convex hull of the vertices made
        of `k` ones and `d-k` zeros. It lies in the `d-1` hyperplane of vectors
        of sum `k`. If you want a projected version to `\RR^{d-1}` (with
        floating point coordinates) then set ``project=True`` in the options.

        .. SEEALSO::

            :meth:`simplex`

        INPUT:

        - ``dim`` -- the dimension

        - ``n`` -- the numbers ``(1,...,n)`` are permuted

        - ``project`` -- (boolean, default ``False``) if ``True``, the polytope
          is (isometrically) projected to a vector space of dimension ``dim-1``.
          This operation turns the coordinates into floating point
          approximations and corresponds to the projection given by the matrix
          from :func:`zero_sum_projection`.

        EXAMPLES::

            sage: h_4_2 = polytopes.hypersimplex(4, 2)
            sage: h_4_2
            A 3-dimensional polyhedron in ZZ^4 defined as the convex hull of 6 vertices
            sage: h_4_2.f_vector()
            (1, 6, 12, 8, 1)
            sage: h_4_2.ehrhart_polynomial()    # optional - latte_int
            2/3*t^3 + 2*t^2 + 7/3*t + 1

            sage: h_7_3 = polytopes.hypersimplex(7, 3, project=True)
            sage: h_7_3
            A 6-dimensional polyhedron in RDF^6 defined as the convex hull of 35 vertices
            sage: h_7_3.f_vector()
            (1, 35, 210, 350, 245, 84, 14, 1)
        """
        verts = Permutations([0] * (dim - k) + [1] * k).list()
        if project: verts = project_points(*verts)
        return Polyhedron(vertices=verts)

    def permutahedron(self, n, project=False):
        """Return the standard permutahedron of (1,...,n)

        The permutahedron (or permutohedron) is the convex hull of the
        permutations of `\{1,\ldots,n\}` seen as vectors. The edges
        between the permutations correspond to multiplication on the
        right by an elementary transposition in the
        :class:`~sage.groups.perm_gps.permgroup_named.SymmetricGroup`.

        If we take the graph in which the vertices correspond to
        vertices of the polyhedron, and edges to edges, we get the
        :meth:`~sage.graphs.graph_generators.GraphGenerators.BubbleSortGraph`.

        INPUT:

        - ``n`` -- integer

        - ``project`` -- (boolean, default ``False``) if ``True``, the polytope
          is (isometrically) projected to a vector space of dimension ``dim-1``.
          This operation turns the coordinates into floating point
          approximations and corresponds to the projection given by the matrix
          from :func:`zero_sum_projection`.

        EXAMPLES::

            sage: perm4 = polytopes.permutahedron(4)
            sage: perm4
            A 3-dimensional polyhedron in ZZ^4 defined as the convex hull of 24 vertices
            sage: perm4.is_lattice_polytope()
            True
            sage: perm4.ehrhart_polynomial()   # optional - latte_int
            16*t^3 + 15*t^2 + 6*t + 1

            sage: perm4 = polytopes.permutahedron(4, project=True)
            sage: perm4
            A 3-dimensional polyhedron in RDF^3 defined as the convex hull of 24 vertices
            sage: perm4.plot()
            Graphics3d Object
            sage: perm4.graph().is_isomorphic(graphs.BubbleSortGraph(4))
            True

        .. SEEALSO::

            * :meth:`~sage.graphs.graph_generators.GraphGenerators.BubbleSortGraph`
        """
        verts = list(itertools.permutations(range(1, n + 1)))
        if project: verts = project_points(*verts)
        return Polyhedron(vertices=verts)

    def hypercube(self, dim):
        """
        Return a hypercube in the given dimension

        The `d` dimensional hypercube is the convex hull of the points `(\pm 1,
        \pm 1, \ldots, \pm 1)` in `\RR^d`. For more information see
        the :wikipedia:`Hypercube`.

        INPUT:

        - ``dim`` -- integer. The dimension of the cube.

        EXAMPLES::

            sage: four_cube = polytopes.hypercube(4)
            sage: four_cube.is_simple()
            True
            sage: four_cube.base_ring()
            Integer Ring
            sage: four_cube.volume()
            16
            sage: four_cube.ehrhart_polynomial()    # optional - latte_int
            16*t^4 + 32*t^3 + 24*t^2 + 8*t + 1
        """
        return Polyhedron(
            vertices=list(itertools.product([1, -1], repeat=dim)))

    def cube(self):
        r"""
        Return the cube.

        The cube is the Platonic solid that is obtained as the convex hull of
        the points `(\pm 1, \pm 1, \pm 1)`. It generalizes into several
        dimension into hypercubes.

        .. SEEALSO::

            :meth:`hypercube`

        EXAMPLES::

            sage: c = polytopes.cube()
            sage: c
            A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 8 vertices
            sage: c.f_vector()
            (1, 8, 12, 6, 1)
            sage: c.volume()
            8
            sage: c.plot()
            Graphics3d Object
        """
        return self.hypercube(3)

    n_cube = deprecated_function_alias(18213, hypercube)

    def cross_polytope(self, dim):
        """
        Return a cross-polytope in dimension ``dim``.

        A cross-polytope is a higher dimensional generalization of the
        octahedron. It is the convex hull of the `2d` points `(\pm 1, 0, \ldots,
        0)`, `(0, \pm 1, \ldots, 0)`, \ldots, `(0, 0, \ldots, \pm 1)`.
        See the :wikipedia:`Cross-polytope` for more information.

        INPUT:

        - ``dim`` -- integer. The dimension of the cross-polytope.

        EXAMPLES::

            sage: four_cross = polytopes.cross_polytope(4)
            sage: four_cross.f_vector()
            (1, 8, 24, 32, 16, 1)
            sage: four_cross.is_simple()
            False
        """
        verts = list((ZZ**dim).basis())
        verts.extend([-v for v in verts])
        return Polyhedron(vertices=verts)

    def parallelotope(self, generators):
        r"""
        Return the parallelotope spanned by the generators.

        The parallelotope is the multi-dimensional generalization of a
        parallelogram (2 generators) and a parallelepiped (3 generators).

        INPUT:

        - ``generators`` -- a list vector of vectors of same dimension

        EXAMPLES::

            sage: polytopes.parallelotope([ (1,0), (0,1) ])
            A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices
            sage: polytopes.parallelotope([[1,2,3,4],[0,1,0,7],[3,1,0,2],[0,0,1,0]])
            A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 16 vertices

            sage: K = QuadraticField(2, 'sqrt2')
            sage: sqrt2 = K.gen()
            sage: polytopes.parallelotope([ (1,sqrt2), (1,-1) ])
            A 2-dimensional polyhedron in (Number Field in sqrt2 with defining
            polynomial x^2 - 2)^2 defined as the convex hull of 4 vertices
        """
        from sage.modules.free_module_element import vector
        from sage.structure.sequence import Sequence
        generators = map(vector, generators)
        V = Sequence(generators).universe()
        R = V.base_ring()

        from itertools import combinations
        par = [V.zero()]
        par.extend(
            sum(c) for k in range(1,
                                  len(generators) + 1)
            for c in combinations(generators, k))
        return Polyhedron(vertices=par, base_ring=R)
Beispiel #12
0
class CoxeterMatrixGroup(UniqueRepresentation,
                         FinitelyGeneratedMatrixGroup_generic):
    r"""
    A Coxeter group represented as a matrix group.

    Let `(W, S)` be a Coxeter system. We construct a vector space `V`
    over `\RR` with a basis of `\{ \alpha_s \}_{s \in S}` and inner product

    .. MATH::

        B(\alpha_s, \alpha_t) = -\cos\left( \frac{\pi}{m_{st}} \right)

    where we have `B(\alpha_s, \alpha_t) = -1` if `m_{st} = \infty`. Next we
    define a representation `\sigma_s : V \to V` by

    .. MATH::

        \sigma_s \lambda = \lambda - 2 B(\alpha_s, \lambda) \alpha_s.

    This representation is faithful so we can represent the Coxeter group `W`
    by the set of matrices `\sigma_s` acting on `V`.

    INPUT:

    - ``data`` -- a Coxeter matrix or graph or a Cartan type
    - ``base_ring`` -- (default: the universal cyclotomic field or
      a number field) the base ring which contains all values
      `\cos(\pi/m_{ij})` where `(m_{ij})_{ij}` is the Coxeter matrix
    - ``index_set`` -- (optional) an indexing set for the generators

    For finite Coxeter groups, the default base ring is taken to be `\QQ` or
    a quadratic number field when possible.

    For more on creating Coxeter groups, see
    :meth:`~sage.combinat.root_system.coxeter_group.CoxeterGroup`.

    .. TODO::

        Currently the label `\infty` is implemented as `-1` in the Coxeter
        matrix.

    EXAMPLES:

    We can create Coxeter groups from Coxeter matrices::

        sage: W = CoxeterGroup([[1, 6, 3], [6, 1, 10], [3, 10, 1]])
        sage: W
        Coxeter group over Universal Cyclotomic Field with Coxeter matrix:
        [ 1  6  3]
        [ 6  1 10]
        [ 3 10  1]
        sage: W.gens()
        (
        [                 -1 -E(12)^7 + E(12)^11                   1]
        [                  0                   1                   0]
        [                  0                   0                   1],
        <BLANKLINE>
        [                  1                   0                   0]
        [-E(12)^7 + E(12)^11                  -1     E(20) - E(20)^9]
        [                  0                   0                   1],
        <BLANKLINE>
        [              1               0               0]
        [              0               1               0]
        [              1 E(20) - E(20)^9              -1]
        )
        sage: m = matrix([[1,3,3,3], [3,1,3,2], [3,3,1,2], [3,2,2,1]])
        sage: W = CoxeterGroup(m)
        sage: W.gens()
        (
        [-1  1  1  1]  [ 1  0  0  0]  [ 1  0  0  0]  [ 1  0  0  0]
        [ 0  1  0  0]  [ 1 -1  1  0]  [ 0  1  0  0]  [ 0  1  0  0]
        [ 0  0  1  0]  [ 0  0  1  0]  [ 1  1 -1  0]  [ 0  0  1  0]
        [ 0  0  0  1], [ 0  0  0  1], [ 0  0  0  1], [ 1  0  0 -1]
        )
        sage: a,b,c,d = W.gens()
        sage: (a*b*c)^3
        [ 5  1 -5  7]
        [ 5  0 -4  5]
        [ 4  1 -4  4]
        [ 0  0  0  1]
        sage: (a*b)^3
        [1 0 0 0]
        [0 1 0 0]
        [0 0 1 0]
        [0 0 0 1]
        sage: b*d == d*b
        True
        sage: a*c*a == c*a*c
        True

    We can create the matrix representation over different base rings and with
    different index sets. Note that the base ring must contain all
    `2*\cos(\pi/m_{ij})` where `(m_{ij})_{ij}` is the Coxeter matrix::

        sage: W = CoxeterGroup(m, base_ring=RR, index_set=['a','b','c','d'])
        sage: W.base_ring()
        Real Field with 53 bits of precision
        sage: W.index_set()
        ('a', 'b', 'c', 'd')

        sage: CoxeterGroup(m, base_ring=ZZ)
        Coxeter group over Integer Ring with Coxeter matrix:
        [1 3 3 3]
        [3 1 3 2]
        [3 3 1 2]
        [3 2 2 1]
        sage: CoxeterGroup([[1,4],[4,1]], base_ring=QQ)
        Traceback (most recent call last):
        ...
        TypeError: unable to convert sqrt(2) to a rational

    Using the well-known conversion between Coxeter matrices and Coxeter
    graphs, we can input a Coxeter graph. Following the standard convention,
    edges with no label (i.e. labelled by ``None``) are treated as 3::

        sage: G = Graph([(0,3,None), (1,3,15), (2,3,7), (0,1,3)])
        sage: W = CoxeterGroup(G); W
        Coxeter group over Universal Cyclotomic Field with Coxeter matrix:
        [ 1  3  2  3]
        [ 3  1  2 15]
        [ 2  2  1  7]
        [ 3 15  7  1]
        sage: G2 = W.coxeter_diagram()
        sage: CoxeterGroup(G2) is W
        True

    Because there currently is no class for `\ZZ \cup \{ \infty \}`, labels
    of `\infty` are given by `-1` in the Coxeter matrix::

        sage: G = Graph([(0,1,None), (1,2,4), (0,2,oo)])
        sage: W = CoxeterGroup(G)
        sage: W.coxeter_matrix()
        [ 1  3 -1]
        [ 3  1  4]
        [-1  4  1]

    We can also create Coxeter groups from Cartan types using the
    ``implementation`` keyword::

        sage: W = CoxeterGroup(['D',5], implementation="reflection")
        sage: W
        Finite Coxeter group over Integer Ring with Coxeter matrix:
        [1 3 2 2 2]
        [3 1 3 2 2]
        [2 3 1 3 3]
        [2 2 3 1 2]
        [2 2 3 2 1]
        sage: W = CoxeterGroup(['H',3], implementation="reflection")
        sage: W
        Finite Coxeter group over Number Field in a with defining polynomial
        x^2 - 5 with Coxeter matrix:
        [1 3 2]
        [3 1 5]
        [2 5 1]
    """
    @staticmethod
    def __classcall_private__(cls, data, base_ring=None, index_set=None):
        """
        Normalize arguments to ensure a unique representation.

        EXAMPLES::

            sage: W1 = CoxeterGroup(['A',2], implementation="reflection", base_ring=ZZ)
            sage: W2 = CoxeterGroup([[1,3],[3,1]], index_set=(1,2))
            sage: W1 is W2
            True
            sage: G1 = Graph([(1,2)])
            sage: W3 = CoxeterGroup(G1)
            sage: W1 is W3
            True
            sage: G2 = Graph([(1,2,3)])
            sage: W4 = CoxeterGroup(G2)
            sage: W1 is W4
            True
        """
        data = CoxeterMatrix(data, index_set=index_set)

        if base_ring is None:
            if data.is_simply_laced():
                base_ring = ZZ
            elif data.is_finite():
                letter = data.coxeter_type().cartan_type().type()
                if letter in ['B', 'C', 'F']:
                    base_ring = QuadraticField(2)
                elif letter == 'G':
                    base_ring = QuadraticField(3)
                elif letter == 'H':
                    base_ring = QuadraticField(5)
                else:
                    base_ring = UniversalCyclotomicField()
            else:
                base_ring = UniversalCyclotomicField()
        return super(CoxeterMatrixGroup,
                     cls).__classcall__(cls, data, base_ring, data.index_set())

    def __init__(self, coxeter_matrix, base_ring, index_set):
        """
        Initialize ``self``.

        EXAMPLES::

            sage: W = CoxeterGroup([[1,3,2],[3,1,3],[2,3,1]])
            sage: TestSuite(W).run() # long time
            sage: W = CoxeterGroup([[1,3,2],[3,1,4],[2,4,1]], base_ring=QQbar)
            sage: TestSuite(W).run() # long time
            sage: W = CoxeterGroup([[1,3,2],[3,1,6],[2,6,1]])
            sage: TestSuite(W).run(max_runs=30) # long time
            sage: W = CoxeterGroup([[1,3,2],[3,1,-1],[2,-1,1]])
            sage: TestSuite(W).run(max_runs=30) # long time

        We check that :trac:`16630` is fixed::

            sage: CoxeterGroup(['D',4], base_ring=QQ).category()
            Category of finite coxeter groups
            sage: CoxeterGroup(['H',4], base_ring=QQbar).category()
            Category of finite coxeter groups
            sage: F = CoxeterGroups().Finite()
            sage: all(CoxeterGroup([letter,i]) in F
            ....:     for i in range(2,5) for letter in ['A','B','D'])
            True
            sage: all(CoxeterGroup(['E',i]) in F for i in range(6,9))
            True
            sage: CoxeterGroup(['F',4]).category()
            Category of finite coxeter groups
            sage: CoxeterGroup(['G',2]).category()
            Category of finite coxeter groups
            sage: all(CoxeterGroup(['H',i]) in F for i in range(3,5))
            True
            sage: all(CoxeterGroup(['I',i]) in F for i in range(2,5))
            True
        """
        self._matrix = coxeter_matrix
        n = coxeter_matrix.rank()
        # Compute the matrix with entries `2 \cos( \pi / m_{ij} )`.
        MS = MatrixSpace(base_ring, n, sparse=True)
        MC = MS._get_matrix_class()
        # FIXME: Hack because there is no ZZ \cup \{ \infty \}: -1 represents \infty
        E = UniversalCyclotomicField().gen
        if base_ring is UniversalCyclotomicField():

            def val(x):
                if x == -1:
                    return 2
                else:
                    return E(2 * x) + ~E(2 * x)
        elif is_QuadraticField(base_ring):

            def val(x):
                if x == -1:
                    return 2
                else:
                    return base_ring(
                        (E(2 * x) + ~E(2 * x)).to_cyclotomic_field())
        else:
            from sage.functions.trig import cos
            from sage.symbolic.constants import pi

            def val(x):
                if x == -1:
                    return 2
                else:
                    return base_ring(2 * cos(pi / x))

        gens = [
            MS.one() +
            MC(MS,
               entries={(i, j): val(coxeter_matrix[index_set[i], index_set[j]])
                        for j in range(n)},
               coerce=True,
               copy=True) for i in range(n)
        ]
        # Make the generators dense matrices for consistency and speed
        gens = [g.dense_matrix() for g in gens]
        category = CoxeterGroups()
        # Now we shall see if the group is finite, and, if so, refine
        # the category to ``category.Finite()``. Otherwise the group is
        # infinite and we refine the category to ``category.Infinite()``.
        if self._matrix.is_finite():
            category = category.Finite()
        else:
            category = category.Infinite()
        self._index_set_inverse = {
            i: ii
            for ii, i in enumerate(self._matrix.index_set())
        }
        FinitelyGeneratedMatrixGroup_generic.__init__(self,
                                                      ZZ(n),
                                                      base_ring,
                                                      gens,
                                                      category=category)

    def _repr_(self):
        """
        Return a string representation of ``self``.

        EXAMPLES::

            sage: CoxeterGroup([[1,3,2],[3,1,4],[2,4,1]])
            Finite Coxeter group over Number Field in a with
            defining polynomial x^2 - 2 with Coxeter matrix:
            [1 3 2]
            [3 1 4]
            [2 4 1]
        """
        rep = "Finite " if self.is_finite() else ""
        rep += "Coxeter group over {} with Coxeter matrix:\n{}".format(
            self.base_ring(), self._matrix)
        return rep

    def index_set(self):
        """
        Return the index set of ``self``.

        EXAMPLES::

            sage: W = CoxeterGroup([[1,3],[3,1]])
            sage: W.index_set()
            (1, 2)
            sage: W = CoxeterGroup([[1,3],[3,1]], index_set=['x', 'y'])
            sage: W.index_set()
            ('x', 'y')
            sage: W = CoxeterGroup(['H',3])
            sage: W.index_set()
            (1, 2, 3)
        """
        return self._matrix.index_set()

    def coxeter_matrix(self):
        """
        Return the Coxeter matrix of ``self``.

        EXAMPLES::

            sage: W = CoxeterGroup([[1,3],[3,1]])
            sage: W.coxeter_matrix()
            [1 3]
            [3 1]
            sage: W = CoxeterGroup(['H',3])
            sage: W.coxeter_matrix()
            [1 3 2]
            [3 1 5]
            [2 5 1]
        """
        return self._matrix

    def coxeter_diagram(self):
        """
        Return the Coxeter diagram of ``self``.

        EXAMPLES::

            sage: W = CoxeterGroup(['H',3], implementation="reflection")
            sage: G = W.coxeter_diagram(); G
            Graph on 3 vertices
            sage: G.edges()
            [(1, 2, 3), (2, 3, 5)]
            sage: CoxeterGroup(G) is W
            True
            sage: G = Graph([(0, 1, 3), (1, 2, oo)])
            sage: W = CoxeterGroup(G)
            sage: W.coxeter_diagram() == G
            True
            sage: CoxeterGroup(W.coxeter_diagram()) is W
            True
        """
        return self._matrix.coxeter_graph()

    coxeter_graph = deprecated_function_alias(17798, coxeter_diagram)

    def bilinear_form(self):
        r"""
        Return the bilinear form associated to ``self``.

        Given a Coxeter group `G` with Coxeter matrix `M = (m_{ij})_{ij}`,
        the associated bilinear form `A = (a_{ij})_{ij}` is given by

        .. MATH::

            a_{ij} = -\cos\left( \frac{\pi}{m_{ij}} \right).

        If `A` is positive definite, then `G` is of finite type (and so
        the associated Coxeter group is a finite group). If `A` is
        positive semidefinite, then `G` is affine type.

        EXAMPLES::

            sage: W = CoxeterGroup(['D',4])
            sage: W.bilinear_form()
            [   1 -1/2    0    0]
            [-1/2    1 -1/2 -1/2]
            [   0 -1/2    1    0]
            [   0 -1/2    0    1]
        """
        return self._matrix.bilinear_form(self.base_ring().fraction_field())

    def is_finite(self):
        """
        Return ``True`` if this group is finite.

        EXAMPLES::

            sage: [l for l in range(2, 9) if
            ....:  CoxeterGroup([[1,3,2],[3,1,l],[2,l,1]]).is_finite()]
            ....:
            [2, 3, 4, 5]
            sage: [l for l in range(2, 9) if
            ....:  CoxeterGroup([[1,3,2,2],[3,1,l,2],[2,l,1,3],[2,2,3,1]]).is_finite()]
            ....:
            [2, 3, 4]
            sage: [l for l in range(2, 9) if
            ....:  CoxeterGroup([[1,3,2,2,2], [3,1,3,3,2], [2,3,1,2,2],
            ....:                [2,3,2,1,l], [2,2,2,l,1]]).is_finite()]
            ....:
            [2, 3]
            sage: [l for l in range(2, 9) if
            ....:  CoxeterGroup([[1,3,2,2,2], [3,1,2,3,3], [2,2,1,l,2],
            ....:                [2,3,l,1,2], [2,3,2,2,1]]).is_finite()]
            ....:
            [2, 3]
            sage: [l for l in range(2, 9) if
            ....:  CoxeterGroup([[1,3,2,2,2,2], [3,1,l,2,2,2], [2,l,1,3,l,2],
            ....:                [2,2,3,1,2,2], [2,2,l,2,1,3], [2,2,2,2,3,1]]).is_finite()]
            ....:
            [2, 3]
        """
        # Finite Coxeter groups are marked as finite in
        # their ``__init__`` method, so we can just check
        # the category of ``self``.
        return "Finite" in self.category().axioms()

    @cached_method
    def order(self):
        """
        Return the order of ``self``.

        If the Coxeter group is finite, this uses an iterator.

        EXAMPLES::

            sage: W = CoxeterGroup([[1,3],[3,1]])
            sage: W.order()
            6
            sage: W = CoxeterGroup([[1,-1],[-1,1]])
            sage: W.order()
            +Infinity
        """
        if self.is_finite():
            return len(self)
        return infinity

    def canonical_representation(self):
        r"""
        Return the canonical faithful representation of ``self``, which
        is ``self``.

        EXAMPLES::

            sage: W = CoxeterGroup([[1,3],[3,1]])
            sage: W.canonical_representation() is W
            True
        """
        return self

    def simple_reflection(self, i):
        """
        Return the simple reflection `s_i`.

        INPUT:

        - ``i`` -- an element from the index set

        EXAMPLES::

            sage: W = CoxeterGroup(['A',3], implementation="reflection")
            sage: W.simple_reflection(1)
            [-1  1  0]
            [ 0  1  0]
            [ 0  0  1]
            sage: W.simple_reflection(2)
            [ 1  0  0]
            [ 1 -1  1]
            [ 0  0  1]
            sage: W.simple_reflection(3)
            [ 1  0  0]
            [ 0  1  0]
            [ 0  1 -1]
        """
        return self.gen(self._index_set_inverse[i])

    @cached_method
    def _positive_roots_reflections(self):
        """
        Return a family whose keys are the positive roots
        and values are the reflections.

        EXAMPLES::

            sage: W = CoxeterGroup(['A', 2])
            sage: F = W._positive_roots_reflections()
            sage: F.keys()
            [(1, 0), (1, 1), (0, 1)]
            sage: list(F)
            [
            [-1  1]  [ 0 -1]  [ 1  0]
            [ 0  1], [-1  0], [ 1 -1]
            ]
        """
        if not self.is_finite():
            raise NotImplementedError('not available for infinite groups')

        word = self.long_element(as_word=True)
        N = len(word)

        from sage.modules.free_module import FreeModule
        simple_roots = FreeModule(self.base_ring(), self.ngens()).gens()

        refls = self.simple_reflections()
        resu = []
        d = {}
        for i in range(1, N + 1):
            segment = word[:i]
            last = segment.pop()
            ref = refls[last]
            rt = simple_roots[last - 1]
            while segment:
                last = segment.pop()
                cr = refls[last]
                ref = cr * ref * cr
                rt = refls[last] * rt
            rt.set_immutable()
            resu += [rt]
            d[rt] = ref
        from sage.sets.family import Family
        return Family(resu, lambda rt: d[rt])

    def positive_roots(self, as_reflections=None):
        """
        Return the positive roots.

        These are roots in the Coxeter sense, that all have the
        same norm. They are given by their coefficients in the
        base of simple roots, also taken to have all the same
        norm.

        .. SEEALSO::

            :meth:`reflections`

        EXAMPLES::

            sage: W = CoxeterGroup(['A',3], implementation='reflection')
            sage: W.positive_roots()
            ((1, 0, 0), (1, 1, 0), (0, 1, 0), (1, 1, 1), (0, 1, 1), (0, 0, 1))
            sage: W = CoxeterGroup(['I',5], implementation='reflection')
            sage: W.positive_roots()
            ((1, 0),
             (-E(5)^2 - E(5)^3, 1),
             (-E(5)^2 - E(5)^3, -E(5)^2 - E(5)^3),
             (1, -E(5)^2 - E(5)^3),
             (0, 1))
        """
        if as_reflections is not None:
            from sage.misc.superseded import deprecation
            deprecation(
                20027,
                "as_reflections is deprecated; instead, use reflections()")
        return tuple(self._positive_roots_reflections().keys())

    def reflections(self):
        """
        Return the set of reflections.

        The order is the one given by :meth:`positive_roots`.

        EXAMPLES::

            sage: W = CoxeterGroup(['A',2], implementation='reflection')
            sage: list(W.reflections())
            [
            [-1  1]  [ 0 -1]  [ 1  0]
            [ 0  1], [-1  0], [ 1 -1]
            ]
        """
        return self._positive_roots_reflections()

    @cached_method
    def roots(self):
        """
        Return the roots.

        These are roots in the Coxeter sense, that all have the
        same norm. They are given by their coefficients in the
        base of simple roots, also taken to have all the same
        norm.

        The positive roots are listed first, then the negative roots
        in the same order. The order is the one given by :meth:`roots`.

        EXAMPLES::

            sage: W = CoxeterGroup(['A',3], implementation='reflection')
            sage: W.roots()
            ((1, 0, 0),
             (1, 1, 0),
             (0, 1, 0),
             (1, 1, 1),
             (0, 1, 1),
             (0, 0, 1),
             (-1, 0, 0),
             (-1, -1, 0),
             (0, -1, 0),
             (-1, -1, -1),
             (0, -1, -1),
             (0, 0, -1))
            sage: W = CoxeterGroup(['I',5], implementation='reflection')
            sage: len(W.roots())
            10
        """
        if not self.is_finite():
            raise NotImplementedError('not available for infinite groups')
        positive = self.positive_roots()
        return positive + tuple([-v for v in positive])

    def simple_root_index(self, i):
        r"""
        Return the index of the simple root `\alpha_i`.

        This is the position of `\alpha_i` in the list of all roots
        as given be :meth:`roots`.

        EXAMPLES::

            sage: W = CoxeterGroup(['A',3], implementation='reflection')
            sage: [W.simple_root_index(i) for i in W.index_set()]
            [0, 2, 5]
        """
        roots = self.roots()
        rt = roots[0].parent().gen(self._index_set_inverse[i])
        return roots.index(rt)

    def fundamental_weights(self):
        """
        Return the fundamental weights for ``self``.

        This is the dual basis to the basis of simple roots.

        The base ring must be a field.

        EXAMPLES::

            sage: W = CoxeterGroup(['A',3], implementation='reflection')
            sage: W.fundamental_weights()
            {1: (3/2, 1, 1/2), 2: (1, 2, 1), 3: (1/2, 1, 3/2)}
        """
        simple_weights = self.bilinear_form().inverse()
        return {i: simple_weights[k] for k, i in enumerate(self.index_set())}

    def fundamental_weight(self, i):
        r"""
        Return the fundamental weight with index ``i``.

        EXAMPLES::

            sage: W = CoxeterGroup(['A',3], implementation='reflection')
            sage: W.fundamental_weight(1)
            (3/2, 1, 1/2)
        """
        return self.fundamental_weights()[i]

    class Element(MatrixGroupElement_generic):
        """
        A Coxeter group element.
        """
        def first_descent(self, side='right', index_set=None, positive=False):
            """
            Return the first left (resp. right) descent of ``self``, as
            ane element of ``index_set``, or ``None`` if there is none.

            See :meth:`descents` for a description of the options.

            EXAMPLES::

                sage: W = CoxeterGroup(['A',3], implementation="reflection")
                sage: a,b,c = W.gens()
                sage: elt = b*a*c
                sage: elt.first_descent()
                1
                sage: elt.first_descent(side='left')
                2
            """
            M = self.matrix()
            if side != 'right':
                M = ~M
            I = self.parent().index_set()
            n = len(I)
            zero = M.base_ring().zero()
            if index_set is None:
                index_set = range(n)
            else:
                I_inv = self.parent()._index_set_inverse
                index_set = [I_inv[i] for i in index_set]
            if positive:
                for i in index_set:
                    if not _matrix_test_right_descent(M, i, n, zero):
                        return I[i]
            else:
                for i in index_set:
                    if _matrix_test_right_descent(M, i, n, zero):
                        return I[i]
            return None

        def descents(self, side='right', index_set=None, positive=False):
            """
            Return the descents of ``self``, as a list of elements of the
            ``index_set``.

            INPUT:

            - ``index_set`` -- (default: all of them) a subset (as a list
              or iterable) of the nodes of the Dynkin diagram
            - ``side`` -- (default: ``'right'``) ``'left'`` or ``'right'``
            - ``positive`` -- (default: ``False``) boolean

            EXAMPLES::

                sage: W = CoxeterGroup(['A',3], implementation="reflection")
                sage: a,b,c = W.gens()
                sage: elt = b*a*c
                sage: elt.descents()
                [1, 3]
                sage: elt.descents(positive=True)
                [2]
                sage: elt.descents(index_set=[1,2])
                [1]
                sage: elt.descents(side='left')
                [2]
            """
            M = self.matrix()
            if side != 'right':
                M = ~M
            I = self.parent().index_set()
            n = len(I)
            zero = M.base_ring().zero()
            if index_set is None:
                index_set = range(n)
            else:
                I_inv = self.parent()._index_set_inverse
                index_set = [I_inv[i] for i in index_set]
            if positive:
                return [
                    I[i] for i in index_set
                    if not _matrix_test_right_descent(M, i, n, zero)
                ]
            return [
                I[i] for i in index_set
                if _matrix_test_right_descent(M, i, n, zero)
            ]

        def has_right_descent(self, i):
            r"""
            Return whether ``i`` is a right descent of ``self``.

            A Coxeter system `(W, S)` has a root system defined as
            `\{ w(\alpha_s) \}_{w \in W}` and we define the positive
            (resp. negative) roots `\alpha = \sum_{s \in S} c_s \alpha_s`
            by all `c_s \geq 0` (resp. `c_s \leq 0`). In particular, we note
            that if `\ell(w s) > \ell(w)` then `w(\alpha_s) > 0` and if
            `\ell(ws) < \ell(w)` then `w(\alpha_s) < 0`.
            Thus `i \in I` is a right descent if `w(\alpha_{s_i}) < 0`
            or equivalently if the matrix representing `w` has all entries
            of the `i`-th column being non-positive.

            INPUT:

            - ``i`` -- an element in the index set

            EXAMPLES::

                sage: W = CoxeterGroup(['A',3], implementation="reflection")
                sage: a,b,c = W.gens()
                sage: elt = b*a*c
                sage: [elt.has_right_descent(i) for i in [1, 2, 3]]
                [True, False, True]
            """
            i = self.parent()._index_set_inverse[i]
            n = len(self.parent().index_set())
            M = self.matrix()
            zero = M.base_ring().zero()
            return _matrix_test_right_descent(M, i, n, zero)

        def canonical_matrix(self):
            r"""
            Return the matrix of ``self`` in the canonical faithful
            representation, which is ``self`` as a matrix.

            EXAMPLES::

                sage: W = CoxeterGroup(['A',3], implementation="reflection")
                sage: a,b,c = W.gens()
                sage: elt = a*b*c
                sage: elt.canonical_matrix()
                [ 0  0 -1]
                [ 1  0 -1]
                [ 0  1 -1]
            """
            return self.matrix()

        @cached_method
        def action_on_root_indices(self, i, side="left"):
            """
            Return the action on the set of roots.

            The roots are ordered as in the output of the method `roots`.

            EXAMPLES::

                sage: W = CoxeterGroup(['A',3], implementation="reflection")
                sage: w = W.w0
                sage: w.action_on_root_indices(0)
                11
            """
            if side == "left":
                w = self
            elif side == "right":
                w = ~self
            else:
                raise ValueError('side must be "left" or "right"')
            roots = self.parent().roots()
            rt = self * roots[i]
            return roots.index(rt)
Beispiel #13
0
"""
Tests Deprecation

EXAMPLES::

    sage: import sage.tests.deprecation_test
    sage: sage.tests.deprecation_test.function_old()
    doctest:...: DeprecationWarning: function_old is deprecated. Please 
    use sage.tests.deprecation_test.function_new instead.
    See http://trac.sagemath.org/12345 for details.
"""

from sage.misc.superseded import deprecated_function_alias

def function_new():
    """
    New function, deprecating ``old_function``.
    
    EXAMPLES::

        sage: from sage.tests.deprecation_test import function_old
        sage: function_old()
        doctest:...: DeprecationWarning: function_old is deprecated. Please 
        use sage.tests.deprecation_test.function_new instead.
        See http://trac.sagemath.org/12345 for details.
    """
    pass


function_old = deprecated_function_alias(12345, function_new)
Beispiel #14
0
# -*- coding: utf-8 -*-
r"""
Finite cubical complexes: deprecated

The current version is :mod:`sage.topology.cubical_complexes`.
"""

from sage.misc.superseded import deprecated_function_alias
import sage.topology.cubical_complex

Cube = deprecated_function_alias(31925, sage.topology.cubical_complex.Cube)
CubicalComplex = deprecated_function_alias(
    31925, sage.topology.cubical_complex.CubicalComplex)
CubicalComplexExamples = deprecated_function_alias(
    31925, sage.topology.cubical_complex.CubicalComplexExamples)
cubical_complexes = deprecated_function_alias(
    31925, sage.topology.cubical_complex.cubical_complexes)
class UniversalCyclotomicFieldElement(FieldElement):
    def __init__(self, parent, obj):
        r"""
        INPUT:

        - ``parent`` - a universal cyclotomic field

        - ``obj`` - a libgap element (either an integer, a rational or a
          cyclotomic)

        TESTS::

            sage: UCF = UniversalCyclotomicField()
            sage: a = UCF.an_element()
            sage: TestSuite(a).run()
        """
        self._obj = obj
        FieldElement.__init__(self, parent)

    def __bool__(self):
        r"""
        TESTS::

            sage: UCF = UniversalCyclotomicField()
            sage: map(bool, [UCF.zero(), UCF.one(), UCF.gen(3), UCF.gen(5) + UCF.gen(5,3)])
            [False, True, True, True]
        """
        return bool(self._obj)

    __nonzero__ = __bool__

    def __reduce__(self):
        r"""
        TESTS::

            sage: UCF = UniversalCyclotomicField()
            sage: a = UCF.zero()
            sage: loads(dumps(a))
            0
            sage: parent(_)
            Universal Cyclotomic Field

            sage: b = UCF.gen(5,1) - 3*UCF.gen(5,4)
            sage: c = loads(dumps(b))
            sage: c
            E(5) - 3*E(5)^4
            sage: c == b
            True
            sage: parent(c)
            Universal Cyclotomic Field
        """
        return self.parent(), (str(self),)

    def __eq__(self, other):
        r"""
        Equality test.

        EXAMPLES::

            sage: UCF = UniversalCyclotomicField()
            sage: UCF.one() == 1
            True
            sage: 1 == UCF.one()
            True

            sage: UCF(2/3) == 2/3
            True
            sage: 2/3 == UCF(2/3)
            True

            sage: UCF.gen(3) == UCF.gen(5)
            False
            sage: UCF.gen(5) + UCF.gen(3) == UCF.gen(3) + UCF.gen(5)
            True

            sage: UCF.zero() == None
            False

            sage: QQbar.zeta(5) == UCF.gen(5)
            True
            sage: UCF.gen(5) == QQbar.zeta(5)
            True
            sage: QQbar.zeta(5) == UCF.gen(5,2)
            False
            sage: UCF.gen(5,2) == QQbar.zeta(5)
            False
        """
        if parent(self) is not parent(other):
            from sage.structure.element import get_coercion_model
            cm = get_coercion_model()
            try:
                self, other = cm.canonical_coercion(self, other)
            except TypeError:
                return False
            return self == other
        return self._obj == other._obj

    def __ne__(self, other):
        r"""
        Difference test.

        EXAMPLES::

            sage: UCF = UniversalCyclotomicField()
            sage: UCF.one() != 1
            False
            sage: 1 != UCF.one()
            False

            sage: UCF(2/3) != 3/2
            True
            sage: 3/2 != UCF(2/3)
            True

            sage: UCF.gen(3) != UCF.gen(5)
            True
            sage: UCF.gen(3) + UCF.gen(5) != UCF.gen(5) + UCF.gen(3)
            False

            sage: UCF.gen(7) != QQbar.zeta(7)
            False
            sage: UCF.gen(7,2) != QQbar.zeta(7)
            True
        """
        return not self == other

    def real(self):
        r"""
        Return the real part of this element.

        EXAMPLES::

            sage: E(3).real()
            -1/2
            sage: E(5).real()
            1/2*E(5) + 1/2*E(5)^4

            sage: a = E(5) - 2*E(3)
            sage: AA(a.real()) == QQbar(a).real()
            True
        """
        P = self.parent()
        return P.element_class(P, self._obj.RealPart())

    real_part = real

    def imag(self):
        r"""
        Return the imaginary part of this element.

        EXAMPLES::

            sage: E(3).imag()
            -1/2*E(12)^7 + 1/2*E(12)^11
            sage: E(5).imag()
            1/2*E(20) - 1/2*E(20)^9

            sage: a = E(5) - 2*E(3)
            sage: AA(a.imag()) == QQbar(a).imag()
            True
        """
        P = self.parent()
        return P.element_class(P, self._obj.ImaginaryPart())

    imag_part = imag

    def is_real(self):
        r"""
        Test whether this element is real.

        EXAMPLES::

            sage: E(3).is_real()
            False
            sage: (E(3) + E(3,2)).is_real()
            True

            sage: a = E(3) - 2*E(7)
            sage: a.real_part().is_real()
            True
            sage: a.imag_part().is_real()
            True
        """
        return self._obj.RealPart() == self._obj

    def conductor(self):
        r"""
        Return the conductor of ``self``.

        EXAMPLES::

            sage: E(3).conductor()
            3
            sage: (E(5) + E(3)).conductor()
            15
        """
        return self._obj.Conductor().sage()

    field_order = deprecated_function_alias(18152, conductor)

    def _symbolic_(self, R):
        r"""
        TESTS::

            sage: SR(E(7))
            e^(2/7*I*pi)
            sage: SR(E(5) + 2*E(5,2) + 3*E(5,3))
            -sqrt(5) + 1/4*I*sqrt(2*sqrt(5) + 10) - 1/4*I*sqrt(-2*sqrt(5) + 10) - 3/2

        Test that the bug reported in :trac:`19912` has been fixed::

            sage: SR(1+E(4))
            I + 1
        """
        from sage.symbolic.constants import pi
        from sage.symbolic.all import i as I
        k = self._obj.Conductor().sage()
        coeffs = self._obj.CoeffsCyc(k).sage()
        s = R.zero()
        for a in range(k):
            if coeffs[a]:
                s += coeffs[a] * (2 * a * I * pi / k).exp()
        return s

    def to_cyclotomic_field(self, R=None):
        r"""
        Return this element as an element of a cyclotomic field.

        EXAMPLES::

            sage: UCF = UniversalCyclotomicField()

            sage: UCF.gen(3).to_cyclotomic_field()
            zeta3
            sage: UCF.gen(3,2).to_cyclotomic_field()
            -zeta3 - 1

            sage: CF = CyclotomicField(5)
            sage: CF(E(5)) # indirect doctest
            zeta5

            sage: CF = CyclotomicField(7)
            sage: CF(E(5)) # indirect doctest
            Traceback (most recent call last):
            ...
            TypeError: Cannot coerce zeta5 into Cyclotomic Field of order 7 and
            degree 6

            sage: CF = CyclotomicField(10)
            sage: CF(E(5)) # indirect doctest
            zeta10^2

        Matrices are correctly dealt with::

            sage: M = Matrix(UCF,2,[E(3),E(4),E(5),E(6)]); M
            [   E(3)    E(4)]
            [   E(5) -E(3)^2]

            sage: Matrix(CyclotomicField(60),M) # indirect doctest
            [zeta60^10 - 1     zeta60^15]
            [    zeta60^12     zeta60^10]

        Using a non-standard embedding::

            sage: CF = CyclotomicField(5,embedding=CC(exp(4*pi*i/5)))
            sage: x = E(5)
            sage: CC(x)
            0.309016994374947 + 0.951056516295154*I
            sage: CC(CF(x))
            0.309016994374947 + 0.951056516295154*I

        Test that the bug reported in :trac:`19912` has been fixed::

            sage: a = 1+E(4); a
            1 + E(4)
            sage: a.to_cyclotomic_field()
            zeta4 + 1
        """
        from sage.rings.number_field.number_field import CyclotomicField
        k = self._obj.Conductor().sage()
        Rcan = CyclotomicField(k)
        if R is None:
            R = Rcan
        obj = self._obj
        if obj.IsRat():
            return R(obj.sage())
        zeta = Rcan.gen()
        coeffs = obj.CoeffsCyc(k).sage()
        return R(sum(coeffs[a] * zeta**a for a in range(k)))

    def __hash__(self):
        r"""
        EXAMPLES::

            sage: UCF = UniversalCyclotomicField()
            sage: hash(UCF.zero())  # indirect doctest
            0
            sage: hash(UCF.gen(3,2))
            313156239               # 32-bit
            1524600308199219855     # 64-bit

        TESTS:

        See :trac:`19514`::

            sage: hash(UCF.one())
            1
        """
        k = self._obj.Conductor().sage()
        coeffs = self._obj.CoeffsCyc(k).sage()
        if k == 1:
            return hash(coeffs[0])
        else:
            return hash((k,) + tuple(coeffs))

    def _algebraic_(self, R):
        r"""
        TESTS::

            sage: UCF = UniversalCyclotomicField()
            sage: AA(UCF.gen(5) + UCF.gen(5,4))
            0.618033988749895?
            sage: AA(UCF.gen(5))
            Traceback (most recent call last):
            ...
            ValueError: Cannot coerce algebraic number with non-zero imaginary
            part to algebraic real
        """
        from sage.rings.qqbar import QQbar
        return R(QQbar(self))

    def __float__(self):
        r"""
        TESTS::

            sage: float(E(7) + E(7,6))
            1.246979603717467
        """
        from sage.rings.real_mpfr import RR
        return float(RR(self))

    def __complex__(self):
        r"""
        TESTS::

            sage: complex(E(3))
            (-0.5+0.8660254037844386j)
        """
        f = self.parent().coerce_embedding()
        return complex(f(self))

    def _eval_complex_(self, R):
        r"""
        Return a complex value of this element in ``R``.

        TESTS::

            sage: CC(E(3))
            -0.500000000000000 + 0.866025403784439*I

        Check that :trac:`19825` is fixed::

            sage: CIF(E(3))
            -0.500000000000000? + 0.866025403784439?*I
            sage: CIF(E(5))
            0.309016994374948? + 0.9510565162951536?*I
            sage: CIF(E(12))
            0.86602540378444? + 0.50000000000000?*I

        If the input is real, the imaginary part is exactly 0::

            sage: CIF(E(17,2) + E(17,15))
            1.47801783444132?
            sage: _.imag().is_zero()
            True
        """
        if self._obj.IsRat():
            return R(self._obj.sage())

        k = self._obj.Conductor().sage()
        coeffs = self._obj.CoeffsCyc(k).sage()
        zeta = R.zeta(k)
        s = sum(coeffs[i] * zeta ** i for i in range(1, k))
        if self.is_real():
            return R(s.real())
        return s

    _complex_mpfi_ = _eval_complex_
    _complex_mpfr_field_ = _eval_complex_

    def _eval_real_(self, R):
        r"""
        Return a real value of this element in ``R``.

        TESTS::

            sage: RR(E(7) + E(7,6))
            1.24697960371747
            sage: 2*cos(2*pi/7).n()
            1.24697960371747
        """
        if not self.is_real():
            raise TypeError("self is not real")

        if self._obj.IsRat():
            return R(self._obj.sage())

        k = self._obj.Conductor().sage()
        coeffs = self._obj.CoeffsCyc(k).sage()
        t = (2 * R.pi()) / k
        return sum(coeffs[i] * (i * t).cos() for i in range(1, k))

    _mpfr_ = _eval_real_

    def _cmp_(self, other):
        r"""
        Comparison (using the complex embedding).

        TESTS::

            sage: UCF = UniversalCyclotomicField()
            sage: l = [UCF.gen(3), UCF.gen(3)+1, UCF.gen(5), UCF.gen(5,2),
            ....:      UCF.gen(4), 2*UCF.gen(4), UCF.gen(5)-22/3]
            sage: lQQbar = map(QQbar,l)
            sage: lQQbar.sort()
            sage: l.sort()
            sage: lQQbar == map(QQbar,l)
            True

            sage: for i in range(len(l)):
            ....:     assert l[i] >= l[i] and l[i] <= l[i]
            ....:     for j in range(i):
            ....:         assert l[i] > l[j] and l[j] < l[i]

            sage: fibonacci(200)*(E(5)+E(5,4)) <= fibonacci(199)
            True
            sage: fibonacci(201)*(E(5)+E(5,4)) <= fibonacci(200)
            False
        """
        if self._obj == other._obj:
            return 0

        s = self.real_part()
        o = other.real_part()
        if s == o:
            s = self.imag_part()
            o = other.imag_part()

        from sage.rings.real_mpfi import RealIntervalField
        prec = 53
        R = RealIntervalField(prec)
        sa = s._eval_real_(R)
        oa = o._eval_real_(R)
        while sa.overlaps(oa):
            prec <<= 2
            R = RealIntervalField(prec)
            sa = s._eval_real_(R)
            oa = o._eval_real_(R)
        return sa._cmp_(oa)

    def denominator(self):
        r"""
        Return the denominator of this element.

        EXAMPLES::

            sage: a = E(5) + 1/2*E(5,2) + 1/3*E(5,3)
            sage: a
            E(5) + 1/2*E(5)^2 + 1/3*E(5)^3
            sage: a.denominator()
            6
            sage: parent(_)
            Integer Ring
        """
        return self._obj.DenominatorCyc().sage()

    def multiplicative_order(self):
        r"""
        The multiplicative order.

        EXAMPLES::

            sage: E(5).multiplicative_order()
            5
            sage: (E(5) + E(12)).multiplicative_order()
            +Infinity
            sage: UniversalCyclotomicField().zero().multiplicative_order()
            Traceback (most recent call last):
            ...
            ValueError: libGAP: Error, argument must be nonzero
        """
        return self._obj.Order().sage()

    def additive_order(self):
        r"""
        The additive order.

        EXAMPLES::

            sage: UCF = UniversalCyclotomicField()
            sage: UCF.zero().additive_order()
            0
            sage: UCF.one().additive_order()
            +Infinity
            sage: UCF.gen(3).additive_order()
            +Infinity
        """
        return Infinity if self else ZZ.zero()

    def is_rational(self):
        r"""
        Test whether this element is a rational number.

        EXAMPLES::

            sage: E(3).is_rational()
            False
            sage: (E(3) + E(3,2)).is_rational()
            True

        TESTS::

            sage: type(E(3).is_rational())
            <type 'bool'>
        """
        return self._obj.IsRat().sage()

    def _rational_(self):
        r"""
        TESTS::

            sage: UCF = UniversalCyclotomicField()
            sage: QQ(UCF.zero())         # indirect doctest
            0
            sage: parent(_)
            Rational Field

            sage: QQ(UCF.one())          # indirect doctest
            1
            sage: parent(_)
            Rational Field

            sage: QQ(E(3)/2 + E(3,2)/2)  # indirect doctest
            -1/2
        """
        if not self._obj.IsRat():
            raise TypeError("Unable to coerce to a rational")
        return Rational(self._obj.sage())

    def _repr_(self):
        r"""
        TESTS::

            sage: U1 = UniversalCyclotomicField(names='E')
            sage: U2 = UniversalCyclotomicField(names='UCF')
            sage: U1.gen(5,2)
            E(5)^2
            sage: U2.gen(5,2)
            E(5)^2
        """
        s = str(self._obj)
        first_char = s[0]
        s = s[1:].replace('+', ' + ').replace('-', ' - ')
        return first_char + s

    def _add_(self, other):
        r"""
        TESTS::

            sage: E(3) + E(5)
            -E(15)^2 - 2*E(15)^8 - E(15)^11 - E(15)^13 - E(15)^14
            sage: 1/2 + E(3)
            1/2*E(3) - 1/2*E(3)^2
        """
        P = self.parent()
        return P.element_class(P, self._obj + other._obj)

    def _sub_(self, other):
        r"""
        TESTS::

            sage: E(3) - E(5)
            -E(15)^2 - E(15)^11 + E(15)^13 - E(15)^14
        """
        P = self.parent()
        return P.element_class(P, self._obj - other._obj)

    def __neg__(self):
        r"""
        Return the inverse of ``self``.

        TESTS::

            sage: -E(5)
            -E(5)
        """
        P = self.parent()
        return P.element_class(P, -self._obj)

    def _mul_(self, other):
        r"""
        TESTS::

            sage: E(3) * E(4)
            E(12)^7
            sage: 3 * E(4)
            3*E(4)
            sage: E(4) * 3
            3*E(4)
        """
        P = self.parent()
        return P.element_class(P, self._obj * other._obj)

    def _div_(self, other):
        r"""
        TESTS::

            sage: E(3)/2
            1/2*E(3)
            sage: 2/E(3)
            2*E(3)^2
        """
        P = self.parent()
        try:
            return P.element_class(P, self._obj / other._obj)
        except ValueError:
            raise ZeroDivisionError("division by zero")

    def __invert__(self):
        r"""
        TESTS::

            sage: UCF = UniversalCyclotomicField()
            sage: ~(UCF.one())
            1
            sage: ~UCF.gen(4)
            -E(4)
        """
        P = self.parent()
        return P.element_class(P, ~self._obj)

    inverse = __invert__

    def conjugate(self):
        r"""
        Return the complex conjugate.

        EXAMPLES::

            sage: (E(7) + 3*E(7,2) - 5 * E(7,3)).conjugate()
            -5*E(7)^4 + 3*E(7)^5 + E(7)^6
        """
        P = self.parent()
        return P.element_class(P, self._obj.ComplexConjugate())

    def galois_conjugates(self, n=None):
        r"""
        Return the Galois conjugates of ``self``.

        INPUT:

        - ``n`` -- an optional integer. If provided, return the orbit of the
          Galois group of the ``n``-th cyclotomic field over `\QQ`. Note that
          ``n`` must be such that this element belongs to the ``n``-th
          cyclotomic field (in other words, it must be a multiple of the
          conductor).

        EXAMPLES::

            sage: E(6).galois_conjugates()
            [-E(3)^2, -E(3)]

            sage: E(6).galois_conjugates()
            [-E(3)^2, -E(3)]

            sage: (E(9,2) - E(9,4)).galois_conjugates()
            [E(9)^2 - E(9)^4,
             E(9)^2 + E(9)^4 + E(9)^5,
             -E(9)^2 - E(9)^5 - E(9)^7,
             -E(9)^2 - E(9)^4 - E(9)^7,
             E(9)^4 + E(9)^5 + E(9)^7,
             -E(9)^5 + E(9)^7]

            sage: zeta = E(5)
            sage: zeta.galois_conjugates(5)
            [E(5), E(5)^2, E(5)^3, E(5)^4]
            sage: zeta.galois_conjugates(10)
            [E(5), E(5)^3, E(5)^2, E(5)^4]
            sage: zeta.galois_conjugates(15)
            [E(5), E(5)^2, E(5)^4, E(5)^2, E(5)^3, E(5), E(5)^3, E(5)^4]

            sage: zeta.galois_conjugates(17)
            Traceback (most recent call last):
            ...
            ValueError: n = 17 must be a multiple of the conductor (5)
        """
        P = self.parent()
        obj = self._obj
        k = obj.Conductor().sage()
        n = k if n is None else ZZ(n)
        if not k.divides(n):
            raise ValueError("n = {} must be a multiple of the conductor ({})".format(n, k))
        return [P.element_class(P, obj.GaloisCyc(i)) for i in range(n) if n.gcd(i) == 1]

    def norm_of_galois_extension(self):
        r"""
        Return the norm as a Galois extension of `\QQ`, which is
        given by the product of all galois_conjugates.

        EXAMPLES::

            sage: E(3).norm_of_galois_extension()
            1
            sage: E(6).norm_of_galois_extension()
            1
            sage: (E(2) + E(3)).norm_of_galois_extension()
            3
            sage: parent(_)
            Integer Ring
        """
        obj = self._obj
        k = obj.Conductor().sage()
        return libgap.Product(libgap([obj.GaloisCyc(i) for i in range(k) if k.gcd(i) == 1])).sage()

    def minpoly(self, var='x'):
        r"""
        The minimal polynomial of ``self`` element over `\QQ`.

        INPUT:

        - ``var`` -- (optional, default 'x') the name of the variable to use.

        EXAMPLES::

            sage: UCF.<E> = UniversalCyclotomicField()

            sage: UCF(4).minpoly()
            x - 4

            sage: UCF(4).minpoly(var='y')
            y - 4

            sage: E(3).minpoly()
            x^2 + x + 1

            sage: E(3).minpoly(var='y')
            y^2 + y + 1

        TESTS::

            sage: for elt in UCF.some_elements():
            ....:     assert elt.minpoly() == elt.to_cyclotomic_field().minpoly()
            ....:     assert elt.minpoly(var='y') == elt.to_cyclotomic_field().minpoly(var='y')

        .. TODO::

            Polynomials with libgap currently does not implement a ``.sage()`` method
            (see :trac:`18266`). It would be faster/safer to not use string to
            construct the polynomial.
        """
        gap_p = libgap.MinimalPolynomial(libgap.eval("Rationals"), self._obj)
        return QQ[var](QQ['x_1'](str(gap_p)))
Beispiel #16
0
                          -identity_matrix(F,d/2),
                          zero_matrix(F,d/2)])

        V = VectorSpace(F,d)
        PV = list(ProjectiveSpace(d-1,F))
        G = Graph([[tuple(_) for _ in PV], lambda x,y:V(x)*(M*V(y)) == 0], loops = False)

    else:
        raise ValueError("unknown algorithm!")

    G.name("Symplectic Polar Graph Sp("+str(d)+","+str(q)+")")
    G.relabel()
    return G

from sage.misc.superseded import deprecated_function_alias
SymplecticGraph = deprecated_function_alias(19136, SymplecticPolarGraph)

def AffineOrthogonalPolarGraph(d,q,sign="+"):
    r"""
    Returns the affine polar graph `VO^+(d,q),VO^-(d,q)` or `VO(d,q)`.

    Affine Polar graphs are built from a `d`-dimensional vector space over
    `F_q`, and a quadratic form which is hyperbolic, elliptic or parabolic
    according to the value of ``sign``.

    Note that `VO^+(d,q),VO^-(d,q)` are strongly regular graphs, while `VO(d,q)`
    is not.

    For more information on Affine Polar graphs, see `Affine Polar
    Graphs page of Andries Brouwer's website
    <http://www.win.tue.nl/~aeb/graphs/VO.html>`_.
    The matrix can be symbolic or can be a matrix over the real
    or complex numbers, but must be provably invertible::

        sage: a,b,c,d = var('a,b,c,d');
        sage: moebius_transform(matrix(2,[a,b,c,d]),I)
        (I*a + b)/(I*c + d)
        sage: moebius_transform(matrix(2,[1,b,c,b*c+1]),I)
        (b + I)/(b*c + I*c + 1)
        sage: moebius_transform(matrix(2,[0,0,0,0]),I)
        Traceback (most recent call last):
        ...
        TypeError: A must be an invertible 2x2 matrix over the complex numbers or a symbolic ring
    """
    if A.ncols() == 2 and A.nrows() == 2 and A.det() != 0:
        (a, b, c, d) = A.list()
        if z == infinity:
            if c == 0:
                return infinity
            return a/c
        if a*d - b*c < 0:
            w = z.conjugate() # Reverses orientation
        else:
            w = z
        if c*z + d == 0:
            return infinity
        return (a*w + b) / (c*w + d)
    raise TypeError("A must be an invertible 2x2 matrix over the"
                    " complex numbers or a symbolic ring")
mobius_transform = deprecated_function_alias(19855, moebius_transform)
class DynkinDiagram_class(DiGraph, CartanType_abstract):
    """
    A Dynkin diagram.

    .. SEEALSO::

        :func:`DynkinDiagram()`

    INPUT:

    - ``t`` -- a Cartan type, Cartan matrix, or ``None``

    EXAMPLES::

        sage: DynkinDiagram(['A', 3])
        O---O---O
        1   2   3
        A3
        sage: C = CartanMatrix([[2, -3], [-4, 2]])
        sage: DynkinDiagram(C)
        Dynkin diagram of rank 2
        sage: C.dynkin_diagram().cartan_matrix() == C
        True

    TESTS:

    Check that the correct type is returned when copied::

        sage: d = DynkinDiagram(['A', 3])
        sage: type(copy(d))
        <class 'sage.combinat.root_system.dynkin_diagram.DynkinDiagram_class'>

    We check that :trac:`14655` is fixed::

        sage: cd = copy(d)
        sage: cd.add_vertex(4)
        sage: d.vertices() != cd.vertices()
        True

    Implementation note: if a Cartan type is given, then the nodes
    are initialized from the index set of this Cartan type.
    """
    def __init__(self, t=None, index_set=None, **options):
        """
        Initialize ``self``.

        EXAMPLES::

            sage: d = DynkinDiagram(["A", 3])
            sage: TestSuite(d).run()
        """
        if isinstance(t, DiGraph):
            if isinstance(t, DynkinDiagram_class):
                self._cartan_type = t._cartan_type
            else:
                self._cartan_type = None
            DiGraph.__init__(self, data=t, **options)
            return

        DiGraph.__init__(self, **options)
        self._cartan_type = t
        if index_set is not None:
            self.add_vertices(index_set)
        elif t is not None:
            self.add_vertices(t.index_set())

    def _repr_(self):
        """
        EXAMPLES::

            sage: DynkinDiagram(['G',2])     # indirect doctest
              3
            O=<=O
            1   2
            G2
        """
        ct = self.cartan_type()
        result = ct.ascii_art() + "\n" if hasattr(ct, "ascii_art") else ""

        if ct is None or isinstance(ct, CartanMatrix):
            return result + "Dynkin diagram of rank %s" % self.rank()
        else:
            return result + "%s" % ct._repr_(compact=True)
            #return result+"Dynkin diagram of type %s"%self.cartan_type()._repr_(compact = True)

    def _latex_(self, scale=0.5):
        r"""
        Return a latex representation of this Dynkin diagram

        EXAMPLES::

            sage: latex(DynkinDiagram(['A',3,1]))
            \begin{tikzpicture}[scale=0.5]
            \draw (-1,0) node[anchor=east] {$A_{3}^{(1)}$};
            \draw (0 cm,0) -- (4 cm,0);
            \draw (0 cm,0) -- (2.0 cm, 1.2 cm);
            \draw (2.0 cm, 1.2 cm) -- (4 cm, 0);
            \draw[fill=white] (0 cm, 0) circle (.25cm) node[below=4pt]{$1$};
            \draw[fill=white] (2 cm, 0) circle (.25cm) node[below=4pt]{$2$};
            \draw[fill=white] (4 cm, 0) circle (.25cm) node[below=4pt]{$3$};
            \draw[fill=white] (2.0 cm, 1.2 cm) circle (.25cm) node[anchor=south east]{$0$};
            \end{tikzpicture}
        """
        if self.cartan_type() is None:
            return "Dynkin diagram of rank %s" % self.rank()
        ret = "\\begin{tikzpicture}[scale=%s]\n" % scale
        ret += "\\draw (-1,0) node[anchor=east] {$%s$};\n" % self.cartan_type(
        )._latex_()
        ret += self.cartan_type()._latex_dynkin_diagram()
        ret += "\n\\end{tikzpicture}"
        return ret

    def _matrix_(self):
        """
        Return a regular matrix from ``self``.

        EXAMPLES::

            sage: M = DynkinDiagram(['C',3])._matrix_(); M
            [ 2 -1  0]
            [-1  2 -2]
            [ 0 -1  2]
            sage: type(M)
            <class 'sage.combinat.root_system.cartan_matrix.CartanMatrix'>
        """
        return self.cartan_matrix()._matrix_()

    def add_edge(self, i, j, label=1):
        """
        EXAMPLES::

            sage: from sage.combinat.root_system.dynkin_diagram import DynkinDiagram_class
            sage: d = DynkinDiagram_class(CartanType(['A',3]))
            sage: list(sorted(d.edges()))
            []
            sage: d.add_edge(2, 3)
            sage: list(sorted(d.edges()))
            [(2, 3, 1), (3, 2, 1)]
        """
        DiGraph.add_edge(self, i, j, label)
        if not self.has_edge(j, i):
            self.add_edge(j, i, 1)

    def __hash__(self):
        """
        EXAMPLES::

            sage: d = CartanType(['A',3]).dynkin_diagram()
            sage: hash(d) == hash((d.cartan_type(), tuple(d.vertices()), tuple(d.edge_iterator(d.vertices()))))
            True
        """
        # Should assert for immutability!

        #return hash(self.cartan_type(), self.vertices(), tuple(self.edges()))
        # FIXME: self.edges() currently tests at some point whether
        # self is a vertex of itself which causes an infinite
        # recursion loop. Current workaround: call self.edge_iterator directly
        return hash((self.cartan_type(), tuple(self.vertices()),
                     tuple(self.edge_iterator(self.vertices()))))

    @staticmethod
    def an_instance():
        """
        Returns an example of Dynkin diagram

        EXAMPLES::

            sage: from sage.combinat.root_system.dynkin_diagram import DynkinDiagram_class
            sage: g = DynkinDiagram_class.an_instance()
            sage: g
            Dynkin diagram of rank 3
            sage: g.cartan_matrix()
            [ 2 -1 -1]
            [-2  2 -1]
            [-1 -1  2]

        """
        # hyperbolic Dynkin diagram of Exercise 4.9 p. 57 of Kac Infinite Dimensional Lie Algebras.
        g = DynkinDiagram()
        g.add_vertices([1, 2, 3])
        g.add_edge(1, 2, 2)
        g.add_edge(1, 3)
        g.add_edge(2, 3)
        return g

    ##########################################################################
    # Cartan type methods

    @cached_method
    def index_set(self):
        """
        EXAMPLES::

            sage: DynkinDiagram(['C',3]).index_set()
            (1, 2, 3)
            sage: DynkinDiagram("A2","B2","F4").index_set()
            (1, 2, 3, 4, 5, 6, 7, 8)
        """
        return tuple(self.vertices())

    def cartan_type(self):
        """
        EXAMPLES::

            sage: DynkinDiagram("A2","B2","F4").cartan_type()
            A2xB2xF4
        """
        return self._cartan_type

    def rank(self):
        r"""
        Returns the index set for this Dynkin diagram

        EXAMPLES::

            sage: DynkinDiagram(['C',3]).rank()
            3
            sage: DynkinDiagram("A2","B2","F4").rank()
            8
        """
        return self.num_verts()

    def dynkin_diagram(self):
        """
        EXAMPLES::

            sage: DynkinDiagram(['C',3]).dynkin_diagram()
            O---O=<=O
            1   2   3
            C3
        """
        return self

    @cached_method
    def cartan_matrix(self):
        r"""
        Returns the Cartan matrix for this Dynkin diagram

        EXAMPLES::

            sage: DynkinDiagram(['C',3]).cartan_matrix()
            [ 2 -1  0]
            [-1  2 -2]
            [ 0 -1  2]
        """
        return CartanMatrix(self)

    def dual(self):
        r"""
        Returns the dual Dynkin diagram, obtained by reversing all edges.

        EXAMPLES::

            sage: D = DynkinDiagram(['C',3])
            sage: D.edges()
            [(1, 2, 1), (2, 1, 1), (2, 3, 1), (3, 2, 2)]
            sage: D.dual()
            O---O=>=O
            1   2   3
            B3
            sage: D.dual().edges()
            [(1, 2, 1), (2, 1, 1), (2, 3, 2), (3, 2, 1)]
            sage: D.dual() == DynkinDiagram(['B',3])
            True

        TESTS::

            sage: D = DynkinDiagram(['A',0]); D
            A0
            sage: D.edges()
            []
            sage: D.dual()
            A0
            sage: D.dual().edges()
            []
            sage: D = DynkinDiagram(['A',1])
            sage: D.edges()
            []
            sage: D.dual()
            O
            1
            A1
            sage: D.dual().edges()
            []
        """
        result = DynkinDiagram_class(None)
        result.add_vertices(self.vertices())
        for source, target, label in self.edges():
            result.add_edge(target, source, label)
        result._cartan_type = self._cartan_type.dual(
        ) if not self._cartan_type is None else None
        return result

    def is_finite(self):
        """
        Check if ``self`` corresponds to a finite root system.

        EXAMPLES::

            sage: CartanType(['F',4]).dynkin_diagram().is_finite()
            True
            sage: D = DynkinDiagram(CartanMatrix([[2, -4], [-3, 2]]))
            sage: D.is_finite()
            False
        """
        if self._cartan_type is not None:
            return self._cartan_type.is_finite()
        return self.cartan_matrix().is_finite()

    def is_affine(self):
        """
        Check if ``self`` corresponds to an affine root system.

        EXAMPLES::

            sage: CartanType(['F',4]).dynkin_diagram().is_affine()
            False
            sage: D = DynkinDiagram(CartanMatrix([[2, -4], [-3, 2]]))
            sage: D.is_affine()
            False
        """
        if self._cartan_type is not None:
            return self._cartan_type.is_affine()
        return self.cartan_matrix().is_affine()

    def is_irreducible(self):
        """
        Check if ``self`` corresponds to an irreducible root system.

        EXAMPLES::

            sage: CartanType(['F',4]).dynkin_diagram().is_irreducible()
            True
        """
        return self._cartan_type.is_irreducible()

    def is_crystallographic(self):
        """
        Implements :meth:`CartanType_abstract.is_crystallographic`

        A Dynkin diagram always corresponds to a crystallographic root system.

        EXAMPLES::

            sage: CartanType(['F',4]).dynkin_diagram().is_crystallographic()
            True

        TESTS::

            sage: CartanType(['G',2]).dynkin_diagram().is_crystalographic()
            doctest:...: DeprecationWarning: is_crystalographic is deprecated. Please use is_crystallographic instead.
            See http://trac.sagemath.org/14673 for details.
            True
        """
        return True

    is_crystalographic = deprecated_function_alias(14673, is_crystallographic)

    def symmetrizer(self):
        """
        Return the symmetrizer of the corresponding Cartan matrix.

        EXAMPLES::

            sage: d = DynkinDiagram()
            sage: d.add_edge(1,2,3)
            sage: d.add_edge(2,3)
            sage: d.add_edge(3,4,3)
            sage: d.symmetrizer()
            Finite family {1: 9, 2: 3, 3: 3, 4: 1}

        TESTS:

        We check that :trac:`15740` is fixed::

            sage: d = DynkinDiagram()
            sage: d.add_edge(1,2,3)
            sage: d.add_edge(2,3)
            sage: d.add_edge(3,4,3)
            sage: L = d.root_system().root_lattice()
            sage: al = L.simple_roots()
            sage: al[1].associated_coroot()
            alphacheck[1]
            sage: al[1].reflection(al[2])
            alpha[1] + 3*alpha[2]
        """
        return self.cartan_matrix().symmetrizer()

    def __getitem__(self, i):
        r"""
        With a tuple (i,j) as argument, returns the scalar product
        `\langle
                \alpha^\vee_i, \alpha_j\rangle`.

        Otherwise, behaves as the usual DiGraph.__getitem__

        EXAMPLES: We use the `C_4` Dynkin diagram as a cartan
        matrix::

            sage: g = DynkinDiagram(['C',4])
            sage: matrix([[g[i,j] for j in range(1,5)] for i in range(1,5)])
            [ 2 -1  0  0]
            [-1  2 -1  0]
            [ 0 -1  2 -2]
            [ 0  0 -1  2]

        The neighbors of a node can still be obtained in the usual way::

            sage: [g[i] for i in range(1,5)]
            [[2], [1, 3], [2, 4], [3]]
        """
        if not isinstance(i, tuple):
            return DiGraph.__getitem__(self, i)
        [i, j] = i
        if i == j:
            return 2
        elif self.has_edge(j, i):
            return -self.edge_label(j, i)
        else:
            return 0

    def column(self, j):
        """
        Returns the `j^{th}` column `(a_{i,j})_i` of the
        Cartan matrix corresponding to this Dynkin diagram, as a container
        (or iterator) of tuples `(i, a_{i,j})`

        EXAMPLES::

            sage: g = DynkinDiagram(["B",4])
            sage: [ (i,a) for (i,a) in g.column(3) ]
            [(3, 2), (2, -1), (4, -2)]
        """
        return [(j, 2)] + [(i, -m) for (j1, i, m) in self.outgoing_edges(j)]

    def row(self, i):
        """
        Returns the `i^{th}` row `(a_{i,j})_j` of the
        Cartan matrix corresponding to this Dynkin diagram, as a container
        (or iterator) of tuples `(j, a_{i,j})`

        EXAMPLES::

            sage: g = DynkinDiagram(["C",4])
            sage: [ (i,a) for (i,a) in g.row(3) ]
            [(3, 2), (2, -1), (4, -2)]
        """
        return [(i, 2)] + [(j, -m) for (j, i1, m) in self.incoming_edges(i)]
Beispiel #19
0
    v, s = _parse_keywords('cinclude', s)
    inc = [_environ_parse(x.replace('"','').replace("'","")) for x in v] + sage_include_directories()
    args, s = _parse_keywords('cargs', s)
    args = ['-w','-O2'] + args
    libdirs = cblas_library_dirs

    # Add cysignals directory to includes
    for path in sys.path:
        cysignals_path = os.path.join(path, "cysignals")
        if os.path.isdir(cysignals_path):
            inc.append(cysignals_path)

    return s, libs, inc, lang, additional_source_files, args, libdirs


parse_keywords = deprecated_function_alias(24105, _parse_keywords)
environ_parse = deprecated_function_alias(24105, _environ_parse)
pyx_preparse = deprecated_function_alias(24105, _pyx_preparse)


################################################################
# If the user attaches a .spyx file and changes it, we have
# to reload an .so.
#
# PROBLEM: Python does not allow one to reload an .so extension module.
# Solution, we create a different .so file and load that one,
# overwriting the definitions of everything in the original .so file.
#
# HOW: By using a sequence_number for each .spyx file; we keep
# these sequence numbers in a dict.
#
Beispiel #20
0
            """
            Return the weight of ``self``.

            EXAMPLES::

                sage: vct = CartanType(['C', 3]).as_folding()
                sage: RC = crystals.infinity.RiggedConfigurations(vct)
                sage: elt = RC(partition_list=[[1],[1,1],[1]], rigging_list=[[0],[-1,-1],[0]])
                sage: elt.weight()
                (-1, -1, 0)

                sage: vct = CartanType(['F', 4, 1]).as_folding()
                sage: RC = crystals.infinity.RiggedConfigurations(vct)
                sage: mg = RC.highest_weight_vector()
                sage: elt = mg.f_string([1,0,3,4,2,2]); ascii_art(elt)
                -1[ ]-1  0[ ]1  -2[ ][ ]-2  0[ ]1  -1[ ]-1
                sage: wt = elt.weight(); wt
                -Lambda[0] + Lambda[1] - 2*Lambda[2] + 3*Lambda[3] - Lambda[4] - delta
                sage: al = RC.weight_lattice_realization().simple_roots()
                sage: wt == -(al[0] + al[1] + 2*al[2] + al[3] + al[4])
                True
            """
            P = self.parent().weight_lattice_realization()
            alpha = list(P.simple_roots())
            return -sum(sum(x) * alpha[i] for i,x in enumerate(self))

# deprecations from trac:18555
from sage.misc.superseded import deprecated_function_alias
InfinityCrystalOfRiggedConfigurations.global_options = deprecated_function_alias(18555, InfinityCrystalOfRiggedConfigurations.options)

# -*- coding: utf-8 -*-
r"""
Generic cell complexes: deprecated

The current version is :mod:`sage.topology.cell_complexes`.
"""

from sage.misc.superseded import deprecated_function_alias
import sage.topology.cell_complex

GenericCellComplex = deprecated_function_alias(
    31925, sage.topology.cell_complex.GenericCellComplex)
Beispiel #22
0
        V = VectorSpace(F, d)
        PV = list(ProjectiveSpace(d - 1, F))
        G = Graph([[tuple(_) for _ in PV], lambda x, y: V(x) *
                   (M * V(y)) == 0],
                  loops=False)

    else:
        raise ValueError("unknown algorithm!")

    G.name("Symplectic Polar Graph Sp(" + str(d) + "," + str(q) + ")")
    G.relabel()
    return G


from sage.misc.superseded import deprecated_function_alias
SymplecticGraph = deprecated_function_alias(19136, SymplecticPolarGraph)


def AffineOrthogonalPolarGraph(d, q, sign="+"):
    r"""
    Returns the affine polar graph `VO^+(d,q),VO^-(d,q)` or `VO(d,q)`.

    Affine Polar graphs are built from a `d`-dimensional vector space over
    `F_q`, and a quadratic form which is hyperbolic, elliptic or parabolic
    according to the value of ``sign``.

    Note that `VO^+(d,q),VO^-(d,q)` are strongly regular graphs, while `VO(d,q)`
    is not.

    For more information on Affine Polar graphs, see `Affine Polar
    Graphs page of Andries Brouwer's website
Beispiel #23
0
class Composition(CombinatorialObject, Element):
    r"""
    Integer compositions

    A composition of a nonnegative integer `n` is a list
    `(i_1, \ldots, i_k)` of positive integers with total sum `n`.

    EXAMPLES:

    The simplest way to create a composition is by specifying its
    entries as a list, tuple (or other iterable)::

        sage: Composition([3,1,2])
        [3, 1, 2]
        sage: Composition((3,1,2))
        [3, 1, 2]
        sage: Composition(i for i in range(2,5))
        [2, 3, 4]

    You can also create a composition from its code. The *code* of
    a composition `(i_1, i_2, \ldots, i_k)` of `n` is a list of length `n`
    that consists of a `1` followed by `i_1-1` zeros, then a `1` followed
    by `i_2-1` zeros, and so on.

    ::

        sage: Composition([4,1,2,3,5]).to_code()
        [1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0]
        sage: Composition(code=_)
        [4, 1, 2, 3, 5]
        sage: Composition([3,1,2,3,5]).to_code()
        [1, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0]
        sage: Composition(code=_)
        [3, 1, 2, 3, 5]

    You can also create the composition of `n` corresponding to a subset of
    `\{1, 2, \ldots, n-1\}` under the bijection that maps the composition
    `(i_1, i_2, \ldots, i_k)` of `n` to the subset
    `\{i_1, i_1 + i_2, i_1 + i_2 + i_3, \ldots, i_1 + \cdots + i_{k-1}\}`
    (see :meth:`to_subset`)::

        sage: Composition(from_subset=({1, 2, 4}, 5))
        [1, 1, 2, 1]
        sage: Composition([1, 1, 2, 1]).to_subset()
        {1, 2, 4}

    The following notation equivalently specifies the composition from the
    set `\{i_1 - 1, i_1 + i_2 - 1, i_1 + i_2 + i_3 - 1, \dots, i_1 + \cdots
    + i_{k-1} - 1, n-1\}` or `\{i_1 - 1, i_1 + i_2 - 1, i_1 + i_2 + i_3
    - 1, \dots, i_1 + \cdots + i_{k-1} - 1\}` and `n`. This provides
    compatibility with Python's `0`-indexing.

    ::

        sage: Composition(descents=[1,0,4,8,11])
        [1, 1, 3, 4, 3]
        sage: Composition(descents=[0,1,3,4])
        [1, 1, 2, 1]
        sage: Composition(descents=([0,1,3],5))
        [1, 1, 2, 1]
        sage: Composition(descents=({0,1,3},5))
        [1, 1, 2, 1]
    """
    __metaclass__ = ClasscallMetaclass

    @staticmethod
    def __classcall_private__(cls,
                              co=None,
                              descents=None,
                              code=None,
                              from_subset=None):
        """
        This constructs a list from optional arguments and delegates the
        construction of a :class:`Composition` to the ``element_class()`` call
        of the appropriate parent.

        EXAMPLES::

            sage: Composition([3,2,1])
            [3, 2, 1]
            sage: Composition(from_subset=({1, 2, 4}, 5))
            [1, 1, 2, 1]
            sage: Composition(descents=[1,0,4,8,11])
            [1, 1, 3, 4, 3]
            sage: Composition([4,1,2,3,5]).to_code()
            [1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0]
            sage: Composition(code=_)
            [4, 1, 2, 3, 5]
        """
        if descents is not None:
            if isinstance(descents, tuple):
                return Compositions().from_descents(descents[0],
                                                    nps=descents[1])
            else:
                return Compositions().from_descents(descents)
        elif code is not None:
            return Compositions().from_code(code)
        elif from_subset is not None:
            return Compositions().from_subset(*from_subset)
        elif isinstance(co, Composition):
            return co
        else:
            return Compositions()(list(co))

    def __init__(self, parent, lst):
        """
        Initialize ``self``.

        EXAMPLES::

            sage: C = Composition([3,1,2])
            sage: TestSuite(C).run()
        """
        CombinatorialObject.__init__(self, lst)
        Element.__init__(self, parent)

    def _ascii_art_(self):
        """
        TESTS::

            sage: ascii_art(Compositions(4).list())
            [ *                                  ]
            [ *  **   *        *                 ]
            [ *  *   **  ***   *   **    *       ]
            [ *, * , * , *  , **, ** , ***, **** ]
            sage: Partitions.global_options(diagram_str='#', convention="French")
            sage: ascii_art(Compositions(4).list())
            [ #                                  ]
            [ #  #   #        ##                 ]
            [ #  #   ##  #     #  ##   ###       ]
            [ #, ##,  #, ###,  #,  ##,   #, #### ]
        """
        from sage.misc.ascii_art import ascii_art
        return ascii_art(self.to_skew_partition())

    def __setstate__(self, state):
        r"""
        In order to maintain backwards compatibility and be able to unpickle a
        old pickle from ``Partition_class`` we have to override the default
        ``__setstate__``.

        EXAMPLES::

            sage: loads("x\x9ck`J.NLO\xd5K\xce\xcfM\xca\xccK,\x011\n\xf2\x8b3K2\xf3\xf3\xb8\x9c\x11\xec\xf8\xe4\x9c\xc4\xe2b\xaeBF\xcd\xc6B\xa6\xdaBf\x8dP\xd6\xf8\x8c\xc4\xe2\x8cB\x16? +'\xb3\xb8\xa4\x905\xb6\x90M\x03bZQf^z\xb1^f^Ijzj\x11Wnbvj<\x8cS\xc8\x1e\xcah\xd8\x1aT\xc8\x91\x01d\x18\x01\x19\x9c\x19P\x11\xae\xd4\xd2$=\x00eW0g")
            [1, 2, 1]
            sage: loads(dumps( Composition([1,2,1]) ))  # indirect doctest
            [1, 2, 1]
        """
        if isinstance(state, dict):  # for old pickles from Composition_class
            self._set_parent(Compositions())
            self.__dict__ = state
        else:
            self._set_parent(state[0])
            self.__dict__ = state[1]

    @combinatorial_map(name='conjugate')
    def conjugate(self):
        r"""
        Returns the conjugate of the composition comp.

        Algorithm from mupad-combinat.

        EXAMPLES::

            sage: Composition([1, 1, 3, 1, 2, 1, 3]).conjugate()
            [1, 1, 3, 3, 1, 3]
        """
        comp = self
        if comp == []:
            return Composition([])
        n = len(comp)
        coofcp = [sum(comp[:j]) - j + 1 for j in range(1, n + 1)]

        cocjg = []
        for i in range(n - 1):
            cocjg += [
                i + 1
                for _ in range(0, (coofcp[n - i - 1] - coofcp[n - i - 2]))
            ]
        cocjg += [n for j in range(coofcp[0])]

        return self.parent()(
            [cocjg[0]] +
            [cocjg[i] - cocjg[i - 1] + 1 for i in range(1, len(cocjg))])

    @combinatorial_map(name='reversed')
    def reversed(self):
        """
        Return the reverse composition of ``self``.

        EXAMPLES::

            sage: Composition([1, 1, 3, 1, 2, 1, 3]).reversed()
            [3, 1, 2, 1, 3, 1, 1]
        """
        return self.parent()(reversed(self))

    @combinatorial_map(name='complement')
    def complement(self):
        """
        Return the complement composition of ``self``. The complement is the
        reverse of the conjugate composition of ``self``.

        EXAMPLES::

            sage: Composition([1, 1, 3, 1, 2, 1, 3]).conjugate()
            [1, 1, 3, 3, 1, 3]
            sage: Composition([1, 1, 3, 1, 2, 1, 3]).complement()
            [3, 1, 3, 3, 1, 1]
        """
        return self.conjugate().reversed()

    def __add__(self, other):
        """
        Return the concatenation of two compositions.

        EXAMPLES::

            sage: Composition([1, 1, 3]) + Composition([4, 1, 2])
            [1, 1, 3, 4, 1, 2]

        TESTS::

            sage: Composition([]) + Composition([]) == Composition([])
            True
        """
        return Composition(list(self) + list(other))

    def size(self):
        """
        Return the size of ``self``, that is the sum of its parts.

        EXAMPLES::

            sage: Composition([7,1,3]).size()
            11
        """
        return sum(self)

    @staticmethod
    def sum(compositions):
        """
        Return the concatenation of the given compositions.

        INPUT:

        - ``compositions`` -- a list (or iterable) of compositions

        EXAMPLES::

            sage: Composition.sum([Composition([1, 1, 3]), Composition([4, 1, 2]), Composition([3,1])])
            [1, 1, 3, 4, 1, 2, 3, 1]

        Any iterable can be provided as input::

            sage: Composition.sum([Composition([i,i]) for i in [4,1,3]])
            [4, 4, 1, 1, 3, 3]

        Empty inputs are handled gracefully::

            sage: Composition.sum([]) == Composition([])
            True
        """
        return sum(compositions, Composition([]))

    def finer(self):
        """
        Return the set of compositions which are finer than ``self``.

        EXAMPLES::

            sage: C = Composition([3,2]).finer()
            sage: C.cardinality()
            8
            sage: list(C)
            [[1, 1, 1, 1, 1], [1, 1, 1, 2], [1, 2, 1, 1], [1, 2, 2], [2, 1, 1, 1], [2, 1, 2], [3, 1, 1], [3, 2]]
        """
        return CartesianProduct(*[Compositions(i)
                                  for i in self]).map(Composition.sum)

    def is_finer(self, co2):
        """
        Return ``True`` if the composition ``self`` is finer than the
        composition ``co2``; otherwise, it returns ``False``.

        EXAMPLES::

            sage: Composition([4,1,2]).is_finer([3,1,3])
            False
            sage: Composition([3,1,3]).is_finer([4,1,2])
            False
            sage: Composition([1,2,2,1,1,2]).is_finer([5,1,3])
            True
            sage: Composition([2,2,2]).is_finer([4,2])
            True
        """
        co1 = self
        if sum(co1) != sum(co2):
            raise ValueError(
                "compositions self (= %s) and co2 (= %s) must be of the same size"
                % (self, co2))

        sum1 = 0
        sum2 = 0
        i1 = 0
        for i2 in range(len(co2)):
            sum2 += co2[i2]
            while sum1 < sum2:
                sum1 += co1[i1]
                i1 += 1
            if sum1 > sum2:
                return False

        return True

    def fatten(self, grouping):
        r"""
        Return the composition fatter than ``self``, obtained by grouping
        together consecutive parts according to grouping.

        INPUT:

        - ``grouping`` -- a composition whose sum is the length of ``self``

        EXAMPLES:

        Let us start with the composition::

            sage: c = Composition([4,5,2,7,1])

        With ``grouping`` equal to `(1, \ldots, 1)`, `c` is left unchanged::

            sage: c.fatten(Composition([1,1,1,1,1]))
            [4, 5, 2, 7, 1]

        With ``grouping`` equal to `(\ell)` where `\ell` is the length of
        ``self``, this yields the coarser composition above `c`::

            sage: c.fatten(Composition([5]))
            [19]

        Other values for ``grouping`` yield (all the) other compositions
        coarser to `c`::

            sage: c.fatten(Composition([2,1,2]))
            [9, 2, 8]
            sage: c.fatten(Composition([3,1,1]))
            [11, 7, 1]

        TESTS::

            sage: Composition([]).fatten(Composition([]))
            []
            sage: c.fatten(Composition([3,1,1])).__class__ == c.__class__
            True
        """
        result = [None] * len(grouping)
        j = 0
        for i in range(len(grouping)):
            result[i] = sum(self[j:j + grouping[i]])
            j += grouping[i]
        return Composition(result)

    def fatter(self):
        """
        Return the set of compositions which are fatter than ``self``.

        Complexity for generation: `O(|c|)` memory, `O(|r|)` time where `|c|`
        is the size of ``self`` and `r` is the result.

        EXAMPLES::

            sage: C = Composition([4,5,2]).fatter()
            sage: C.cardinality()
            4
            sage: list(C)
            [[4, 5, 2], [4, 7], [9, 2], [11]]

        Some extreme cases::

            sage: list(Composition([5]).fatter())
            [[5]]
            sage: list(Composition([]).fatter())
            [[]]
            sage: list(Composition([1,1,1,1]).fatter()) == list(Compositions(4))
            True
        """
        return Compositions(len(self)).map(self.fatten)

    def refinement_splitting(self, J):
        r"""
        Return the refinement splitting of ``self`` according to ``J``.

        INPUT:

        - ``J`` -- A composition such that ``I`` is finer than ``J``

        OUTPUT:

        - the unique list of compositions `(I^{(p)})_{p=1\ldots m}`,
          obtained by splitting `I`, such that
          `|I^{(p)}| = J_p` for all `p = 1, \ldots, m`.

        .. SEEALSO::

            :meth:`refinement_splitting_lengths`

        EXAMPLES::

            sage: Composition([1,2,2,1,1,2]).refinement_splitting([5,1,3])
            [[1, 2, 2], [1], [1, 2]]
            sage: Composition([]).refinement_splitting([])
            []
            sage: Composition([3]).refinement_splitting([2])
            Traceback (most recent call last):
            ...
            ValueError: compositions self (= [3]) and J (= [2]) must be of the same size
            sage: Composition([2,1]).refinement_splitting([1,2])
            Traceback (most recent call last):
            ...
            ValueError: composition J (= [2, 1]) does not refine self (= [1, 2])
        """
        I = self
        if sum(I) != sum(J):
            #Error: compositions are not of the same size
            raise ValueError(
                "compositions self (= %s) and J (= %s) must be of the same size"
                % (I, J))
        sum1 = 0
        sum2 = 0
        i1 = -1
        decomp = []
        for i2 in range(len(J)):
            new_comp = []
            sum2 += J[i2]
            while sum1 < sum2:
                i1 += 1
                new_comp.append(I[i1])
                sum1 += new_comp[-1]
            if sum1 > sum2:
                raise ValueError(
                    "composition J (= %s) does not refine self (= %s)" %
                    (I, J))
            decomp.append(Composition(new_comp))
        return decomp

    def refinement_splitting_lengths(self, J):
        """
        Return the lengths of the compositions in the refinement splitting of
        ``I=self`` according to ``J``.

        .. SEEALSO::

            :meth:`refinement_splitting` for the definition of refinement splitting

        EXAMPLES::

            sage: Composition([1,2,2,1,1,2]).refinement_splitting_lengths([5,1,3])
            [3, 1, 2]
            sage: Composition([]).refinement_splitting_lengths([])
            []
            sage: Composition([3]).refinement_splitting_lengths([2])
            Traceback (most recent call last):
            ...
            ValueError: compositions self (= [3]) and J (= [2]) must be of the same size
            sage: Composition([2,1]).refinement_splitting_lengths([1,2])
            Traceback (most recent call last):
            ...
            ValueError: composition J (= [2, 1]) does not refine self (= [1, 2])
        """
        return Composition(map(len, self.refinement_splitting(J)))

    refinement = deprecated_function_alias(13243, refinement_splitting_lengths)

    def major_index(self):
        """
        Return the major index of ``self``. The major index is
        defined as the sum of the descents.

        EXAMPLES::

            sage: Composition([1, 1, 3, 1, 2, 1, 3]).major_index()
            31
        """
        co = self
        lv = len(co)
        if lv == 1:
            return 0
        else:
            return sum([(lv - (i + 1)) * co[i] for i in range(lv)])

    def to_code(self):
        """
        Return the code of the composition ``self``. The code of a composition
        is a list of length ``self.size()`` of 1s and 0s such that there is a 1
        wherever a new part starts.

        EXAMPLES::

            sage: Composition([4,1,2,3,5]).to_code()
            [1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0]
        """
        if self == []:
            return [0]

        code = []
        for i in range(len(self)):
            code += [1] + [0] * (self[i] - 1)

        return code

    def partial_sums(self, final=True):
        r"""
        The partial sums of the sequence defined by the entries of the
        composition.

        If `I = (i_1, \ldots, i_m)` is a composition, then the partial sums of
        the entries of the composition are
        `[i_1, i_1 + i_2, \ldots, i_1 + i_2 + \cdots + i_{m}]`.

        INPUT:

        - ``final`` -- (default: ``True``) whether or not to include the final
          partial sum, which is always the size of the composition.

        .. SEEALSO::

            :meth:`to_subset`

        EXAMPLES::

            sage: Composition([1,1,3,1,2,1,3]).partial_sums()
            [1, 2, 5, 6, 8, 9, 12]

        With ``final = False``, the last partial sum is not included::

            sage: Composition([1,1,3,1,2,1,3]).partial_sums(final=False)
            [1, 2, 5, 6, 8, 9]

        """
        s = 0
        partial_sums = []
        for i in self:
            s += i
            partial_sums.append(s)
        if final is False:
            partial_sums.pop()
        return partial_sums

    def to_subset(self, final=False):
        r"""
        The subset corresponding to ``self`` under the bijection (see below)
        between compositions of `n` and subsets of `\{1, 2, \ldots, n-1\}`.

        The bijection maps a composition `(i_1, \ldots, i_k)` of `n` to
        `\{i_1, i_1 + i_2, i_1 + i_2 + i_3, \ldots, i_1 + \cdots + i_{k-1}\}`.

        INPUT:

        - ``final`` -- (default: ``False``) whether or not to include the final
          partial sum, which is always the size of the composition.

        .. SEEALSO::

            :meth:`partial_sums`

        EXAMPLES::

            sage: Composition([1,1,3,1,2,1,3]).to_subset()
            {1, 2, 5, 6, 8, 9}
            sage: for I in Compositions(3): print I.to_subset()
            {1, 2}
            {1}
            {2}
            {}

        With ``final=True``, the sum of all the elements of the composition is
        included in the subset::

            sage: Composition([1,1,3,1,2,1,3]).to_subset(final=True)
            {1, 2, 5, 6, 8, 9, 12}

        TESTS:

        We verify that ``to_subset`` is indeed a bijection for compositions of
        size `n = 8`::

            sage: n = 8
            sage: all(Composition(from_subset=(S, n)).to_subset() == S \
            ...       for S in Subsets(n-1))
            True
            sage: all(Composition(from_subset=(I.to_subset(), n)) == I \
            ...       for I in Compositions(n))
            True

        """
        from sage.sets.set import Set
        return Set(self.partial_sums(final=final))

    def descents(self, final_descent=False):
        r"""
        This gives one fewer than the partial sums of the composition.

        This is here to maintain some sort of backward compatibility, even
        through the original implementation was broken (it gave the wrong
        answer). The same information can be found in :meth:`partial_sums`.

        .. SEEALSO::

            :meth:`partial_sums`

        INPUT:

        - ``final_descent`` -- (Default: ``False``) a boolean integer

        OUTPUT:

        - Returns the list of partial sums of ``self`` with each part
          subtracted by `1`. This includes the sum of all entries when
          ``final_descent`` is ``True``.

        EXAMPLES::

            sage: c = Composition([2,1,3,2])
            sage: c.descents()
            [1, 2, 5]
            sage: c.descents(final_descent=True)
            [1, 2, 5, 7]
        """
        return [i - 1 for i in self.partial_sums(final=final_descent)]

    def peaks(self):
        """
        Return a list of the peaks of the composition ``self``. The
        peaks of a composition are the descents which do not
        immediately follow another descent.

        EXAMPLES::

            sage: Composition([1, 1, 3, 1, 2, 1, 3]).peaks()
            [4, 7]
        """
        descents = dict((d - 1, True) for d in self.to_subset(final=True))
        return [
            i + 1 for i in range(len(self))
            if i not in descents and i + 1 in descents
        ]

    @combinatorial_map(name='to partition')
    def to_partition(self):
        """
        Sorts ``self`` into decreasing order and returns the corresponding
        partition.

        EXAMPLES::

            sage: Composition([2,1,3]).to_partition()
            [3, 2, 1]
            sage: Composition([4,2,2]).to_partition()
            [4, 2, 2]
        """
        from sage.combinat.partition import Partition
        return Partition(sorted(self, reverse=True))

    def to_skew_partition(self, overlap=1):
        """
        Return the skew partition obtained from ``self``. The
        parameter overlap indicates the number of cells that are covered by
        cells of the previous line.

        EXAMPLES::

            sage: Composition([3,4,1]).to_skew_partition()
            [6, 6, 3] / [5, 2]
            sage: Composition([3,4,1]).to_skew_partition(overlap=0)
            [8, 7, 3] / [7, 3]
        """
        from sage.combinat.skew_partition import SkewPartition
        outer = []
        inner = []
        sum_outer = -1 * overlap

        for k in range(len(self) - 1):
            outer += [self[k] + sum_outer + overlap]
            sum_outer += self[k] - overlap
            inner += [sum_outer + overlap]

        if self != []:
            outer += [self[-1] + sum_outer + overlap]
        else:
            return SkewPartition([[], []])

        return SkewPartition([
            filter(lambda x: x != 0, [l for l in reversed(outer)]),
            filter(lambda x: x != 0, [l for l in reversed(inner)])
        ])

    def shuffle_product(self, other, overlap=False):
        r"""
        The (overlapping) shuffles of ``self`` and ``other``.

        Suppose `I = (i_1, \ldots, i_k)` and `J = (j_1, \ldots, j_l)` are two
        compositions. A *shuffle* of `I` and `J` is a composition of length
        `k + l` that contains both `I` and `J` as subsequences.

        More generally, an *overlapping shuffle* of `I` and `J` is obtained by
        distributing the elements of `I` and `J` (preserving the relative
        ordering of these elements) among the positions of an empty list; an
        element of `I` and an element of `J` are permitted to share the same
        position, in which case they are replaced by their sum. In particular,
        a shuffle of `I` and `J` is an overlapping shuffle of `I` and `J`.

        INPUT:

        -  ``other`` -- composition

        -  ``overlap`` -- boolean (default: ``False``); if ``True``, the
           overlapping shuffle product is returned.

        OUTPUT:

        An enumerated set (allowing for mutliplicities)

        EXAMPLES:

        The shuffle product of `[2,2]` and `[1,1,3]`::

            sage: alph = Composition([2,2])
            sage: beta = Composition([1,1,3])
            sage: S = alph.shuffle_product(beta); S
            Shuffle product of [2, 2] and [1, 1, 3]
            sage: S.list()
            [[2, 2, 1, 1, 3], [2, 1, 2, 1, 3], [2, 1, 1, 2, 3], [2, 1, 1, 3, 2], [1, 2, 2, 1, 3], [1, 2, 1, 2, 3], [1, 2, 1, 3, 2], [1, 1, 2, 2, 3], [1, 1, 2, 3, 2], [1, 1, 3, 2, 2]]

        The *overlapping* shuffle product of `[2,2]` and `[1,1,3]`::

            sage: alph = Composition([2,2])
            sage: beta = Composition([1,1,3])
            sage: O = alph.shuffle_product(beta, overlap=True); O
            Overlapping shuffle product of [2, 2] and [1, 1, 3]
            sage: O.list()
            [[2, 2, 1, 1, 3], [2, 1, 2, 1, 3], [2, 1, 1, 2, 3], [2, 1, 1, 3, 2], [1, 2, 2, 1, 3], [1, 2, 1, 2, 3], [1, 2, 1, 3, 2], [1, 1, 2, 2, 3], [1, 1, 2, 3, 2], [1, 1, 3, 2, 2], [3, 2, 1, 3], [2, 3, 1, 3], [3, 1, 2, 3], [2, 1, 3, 3], [3, 1, 3, 2], [2, 1, 1, 5], [1, 3, 2, 3], [1, 2, 3, 3], [1, 3, 3, 2], [1, 2, 1, 5], [1, 1, 5, 2], [1, 1, 2, 5], [3, 3, 3], [3, 1, 5], [1, 3, 5]]

        Note that the shuffle product of two compositions can include the same
        composition more than once since a composition can be a shuffle of two
        compositions in several ways. For example::

            sage: S = Composition([1]).shuffle_product([1]); S
            Shuffle product of [1] and [1]
            sage: S.list()
            [[1, 1], [1, 1]]
            sage: O = Composition([1]).shuffle_product([1], overlap=True); O
            Overlapping shuffle product of [1] and [1]
            sage: O.list()
            [[1, 1], [1, 1], [2]]

        TESTS::

            sage: Composition([]).shuffle_product([]).list()
            [[]]

        """
        if overlap:
            from sage.combinat.words.shuffle_product import ShuffleProduct_overlapping
            return ShuffleProduct_overlapping(self, other)
        else:
            from sage.combinat.words.shuffle_product import ShuffleProduct_w1w2
            return ShuffleProduct_w1w2(self, other)
Beispiel #24
0
class LazyPowerSeriesRing(Algebra):
    def __init__(self, R, element_class = None, names=None):
        """
        TESTS::

            sage: from sage.combinat.species.series import LazyPowerSeriesRing
            sage: L = LazyPowerSeriesRing(QQ)
            sage: loads(dumps(L))
            Lazy Power Series Ring over Rational Field
        """
        #Make sure R is a ring with unit element
        if not R in Rings():
            raise TypeError("Argument R must be a ring.")
        try:
            z = R(Integer(1))
        except Exception:
            raise ValueError("R must have a unit element")

        #Take care of the names
        if names is None:
            names = 'x'
        else:
            names = names[0]

        self._element_class = element_class if element_class is not None else LazyPowerSeries
        self._order = None
        self._name = names
        sage.structure.parent_base.ParentWithBase.__init__(self, R)

    def ngens(self):
        """
        EXAMPLES::

            sage: LazyPowerSeriesRing(QQ).ngens()
            1
        """
        return 1

    def __repr__(self):
        """
        EXAMPLES::

            sage: LazyPowerSeriesRing(QQ)
            Lazy Power Series Ring over Rational Field
        """
        return "Lazy Power Series Ring over %s"%self.base_ring()

    def __cmp__(self, x):
        """
        EXAMPLES::

            sage: LQ = LazyPowerSeriesRing(QQ)
            sage: LZ = LazyPowerSeriesRing(ZZ)
            sage: LQ == LQ
            True
            sage: LZ == LQ
            False
        """
        if self.__class__ is not x.__class__:
            return cmp(self.__class__, x.__class__)
        return cmp(self.base_ring(), x.base_ring())

    def _coerce_impl(self, x):
        """
        EXAMPLES::

            sage: L1 = LazyPowerSeriesRing(QQ)
            sage: L2 = LazyPowerSeriesRing(RR)
            sage: L2.has_coerce_map_from(L1)
            True
            sage: L1.has_coerce_map_from(L2)
            False

        ::

            sage: a = L1([1]) + L2([1])
            sage: a.coefficients(3)
            [2.00000000000000, 2.00000000000000, 2.00000000000000]
        """
        return self(x)

    def __call__(self, x=None, order=unk):
        """
        EXAMPLES::

            sage: from sage.combinat.species.stream import Stream
            sage: L = LazyPowerSeriesRing(QQ)
            sage: L()
            Uninitialized lazy power series
            sage: L(1)
            1
            sage: L(ZZ).coefficients(10)
            [0, 1, -1, 2, -2, 3, -3, 4, -4, 5]
            sage: L(iter(ZZ)).coefficients(10)
            [0, 1, -1, 2, -2, 3, -3, 4, -4, 5]
            sage: L(Stream(ZZ)).coefficients(10)
            [0, 1, -1, 2, -2, 3, -3, 4, -4, 5]

        ::

            sage: a = L([1,2,3])
            sage: a.coefficients(3)
            [1, 2, 3]
            sage: L(a) is a
            True
            sage: L_RR = LazyPowerSeriesRing(RR)
            sage: b = L_RR(a)
            sage: b.coefficients(3)
            [1.00000000000000, 2.00000000000000, 3.00000000000000]
            sage: L(b)
            Traceback (most recent call last):
            ...
            TypeError: do not know how to coerce ... into self

        TESTS::

            sage: L(pi)
            Traceback (most recent call last):
            ...
            TypeError: do not know how to coerce pi into self
        """
        cls = self._element_class
        BR = self.base_ring()

        if x is None:
            res = cls(self, stream=None, order=unk, aorder=unk,
                      aorder_changed=True, is_initialized=False)
            res.compute_aorder = uninitialized
            return res

        if isinstance(x, LazyPowerSeries):
            x_parent = x.parent()
            if x_parent.__class__ != self.__class__:
                raise ValueError

            if x_parent.base_ring() == self.base_ring():
                return x
            else:
                if self.base_ring().has_coerce_map_from(x_parent.base_ring()):
                    return x._new(partial(x._change_ring_gen, self.base_ring()), lambda ao: ao, x, parent=self)


        if BR.has_coerce_map_from(parent(x)):
            x = BR(x)
            return self.term(x, 0)

        if hasattr(x, "__iter__") and not isinstance(x, Stream_class):
            x = iter(x)

        if is_iterator(x):
            x = Stream(x)

        if isinstance(x, Stream_class):
            aorder = order if order != unk else 0
            return cls(self, stream=x, order=order, aorder=aorder,
                       aorder_changed=False, is_initialized=True)
        elif not isinstance(x, Element):
            x = BR(x)
            return self.term(x, 0)

        raise TypeError("do not know how to coerce %s into self"%x)

    def zero(self):
        """
        Returns the zero power series.

        EXAMPLES::

            sage: L = LazyPowerSeriesRing(QQ)
            sage: L.zero()
            0

        TESTS:

        Check that the method `zero_element` raises a warning (:trac:`17694`)::

            sage: L.zero_element()
            doctest:...: DeprecationWarning: zero_element is deprecated. Please use zero instead.
            See http://trac.sagemath.org/17694 for details.
            0
        """
        return self(self.base_ring().zero())

    zero_element = deprecated_function_alias(17694, zero)

    def identity_element(self):
        """
        Returns the one power series.

        EXAMPLES::

            sage: L = LazyPowerSeriesRing(QQ)
            sage: L.identity_element()
            1
        """
        return self(self.base_ring()(1))

    def gen(self, i=0):
        """
        EXAMPLES::

            sage: L = LazyPowerSeriesRing(QQ)
            sage: L.gen().coefficients(5)
            [0, 1, 0, 0, 0]
        """
        res = self._new_initial(1, Stream([0,1,0]))
        res._name = self._name
        return res

    def term(self, r, n):
        """
        EXAMPLES::

            sage: L = LazyPowerSeriesRing(QQ)
            sage: L.term(0,0)
            0
            sage: L.term(3,2).coefficients(5)
            [0, 0, 3, 0, 0]
        """
        if n < 0:
            raise ValueError("n must be non-negative")
        BR = self.base_ring()
        if r == 0:
            res = self._new_initial(inf, Stream([0]))
            res._name = "0"
        else:
            zero = BR(0)
            s = [zero]*n+[BR(r),zero]
            res = self._new_initial(n, Stream(s))

            if n == 0:
                res._name = repr(r)
            elif n == 1:
                res._name = repr(r) + "*" + self._name
            else:
                res._name = "%s*%s^%s"%(repr(r), self._name, n)

        return res

    def _new_initial(self, order, stream):
        """
        Returns a new power series with specified order.

        INPUT:


        -  ``order`` - a non-negative integer

        -  ``stream`` - a Stream object


        EXAMPLES::

            sage: from sage.combinat.species.stream import Stream
            sage: L = LazyPowerSeriesRing(QQ)
            sage: L._new_initial(0, Stream([1,2,3,0])).coefficients(5)
            [1, 2, 3, 0, 0]
        """
        return self._element_class(self, stream=stream, order=order, aorder=order,
                                   aorder_changed=False, is_initialized=True)


    def _sum_gen(self, series_list):
        """
        Return a generator for the coefficients of the sum of the lazy
        power series in series_list.

        INPUT:


        -  ``series_list`` - a list of lazy power series


        EXAMPLES::

            sage: L = LazyPowerSeriesRing(QQ)
            sage: series_list = [ L([1]), L([0,1]), L([0,0,1]) ]
            sage: g = L._sum_gen(series_list)
            sage: [next(g) for i in range(5)]
            [1, 2, 3, 3, 3]
        """
        last_index = len(series_list) - 1
        assert last_index >= 0
        n = 0
        while True:
            r = sum( [f.coefficient(n) for f in series_list] )
            yield r
            n += 1

    def sum(self, a):
        """
        EXAMPLES::

            sage: L = LazyPowerSeriesRing(QQ)
            sage: l = [L(ZZ)]*3
            sage: L.sum(l).coefficients(10)
            [0, 3, -3, 6, -6, 9, -9, 12, -12, 15]
        """
        return self( self._sum_gen(a) )

    #Potentially infinite sum
    def _sum_generator_gen(self, g):
        """
        EXAMPLES::

            sage: L = LazyPowerSeriesRing(QQ)
            sage: s = L([1])
            sage: def f():
            ....:     while True:
            ....:         yield s
            sage: g = L._sum_generator_gen(f())
            sage: [next(g) for i in range(10)]
            [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
        """
        s = Stream(g)
        n = 0
        while True:
            r = s[n].coefficient(n)
            for i in range(len(s)-1):
                r += s[i].coefficient(n)
            yield r
            n += 1

    def sum_generator(self, g):
        """
        EXAMPLES::

            sage: L = LazyPowerSeriesRing(QQ)
            sage: g = [L([1])]*6 + [L(0)]
            sage: t = L.sum_generator(g)
            sage: t.coefficients(10)
            [1, 2, 3, 4, 5, 6, 6, 6, 6, 6]

        ::

            sage: s = L([1])
            sage: def g():
            ....:     while True:
            ....:         yield s
            sage: t = L.sum_generator(g())
            sage: t.coefficients(9)
            [1, 2, 3, 4, 5, 6, 7, 8, 9]
        """
        return self(self._sum_generator_gen(g))

    #Potentially infinite product
    def _product_generator_gen(self, g):
        """
        EXAMPLES::

            sage: from builtins import map
            sage: from sage.combinat.species.stream import _integers_from
            sage: L = LazyPowerSeriesRing(QQ)
            sage: g = map(lambda i: L([1]+[0]*i+[1]), _integers_from(0))
            sage: g2 = L._product_generator_gen(g)
            sage: [next(g2) for i in range(10)]
            [1, 1, 2, 4, 7, 12, 20, 33, 53, 84]
        """
        z = next(g)
        yield z.coefficient(0)
        yield z.coefficient(1)

        n = 2

        for x in g:
            z = z * x
            yield z.coefficient(n)
            n += 1

        while True:
            yield z.coefficient(n)
            n += 1

    def product_generator(self, g):
        """
        EXAMPLES::

            sage: L = LazyPowerSeriesRing(QQ)
            sage: s1 = L([1,1,0])
            sage: s2 = L([1,0,1,0])
            sage: s3 = L([1,0,0,1,0])
            sage: s4 = L([1,0,0,0,1,0])
            sage: s5 = L([1,0,0,0,0,1,0])
            sage: s6 = L([1,0,0,0,0,0,1,0])
            sage: s = [s1, s2, s3, s4, s5, s6]
            sage: def g():
            ....:     for a in s:
            ....:         yield a
            sage: p = L.product_generator(g())
            sage: p.coefficients(26)
            [1, 1, 1, 2, 2, 3, 4, 4, 4, 5, 5, 5, 5, 4, 4, 4, 3, 2, 2, 1, 1, 1, 0, 0, 0, 0]

        ::

            sage: def m(n):
            ....:     yield 1
            ....:     while True:
            ....:         for i in range(n-1):
            ....:             yield 0
            ....:         yield 1
            sage: def s(n):
            ....:     q = 1/n
            ....:     yield 0
            ....:     while True:
            ....:         for i in range(n-1):
            ....:             yield 0
            ....:         yield q

        ::

            sage: def lhs_gen():
            ....:     n = 1
            ....:     while True:
            ....:         yield L(m(n))
            ....:         n += 1

        ::

            sage: def rhs_gen():
            ....:     n = 1
            ....:     while True:
            ....:         yield L(s(n))
            ....:         n += 1
            sage: lhs = L.product_generator(lhs_gen())
            sage: rhs = L.sum_generator(rhs_gen()).exponential()
            sage: lhs.coefficients(10)
            [1, 1, 2, 3, 5, 7, 11, 15, 22, 30]
            sage: rhs.coefficients(10)
            [1, 1, 2, 3, 5, 7, 11, 15, 22, 30]
        """
        return self(self._product_generator_gen(g))
Beispiel #25
0
            verbose(
                "Trying to generate the %s-dimensional cuspidal submodule at weight %s using generators of weight up to %s"
                % (d, weight, gen_weight))
            G = self.cuspidal_ideal_generators(maxweight=gen_weight,
                                               prec=working_prec)

            flist = []
            for (j, f, F) in G:
                for g in self.q_expansion_basis(weight - j, prec=working_prec):
                    flist.append(g * f)

            A = self.base_ring()**working_prec
            W = A.span([A(f.padded_list(working_prec)) for f in flist])
            if W.rank() == d and (self.base_ring().is_field()
                                  or W.index_in_saturation() == 1):
                break
            else:
                gen_weight += 1
                verbose(
                    "Need more generators: trying again with generators of weight up to %s"
                    % gen_weight)

        R = G[0][1].parent()
        return [R(list(x), prec=prec) for x in W.gens()]


# Deprecated functions
find_generators = deprecated_function_alias(31559, ModularFormsRing.generators)
basis_for_modform_space = deprecated_function_alias(
    31559, ModularFormsRing.q_expansion_basis)
Beispiel #26
0
class Stock:
    """
    Class for retrieval of stock market information.
    """
    def __init__(self, symbol, cid=''):
        """
        Create a ``Stock`` object. Optional initialization by ``cid``: an
        identifier for each equity used by Google Finance.

        INPUT:

        - ``symbol`` -- string, a ticker symbol (with or without market).
          Format: ``"MARKET:SYMBOL"`` or ``"SYMBOL"``. If you don't
          supply the market, it is assumed to be NYSE or NASDAQ.
          e.g. "goog" or "OTC:NTDOY"

        - ``cid`` -- Integer, a Google contract ID (optional).


        .. NOTE::

            Currently, the symbol and cid do not have to match.  When using
            :meth:`history`, the cid will take precedence.

        EXAMPLES::

            sage: S = finance.Stock('ibm') # optional -- internet
            sage: S        # optional -- internet
            IBM (...)
        """
        self.symbol = symbol.upper()
        self.cid = cid

    def __repr__(self):
        """
        Return string representation of this stock.

        EXAMPLES::

            sage: finance.Stock('ibm').__repr__()     # optional -- internet
            'IBM (...)'
        """
        return "%s (%s)" % (self.symbol, self.market_value())

    def market_value(self):
        """
        Return the current market value of this stock.

        OUTPUT:

        A Python float.

        EXAMPLES::

            sage: finance.Stock('goog').market_value()   # random; optional - internet
            575.83000000000004
        """
        return float(self.current_price_data()['price'])

    def current_price_data(self):
        r"""
        Get Yahoo current price data for this stock.

        This method returns a dictionary with the following keys:


        .. csv-table::
            :class: contentstable
            :widths: 25,25,25,25
            :delim: |

            ``'price'`` | ``'change'`` | ``'volume'`` | ``'avg_daily_volume'``
            ``'stock_exchange'`` | ``'market_cap'`` | ``'book_value'`` | ``'ebitda'``
            ``'dividend_per_share'`` | ``'dividend_yield'`` | ``'earnings_per_share'`` | ``'52_week_high'``
            ``'52_week_low'`` | ``'50day_moving_avg'`` | ``'200day_moving_avg'`` | ``'price_earnings_ratio'``
            ``'price_earnings_growth_ratio'`` | ``'price_sales_ratio'`` | ``'price_book_ratio'`` | ``'short_ratio'``.

        EXAMPLES::

            sage: finance.Stock('GOOG').current_price_data()  #  random; optional - internet
            {'200day_moving_avg': '536.57',
             '50day_moving_avg': '546.01',
             '52_week_high': '599.65',
             '52_week_low': '487.56',
             'avg_daily_volume': '1826450',
             'book_value': '153.64',
             'change': '+0.56',
             'dividend_per_share': 'N/A',
             'dividend_yield': 'N/A',
             'earnings_per_share': '20.99',
             'ebitda': '21.48B',
             'market_cap': '366.11B',
             'price': '537.90',
             'price_book_ratio': '3.50',
             'price_earnings_growth_ratio': '0.00',
             'price_earnings_ratio': '25.62',
             'price_sales_ratio': '5.54',
             'short_ratio': '1.50',
             'stock_exchange': '"NMS"',
             'volume': '1768181'}

        TESTS::

            sage: finance.Stock('GOOG').current_price_data()  # optional -- internet
            {'200day_moving_avg': ...,
             '50day_moving_avg': ...,
             '52_week_high': ...,
             '52_week_low': ...,
             'avg_daily_volume': ...,
             'book_value': ...,
             'change': ...,
             'dividend_per_share': ...,
             'dividend_yield': ...,
             'earnings_per_share': ...,
             'ebitda': ...,
             'market_cap': ...,
             'price': ...,
             'price_book_ratio': ...,
             'price_earnings_growth_ratio': ...,
             'price_earnings_ratio': ...,
             'price_sales_ratio': ...,
             'short_ratio': ...,
             'stock_exchange': ...,
             'volume': ...}
        """
        url = 'http://finance.yahoo.com/d/quotes.csv?s=%s&f=%s' % (
            self.symbol, 'l1c1va2xj1b4j4dyekjm3m4rr5p5p6s7')
        values = urlopen(url).read().strip().strip('"').split(',')
        data = {}
        data['price'] = values[0]
        data['change'] = values[1]
        data['volume'] = values[2]
        data['avg_daily_volume'] = values[3]
        data['stock_exchange'] = values[4]
        data['market_cap'] = values[5]
        data['book_value'] = values[6]
        data['ebitda'] = values[7]
        data['dividend_per_share'] = values[8]
        data['dividend_yield'] = values[9]
        data['earnings_per_share'] = values[10]
        data['52_week_high'] = values[11]
        data['52_week_low'] = values[12]
        data['50day_moving_avg'] = values[13]
        data['200day_moving_avg'] = values[14]
        data['price_earnings_ratio'] = values[15]
        data['price_earnings_growth_ratio'] = values[16]
        data['price_sales_ratio'] = values[17]
        data['price_book_ratio'] = values[18]
        data['short_ratio'] = values[19]
        return data

    yahoo = deprecated_function_alias(18355, current_price_data)

    def history(self,
                startdate='Jan+1,+1900',
                enddate=None,
                histperiod='daily'):
        """
        Return an immutable sequence of historical price data
        for this stock, obtained from Google. OHLC data is stored
        internally as well. By default, returns the past year's daily
        OHLC data.

        Dates ``startdate`` and ``enddate`` should be formatted
        ``'Mon+d,+yyyy'``, where ``'Mon'`` is a three character abbreviation
        of the month's name.

        .. NOTE::

            Google Finance returns the past year's financial data by default
            when ``startdate`` is set too low from the equity's date of going
            public.  By default, this function only looks at the NASDAQ and
            NYSE markets.  However, if you specified the market during
            initialization of the stock (i.e. ``finance.Stock("OTC:NTDOY")``),
            this method will give correct results.

        INPUT:

        - ``startdate`` -- string, (default: ``'Jan+1,+1900'``)

        - ``enddate`` -- string, (default: current date)

        - ``histperiod`` -- string, (``'daily'`` or ``'weekly'``)

        OUTPUT:

        A sequence.

        EXAMPLES:

        We get the first five days of VMware's stock history::

            sage: finance.Stock('vmw').history('Aug+13,+2007')[:5] # optional -- internet
            [
             14-Aug-07 50.00 55.50 48.00 51.00   38262850,
             15-Aug-07 52.11 59.87 51.50 57.71   10689100,
             16-Aug-07 60.99 61.49 52.71 56.99    6919500,
             17-Aug-07 59.00 59.00 54.45 55.55    3087000,
             20-Aug-07 56.05 57.50 55.61 57.33    2141900
            ]
            sage: finance.Stock('F').history('Aug+20,+1992', 'Jul+7,+2008')[:5] # optional -- internet
            [
             20-Aug-92 0.00 7.90 7.73 7.83    5492698,
             21-Aug-92 0.00 7.92 7.66 7.68    5345999,
             24-Aug-92 0.00 7.59 7.33 7.35   11056299,
             25-Aug-92 0.00 7.66 7.38 7.61    8875299,
             26-Aug-92 0.00 7.73 7.64 7.68    6447201
            ]

        Note that when ``startdate`` is too far prior to a stock's actual start
        date, Google Finance defaults to a year's worth of stock history
        leading up to the specified end date.  For example, Apple's (AAPL)
        stock history only dates back to September 7, 1984::

            sage: finance.Stock('AAPL').history('Sep+1,+1900', 'Jan+1,+2000')[0:5] # optional -- internet
            [
              4-Jan-99 0.00 1.51 1.43 1.47  238221200,
              5-Jan-99 0.00 1.57 1.48 1.55  352522800,
              6-Jan-99 0.00 1.58 1.46 1.49  337125600,
              7-Jan-99 0.00 1.61 1.50 1.61  357254800,
              8-Jan-99 0.00 1.67 1.57 1.61  169680000
            ]

        Here is an example where we create and get the history of a stock
        that is not in NASDAQ or NYSE::

            sage: finance.Stock("OTC:NTDOY").history(startdate="Jan+1,+2007", enddate="Jan+1,+2008")[:5]  # optional -- internet
            [
              3-Jan-07 32.44 32.75 32.30 32.44     156283,
              4-Jan-07 31.70 32.40 31.20 31.70     222643,
              5-Jan-07 30.15 30.50 30.15 30.15      65670,
              8-Jan-07 30.10 30.50 30.00 30.10     130765,
              9-Jan-07 29.90 30.05 29.60 29.90     103338
            ]

        Here, we create a stock by cid, and get historical data.
        Note that when using historical, if a cid is specified,
        it will take precedence over the stock's symbol.  So, if
        the symbol and cid do not match, the history based on the
        contract id will be returned. ::

            sage: sage.finance.stock.Stock("AAPL", 22144).history(startdate='Jan+1,+1990')[:5] #optional -- internet
            [
              8-Jun-99 0.00 1.74 1.70 1.70   78414000,
              9-Jun-99 0.00 1.73 1.69 1.73   88446400,
             10-Jun-99 0.00 1.72 1.69 1.72   79262400,
             11-Jun-99 0.00 1.73 1.65 1.66   46261600,
             14-Jun-99 0.00 1.67 1.61 1.62   39270000
            ]
        """
        if enddate is None:
            enddate = date.today().strftime("%b+%d,+%Y")

        symbol = self.symbol

        if self.cid == '':
            if ':' in symbol:
                R = self._get_data('', startdate, enddate, histperiod)
            else:
                try:
                    R = self._get_data('NASDAQ:', startdate, enddate,
                                       histperiod)
                except RuntimeError:
                    R = self._get_data("NYSE:", startdate, enddate, histperiod)
        else:
            R = self._get_data('', startdate, enddate, histperiod)
        self.__historical = []
        self.__historical = self._load_from_csv(R)
        return self.__historical

    google = deprecated_function_alias(18355, history)

    def open(self, *args, **kwds):
        r"""
        Return a time series containing historical opening prices for this
        stock. If no arguments are given, will return last acquired historical
        data. Otherwise, data will be gotten from Google Finance.

        INPUT:

        - ``startdate`` -- string, (default: ``'Jan+1,+1900'``)

        - ``enddate`` -- string, (default: current date)

        - ``histperiod`` -- string, (``'daily'`` or ``'weekly'``)

        OUTPUT:

        A time series -- close price data.

        EXAMPLES:

        You can directly obtain Open data as so::

            sage: finance.Stock('vmw').open(startdate='Jan+1,+2008', enddate='Feb+1,+2008')                 # optional -- internet
            [85.4900, 84.9000, 82.0000, 81.2500, ... 82.0000, 58.2700, 54.4900, 55.6000, 56.9800]

        Or, you can initialize stock data first and then extract the Open
        data::

            sage: c = finance.Stock('vmw') # optional -- internet
            sage: c.history(startdate='Feb+1,+2008', enddate='Mar+1,+2008')[:5]    # optional -- internet
            [
              1-Feb-08 56.98 58.14 55.06 57.85    2490481,
              4-Feb-08 58.00 60.47 56.91 58.05    1840709,
              5-Feb-08 57.60 59.30 57.17 59.30    1712179,
              6-Feb-08 60.32 62.00 59.50 61.52    2211775,
              7-Feb-08 60.50 62.75 59.56 60.80    1521651
            ]
            sage: c.open()    # optional -- internet
            [56.9800, 58.0000, 57.6000, 60.3200, ... 56.5500, 59.3000, 60.0000, 59.7900, 59.2600]

        Otherwise, :meth:`history` will be called with the default
        arguments returning a year's worth of data::

            sage: finance.Stock('vmw').open()   # random; optional -- internet
            [52.1100, 60.9900, 59.0000, 56.0500, 57.2500, ... 83.0500, 85.4900, 84.9000, 82.0000, 81.2500]
        """

        from .time_series import TimeSeries

        if len(args) != 0:
            return TimeSeries([x.open for x in self.history(*args, **kwds)])

        try:
            return TimeSeries([x.open for x in self.__historical])
        except AttributeError:
            pass

        return TimeSeries([x.open for x in self.history(*args, **kwds)])

    def close(self, *args, **kwds):
        r"""
        Return the time series of all historical closing prices for this stock.
        If no arguments are given, will return last acquired historical data.
        Otherwise, data will be gotten from Google Finance.

        INPUT:

        - ``startdate`` -- string, (default: ``'Jan+1,+1900'``)

        - ``enddate`` -- string, (default: current date)

        - ``histperiod`` -- string, (``'daily'`` or ``'weekly'``)

        OUTPUT:

        A time series -- close price data.

        EXAMPLES:

        You can directly obtain close data as so::

            sage: finance.Stock('vmw').close(startdate='Jan+1,+2008', enddate='Feb+1,+2008')                 # optional -- internet
            [84.6000, 83.9500, 80.4900, 72.9900, ... 83.0000, 54.8700, 56.4200, 56.6700, 57.8500]

        Or, you can initialize stock data first and then extract the Close
        data::

            sage: c = finance.Stock('vmw')  # optional -- internet
            sage: c.history(startdate='Feb+1,+2008', enddate='Mar+1,+2008')[:5]    # optional -- internet
            [
              1-Feb-08 56.98 58.14 55.06 57.85    2490481,
              4-Feb-08 58.00 60.47 56.91 58.05    1840709,
              5-Feb-08 57.60 59.30 57.17 59.30    1712179,
              6-Feb-08 60.32 62.00 59.50 61.52    2211775,
              7-Feb-08 60.50 62.75 59.56 60.80    1521651
            ]
            sage: c.close()    # optional -- internet
            [57.8500, 58.0500, 59.3000, 61.5200, ... 58.2900, 60.1800, 59.8600, 59.9500, 58.6700]

        Otherwise, :meth:`history` will be called with the default
        arguments returning a year's worth of data::

            sage: finance.Stock('vmw').close()   # random; optional -- internet
            [57.7100, 56.9900, 55.5500, 57.3300, 65.9900 ... 84.9900, 84.6000, 83.9500, 80.4900, 72.9900]
        """

        from .time_series import TimeSeries

        if len(args) != 0:
            return TimeSeries([x.close for x in self.history(*args, **kwds)])

        try:
            return TimeSeries([x.close for x in self.__historical])
        except AttributeError:
            pass

        return TimeSeries([x.close for x in self.history(*args, **kwds)])

    def load_from_file(self, file):
        r"""
        Load historical data from a local csv formatted data file. Note
        that no symbol data is included in Google Finance's csv data.
        The csv file must be formatted in the following way, just as
        on Google Finance::

            Timestamp,Open,High,Low,Close,Volume

        INPUT:

        - ``file`` -- local file with Google Finance formatted OHLC data.

        OUTPUT:

        A sequence -- OHLC data.

        EXAMPLES:

        Suppose you have a file in your home directory containing Apple stock
        OHLC data, such as that from Google Finance, called
        ``AAPL-minutely.csv``. One can load this information into a Stock
        object like so. Note that the path must be explicit::

            sage: filename = tmp_filename(ext='.csv')
            sage: with open(filename, 'w') as fobj:
            ....:     _ = fobj.write("Date,Open,High,Low,Close,Volume\n1212405780,187.80,187.80,187.80,187.80,100\n1212407640,187.75,188.00,187.75,188.00,2000\n1212407700,188.00,188.00,188.00,188.00,1000\n1212408000,188.00,188.11,188.00,188.00,2877\n1212408060,188.00,188.00,188.00,188.00,687")
            sage: finance.Stock('aapl').load_from_file(filename)[:5]
            [
            1212408060 188.00 188.00 188.00 188.00        687,
            1212408000 188.00 188.11 188.00 188.00       2877,
            1212407700 188.00 188.00 188.00 188.00       1000,
            1212407640 187.75 188.00 187.75 188.00       2000,
            1212405780 187.80 187.80 187.80 187.80        100
            ]


        Note that since the source file doesn't contain information on which
        equity the information comes from, the symbol designated at
        initialization of Stock need not match the source of the data. For
        example, we can initialize a Stock object with the symbol ``'goog'``,
        but load data from ``'aapl'`` stock prices::

            sage: finance.Stock('goog').load_from_file(filename)[:5]
            [
            1212408060 188.00 188.00 188.00 188.00        687,
            1212408000 188.00 188.11 188.00 188.00       2877,
            1212407700 188.00 188.00 188.00 188.00       1000,
            1212407640 187.75 188.00 187.75 188.00       2000,
            1212405780 187.80 187.80 187.80 187.80        100
            ]

        This tests a file that doesn't exist::

            sage: finance.Stock("AAPL").load_from_file("I am not a file")
            Traceback (most recent call last):
            ...
            IOError: [Errno 2] No such file or directory: 'I am not a file'
        """
        file_obj = open(file, 'r')
        R = file_obj.read()
        self.__historical = self._load_from_csv(R)
        file_obj.close()
        return self.__historical

    def _load_from_csv(self, R):
        r"""
        EXAMPLES:

        This indirectly tests ``_load_from_csv()``::

            sage: filename = tmp_filename(ext='.csv')
            sage: with open(filename,'w') as fobj:
            ....:     _ = fobj.write("Date,Open,High,Low,Close,Volume\n1212405780,187.80,187.80,187.80,187.80,100\n1212407640,187.75,188.00,187.75,188.00,2000\n1212407700,188.00,188.00,188.00,188.00,1000\n1212408000,188.00,188.11,188.00,188.00,2877\n1212408060,188.00,188.00,188.00,188.00,687")
            sage: finance.Stock('aapl').load_from_file(filename)
            [
            1212408060 188.00 188.00 188.00 188.00        687,
            1212408000 188.00 188.11 188.00 188.00       2877,
            1212407700 188.00 188.00 188.00 188.00       1000,
            1212407640 187.75 188.00 187.75 188.00       2000,
            1212405780 187.80 187.80 187.80 187.80        100
            ]
        """
        R = R.splitlines()
        hist_data = []
        for x in reversed(R[1:]):
            try:
                timestamp, opn, high, low, close, volume = x.split(',')
                ohlc = OHLC(timestamp, opn, high, low, close, volume)
                hist_data.append(ohlc)
            except ValueError:
                pass
        hist_data = Sequence(hist_data,
                             cr=True,
                             universe=lambda x: x,
                             immutable=True)
        return hist_data

    def _get_data(self, exchange, startdate, enddate, histperiod='daily'):
        """
        This function is used internally.

        EXAMPLES:

        This indirectly tests the use of ``_get_data()``::

            sage: finance.Stock('aapl').history(startdate='Jan+1,+1990',enddate='Jan+1,+1991')[:2]    # optional -- internet
            [
              2-Jan-90 0.00 1.34 1.25 1.33   45799600,
              3-Jan-90 0.00 1.36 1.34 1.34   51998800
            ]

        TESTS::

            sage: finance.Stock('whatever').history() # optional -- internet
            Traceback (most recent call last):
            ...
            RuntimeError: Google reported a wrong request (did you specify a cid?)
        """
        symbol = self.symbol
        cid = self.cid
        if cid == '':
            url = 'http://finance.google.com/finance/historical?q=%s%s&startdate=%s&enddate=%s&histperiod=%s&output=csv' % (
                exchange, symbol.upper(), startdate, enddate, histperiod)
        else:
            url = 'http://finance.google.com/finance/historical?cid=%s&startdate=%s&enddate=%s&histperiod=%s&output=csv' % (
                cid, startdate, enddate, histperiod)
        data = urlopen(url).read()
        if "Bad Request" in data or "The requested URL was not found on this server." in data:
            raise RuntimeError(
                "Google reported a wrong request (did you specify a cid?)")
        return data
Beispiel #27
0
class WeylGroup_gens(ClearCacheOnPickle, UniqueRepresentation,
                     FinitelyGeneratedMatrixGroup_gap):

    @staticmethod
    def __classcall__(cls, domain, prefix=None):
        return super(WeylGroup_gens, cls).__classcall__(cls, domain, prefix)

    def __init__(self, domain, prefix):
        """
        EXAMPLES::

            sage: G = WeylGroup(['B',3])
            sage: TestSuite(G).run()
            sage: cm = CartanMatrix([[2,-5,0],[-2,2,-1],[0,-1,2]])
            sage: W = WeylGroup(cm)
            sage: TestSuite(W).run() # long time
        """
        self._domain = domain
        if self.cartan_type().is_affine():
            category = AffineWeylGroups()
        elif self.cartan_type().is_finite():
            category = FiniteWeylGroups()
        else:
            category = WeylGroups()
        self.n = domain.dimension() # Really needed?
        self._prefix = prefix

        # FinitelyGeneratedMatrixGroup_gap takes plain matrices as input
        gens_matrix = [self.morphism_matrix(self.domain().simple_reflection(i))
                       for i in self.index_set()]
        from sage.libs.all import libgap
        libgap_group = libgap.Group(gens_matrix)
        degree = ZZ(self.domain().dimension())
        ring = self.domain().base_ring()
        FinitelyGeneratedMatrixGroup_gap.__init__(
            self, degree, ring, libgap_group, category=category)

    @cached_method
    def cartan_type(self):
        """
        Returns the CartanType associated to self.

        EXAMPLES::

            sage: G = WeylGroup(['F',4])
            sage: G.cartan_type()
            ['F', 4]
        """
        return self.domain().cartan_type()

    @cached_method
    def index_set(self):
        """
        Returns the index set of self.

        EXAMPLES::

            sage: G = WeylGroup(['F',4])
            sage: G.index_set()
            (1, 2, 3, 4)
            sage: G = WeylGroup(['A',3,1])
            sage: G.index_set()
            (0, 1, 2, 3)
        """
        return self.cartan_type().index_set()

    # Should be implemented in (morphisms of) modules with basis
    def morphism_matrix(self, f):
        return matrix(self.domain().base_ring(), [f(b).to_vector()
                           for b in self.domain().basis()]).transpose()

    def from_morphism(self, f):
        return self._element_constructor_(self.morphism_matrix(f))

    @cached_method
    def simple_reflections(self):
        """
        Returns the simple reflections of self, as a family.

        EXAMPLES:

        There are the simple reflections for the symmetric group::

            sage: W=WeylGroup(['A',2])
            sage: s = W.simple_reflections(); s
            Finite family {1: [0 1 0]
            [1 0 0]
            [0 0 1], 2: [1 0 0]
            [0 0 1]
            [0 1 0]}

        As a special feature, for finite irreducible root systems,
        s[0] gives the reflection along the highest root::

            sage: s[0]
            [0 0 1]
            [0 1 0]
            [1 0 0]

        We now look at some further examples::

            sage: W=WeylGroup(['A',2,1])
            sage: W.simple_reflections()
            Finite family {0: [-1  1  1]
            [ 0  1  0]
            [ 0  0  1], 1: [ 1  0  0]
            [ 1 -1  1]
            [ 0  0  1], 2: [ 1  0  0]
            [ 0  1  0]
            [ 1  1 -1]}
            sage: W = WeylGroup(['F',4])
            sage: [s1,s2,s3,s4] = W.simple_reflections()
            sage: w = s1*s2*s3*s4; w
            [ 1/2  1/2  1/2  1/2]
            [-1/2  1/2  1/2 -1/2]
            [ 1/2  1/2 -1/2 -1/2]
            [ 1/2 -1/2  1/2 -1/2]
            sage: s4^2 == W.unit()
            True
            sage: type(w) == W.element_class
            True

        """
        return self.domain().simple_reflections().map(self.from_morphism)

    def reflections(self):
        """
        The reflections of W are the conjugates of the simple reflections.
        They are in bijection with the positive roots, for given a positive
        root, we may have the reflection in the hyperplane orthogonal to it.
        This method returns a dictionary indexed by the reflections taking
        values in the positive roots. This requires self to be a finite
        Weyl group.

        EXAMPLES::

            sage: W = WeylGroup("B2", prefix="s")
            sage: refdict = W.reflections(); refdict
            Finite family {s1: (1, -1), s2*s1*s2: (1, 1), s1*s2*s1: (1, 0), s2: (0, 1)}
            sage: [refdict[r]+r.action(refdict[r]) for r in refdict.keys()]
            [(0, 0), (0, 0), (0, 0), (0, 0)]

        """
        ret = {}
        try:
            for alp in self.domain().positive_roots():
                m = Matrix([self.domain().reflection(alp)(x).to_vector()
                            for x in self.domain().basis()])
                r = self(m)
                ret[r] = alp
            return Family(ret)
        except StandardError:
            raise NotImplementedError, "reflections are only implemented for finite Weyl groups"

    def _repr_(self):
        """
        EXAMPLES::

            sage: WeylGroup(['A', 1])
            Weyl Group of type ['A', 1] (as a matrix group acting on the ambient space)
            sage: WeylGroup(['A', 3, 1])
            Weyl Group of type ['A', 3, 1] (as a matrix group acting on the root space)
        """
        return "Weyl Group of type %s (as a matrix group acting on the %s)"%(self.cartan_type(),
                                                                           self._domain._name_string(capitalize=False,
                                                                                                      base_ring=False,
                                                                                                      type=False))

    def character_table(self):
        """
        Returns the character table as a matrix

        Each row is an irreducible character. For larger tables you
        may preface this with a command such as
        gap.eval("SizeScreen([120,40])") in order to widen the screen.

        EXAMPLES::

            sage: WeylGroup(['A',3]).character_table()
            CT1
            <BLANKLINE>
                 2  3  2  2  .  3
                 3  1  .  .  1  .
            <BLANKLINE>
                   1a 4a 2a 3a 2b
            <BLANKLINE>
            X.1     1 -1 -1  1  1
            X.2     3  1 -1  . -1
            X.3     2  .  . -1  2
            X.4     3 -1  1  . -1
            X.5     1  1  1  1  1
        """
        gens_str = ', '.join(str(g.gap()) for g  in self.gens())
        ctbl = gap('CharacterTable(Group({0}))'.format(gens_str))
        return ctbl.Display()

    @cached_method
    def one(self):
        """
        Returns the unit element of the Weyl group

        EXAMPLES::
            sage: W = WeylGroup(['A',3])
            sage: e = W.unit(); e
            [1 0 0 0]
            [0 1 0 0]
            [0 0 1 0]
            [0 0 0 1]
            sage: type(e) == W.element_class
            True
        """
        return self._element_constructor_(matrix(QQ,self.n,self.n,1))

    unit = one # For backward compatibility

    def domain(self):
        """
        Returns the domain of the element of ``self``, that is the
        root lattice realization on which they act.

        EXAMPLES::

            sage: G = WeylGroup(['F',4])
            sage: G.domain()
            Ambient space of the Root system of type ['F', 4]
            sage: G = WeylGroup(['A',3,1])
            sage: G.domain()
            Root space over the Rational Field of the Root system of type ['A', 3, 1]

        This method used to be called ``lattice``:

            sage: G.lattice()
            doctest:...: DeprecationWarning: lattice is deprecated. Please use domain instead.
            See http://trac.sagemath.org/8414 for details.
            Root space over the Rational Field of the Root system of type ['A', 3, 1]
        """
        return self._domain

    lattice = deprecated_function_alias(8414, domain)

    def simple_reflection(self, i):
        """
        Returns the `i^{th}` simple reflection.

        EXAMPLES::

            sage: G = WeylGroup(['F',4])
            sage: G.simple_reflection(1)
            [1 0 0 0]
            [0 0 1 0]
            [0 1 0 0]
            [0 0 0 1]
            sage: W=WeylGroup(['A',2,1])
            sage: W.simple_reflection(1)
            [ 1  0  0]
            [ 1 -1  1]
            [ 0  0  1]
        """
        if i not in self.index_set():
            raise ValueError, "i must be in the index set"
        return self.simple_reflections()[i]

    def long_element_hardcoded(self):
        """
        Returns the long Weyl group element (hardcoded data)

        Do we really want to keep it? There is a generic
        implementation which works in all cases. The hardcoded should
        have a better complexity (for large classical types), but
        there is a cache, so does this really matter?

        EXAMPLES::

            sage: types = [ ['A',5],['B',3],['C',3],['D',4],['G',2],['F',4],['E',6] ]
            sage: [WeylGroup(t).long_element().length() for t in types]
            [15, 9, 9, 12, 6, 24, 36]
            sage: all( WeylGroup(t).long_element() == WeylGroup(t).long_element_hardcoded() for t in types )  # long time (17s on sage.math, 2011)
            True
        """
        type = self.cartan_type()
        if type[0] == 'D' and type[1]%2 == 1:
            l = [-1 for i in range(self.n-1)]
            l.append(1)
            m = diagonal_matrix(QQ,l)
        elif type[0] == 'A':
            l = [0 for k in range((self.n)**2)]
            for k in range(self.n-1, (self.n)**2-1, self.n-1):
                l[k] = 1
            m = matrix(QQ, self.n, l)
        elif type[0] == 'E':
            if type[1] == 6:
                half = ZZ(1)/ZZ(2)
                l = [[-half, -half, -half, half, 0, 0, 0, 0],
                     [-half, -half, half, -half, 0, 0, 0, 0],
                     [-half, half, -half, -half, 0, 0, 0, 0],
                     [half, -half, -half, -half, 0, 0, 0, 0],
                     [0, 0, 0, 0, half, half, half, -half],
                     [0, 0, 0, 0, half, half, -half, half],
                     [0, 0, 0, 0, half, -half, half, half],
                     [0, 0, 0, 0, -half, half, half, half]]
                m = matrix(QQ, 8, l)
            else:
                raise NotImplementedError, "Not implemented yet for this type"
        elif type[0] == 'G':
            third = ZZ(1)/ZZ(3)
            twothirds = ZZ(2)/ZZ(3)
            l = [[-third, twothirds, twothirds],
                 [twothirds, -third, twothirds],
                 [twothirds, twothirds, -third]]
            m = matrix(QQ, 3, l)
        else:
            m = diagonal_matrix([-1 for i in range(self.n)])
        return self.__call__(m)

    def __cmp__(self, other):
        """
        TESTS::

            sage: G1 = WeylGroup(CartanType(['A',2]))
            sage: G2 = WeylGroup(CartanType(['A',2]))
            sage: G1 == G2
            True
        """
        if self.__class__ != other.__class__:
            return cmp(self.__class__, other.__class__)
        if self.cartan_type() != other.cartan_type():
            return cmp(self.cartan_type(), other.cartan_type())
        return 0

    def classical(self):
        """
        If self is a Weyl group from an affine Cartan Type, this give
        the classical parabolic subgroup of self.

        Caveat: we assume that 0 is a special node of the Dynkin diagram

        TODO: extract parabolic subgroup method
        """
        assert(self.cartan_type().is_affine())
        return ClassicalWeylSubgroup(self._domain, prefix=self._prefix)

    def bruhat_graph(self, x, y):
        """
        The Bruhat graph Gamma(x,y), defined if x <= y in the Bruhat order, has
        as its vertices the Bruhat interval, {t | x <= t <= y}, and as its
        edges the pairs u, v such that u = r.v where r is a reflection, that
        is, a conjugate of a simple reflection.

        Returns the Bruhat graph as a directed graph, with an edge u --> v
        if and only if u < v in the Bruhat order, and u = r.v.

        See:

        Carrell, The Bruhat graph of a Coxeter group, a conjecture of Deodhar, and
        rational smoothness of Schubert varieties. Algebraic groups and their
        generalizations: classical methods (University Park, PA, 1991), 53--61,
        Proc. Sympos. Pure Math., 56, Part 1, Amer. Math. Soc., Providence, RI, 1994.

        EXAMPLES:

            sage: W = WeylGroup("A3", prefix = "s")
            sage: [s1,s2,s3] = W.simple_reflections()
            sage: W.bruhat_graph(s1*s3,s1*s2*s3*s2*s1)
            Digraph on 10 vertices
        """
        g = self.bruhat_interval(x, y)
        ref = self.reflections()
        d = {}
        for x in g:
            d[x] = [y for y in g if x.length() < y.length() and ref.has_key(x*y.inverse())]
        return DiGraph(d)
Beispiel #28
0
class HochschildComplex(UniqueRepresentation, Parent):
    r"""
    The Hochschild complex.

    Let `A` be an algebra over a commutative ring `R` such
    that `A` a projective `R`-module, and `M` an `A`-bimodule.
    The *Hochschild complex* is the chain complex given by

    .. MATH::

        C_n(A, M) := M \otimes A^{\otimes n}

    with the boundary operators given as follows. For fixed `n`, define
    the face maps

    .. MATH::

        f_{n,i}(m \otimes a_1 \otimes \cdots \otimes a_n) = \begin{cases}
        m a_1 \otimes \cdots \otimes a_n & \text{if } i = 0, \\
        a_n m \otimes a_1 \otimes \cdots \otimes a_{n-1}
        & \text{if } i = n, \\
        m \otimes a_1 \otimes \cdots \otimes a_i a_{i+1} \otimes
        \cdots \otimes a_n & \text{otherwise.}
        \end{cases}

    We define the boundary operators as

    .. MATH::

        d_n = \sum_{i=0}^n (-1)^i f_{n,i}.

    The *Hochschild homology* of `A` is the homology of this complex.
    Alternatively, the Hochschild homology can be described by
    `HH_n(A, M) = \operatorname{Tor}_n^{A^e}(A, M)`, where
    `A^e = A \otimes A^o` (`A^o` is the opposite algebra of `A`)
    is the enveloping algebra of `A`.

    *Hochschild cohomology* is the homology of the dual complex and
    can be described by `HH^n(A, M) = \operatorname{Ext}^n_{A^e}(A, M)`.

    Another perspective on Hochschild homology is that `f_{n,i}`
    make the family `C_n(A, M)` a simplicial object in the
    category of `R`-modules, and the degeneracy maps are

    .. MATH::

        s_i(a_0 \otimes \cdots \otimes a_n) =
        a_0 \otimes \cdots \otimes a_i \otimes 1 \otimes a_{i+1}
        \otimes \cdots \otimes a_n

    The Hochschild homology can also be constructed as the homology
    of this simplicial module.

    REFERENCES:

    - :wikipedia:`Hochschild_homology`
    - https://ncatlab.org/nlab/show/Hochschild+cohomology
    - [Red2001]_
    """
    def __init__(self, A, M):
        """
        Initialize ``self``.

        EXAMPLES::

            sage: SGA = SymmetricGroupAlgebra(QQ, 3)
            sage: T = SGA.trivial_representation()
            sage: H = SGA.hochschild_complex(T)

            sage: H.category()
            Category of chain complexes over Rational Field
            sage: H in ChainComplexes(QQ)
            True

            sage: TestSuite(H).run()
        """
        self._A = A
        self._M = M
        Parent.__init__(self,
                        base=A.base_ring(),
                        category=ChainComplexes(A.base_ring()))

    def _repr_(self):
        """
        Return a string representation of ``self``.

        EXAMPLES::

            sage: SGA = SymmetricGroupAlgebra(QQ, 3)
            sage: T = SGA.trivial_representation()
            sage: T.rename("Trivial representation of SGA")
            sage: SGA.hochschild_complex(T)
            Hochschild complex of Symmetric group algebra of order 3 over Rational Field
             with coefficients in Trivial representation of SGA
            sage: T.rename()  # reset the name
        """
        return "Hochschild complex of {} with coefficients in {}".format(
            self._A, self._M)

    def _latex_(self):
        r"""
        Return a latex representation of ``self``.

        EXAMPLES::

            sage: SGA = SymmetricGroupAlgebra(QQ, 3)
            sage: T = SGA.trivial_representation()
            sage: H = SGA.hochschild_complex(T)
            sage: latex(H)
            C_{\bullet}\left(..., ...\right)
        """
        from sage.misc.latex import latex
        return "C_{{\\bullet}}\\left({}, {}\\right)".format(
            latex(self._A), latex(self._M))

    def algebra(self):
        """
        Return the defining algebra of ``self``.

        EXAMPLES::

            sage: SGA = SymmetricGroupAlgebra(QQ, 3)
            sage: T = SGA.trivial_representation()
            sage: H = SGA.hochschild_complex(T)
            sage: H.algebra()
            Symmetric group algebra of order 3 over Rational Field
        """
        return self._A

    def coefficients(self):
        """
        Return the coefficients of ``self``.

        EXAMPLES::

            sage: SGA = SymmetricGroupAlgebra(QQ, 3)
            sage: T = SGA.trivial_representation()
            sage: H = SGA.hochschild_complex(T)
            sage: H.coefficients()
            Trivial representation of Standard permutations of 3 over Rational Field
        """
        return self._M

    def module(self, d):
        """
        Return the module in degree ``d``.

        EXAMPLES::

            sage: SGA = SymmetricGroupAlgebra(QQ, 3)
            sage: T = SGA.trivial_representation()
            sage: H = SGA.hochschild_complex(T)
            sage: H.module(0)
            Trivial representation of Standard permutations of 3 over Rational Field
            sage: H.module(1)
            Trivial representation of Standard permutations of 3 over Rational Field
             # Symmetric group algebra of order 3 over Rational Field
            sage: H.module(2)
            Trivial representation of Standard permutations of 3 over Rational Field
             # Symmetric group algebra of order 3 over Rational Field
             # Symmetric group algebra of order 3 over Rational Field
        """
        if d < 0:
            raise ValueError("only defined for non-negative degree")
        return tensor([self._M] + [self._A] * d)

    free_module = deprecated_function_alias(21386, module)

    @cached_method
    def trivial_module(self):
        """
        Return the trivial module of ``self``.

        EXAMPLES::

            sage: E.<x,y> = ExteriorAlgebra(QQ)
            sage: H = E.hochschild_complex(E)
            sage: H.trivial_module()
            Free module generated by {} over Rational Field
        """
        return CombinatorialFreeModule(self._A.base_ring(), [])

    def boundary(self, d):
        """
        Return the boundary operator in degree ``d``.

        EXAMPLES::

            sage: E.<x,y> = ExteriorAlgebra(QQ)
            sage: H = E.hochschild_complex(E)
            sage: d1 = H.boundary(1)
            sage: z = d1.domain().an_element(); z
            2*1 # 1 + 2*1 # x + 3*1 # y
            sage: d1(z)
            0
            sage: d1.matrix()
            [ 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0]
            [ 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0]
            [ 0  0  0  0  0  0  0  0  0  0  0  0  0  0  0  0]
            [ 0  0  0  0  0  0  2  0  0 -2  0  0  0  0  0  0]

            sage: s = SymmetricFunctions(QQ).s()
            sage: H = s.hochschild_complex(s)
            sage: d1 = H.boundary(1)
            sage: x = d1.domain().an_element(); x
            2*s[] # s[] + 2*s[] # s[1] + 3*s[] # s[2]
            sage: d1(x)
            0
            sage: y = tensor([s.an_element(), s.an_element()])
            sage: d1(y)
            0
            sage: z = tensor([s[2,1] + s[3], s.an_element()])
            sage: d1(z)
            0

        TESTS::

            sage: def test_complex(H, n):
            ....:     phi = H.boundary(n)
            ....:     psi = H.boundary(n+1)
            ....:     comp = phi * psi
            ....:     zero = H.module(n-1).zero()
            ....:     return all(comp(b) == zero for b in H.module(n+1).basis())

            sage: SGA = SymmetricGroupAlgebra(QQ, 3)
            sage: H = SGA.hochschild_complex(SGA)
            sage: test_complex(H, 1)
            True
            sage: test_complex(H, 2)
            True
            sage: test_complex(H, 3) # long time
            True

            sage: E.<x,y> = ExteriorAlgebra(QQ)
            sage: H = E.hochschild_complex(E)
            sage: test_complex(H, 1)
            True
            sage: test_complex(H, 2)
            True
            sage: test_complex(H, 3)
            True
        """
        R = self._A.base_ring()
        one = R.one()
        if d == 0:
            t = self.trivial_module()
            zero = t.zero()
            return self.module(0).module_morphism(lambda x: zero, codomain=t)
        Fd = self.module(d - 1)
        Fd1 = self.module(d)
        mone = -one

        def on_basis(k):
            p = self._M.monomial(k[0]) * self._A.monomial(k[1])
            ret = Fd._from_dict({(m, ) + k[2:]: c
                                 for m, c in p},
                                remove_zeros=False)
            for i in range(1, d):
                p = self._A.monomial(k[i]) * self._A.monomial(k[i + 1])
                ret += mone**i * Fd._from_dict(
                    {k[:i] + (m, ) + k[i + 2:]: c
                     for m, c in p},
                    remove_zeros=False)
            p = self._A.monomial(k[-1]) * self._M.monomial(k[0])
            ret += mone**d * Fd._from_dict({(m, ) + k[1:-1]: c
                                            for m, c in p},
                                           remove_zeros=False)
            return ret

        return Fd1.module_morphism(on_basis, codomain=Fd)

    def coboundary(self, d):
        """
        Return the coboundary morphism of degree ``d``.

        EXAMPLES::

            sage: E.<x,y> = ExteriorAlgebra(QQ)
            sage: H = E.hochschild_complex(E)
            sage: del1 = H.coboundary(1)
            sage: z = del1.domain().an_element(); z
            2 + 2*x + 3*y
            sage: del1(z)
            0
            sage: del1.matrix()
            [ 0  0  0  0]
            [ 0  0  0  0]
            [ 0  0  0  0]
            [ 0  0  0  0]
            [ 0  0  0  0]
            [ 0  0  0  0]
            [ 0  0  0  2]
            [ 0  0  0  0]
            [ 0  0  0  0]
            [ 0  0  0 -2]
            [ 0  0  0  0]
            [ 0  0  0  0]
            [ 0  0  0  0]
            [ 0  0  0  0]
            [ 0  0  0  0]
            [ 0  0  0  0]

        TESTS::

            sage: def test_complex(H, n):
            ....:     phi = H.coboundary(n)
            ....:     psi = H.coboundary(n+1)
            ....:     comp = psi * phi
            ....:     zero = H.module(n+1).zero()
            ....:     return all(comp(b) == zero for b in H.module(n-1).basis())

            sage: SGA = SymmetricGroupAlgebra(QQ, 3)
            sage: H = SGA.hochschild_complex(SGA)
            sage: test_complex(H, 1)
            True
            sage: test_complex(H, 2)
            True

            sage: E.<x,y> = ExteriorAlgebra(QQ)
            sage: H = E.hochschild_complex(E)
            sage: test_complex(H, 1)
            True
            sage: test_complex(H, 2)
            True
            sage: test_complex(H, 3)
            True
        """
        if self._A.category() is not self._A.category().FiniteDimensional():
            raise NotImplementedError("the algebra must be finite dimensional")
        bdry = self.boundary(d)
        dom = bdry.domain()
        cod = bdry.codomain()
        return cod.module_morphism(matrix=bdry.matrix().transpose(),
                                   codomain=dom)

    def homology(self, d):
        r"""
        Return the ``d``-th homology group.

        EXAMPLES::

            sage: E.<x,y> = ExteriorAlgebra(QQ)
            sage: H = E.hochschild_complex(E)
            sage: H.homology(0)
            Vector space of dimension 3 over Rational Field
            sage: H.homology(1)
            Vector space of dimension 4 over Rational Field
            sage: H.homology(2)
            Vector space of dimension 6 over Rational Field

            sage: SGA = SymmetricGroupAlgebra(QQ, 3)
            sage: T = SGA.trivial_representation()
            sage: H = SGA.hochschild_complex(T)
            sage: H.homology(0)
            Vector space of dimension 1 over Rational Field
            sage: H.homology(1)
            Vector space of dimension 0 over Rational Field
            sage: H.homology(2)
            Vector space of dimension 0 over Rational Field

        When working over general rings (except `\ZZ`) and we can
        construct a unitriangular basis for the image quotient,
        we fallback to a slower implementation using (combinatorial)
        free modules::

            sage: R.<x,y> = QQ[]
            sage: SGA = SymmetricGroupAlgebra(R, 2)
            sage: T = SGA.trivial_representation()
            sage: H = SGA.hochschild_complex(T)
            sage: H.homology(1)
            Free module generated by {} over Multivariate Polynomial Ring in x, y over Rational Field
        """
        if self._A.category() is not self._A.category().FiniteDimensional():
            raise NotImplementedError("the algebra must be finite dimensional")

        maps = {
            d: self.boundary(d).matrix(),
            d + 1: self.boundary(d + 1).matrix()
        }
        C = ChainComplex(maps, degree_of_differential=-1)
        try:
            return C.homology(d)
        except NotImplementedError:
            pass
        # Fallback if we are not working over a field or \ZZ
        bdry = self.boundary(d)
        bdry1 = self.boundary(d + 1)
        ker = bdry.kernel()
        im_retract = ker.submodule(
            [ker.retract(b) for b in bdry1.image_basis()], unitriangular=True)
        return ker.quotient_module(im_retract)

    def cohomology(self, d):
        r"""
        Return the ``d``-th cohomology group.

        EXAMPLES::

            sage: E.<x,y> = ExteriorAlgebra(QQ)
            sage: H = E.hochschild_complex(E)
            sage: H.cohomology(0)
            Vector space of dimension 3 over Rational Field
            sage: H.cohomology(1)
            Vector space of dimension 4 over Rational Field
            sage: H.cohomology(2)
            Vector space of dimension 6 over Rational Field

            sage: SGA = SymmetricGroupAlgebra(QQ, 3)
            sage: T = SGA.trivial_representation()
            sage: H = SGA.hochschild_complex(T)
            sage: H.cohomology(0)
            Vector space of dimension 1 over Rational Field
            sage: H.cohomology(1)
            Vector space of dimension 0 over Rational Field
            sage: H.cohomology(2)
            Vector space of dimension 0 over Rational Field

        When working over general rings (except `\ZZ`) and we can
        construct a unitriangular basis for the image quotient,
        we fallback to a slower implementation using (combinatorial)
        free modules::

            sage: R.<x,y> = QQ[]
            sage: SGA = SymmetricGroupAlgebra(R, 2)
            sage: T = SGA.trivial_representation()
            sage: H = SGA.hochschild_complex(T)
            sage: H.cohomology(1)
            Free module generated by {} over Multivariate Polynomial Ring in x, y over Rational Field
        """
        if self._A.category() is not self._A.category().FiniteDimensional():
            raise NotImplementedError("the algebra must be finite dimensional")

        maps = {
            d + 1: self.coboundary(d + 1).matrix(),
            d: self.coboundary(d).matrix()
        }
        C = ChainComplex(maps, degree_of_differential=1)
        try:
            return C.homology(d + 1)
        except NotImplementedError:
            pass
        # Fallback if we are not working over a field or \ZZ
        cb = self.coboundary(d)
        cb1 = self.coboundary(d + 1)
        ker = cb1.kernel()
        im_retract = ker.submodule([ker.retract(b) for b in cb.image_basis()],
                                   unitriangular=True)
        return ker.quotient_module(im_retract)

    def _element_constructor_(self, vectors):
        """
        Construct an element of ``self`` from ``vectors``.

        TESTS::

            sage: E.<x,y> = ExteriorAlgebra(QQ)
            sage: H = E.hochschild_complex(E)
            sage: H(0)
            Trivial chain
            sage: H(2)
            Chain(0: 2)
            sage: H(x+2*y)
            Chain(0: x + 2*y)
            sage: H({0: H.module(0).an_element()})
            Chain(0: 2 + 2*x + 3*y)
            sage: H({2: H.module(2).an_element()})
            Chain(2: 2*1 # 1 # 1 + 2*1 # 1 # x + 3*1 # 1 # y)
            sage: H({0:x-y, 2: H.module(2).an_element()})
            Chain with 2 nonzero terms over Rational Field
            sage: H([2])
            Traceback (most recent call last):
            ...
            ValueError: cannot construct an element from [2]
        """
        if not vectors:  # special case: the zero chain
            return self.element_class(self, {})
        # special case: an element of the defining module
        if self._M.has_coerce_map_from(parent(vectors)):
            vectors = self._M(vectors)
        if parent(vectors) is self._M:
            mc = vectors.monomial_coefficients(copy=False)
            vec = self.module(0)._from_dict({(k, ): mc[k] for k in mc})
            return self.element_class(self, {0: vec})
        if isinstance(vectors, (Chain_class, self.element_class)):
            vectors = vectors._vec
        data = dict()
        if not isinstance(vectors, dict):
            raise ValueError(
                "cannot construct an element from {}".format(vectors))
        # Special handling for the 0 free module
        # FIXME: Allow coercions between the 0 free module and the defining module
        if 0 in vectors:
            vec = vectors.pop(0)
            if parent(vec) is self._M:
                mc = vec.monomial_coefficients(copy=False)
                data[0] = self.module(0)._from_dict({(k, ): mc[k] for k in mc})
            else:
                data[0] = self.module(0)(vec)
        for degree in vectors:
            vec = self.module(degree)(vectors[degree])
            if not vec:
                continue
            data[degree] = vec
        return self.element_class(self, data)

    def _an_element_(self):
        """
        Return an element of ``self``.

        EXAMPLES::

            sage: F.<x,y> = FreeAlgebra(ZZ)
            sage: H = F.hochschild_complex(F)
            sage: v = H.an_element()
            sage: [v.vector(i) for i in range(6)]
            [2*F[1] + 2*F[x] + 3*F[y],
             2*F[1] # F[1] + 2*F[1] # F[x] + 3*F[1] # F[y],
             2*F[1] # F[1] # F[1] + 2*F[1] # F[1] # F[x] + 3*F[1] # F[1] # F[y],
             2*F[1] # F[1] # F[1] # F[1] + 2*F[1] # F[1] # F[1] # F[x]
                + 3*F[1] # F[1] # F[1] # F[y],
             0,
             0]
        """
        return self.element_class(
            self, {d: self.module(d).an_element()
                   for d in range(4)})

    class Element(ModuleElement):
        """
        A chain of the Hochschild complex.

        INPUT:

        Can be one of the following:

        - A dictionary whose keys are the degree and whose `d`-th
          value is an element in the degree `d` module.
        - An element in the coefficient module `M`.

        EXAMPLES::

            sage: SGA = SymmetricGroupAlgebra(QQ, 3)
            sage: T = SGA.trivial_representation()
            sage: H = SGA.hochschild_complex(T)
            sage: H(T.an_element())
            Chain(0: 2*B['v'])
            sage: H({0: T.an_element()})
            Chain(0: 2*B['v'])
            sage: H({1: H.module(1).an_element()})
            Chain(1: 2*B['v'] # [1, 2, 3] + 2*B['v'] # [1, 3, 2] + 3*B['v'] # [2, 1, 3])
            sage: H({0: H.module(0).an_element(), 3: H.module(3).an_element()})
            Chain with 2 nonzero terms over Rational Field

            sage: F.<x,y> = FreeAlgebra(ZZ)
            sage: H = F.hochschild_complex(F)
            sage: H(x + 2*y^2)
            Chain(0: F[x] + 2*F[y^2])
            sage: H({0: x*y - x})
            Chain(0: -F[x] + F[x*y])
            sage: H(2)
            Chain(0: 2*F[1])
            sage: H({0: x-y, 2: H.module(2).basis().an_element()})
            Chain with 2 nonzero terms over Integer Ring
        """
        def __init__(self, parent, vectors):
            """
            Initialize ``self``.

            EXAMPLES::

                sage: F.<x,y> = FreeAlgebra(ZZ)
                sage: H = F.hochschild_complex(F)
                sage: a = H({0: x-y, 2: H.module(2).basis().an_element()})
                sage: TestSuite(a).run()
            """
            self._vec = vectors
            ModuleElement.__init__(self, parent)

        def vector(self, degree):
            """
            Return the free module element in ``degree``.

            EXAMPLES::

                sage: F.<x,y> = FreeAlgebra(ZZ)
                sage: H = F.hochschild_complex(F)
                sage: a = H({0: x-y, 2: H.module(2).basis().an_element()})
                sage: [a.vector(i) for i in range(3)]
                [F[x] - F[y], 0, F[1] # F[1] # F[1]]
            """
            try:
                return self._vec[degree]
            except KeyError:
                return self.parent().module(degree).zero()

        def _repr_(self):
            """
            Print representation.

            EXAMPLES::

                sage: E.<x,y> = ExteriorAlgebra(QQ)
                sage: H = E.hochschild_complex(E)
                sage: H(0)
                Trivial chain
                sage: H(x+2*y)
                Chain(0: x + 2*y)
                sage: H({2: H.module(2).an_element()})
                Chain(2: 2*1 # 1 # 1 + 2*1 # 1 # x + 3*1 # 1 # y)
                sage: H({0:x-y, 2: H.module(2).an_element()})
                Chain with 2 nonzero terms over Rational Field
            """
            n = len(self._vec)
            if n == 0:
                return 'Trivial chain'

            if n == 1:
                (deg, vec), = self._vec.items()
                return 'Chain({0}: {1})'.format(deg, vec)

            return 'Chain with {0} nonzero terms over {1}'.format(
                n,
                self.parent().base_ring())

        def _ascii_art_(self):
            """
            Return an ascii art representation.

            Note that arrows go to the left so that composition of
            differentials is the usual matrix multiplication.

            EXAMPLES::

                sage: F.<x,y> = FreeAlgebra(ZZ)
                sage: H = F.hochschild_complex(F)
                sage: a = H({0: x - y,
                ....:        1: H.module(1).basis().an_element(),
                ....:        2: H.module(2).basis().an_element()})
                sage: ascii_art(a)
                   d_0           d_1         d_2             d_3
                0 <---- F  - F  <---- 1 # 1 <---- 1 # 1 # 1 <---- 0
                         x    y
            """
            from sage.typeset.ascii_art import AsciiArt, ascii_art

            if not self._vec:  # 0 chain
                return AsciiArt(['0'])

            def arrow_art(d):
                d_str = ['  d_{0}  '.format(d)]
                arrow = ' <' + '-' * (len(d_str[0]) - 3) + ' '
                d_str.append(arrow)
                return AsciiArt(d_str, baseline=0)

            result = AsciiArt(['0'])
            max_deg = max(self._vec)
            for deg in range(min(self._vec), max_deg + 1):
                A = ascii_art(self.vector(deg))
                A._baseline = A.height() // 2
                result += arrow_art(deg) + A
            return result + arrow_art(max_deg + 1) + AsciiArt(['0'])

        def _add_(self, other):
            """
            Module addition
            
            EXAMPLES::

                sage: F.<x,y> = FreeAlgebra(ZZ)
                sage: H = F.hochschild_complex(F)
                sage: a = H({0: x - y,
                ....:        1: H.module(1).basis().an_element(),
                ....:        2: H.module(2).basis().an_element()})
                sage: [a.vector(i) for i in range(3)]
                [F[x] - F[y], F[1] # F[1], F[1] # F[1] # F[1]]
                sage: [H.an_element().vector(i) for i in range(3)]
                [2*F[1] + 2*F[x] + 3*F[y],
                 2*F[1] # F[1] + 2*F[1] # F[x] + 3*F[1] # F[y],
                 2*F[1] # F[1] # F[1] + 2*F[1] # F[1] # F[x] + 3*F[1] # F[1] # F[y]]

                sage: v = a + H.an_element()
                sage: [v.vector(i) for i in range(3)]
                [2*F[1] + 3*F[x] + 2*F[y],
                 3*F[1] # F[1] + 2*F[1] # F[x] + 3*F[1] # F[y],
                 3*F[1] # F[1] # F[1] + 2*F[1] # F[1] # F[x] + 3*F[1] # F[1] # F[y]]
            """
            vectors = dict(self._vec)  # Make a (shallow) copy
            for d in other._vec:
                if d in vectors:
                    vectors[d] += other._vec[d]
                    if not vectors[d]:
                        del vectors[d]
                else:
                    vectors[d] = other._vec
            parent = self.parent()
            return parent.element_class(parent, vectors)

        def _lmul_(self, scalar):
            """
            Scalar multiplication

            EXAMPLES::

                sage: F.<x,y> = FreeAlgebra(ZZ)
                sage: H = F.hochschild_complex(F)
                sage: a = H({0: x - y,
                ....:        1: H.module(1).basis().an_element(),
                ....:        2: H.module(2).basis().an_element()})
                sage: v = 3*a
                sage: [v.vector(i) for i in range(3)]
                [3*F[x] - 3*F[y], 3*F[1] # F[1], 3*F[1] # F[1] # F[1]]
            """
            if scalar == 0:
                return self.zero()
            vectors = dict()
            for d in self._vec:
                vec = scalar * self._vec[d]
                if vec:
                    vectors[d] = vec
            return self.__class__(self.parent(), vectors)

        def _richcmp_(self, other, op):
            """
            Rich comparison of ``self`` to ``other``.

            EXAMPLES::

                sage: F.<x,y> = FreeAlgebra(ZZ)
                sage: H = F.hochschild_complex(F)
                sage: a = H({0: x - y,
                ....:        1: H.module(1).basis().an_element(),
                ....:        2: H.module(2).basis().an_element()})
                sage: a == 3*a
                False
                sage: a + a == 2*a
                True
                sage: a == H.zero()
                False

                sage: a != 3*a
                True
                sage: a + a != 2*a
                False
                sage: a != H.zero()
                True
            """
            return richcmp(self._vec, other._vec, op)
Beispiel #29
0
from sage.combinat.designs.latin_squares import mutually_orthogonal_latin_squares

from sage.combinat.designs.orthogonal_arrays import transversal_design, incomplete_orthogonal_array

from sage.combinat.designs.difference_family import difference_family
from difference_matrices import difference_matrix

from sage.combinat.designs.incidence_structures import IncidenceStructure
Hypergraph = BlockDesign = IncidenceStructure  # just an alias
from sage.combinat.designs.bibd import balanced_incomplete_block_design, steiner_triple_system
from sage.combinat.designs.resolvable_bibd import resolvable_balanced_incomplete_block_design, kirkman_triple_system
from sage.combinat.designs.group_divisible_designs import group_divisible_design

# deprecated in june 2014 (#16446)
from sage.misc.superseded import deprecated_function_alias, deprecated_callable_import
BalancedIncompleteBlockDesign = deprecated_function_alias(
    16446, balanced_incomplete_block_design)

from orthogonal_arrays import OAMainFunctions as orthogonal_arrays

# When this deprecated function is removed, remove the handling of k=None in the
# function orthogonal_arrays.orthogonal_array()
deprecated_callable_import(
    17034, 'sage.combinat.designs.orthogonal_arrays', globals(), locals(),
    ["orthogonal_array"],
    ("This function will soon be removed. Use the designs.orthogonal_arrays.* functions instead"
     ))

# We don't want this to appear in designs.<tab>
del deprecated_function_alias
del deprecated_callable_import
Beispiel #30
0
        p = Partition(shape).conjugate()
        # The column canonical tableau, read by columns
        module_generator = flatten([[val-i for i in range(val)] for val in p])
        if invert:
            module_generator = [(-x if x == type[1] else x) for x in module_generator]
        return self(list=[self.letters(x) for x in module_generator])

    def _element_constructor_(self, *args, **options):
        """
        Return a
        :class:`~sage.combinat.crystals.tensor_product.CrystalOfTableauxElement`.

        EXAMPLES::

            sage: T = crystals.Tableaux(['A',3], shape = [2,2])
            sage: T(rows=[[1,2],[3,4]])
            [[1, 2], [3, 4]]
            sage: T(columns=[[3,1],[4,2]])
            [[1, 2], [3, 4]]
        """
        return self.element_class(self, *args, **options)

    class Element(CrystalOfTableauxElement):
        pass

# deprecations from trac:18555
from sage.misc.superseded import deprecated_function_alias
TensorProductOfCrystals.global_options=deprecated_function_alias(18555, TensorProductOfCrystals.options)
TensorProductOfCrystalsOptions=deprecated_function_alias(18555, TensorProductOfCrystals.options)

Beispiel #31
0
            sage: from sage.schemes.generic.spec import SpecFunctor
            sage: F = SpecFunctor(GF(7))
            sage: A.<x, y> = GF(7)[]
            sage: B.<t> = GF(7)[]
            sage: f = A.hom((t^2, t^3))
            sage: Spec(f) # indirect doctest
            Affine Scheme morphism:
              From: Spectrum of Univariate Polynomial Ring in t over Finite Field of size 7
              To:   Spectrum of Multivariate Polynomial Ring in x, y over Finite Field of size 7
              Defn: Ring morphism:
                     From: Multivariate Polynomial Ring in x, y over Finite Field of size 7
                     To:   Univariate Polynomial Ring in t over Finite Field of size 7
                     Defn: x |--> t^2
                           y |--> t^3
        """
        A = f.domain()
        B = f.codomain()
        return self(B).hom(f, self(A))


SpecZ = Spec(ZZ)


# Compatibility with older versions of this module

from sage.misc.superseded import deprecated_function_alias
is_Spec = deprecated_function_alias(16158, is_AffineScheme)

from sage.structure.sage_object import register_unpickle_override
register_unpickle_override('sage.schemes.generic.spec', 'Spec', AffineScheme)
Beispiel #32
0
        if invert:
            module_generator = [(-x if x == type[1] else x)
                                for x in module_generator]
        return self(list=[self.letters(x) for x in module_generator])

    def _element_constructor_(self, *args, **options):
        """
        Return a
        :class:`~sage.combinat.crystals.tensor_product.CrystalOfTableauxElement`.

        EXAMPLES::

            sage: T = crystals.Tableaux(['A',3], shape = [2,2])
            sage: T(rows=[[1,2],[3,4]])
            [[1, 2], [3, 4]]
            sage: T(columns=[[3,1],[4,2]])
            [[1, 2], [3, 4]]
        """
        return self.element_class(self, *args, **options)

    class Element(CrystalOfTableauxElement):
        pass


# deprecations from trac:18555
from sage.misc.superseded import deprecated_function_alias
TensorProductOfCrystals.global_options = deprecated_function_alias(
    18555, TensorProductOfCrystals.options)
TensorProductOfCrystalsOptions = deprecated_function_alias(
    18555, TensorProductOfCrystals.options)
Beispiel #33
0
class Cusps_class(ParentWithBase):
    """
    The set of cusps.

    EXAMPLES::

        sage: C = Cusps; C
        Set P^1(QQ) of all cusps
        sage: loads(C.dumps()) == C
        True
    """
    def __init__(self):
        r"""
        The set of cusps, i.e. `\mathbb{P}^1(\QQ)`.

        EXAMPLES::

            sage: C = sage.modular.cusps.Cusps_class() ; C
            Set P^1(QQ) of all cusps
            sage: Cusps == C
            True
        """
        ParentWithBase.__init__(self, self)

    def __eq__(self, right):
        """
        Return equality only if ``right`` is the set of cusps.

        EXAMPLES::

            sage: Cusps == Cusps
            True
            sage: Cusps == QQ
            False
        """
        return isinstance(right, Cusps_class)

    def __ne__(self, right):
        """
        Check that ``self`` is not equal to ``right``.

        EXAMPLES::

            sage: Cusps != Cusps
            False
            sage: Cusps != QQ
            True
        """
        return not (self == right)

    def _repr_(self):
        """
        String representation of the set of cusps.

        EXAMPLES::

            sage: Cusps
            Set P^1(QQ) of all cusps
            sage: Cusps._repr_()
            'Set P^1(QQ) of all cusps'
            sage: Cusps.rename('CUSPS'); Cusps
            CUSPS
            sage: Cusps.rename(); Cusps
            Set P^1(QQ) of all cusps
            sage: Cusps
            Set P^1(QQ) of all cusps
        """
        return "Set P^1(QQ) of all cusps"

    def _latex_(self):
        """
        Return latex representation of self.

        EXAMPLES::

            sage: latex(Cusps)
            \mathbf{P}^1(\QQ)
            sage: latex(Cusps) == Cusps._latex_()
            True
        """
        return "\\mathbf{P}^1(\\QQ)"

    def __call__(self, x):
        """
        Coerce x into the set of cusps.

        EXAMPLES::

            sage: a = Cusps(-4/5); a
            -4/5
            sage: Cusps(a) is a
            False
            sage: Cusps(1.5)
            3/2
            sage: Cusps(oo)
            Infinity
            sage: Cusps(I)
            Traceback (most recent call last):
            ...
            TypeError: unable to convert I to a cusp
        """
        return Cusp(x, parent=self)

    def _coerce_impl(self, x):
        """
        Canonical coercion of x into the set of cusps.

        EXAMPLES::

            sage: Cusps._coerce_(7/13)
            7/13
            sage: Cusps._coerce_(GF(7)(3))
            Traceback (most recent call last):
            ...
            TypeError: no canonical coercion of element into self
            sage: Cusps(GF(7)(3))
            3
            sage: Cusps._coerce_impl(GF(7)(3))
            Traceback (most recent call last):
            ...
            TypeError: no canonical coercion of element into self
        """
        if is_Infinite(x):
            return Cusp(x, parent=self)
        else:
            return self._coerce_try(x, QQ)

    @cached_method
    def zero(self):
        """
        Return the zero cusp.

        .. NOTE::

            The existence of this method is assumed by some
            parts of Sage's coercion model.

        EXAMPLES::

            sage: Cusps.zero()
            0
        """
        return Cusp(0, parent=self)

    zero_element = deprecated_function_alias(17694, zero)
Beispiel #34
0
class IncidenceAlgebra(CombinatorialFreeModule):
    r"""
    The incidence algebra of a poset.

    Let `P` be a poset and `R` be a commutative unital associative ring.
    The *incidence algebra* `I_P` is the algebra of functions
    `\alpha \colon P \times P \to R` such that `\alpha(x, y) = 0`
    if `x \not\leq y` where multiplication is given by convolution:

    .. MATH::

        (\alpha \ast \beta)(x, y) = \sum_{x \leq k \leq y}
        \alpha(x, k) \beta(k, y).

    This has a natural basis given by indicator functions for the
    interval `[a, b]`, i.e. `X_{a,b}(x,y) = \delta_{ax} \delta_{by}`.
    The incidence algebra is a unital algebra with the identity given
    by the Kronecker delta `\delta(x, y) = \delta_{xy}`. The Möbius
    function of `P` is another element of `I_p` whose inverse is the
    `\zeta` function of the poset (so `\zeta(x, y) = 1` for
    every interval `[x, y]`).

    .. TODO::

        Implement the incidence coalgebra.

    REFERENCES:

    - :wikipedia:`Incidence_algebra`
    """
    def __init__(self, R, P, prefix='I'):
        """
        Initialize ``self``.

        TESTS::

            sage: P = posets.BooleanLattice(4)
            sage: I = P.incidence_algebra(QQ)
            sage: TestSuite(I).run()  # long time
        """
        cat = Algebras(R).WithBasis()
        if P in FiniteEnumeratedSets():
            cat = cat.FiniteDimensional()
        self._poset = P
        CombinatorialFreeModule.__init__(self,
                                         R,
                                         map(tuple, P.relations()),
                                         prefix=prefix,
                                         category=cat)

    def _repr_term(self, A):
        """
        Return a string representation of the term labeled by ``A``.

        EXAMPLES::

            sage: P = posets.BooleanLattice(4)
            sage: I = P.incidence_algebra(QQ)
            sage: I._repr_term((4, 12))
            'I[4, 12]'
        """
        return self.prefix() + str(list(A))

    def _repr_(self):
        r"""
        Return a string representation of ``self``.

        EXAMPLES::

            sage: P = posets.BooleanLattice(4)
            sage: P.incidence_algebra(QQ)
            Incidence algebra of Finite lattice containing 16 elements
             over Rational Field
        """
        return "Incidence algebra of {} over {}".format(
            self._poset, self.base_ring())

    def _coerce_map_from_(self, R):
        """
        Return the coerce map from ``R`` into ``self`` if it exists
        or ``False`` otherwise.

        EXAMPLES::

            sage: P = posets.BooleanLattice(4)
            sage: I = P.incidence_algebra(QQ)
            sage: R = I.reduced_subalgebra()
            sage: I.has_coerce_map_from(R)
            True
            sage: I.has_coerce_map_from(QQ)
            True
            sage: Pp = posets.BooleanLattice(3)
            sage: Rp = Pp.incidence_algebra(QQ).reduced_subalgebra()
            sage: I.has_coerce_map_from(Rp)
            False
        """
        if isinstance(R, ReducedIncidenceAlgebra) and R._ambient is self:
            return copy(R.lift)
        return super(IncidenceAlgebra, self)._coerce_map_from_(R)

    def reduced_subalgebra(self, prefix='R'):
        """
        Return the reduced incidence subalgebra.

        EXAMPLES::

            sage: P = posets.BooleanLattice(4)
            sage: I = P.incidence_algebra(QQ)
            sage: I.reduced_subalgebra()
            Reduced incidence algebra of Finite lattice containing 16 elements
             over Rational Field
        """
        return ReducedIncidenceAlgebra(self, prefix)

    def poset(self):
        """
        Return the defining poset of ``self``.

        EXAMPLES::

            sage: P = posets.BooleanLattice(4)
            sage: I = P.incidence_algebra(QQ)
            sage: I.poset()
            Finite lattice containing 16 elements
            sage: I.poset() == P
            True
        """
        return self._poset

    def some_elements(self):
        """
        Return a list of elements of ``self``.

        EXAMPLES::

            sage: P = posets.BooleanLattice(1)
            sage: I = P.incidence_algebra(QQ)
            sage: I.some_elements()
            [2*I[0, 0] + 2*I[0, 1] + 3*I[1, 1],
             I[0, 0] - I[0, 1] + I[1, 1],
             I[0, 0] + I[0, 1] + I[1, 1]]
        """
        return [self.an_element(), self.moebius(), self.zeta()]

    def product_on_basis(self, A, B):
        r"""
        Return the product of basis elements indexed by ``A`` and ``B``.

        EXAMPLES::

            sage: P = posets.BooleanLattice(4)
            sage: I = P.incidence_algebra(QQ)
            sage: I.product_on_basis((1, 3), (3, 11))
            I[1, 11]
            sage: I.product_on_basis((1, 3), (2, 2))
            0
        """
        if A[1] == B[0]:
            return self.monomial((A[0], B[1]))
        return self.zero()

    @cached_method
    def one(self):
        """
        Return the element `1` in ``self`` (which is the Kronecker
        delta `\delta(x, y)`).

        EXAMPLES::

            sage: P = posets.BooleanLattice(4)
            sage: I = P.incidence_algebra(QQ)
            sage: I.one()
            I[0, 0] + I[1, 1] + I[2, 2] + I[3, 3] + I[4, 4] + I[5, 5]
             + I[6, 6] + I[7, 7] + I[8, 8] + I[9, 9] + I[10, 10]
             + I[11, 11] + I[12, 12] + I[13, 13] + I[14, 14] + I[15, 15]
        """
        return self.sum_of_monomials((x, x) for x in self._poset)

    delta = one

    @cached_method
    def zeta(self):
        r"""
        Return the `\zeta` function in ``self``.

        The `\zeta` function on a poset `P` is given by

        .. MATH::

            \zeta(x, y) = \begin{cases}
            1 & x \leq y, \\
            0 & x \not\leq y.
            \end{cases}

        EXAMPLES::

            sage: P = posets.BooleanLattice(4)
            sage: I = P.incidence_algebra(QQ)
            sage: I.zeta() * I.moebius() == I.one()
            True
        """
        return self.sum(self.basis())

    from sage.misc.superseded import deprecated_function_alias

    @cached_method
    def moebius(self):
        """
        Return the Möbius function of ``self``.

        EXAMPLES::

            sage: P = posets.BooleanLattice(2)
            sage: I = P.incidence_algebra(QQ)
            sage: I.moebius()
            I[0, 0] - I[0, 1] - I[0, 2] + I[0, 3] + I[1, 1]
             - I[1, 3] + I[2, 2] - I[2, 3] + I[3, 3]
        """
        mu = self._poset.moebius_function
        R = self.base_ring()
        return self.sum_of_terms((A, R(mu(*A))) for A in self.basis().keys())

    mobius = deprecated_function_alias(19855, moebius)

    def __getitem__(self, A):
        """
        Return the basis element indexed by ``A``.

        EXAMPLES::

            sage: P = posets.BooleanLattice(4)
            sage: I = P.incidence_algebra(QQ)
            sage: I[2, 6]
            I[2, 6]
            sage: I[(2, 6)]
            I[2, 6]
            sage: I[[2, 6]]
            I[2, 6]
            sage: I[2]
            I[2, 2]

            sage: I[2, 5]
            Traceback (most recent call last):
            ...
            ValueError: not an interval

            sage: I[-1]
            Traceback (most recent call last):
            ...
            ValueError: not an element of the poset
        """
        if not isinstance(A, (list, tuple)):
            if A not in self._poset.list():
                raise ValueError("not an element of the poset")
            A = (A, A)
        else:
            A = tuple(A)
            if len(A) != 2 or A not in self.basis().keys():
                raise ValueError("not an interval")
        return self.monomial(A)

    @lazy_attribute
    def _linear_extension(self):
        """
        Return a fixed linear extension of the defining poset of ``self``.

        EXAMPLES::

            sage: P = posets.BooleanLattice(4)
            sage: I = P.incidence_algebra(QQ)
            sage: I._linear_extension
            (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
        """
        return tuple(self._poset.linear_extension())

    class Element(CombinatorialFreeModule.Element):
        """
        An element of an incidence algebra.
        """
        def __call__(self, x, y):
            """
            Return ``self(x, y)``.

            EXAMPLES::

                sage: P = posets.BooleanLattice(4)
                sage: I = P.incidence_algebra(QQ)
                sage: mu = I.moebius()
                sage: mu(0, 12)
                1
                sage: mu(0, 7)
                -1
                sage: mu(15, 0)
                0
            """
            P = self.parent()._poset
            x = P(x)
            y = P(y)
            return self[x, y]

        @cached_method
        def to_matrix(self):
            r"""
            Return ``self`` as a matrix.

            We define a matrix `M_{xy} = \alpha(x, y)` for some element
            `\alpha \in I_P` in the incidence algebra `I_P` and we order
            the elements `x,y \in P` by some linear extension of `P`. This
            defines an algebra (iso)morphism; in particular, multiplication
            in the incidence algebra goes to matrix multiplication.

            EXAMPLES::

                sage: P = posets.BooleanLattice(2)
                sage: I = P.incidence_algebra(QQ)
                sage: I.moebius().to_matrix()
                [ 1 -1 -1  1]
                [ 0  1  0 -1]
                [ 0  0  1 -1]
                [ 0  0  0  1]
                sage: I.zeta().to_matrix()
                [1 1 1 1]
                [0 1 0 1]
                [0 0 1 1]
                [0 0 0 1]

            TESTS:

            We check that this is an algebra (iso)morphism::

                sage: P = posets.BooleanLattice(4)
                sage: I = P.incidence_algebra(QQ)
                sage: mu = I.moebius()
                sage: (mu*mu).to_matrix() == mu.to_matrix() * mu.to_matrix()
                True
            """
            P = self.parent()
            MS = MatrixSpace(P.base_ring(),
                             P._poset.cardinality(),
                             sparse=True)
            L = P._linear_extension
            M = copy(MS.zero())
            for i, c in self:
                M[L.index(i[0]), L.index(i[1])] = c
            M.set_immutable()
            return M

        def is_unit(self):
            """
            Return if ``self`` is a unit.

            EXAMPLES::

                sage: P = posets.BooleanLattice(2)
                sage: I = P.incidence_algebra(QQ)
                sage: mu = I.moebius()
                sage: mu.is_unit()
                True
                sage: zeta = I.zeta()
                sage: zeta.is_unit()
                True
                sage: x = mu - I.zeta() + I[2,2]
                sage: x.is_unit()
                False
                sage: y = I.moebius() + I.zeta()
                sage: y.is_unit()
                True

            This depends on the base ring::

                sage: I = P.incidence_algebra(ZZ)
                sage: y = I.moebius() + I.zeta()
                sage: y.is_unit()
                False
            """
            return all(self[x, x].is_unit() for x in self.parent()._poset)

        def __invert__(self):
            """
            Return the inverse of ``self`` if it exists.

            EXAMPLES::

                sage: P = posets.BooleanLattice(2)
                sage: I = P.incidence_algebra(QQ)
                sage: mu = I.moebius()
                sage: ~mu
                I[0, 0] + I[0, 1] + I[0, 2] + I[0, 3] + I[1, 1]
                 + I[1, 3] + I[2, 2] + I[2, 3] + I[3, 3]
                sage: x = mu - I.zeta() + I[2,2]
                sage: ~x
                Traceback (most recent call last):
                ...
                ValueError: element is not invertible

            TESTS::

                sage: P = posets.BooleanLattice(4)
                sage: I = P.incidence_algebra(QQ)
                sage: mu = I.moebius()
                sage: ~mu == I.zeta()
                True
                sage: ~I.one() == I.one()
                True
            """
            M = self.to_matrix()
            if not M.is_invertible():
                raise ValueError("element is not invertible")
            inv = ~M
            L = self.parent()._linear_extension
            return self.parent().sum_of_terms(
                ((L[i], L[j]), inv[i, j])
                for i, j in inv.nonzero_positions(copy=False))
Beispiel #35
0
class NFCuspsSpace(ParentWithBase):
    """
    The set of cusps of a number field. See ``NFCusps`` for full documentation.

    EXAMPLES::

        sage: k.<a> = NumberField(x^2 + 5)
        sage: kCusps = NFCusps(k); kCusps
        Set of all cusps of Number Field in a with defining polynomial x^2 + 5
    """
    def __init__(self, number_field):
        """
        See ``NFCusps`` for full documentation.

        EXAMPLES::

            sage: k.<a> = NumberField(x^3 + x^2 + 13)
            sage: kCusps = NFCusps(k); kCusps
            Set of all cusps of Number Field in a with defining polynomial x^3 + x^2 + 13
        """
        self.__number_field = number_field
        ParentWithBase.__init__(self, self)

    def __cmp__(self, right):
        """
        Return equality only if right is the set of cusps for the same field.

        Comparing sets of cusps for two different fields gives the same
        result as comparing the two fields.

        EXAMPLES::

            sage: k.<a> = NumberField(x^2 + 5)
            sage: L.<a> = NumberField(x^2 + 23)
            sage: kCusps = NFCusps(k); kCusps
            Set of all cusps of Number Field in a with defining polynomial x^2 + 5
            sage: LCusps = NFCusps(L); LCusps
            Set of all cusps of Number Field in a with defining polynomial x^2 + 23
            sage: kCusps == NFCusps(k)
            True
            sage: LCusps == NFCusps(L)
            True
            sage: LCusps == kCusps
            False

        """
        t = cmp(type(self), type(right))
        if t:
            return t
        else:
            return cmp(self.number_field(), right.number_field())

    def _repr_(self):
        """
        String representation of the set of cusps of a number field.

        EXAMPLES::

            sage: k.<a> = NumberField(x^2 + 2)
            sage: kCusps = NFCusps(k)
            sage: kCusps
            Set of all cusps of Number Field in a with defining polynomial x^2 + 2
            sage: kCusps._repr_()
            'Set of all cusps of Number Field in a with defining polynomial x^2 + 2'
            sage: kCusps.rename('Number Field Cusps'); kCusps
            Number Field Cusps
            sage: kCusps.rename(); kCusps
            Set of all cusps of Number Field in a with defining polynomial x^2 + 2

        """
        return "Set of all cusps of %s" % (self.number_field())

    def _latex_(self):
        """
        Return latex representation of self.

        EXAMPLES::

            sage: k.<a> = NumberField(x^2 + 5)
            sage: kCusps = NFCusps(k)
            sage: latex(kCusps) # indirect doctest
            \mathbf{P}^1(\Bold{Q}[a]/(a^{2} + 5))
        """
        return "\\mathbf{P}^1(%s)" % (self.number_field()._latex_())

    def __call__(self, x):
        """
        Convert x into the set of cusps of a number field.

        EXAMPLES::

            sage: k.<a> = NumberField(x^2 + 5)
            sage: kCusps = NFCusps(k)
            sage: c = kCusps(a,2)
            Traceback (most recent call last):
            ...
            TypeError: __call__() takes exactly 2 arguments (3 given)

         ::

            sage: c = kCusps((a,2)); c
            Cusp [a: 2] of Number Field in a with defining polynomial x^2 + 5
            sage: kCusps(2/a)
            Cusp [-2*a: 5] of Number Field in a with defining polynomial x^2 + 5
            sage: kCusps(oo)
            Cusp Infinity of Number Field in a with defining polynomial x^2 + 5
        """
        return NFCusp(self.number_field(), x, parent=self)

    @cached_method
    def zero(self):
        """
        Return the zero cusp.

        NOTE:

        This method just exists to make some general algorithms work.
        It is not intended that the returned cusp is an additive
        neutral element.

        EXAMPLE::

             sage: k.<a> = NumberField(x^2 + 5)
             sage: kCusps = NFCusps(k)
             sage: kCusps.zero()
             Cusp [0: 1] of Number Field in a with defining polynomial x^2 + 5

        """
        return self(0)

    zero_element = deprecated_function_alias(17694, zero)

    def number_field(self):
        """
        Return the number field that this set of cusps is attached to.

        EXAMPLES::

            sage: k.<a> = NumberField(x^2 + 1)
            sage: kCusps = NFCusps(k)
            sage: kCusps.number_field()
            Number Field in a with defining polynomial x^2 + 1
        """
        return self.__number_field
Beispiel #36
0
    # now that we know that (S1,S2) is a partition, we look for an invertible
    # element b that maps S1 to S2 by multiplication
    for b in range(2, n):
        if GCD(b, n) == 1 and all(b * x in S2 for x in S1):
            if return_automorphism:
                return True, b
            else:
                return True
    if return_automorphism:
        return False, None
    else:
        return False


is_a_splitting = deprecated_function_alias(21165, _is_a_splitting)


def _lift2smallest_field(a):
    """
    INPUT: a is an element of a finite field GF(q)

    OUTPUT: the element b of the smallest subfield F of GF(q) for
    which F(b)=a.

    EXAMPLES::

        sage: from sage.coding.code_constructions import lift2smallest_field
        sage: FF.<z> = GF(3^4,"z")
        sage: a = z^10
        sage: lift2smallest_field(a)
Beispiel #37
0
class PerfectMatching(SetPartition):
    r"""
    A perfect matching.

    A *perfect matching* of a set `X` is a set partition of `X` where
    all parts have size 2.

    A perfect matching can be created from a list of pairs or from a
    fixed point-free involution as follows::

        sage: m = PerfectMatching([('a','e'),('b','c'),('d','f')]);m
        [('a', 'e'), ('b', 'c'), ('d', 'f')]
        sage: n = PerfectMatching([3,8,1,7,6,5,4,2]);n
        [(1, 3), (2, 8), (4, 7), (5, 6)]
        sage: isinstance(m,PerfectMatching)
        True

    The parent, which is the set of perfect matchings of the ground set, is
    automatically created::

        sage: n.parent()
        Perfect matchings of {1, 2, 3, 4, 5, 6, 7, 8}

    If the ground set is ordered, one can, for example, ask if the matching is
    non crossing::

        sage: PerfectMatching([(1, 4), (2, 3), (5, 6)]).is_noncrossing()
        True

    TESTS::

        sage: m = PerfectMatching([]); m
        []
        sage: m.parent()
        Perfect matchings of {}
    """
    @staticmethod
    def __classcall_private__(cls, parts):
        """
        Create a perfect matching from ``parts`` with the appropriate parent.

        This function tries to recognize the input (it can be either a list or
        a tuple of pairs, or a fix-point free involution given as a list or as
        a permutation), constructs the parent (enumerated set of
        PerfectMatchings of the ground set) and calls the __init__ function to
        construct our object.

        EXAMPLES::

            sage: m = PerfectMatching([('a','e'),('b','c'),('d','f')]);m
            [('a', 'e'), ('b', 'c'), ('d', 'f')]
            sage: isinstance(m, PerfectMatching)
            True
            sage: n = PerfectMatching([3, 8, 1, 7, 6, 5, 4, 2]);n
            [(1, 3), (2, 8), (4, 7), (5, 6)]
            sage: n.parent()
            Perfect matchings of {1, 2, 3, 4, 5, 6, 7, 8}
            sage: PerfectMatching([(1, 4), (2, 3), (5, 6)]).is_noncrossing()
            True

        The function checks that the given list or permutation is
        a valid perfect matching (i.e. a list of pairs with pairwise
        disjoint elements or a fix point free involution) and raises
        a ``ValueError`` otherwise::

            sage: PerfectMatching([(1, 2, 3), (4, 5)])
            Traceback (most recent call last):
            ...
            ValueError: [(1, 2, 3), (4, 5)] is not an element of
             Perfect matchings of {1, 2, 3, 4, 5}

        TESTS::

            sage: m = PerfectMatching([('a','e'),('b','c'),('d','f')])
            sage: TestSuite(m).run()
            sage: m = PerfectMatching([])
            sage: TestSuite(m).run()
            sage: PerfectMatching(6)
            Traceback (most recent call last):
            ...
            TypeError: 'sage.rings.integer.Integer' object is not iterable
            sage: PerfectMatching([(1,2,3)])
            Traceback (most recent call last):
            ...
            ValueError: [(1, 2, 3)] is not an element of
             Perfect matchings of {1, 2, 3}

            sage: PerfectMatching([(1,1)])
            Traceback (most recent call last):
            ...
            ValueError: [(1)] is not an element of Perfect matchings of {1}

            sage: PerfectMatching(Permutation([4,2,1,3]))
            Traceback (most recent call last):
            ...
            ValueError: permutation p (= [4, 2, 1, 3]) is not a
             fixed point free involution
        """
        if ((isinstance(parts, list) and all(
            (isinstance(x, (int, Integer)) for x in parts)))
                or isinstance(parts, Permutation)):
            s = Permutation(parts)
            if not all(e == 2 for e in s.cycle_type()):
                raise ValueError("permutation p (= {}) is not a "
                                 "fixed point free involution".format(s))
            parts = s.to_cycles()

        base_set = frozenset(e for p in parts for e in p)
        P = PerfectMatchings(base_set)
        return P(parts)

    def _repr_(self):
        r"""
        Return a string representation of the matching ``self``.

        EXAMPLES::

            sage: PerfectMatching([('a','e'), ('b','c'), ('d','f')])
            [('a', 'e'), ('b', 'c'), ('d', 'f')]
            sage: PerfectMatching([3,8,1,7,6,5,4,2])
            [(1, 3), (2, 8), (4, 7), (5, 6)]
        """
        return '[' + ', '.join(
            ('(' + repr(sorted(x))[1:-1] + ')' for x in self)) + ']'

    def _latex_(self):
        r"""
        A latex representation of ``self`` using the tikzpicture package.

        EXAMPLES::

            sage: P = PerfectMatching([(1,3),(2,5),(4,6)])
            sage: latex(P)  # optional - dot2tex; random
            \begin{tikzpicture}
            ...
            \end{tikzpicture}

        TESTS:

        Above we added ``random`` since warnings might be displayed
        once. The second time, there should be no warnings::

            sage: print(P._latex_())  # optional - dot2tex
            \begin{tikzpicture}
            ...
            \end{tikzpicture}

        ..TODO::

            This should probably call the latex method of
            :class:`SetPartition` with appropriate defaults.
        """
        G = self.to_graph()
        G.set_pos(G.layout_circular())
        G.set_latex_options(
            vertex_size=0.4,
            edge_thickness=0.04,
        )
        return G._latex_()

    def standardization(self):
        """
        Return the standardization of ``self``.

        See :meth:`SetPartition.standardization` for details.

        EXAMPLES::

            sage: n = PerfectMatching([('c','b'),('d','f'),('e','a')])
            sage: n.standardization()
            [(1, 5), (2, 3), (4, 6)]

        """
        P = PerfectMatchings(2 * len(self))
        return P(SetPartition.standardization(self))

    def partner(self, x):
        r"""
        Return the element in the same pair than ``x``
        in the matching ``self``.

        EXAMPLES::

            sage: m = PerfectMatching([(-3, 1), (2, 4), (-2, 7)])
            sage: m.partner(4)
            2
            sage: n = PerfectMatching([('c','b'),('d','f'),('e','a')])
            sage: n.partner('c')
            'b'
        """
        for a, b in self:
            if a == x:
                return b
            if b == x:
                return a
        raise ValueError("%s in not an element of the %s" % (x, self))

    def loops_iterator(self, other=None):
        r"""
        Iterate through the loops of ``self``.

        INPUT:

        - ``other`` -- a perfect matching of the same set of ``self``.
          (if the second argument is empty, the method :meth:`an_element` is
          called on the parent of the first)

        OUTPUT:

        If we draw the two perfect matchings simultaneously as edges of a
        graph, the graph obtained is a union of cycles of even lengths.
        The function returns an iterator for these cycles (each cycle is
        given as a list).

        EXAMPLES::

            sage: o = PerfectMatching([(1, 7), (2, 4), (3, 8), (5, 6)])
            sage: p = PerfectMatching([(1, 6), (2, 7), (3, 4), (5, 8)])
            sage: it = o.loops_iterator(p)
            sage: next(it)
            [1, 7, 2, 4, 3, 8, 5, 6]
            sage: next(it)
            Traceback (most recent call last):
            ...
            StopIteration
        """
        if other is None:
            other = self.parent().an_element()
        elif self.parent() != other.parent():
            raise ValueError("%s is not a matching of the ground set of %s" %
                             (other, self))
        remain = self.base_set().set()
        while remain:
            a = remain.pop()
            b = self.partner(a)
            remain.remove(b)
            loop = [a, b]
            c = other.partner(b)
            while c != a:
                b = self.partner(c)
                remain.remove(c)
                loop.append(c)
                remain.remove(b)
                loop.append(b)
                c = other.partner(b)
            yield loop

    def loops(self, other=None):
        r"""
        Return the loops of ``self``.

        INPUT:

        - ``other`` -- a perfect matching of the same set of ``self``.
          (if the second argument is empty, the method :meth:`an_element` is
          called on the parent of the first)

        OUTPUT:

        If we draw the two perfect matchings simultaneously as edges of a
        graph, the graph obtained is a union of cycles of even lengths.
        The function returns the list of these cycles (each cycle is given
        as a list).

        EXAMPLES::

            sage: m = PerfectMatching([('a','e'),('b','c'),('d','f')])
            sage: n = PerfectMatching([('a','b'),('d','f'),('e','c')])
            sage: m.loops(n)
            [['a', 'e', 'c', 'b'], ['d', 'f']]

            sage: o = PerfectMatching([(1, 7), (2, 4), (3, 8), (5, 6)])
            sage: p = PerfectMatching([(1, 6), (2, 7), (3, 4), (5, 8)])
            sage: o.loops(p)
            [[1, 7, 2, 4, 3, 8, 5, 6]]
        """
        return list(self.loops_iterator(other))

    def loop_type(self, other=None):
        r"""
        Return the loop type of ``self``.

        INPUT:

        - ``other`` -- a perfect matching of the same set of ``self``.
          (if the second argument is empty, the method :meth:`an_element` is
          called on the parent of the first)

        OUTPUT:

        If we draw the two perfect matchings simultaneously as edges of a
        graph, the graph obtained is a union of cycles of even
        lengths. The function returns the ordered list of the semi-length
        of these cycles (considered as a partition)

        EXAMPLES::

            sage: m = PerfectMatching([('a','e'),('b','c'),('d','f')])
            sage: n = PerfectMatching([('a','b'),('d','f'),('e','c')])
            sage: m.loop_type(n)
            [2, 1]

        TESTS::

            sage: m = PerfectMatching([]); m.loop_type()
            []
        """
        return Partition(
            sorted((len(l) // 2 for l in self.loops_iterator(other)),
                   reverse=True))

    def number_of_loops(self, other=None):
        r"""
        Return the number of loops of ``self``.

        INPUT:

        - ``other`` -- a perfect matching of the same set of ``self``.
          (if the second argument is empty, the method :meth:`an_element` is
          called on the parent of the first)

        OUTPUT:

        If we draw the two perfect matchings simultaneously as edges of a
        graph, the graph obtained is a union of cycles of even lengths.
        The function returns their numbers.

        EXAMPLES::

            sage: m = PerfectMatching([('a','e'),('b','c'),('d','f')])
            sage: n = PerfectMatching([('a','b'),('d','f'),('e','c')])
            sage: m.number_of_loops(n)
            2
        """
        return Integer(len(list(self.loops_iterator(other))))

    def Weingarten_function(self, d, other=None):
        r"""
        Return the Weingarten function of two pairings.

        This function is the value of some integrals over the orthogonal
        groups `O_N`.  With the convention of [CM]_, the method returns
        `Wg^{O(d)}(other,self)`.

        EXAMPLES::

            sage: var('N')
            N
            sage: m = PerfectMatching([(1,3),(2,4)])
            sage: n = PerfectMatching([(1,2),(3,4)])
            sage: factor(m.Weingarten_function(N,n))
            -1/((N + 2)*(N - 1)*N)
        """
        if other is None:
            other = self.parent().an_element()
        W = self.parent().Weingarten_matrix(d)
        return W[other.rank()][self.rank()]

    def to_graph(self):
        r"""
        Return the graph corresponding to the perfect matching.

        OUTPUT:

        The realization of ``self`` as a graph.

        EXAMPLES::

            sage: PerfectMatching([[1,3], [4,2]]).to_graph().edges(labels=False)
            [(1, 3), (2, 4)]
            sage: PerfectMatching([[1,4], [3,2]]).to_graph().edges(labels=False)
            [(1, 4), (2, 3)]
            sage: PerfectMatching([]).to_graph().edges(labels=False)
            []
        """
        from sage.graphs.graph import Graph
        return Graph([list(p) for p in self], format='list_of_edges')

    def to_noncrossing_set_partition(self):
        r"""
        Return the noncrossing set partition (on half as many elements)
        corresponding to the perfect matching if the perfect matching is
        noncrossing, and otherwise gives an error.

        OUTPUT:

        The realization of ``self`` as a noncrossing set partition.

        EXAMPLES::

            sage: PerfectMatching([[1,3], [4,2]]).to_noncrossing_set_partition()
            Traceback (most recent call last):
            ...
            ValueError: matching must be non-crossing
            sage: PerfectMatching([[1,4], [3,2]]).to_noncrossing_set_partition()
            {{1, 2}}
            sage: PerfectMatching([]).to_noncrossing_set_partition()
            {}
        """
        if not self.is_noncrossing():
            raise ValueError("matching must be non-crossing")
        else:
            perm = self.to_permutation()
            perm2 = Permutation(
                [perm[2 * i] // 2 for i in range(len(perm) // 2)])
        return SetPartition(perm2.cycle_tuples())

    from sage.misc.superseded import deprecated_function_alias
    to_non_crossing_set_partition = deprecated_function_alias(
        23982, to_noncrossing_set_partition)
    is_non_crossing = deprecated_function_alias(23982,
                                                SetPartition.is_noncrossing)
    is_non_nesting = deprecated_function_alias(23982,
                                               SetPartition.is_nonnesting)
    conjugate_by_permutation = deprecated_function_alias(
        23982, SetPartition.apply_permutation)
Beispiel #38
0
class CartesianProduct(UniqueRepresentation, Parent):
    """
    A class implementing a raw data structure for Cartesian products
    of sets (and elements thereof). See :obj:`cartesian_product` for
    how to construct full fledged Cartesian products.

    EXAMPLES::

        sage: G = cartesian_product([GF(5), Permutations(10)])
        sage: G.cartesian_factors()
        (Finite Field of size 5, Standard permutations of 10)
        sage: G.cardinality()
        18144000
        sage: G.random_element()    # random
        (1, [4, 7, 6, 5, 10, 1, 3, 2, 8, 9])
        sage: G.category()
        Join of Category of finite monoids
            and Category of Cartesian products of monoids
            and Category of Cartesian products of finite enumerated sets

    .. automethod:: _cartesian_product_of_elements
    """
    def __init__(self, sets, category, flatten=False):
        r"""
        INPUT:

         - ``sets`` -- a tuple of parents
         - ``category`` -- a subcategory of ``Sets().CartesianProducts()``
         - ``flatten`` -- a boolean (default: ``False``)

        ``flatten`` is current ignored, and reserved for future use.

        No other keyword arguments (``kwargs``) are accepted.

        TESTS::

            sage: from sage.sets.cartesian_product import CartesianProduct
            sage: C = CartesianProduct((QQ, ZZ, ZZ), category = Sets().CartesianProducts())
            sage: C
            The Cartesian product of (Rational Field, Integer Ring, Integer Ring)
            sage: C.an_element()
            (1/2, 1, 1)
            sage: TestSuite(C).run()
            sage: cartesian_product([ZZ, ZZ], blub=None)
            Traceback (most recent call last):
            ...
            TypeError: __init__() got an unexpected keyword argument 'blub'
        """
        self._sets = tuple(sets)
        Parent.__init__(self, category=category)

    def _element_constructor_(self,x):
        r"""
        Construct an element of a Cartesian product from a list or iterable

        INPUT:

        - ``x`` -- a list (or iterable)

        Each component of `x` is converted to the corresponding
        Cartesian factor.

        EXAMPLES::

            sage: C = cartesian_product([GF(5), GF(3)])
            sage: x = C((1,3)); x
            (1, 0)
            sage: x.parent()
            The Cartesian product of (Finite Field of size 5, Finite Field of size 3)
            sage: x[0].parent()
            Finite Field of size 5
            sage: x[1].parent()
            Finite Field of size 3

        An iterable is also accepted as input::

            sage: C(i for i in range(2))
            (0, 1)

        TESTS::

            sage: C((1,3,4))
            Traceback (most recent call last):
            ...
            ValueError: (1, 3, 4) should be of length 2
        """
        x = tuple(x)
        if len(x) != len(self._sets):
            raise ValueError(
                "{} should be of length {}".format(x, len(self._sets)))
        x = tuple(c(xx) for c,xx in itertools.izip(self._sets,x))
        return self.element_class(self, x)

    def _repr_(self):
        """
        EXAMPLES::

            sage: cartesian_product([QQ, ZZ, ZZ]) # indirect doctest
            The Cartesian product of (Rational Field, Integer Ring, Integer Ring)
        """
        return "The Cartesian product of %s"%(self._sets,)

    def __contains__(self, x):
        """
        Check if ``x`` is contained in ``self``.

        EXAMPLES::

            sage: C = cartesian_product([range(5), range(5)])
            sage: (1, 1) in C
            True
            sage: (1, 6) in C
            False
        """
        if isinstance(x, self.Element):
            if x.parent() == self:
                return True
        elif not isinstance(x, tuple):
            return False
        return ( len(x) == len(self._sets)
                 and all(elt in self._sets[i] for i,elt in enumerate(x)) )

    def cartesian_factors(self):
        """
        Return the Cartesian factors of ``self``.

        .. SEEALSO::

            :meth:`Sets.CartesianProducts.ParentMethods.cartesian_factors()
            <sage.categories.sets_cat.Sets.CartesianProducts.ParentMethods.cartesian_factors>`.

        EXAMPLES::

            sage: cartesian_product([QQ, ZZ, ZZ]).cartesian_factors()
            (Rational Field, Integer Ring, Integer Ring)
        """
        return self._sets

    def _sets_keys(self):
        """
        Return the indices of the Cartesian factors of ``self``
        as per
        :meth:`Sets.CartesianProducts.ParentMethods._sets_keys()
        <sage.categories.sets_cat.Sets.CartesianProducts.ParentMethods._sets_keys>`.

        EXAMPLES::

            sage: cartesian_product([QQ, ZZ, ZZ])._sets_keys()
            {0, 1, 2}
            sage: cartesian_product([ZZ]*100)._sets_keys()
            {0, ..., 99}
        """
        from sage.sets.integer_range import IntegerRange
        return IntegerRange(len(self._sets))

    @cached_method
    def cartesian_projection(self, i):
        """
        Return the natural projection onto the `i`-th Cartesian
        factor of ``self`` as per
        :meth:`Sets.CartesianProducts.ParentMethods.cartesian_projection()
        <sage.categories.sets_cat.Sets.CartesianProducts.ParentMethods.cartesian_projection>`.

        INPUT:

        - ``i`` -- the index of a Cartesian factor of ``self``

        EXAMPLES::

            sage: C = Sets().CartesianProducts().example(); C
            The Cartesian product of (Set of prime numbers (basic implementation), An example of an infinite enumerated set: the non negative integers, An example of a finite enumerated set: {1,2,3})
            sage: x = C.an_element(); x
            (47, 42, 1)
            sage: pi = C.cartesian_projection(1)
            sage: pi(x)
            42

            sage: C.cartesian_projection('hey')
            Traceback (most recent call last):
            ...
            ValueError: i (=hey) must be in {0, 1, 2}
        """
        if i not in self._sets_keys():
            raise ValueError("i (={}) must be in {}".format(i, self._sets_keys()))
        return attrcall("cartesian_projection", i)

    summand_projection = deprecated_function_alias(10963, cartesian_projection)

    def _cartesian_product_of_elements(self, elements):
        """
        Return the Cartesian product of the given ``elements``.

        This implements :meth:`Sets.CartesianProducts.ParentMethods._cartesian_product_of_elements`.
        INPUT:

        - ``elements`` -- an iterable (e.g. tuple, list) with one element of
          each Cartesian factor of ``self``

        .. WARNING::

            This is meant as a fast low-level method. In particular,
            no coercion is attempted. When coercion or sanity checks
            are desirable, please use instead ``self(elements)`` or
            ``self._element_constructor(elements)``.

        EXAMPLES::

            sage: S1 = Sets().example()
            sage: S2 = InfiniteEnumeratedSets().example()
            sage: C = cartesian_product([S2, S1, S2])
            sage: C._cartesian_product_of_elements([S2.an_element(), S1.an_element(), S2.an_element()])
            (42, 47, 42)
        """
        elements = tuple(elements)
        assert len(elements) == len(self._sets)
        return self.element_class(self, elements)

    def construction(self):
        r"""
        Return the construction functor and its arguments for this
        Cartesian product.

        OUTPUT:

        A pair whose first entry is a Cartesian product functor and
        its second entry is a list of the Cartesian factors.

        EXAMPLES::

            sage: cartesian_product([ZZ, QQ]).construction()
            (The cartesian_product functorial construction,
             (Integer Ring, Rational Field))
        """
        from sage.categories.cartesian_product import cartesian_product
        return cartesian_product, self.cartesian_factors()

    def _coerce_map_from_(self, S):
        r"""
        Return ``True`` if ``S`` coerces into this Cartesian product.

        TESTS::

            sage: Z = cartesian_product([ZZ])
            sage: Q = cartesian_product([QQ])
            sage: Z.has_coerce_map_from(Q)  # indirect doctest
            False
            sage: Q.has_coerce_map_from(Z)  # indirect doctest
            True
        """
        if isinstance(S, CartesianProduct):
            S_factors = S.cartesian_factors()
            R_factors = self.cartesian_factors()
            if len(S_factors) == len(R_factors):
                if all(r.has_coerce_map_from(s) for r, s in zip(R_factors, S_factors)):
                    return True

    an_element = Sets.CartesianProducts.ParentMethods.an_element

    class Element(ElementWrapperCheckWrappedClass):

        wrapped_class = tuple

        def cartesian_projection(self, i):
            r"""
            Return the projection of ``self`` on the `i`-th factor of
            the Cartesian product, as per
            :meth:`Sets.CartesianProducts.ElementMethods.cartesian_projection()
            <sage.categories.sets_cat.Sets.CartesianProducts.ElementMethods.cartesian_projection>`.

            INPUT:

            - ``i`` -- the index of a factor of the Cartesian product

            EXAMPLES::

                sage: C = Sets().CartesianProducts().example(); C
                The Cartesian product of (Set of prime numbers (basic implementation), An example of an infinite enumerated set: the non negative integers, An example of a finite enumerated set: {1,2,3})
                sage: x = C.an_element(); x
                (47, 42, 1)
                sage: x.cartesian_projection(1)
                42

                sage: x.summand_projection(1)
                doctest:...: DeprecationWarning: summand_projection is deprecated. Please use cartesian_projection instead.
                See http://trac.sagemath.org/10963 for details.
                42
            """
            return self.value[i]

        __getitem__ = cartesian_projection

        def __iter__(self):
            r"""
            Iterate over the components of an element.

            EXAMPLES::

                sage: C = Sets().CartesianProducts().example(); C
                The Cartesian product of
                (Set of prime numbers (basic implementation),
                 An example of an infinite enumerated set: the non negative integers,
                 An example of a finite enumerated set: {1,2,3})
                sage: c = C.an_element(); c
                (47, 42, 1)
                sage: for i in c:
                ....:     print(i)
                47
                42
                1
            """
            return iter(self.value)

        def cartesian_factors(self):
            r"""
            Return the tuple of elements that compose this element.

            EXAMPLES::

                sage: A = cartesian_product([ZZ, RR])
                sage: A((1, 1.23)).cartesian_factors()
                (1, 1.23000000000000)
                sage: type(_)
                <type 'tuple'>
            """
            return self.value
Beispiel #39
0
from sage.combinat.designs.latin_squares import mutually_orthogonal_latin_squares

from sage.combinat.designs.orthogonal_arrays import transversal_design, incomplete_orthogonal_array


from sage.combinat.designs.difference_family import difference_family
from difference_matrices import difference_matrix

from sage.combinat.designs.incidence_structures import IncidenceStructure
BlockDesign = IncidenceStructure    # just an alias
from sage.combinat.designs.bibd import balanced_incomplete_block_design, steiner_triple_system

# deprecated in june 2014 (#16446)
from sage.misc.superseded import deprecated_function_alias, deprecated_callable_import
BalancedIncompleteBlockDesign = deprecated_function_alias(16446,
        balanced_incomplete_block_design)

from orthogonal_arrays import OAMainFunctions as orthogonal_arrays

# When this deprecated function is removed, remove the handling of k=None in the
# function orthogonal_arrays.orthogonal_array()
deprecated_callable_import(17034,
                           'sage.combinat.designs.orthogonal_arrays',
                           globals(),
                           locals(),
                           ["orthogonal_array"],
                           ("This function will soon be removed. Use the designs.orthogonal_arrays.* functions instead"))

# We don't want this to appear in designs.<tab>
del deprecated_function_alias
Beispiel #40
0
            return False

    # now that we know that (S1,S2) is a partition, we look for an invertible
    # element b that maps S1 to S2 by multiplication
    for b in range(2,n):
        if GCD(b,n) == 1 and all(b*x in S2 for x in S1):
            if return_automorphism:
                return True, b
            else:
                return True
    if return_automorphism:
        return False, None
    else:
        return False

is_a_splitting = deprecated_function_alias(21165, _is_a_splitting)

def _lift2smallest_field(a):
    """
    INPUT: a is an element of a finite field GF(q)

    OUTPUT: the element b of the smallest subfield F of GF(q) for
    which F(b)=a.

    EXAMPLES::

        sage: from sage.coding.code_constructions import lift2smallest_field
        sage: FF.<z> = GF(3^4,"z")
        sage: a = z^10
        sage: lift2smallest_field(a)
        doctest:...: DeprecationWarning: lift2smallest_field is deprecated. Please use sage.coding.code_constructions._lift2smallest_field instead.
Beispiel #41
0
    AUTHOR:

    - William Stein (2007-12-07)
    """
    try:
        return f.find_local_minimum(a=a, b=b, tol=tol, maxfun=maxfun)
    except AttributeError:
        pass
    a = float(a); b = float(b)
    import scipy.optimize
    xmin, fval, iter, funcalls = scipy.optimize.fminbound(f, a, b, full_output=1, xtol=tol, maxfun=maxfun)
    return fval, xmin


find_maximum_on_interval = deprecated_function_alias(2607, find_local_maximum)
find_minimum_on_interval = deprecated_function_alias(2607, find_local_minimum)


def minimize(func,x0,gradient=None,hessian=None,algorithm="default",**args):
    r"""
    This function is an interface to a variety of algorithms for computing
    the minimum of a function of several variables.


    INPUT:

    - ``func`` -- Either a symbolic function or a Python function whose
      argument is a tuple with `n` components

    - ``x0`` -- Initial point for finding minimum.
Beispiel #42
0
        """
        for p in descents_composition_list(self.shape):
            yield self.from_permutation(p)

class Ribbon_class(RibbonShapedTableau):
    """
    This exists solely for unpickling ``Ribbon_class`` objects.
    """
    def __setstate__(self, state):
        r"""
        Unpickle old ``Ribbon_class`` objects.

        EXAMPLES::

            sage: loads('x\x9ck`J.NLO\xd5K\xce\xcfM\xca\xccK,\xd1+\xcaLJ\xca\xcf\xe3\n\x02S\xf1\xc99\x89\xc5\xc5\\\x85\x8c\x9a\x8d\x85L\xb5\x85\xcc\x1a\xa1\xac\xf1\x19\x89\xc5\x19\x85,~@VNfqI!kl!\x9bFl!\xbb\x06\xc4\x9c\xa2\xcc\xbc\xf4b\xbd\xcc\xbc\x92\xd4\xf4\xd4"\xae\xdc\xc4\xec\xd4x\x18\xa7\x90#\x94\xd1\xb05\xa8\x903\x03\xc80\x022\xb8Rc\x0b\xb95@<c \x8f\x07\xc40\x012xSSK\x93\xf4\x00l\x811\x17')
            [[None, 1, 2], [3, 4]]
            sage: loads(dumps( RibbonShapedTableau([[3,2,1], [1,1]]) ))  # indirect doctest
            [[None, 3, 2, 1], [1, 1]]
        """
        self.__class__ = RibbonShapedTableau
        self.__init__(RibbonShapedTableaux(), state['_list'])

from sage.structure.sage_object import register_unpickle_override
register_unpickle_override('sage.combinat.ribbon', 'Ribbon_class', Ribbon_class)
register_unpickle_override('sage.combinat.ribbon', 'StandardRibbons_shape', StandardRibbonShapedTableaux)

# Deprecations from trac:18555. July 2016
from sage.misc.superseded import deprecated_function_alias
RibbonShapedTableaux.global_options = deprecated_function_alias(18555, RibbonShapedTableaux.options)
StandardRibbonShapedTableaux.global_options = deprecated_function_alias(18555, StandardRibbonShapedTableaux.options)
Beispiel #43
0
        qr.append(i)  # Values are always inserted to the right

    if check_standard:
        try:
            P = StandardTableau(p)
        except ValueError:
            P = SemistandardTableau(p)
        try:
            Q = StandardTableau(q)
        except ValueError:
            Q = SemistandardTableau(q)
        return [P, Q]
    return [SemistandardTableau(p), SemistandardTableau(q)]


RobinsonSchenstedKnuth = deprecated_function_alias(15142, RSK)
robinson_schensted_knuth = RSK


def RSK_inverse(p, q, output="array", insertion="RSK"):
    r"""
    Return the generalized permutation corresponding to the pair of tableaux
    `(p,q)` under the inverse of the Robinson-Schensted-Knuth algorithm.

    For more information on the bijeciton, see :func:`RSK`.

    INPUT:

    - ``p``, ``q`` -- Two semi-standard tableaux of the same shape

    - ``output`` -- (Default: ``'array'``) if ``q`` is semi-standard: