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)
""" 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)
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())
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
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:
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
""" 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)
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)
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)
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 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)
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)
""" 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)
# -*- 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)))
-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)]
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. #
""" 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)
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
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)
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))
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)
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
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)
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)
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
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)
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)
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)
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)
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))
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
# 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)
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)
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
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
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.
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.
""" 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)
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: