def __classcall_private__(cls, basis, ambient=None, unitriangular=False, category=None, *args, **opts): r""" Normalize the input. TESTS:: sage: from sage.modules.with_basis.subquotient import SubmoduleWithBasis sage: X = CombinatorialFreeModule(QQ, range(3)); x = X.basis() sage: Y1 = SubmoduleWithBasis((x[0]-x[1], x[1]-x[2]), X) sage: Y2 = SubmoduleWithBasis([x[0]-x[1], x[1]-x[2]], X) sage: Y1 is Y2 True """ basis = Family(basis) if ambient is None: ambient = basis.an_element().parent() default_category = ModulesWithBasis(ambient.category().base_ring()).Subobjects() category = default_category.or_subcategory(category, join=True) return super(SubmoduleWithBasis, cls).__classcall__(cls, basis, ambient, unitriangular, category, *args, **opts)
def basis(self): r""" Return the basis of ``self``. EXAMPLES:: sage: O = lie_algebras.OnsagerAlgebra(QQ) sage: O.basis() Lazy family (Onsager monomial(i))_{i in Disjoint union of Family (Integer Ring, Positive integers)} """ from sage.rings.all import ZZ from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets from sage.sets.positive_integers import PositiveIntegers I = DisjointUnionEnumeratedSets([ZZ, PositiveIntegers()], keepkey=True, facade=True) return Family(I, self.monomial, name='Onsager monomial')
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() Finite family {1: (3/2, 1, 1/2), 2: (1, 2, 1), 3: (1/2, 1, 3/2)} """ simple_weights = self.bilinear_form().inverse() I = self.index_set() D = {i: simple_weights[k] for k, i in enumerate(I)} return Family(I, D.__getitem__)
def algebra_generators(self): r""" Return the generators of this algebra. EXAMPLES:: sage: C = CombinatorialFreeModule(QQ, ['a','b','c']) sage: TA = TensorAlgebra(C) sage: TA.algebra_generators() Finite family {'a': B['a'], 'b': B['b'], 'c': B['c']} sage: m = SymmetricFunctions(QQ).m() sage: Tm = TensorAlgebra(m) sage: Tm.algebra_generators() Lazy family (generator(i))_{i in Partitions} """ return Family(self._indices.indices(), lambda i: self.monomial(self._indices.gen(i)), name='generator')
def _part_generators(self, positive=False): r""" Return the Lie algebra generators for the positive or negative half of ``self``. .. NOTE:: If the positive/negative generators correspond to the generators with (negative) simple roots, then this method will find them. If they do not, then this method *must* be overwritten. One should also overwrite this method in object classes when there is a better method to obtain them. Furthermore, this assumes that :meth:`lie_algebra_generators` is a finite set. INPUT: - ``positive`` -- boolean (default: ``False``); if ``True`` then return positive part generators, otherwise the return the negative part generators OUTPUT: A :func:`~sage.sets.family.Family` whose keys are the index set of ``self``. EXAMPLES:: sage: L = LieAlgebra(QQ, cartan_type=['E',6]) sage: list(L._part_generators(False)) [E[-alpha[1]], E[-alpha[2]], E[-alpha[3]], E[-alpha[4]], E[-alpha[5]], E[-alpha[6]]] """ I = self._cartan_type.index_set() P = self._cartan_type.root_system().root_lattice() ali = P.simple_roots().inverse_family() if positive: d = {ali[g.degree()]: g for g in self.lie_algebra_generators() if self._part(g) > 0} if not positive: d = {ali[-g.degree()]: g for g in self.lie_algebra_generators() if self._part(g) < 0} from sage.sets.family import Family return Family(I, d.__getitem__)
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 reflections(self): """ Return the reflections of ``self``. The reflections of a Coxeter group `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 family indexed by the positive roots taking values in the reflections. This requires ``self`` to be a finite Weyl group. .. NOTE:: Prior to :trac:`20027`, the reflections were the keys of the family and the values were the positive roots. EXAMPLES:: sage: W = WeylGroup("B2", prefix="s") sage: refdict = W.reflections(); refdict Finite family {(1, -1): s1, (0, 1): s2, (1, 1): s2*s1*s2, (1, 0): s1*s2*s1} sage: [r+refdict[r].action(r) for r in refdict.keys()] [(0, 0), (0, 0), (0, 0), (0, 0)] sage: W = WeylGroup(['A',2,1], prefix="s") sage: W.reflections() Lazy family (real root to reflection(i))_{i in Positive real roots of type ['A', 2, 1]} TESTS:: sage: CM = CartanMatrix([[2,-6],[-1,2]]) sage: W = WeylGroup(CM, prefix='s') sage: W.reflections() Traceback (most recent call last): ... NotImplementedError: only implemented for finite and affine Cartan types """ prr = self.domain().positive_real_roots() def to_elt(alp): ref = self.domain().reflection(alp) m = Matrix([ref(x).to_vector() for x in self.domain().basis()]) return self(m.transpose()) return Family(prr, to_elt, name="real root to reflection")
def cells(self): """ Return the cells of ``self``. EXAMPLES:: sage: from sage.categories.cw_complexes import CWComplexes sage: X = CWComplexes().example() sage: C = X.cells() sage: sorted((d, C[d]) for d in C.keys()) [(0, (0-cell v,)), (1, (0-cell e1, 0-cell e2)), (2, (2-cell f,))] """ d = {0: (self.element_class(self, 0, 'v'), )} d[1] = tuple( [self.element_class(self, 0, 'e' + str(e)) for e in self._edges]) d[2] = (self.an_element(), ) return Family(d)
def semigroup_generators(self): """ Return the generators of ``self`` as a semigroup. The generators of a monoid `M` as a semigroup are the generators of `M` as a monoid and the unit. EXAMPLES:: sage: M = Monoids().free([1,2,3]) sage: M.semigroup_generators() Family (1, F[1], F[2], F[3]) """ G = self.monoid_generators() from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets if G not in FiniteEnumeratedSets(): raise NotImplementedError("currently only implemented for finitely generated monoids") from sage.sets.family import Family return Family((self.one(),) + tuple(G))
def distinguished_reflections(self): """ Return the reflections of ``self``. EXAMPLES:: sage: W = WeylGroup(['B',2], implementation="permutation") sage: W.distinguished_reflections() Finite family {1: (1,5)(2,4)(6,8), 2: (1,3)(2,6)(5,7), 3: (2,8)(3,7)(4,6), 4: (1,7)(3,5)(4,8)} """ Q = self._cartan_type.root_system().root_lattice() pos_roots = list(Q.positive_roots()) Phi = pos_roots + [-x for x in pos_roots] def build_elt(index): r = pos_roots[index] perm = [Phi.index(x.reflection(r))+1 for x in Phi] return self.element_class(perm, self, check=False) return Family(self.reflection_index_set(), lambda i: build_elt(i-1))
def algebra_generators(self): """ Return the algebra generators of ``self``. """ M = self._indices._sets[1] # The monomials wt = M.one() # The identity of the monomials P = self._indices._sets[2] # The idemponents I = ['s%i'%i for i in range(1, self._d)] # The transpositions I += ['x%i'%i for i in range(1, self._d+1)] # The beads I += list(tuple(x) for x in P) # For the idempotents d = {} for i in range(1,self._d): d['s%i'%i] = self.sum_of_monomials( ((i,), wt, x) for x in P ) d['x%i'%i] = self.sum_of_monomials( ((), M.gen(i-1), x) for x in P ) d['x%i'%self._d] = self.sum_of_monomials( ((), M.gen(self._d-1), x) for x in P ) for x in P: d[tuple(x)] = self.monomial( ((), wt, x) ) return Family(I, lambda x: d[x], name="generator map")
def lie_algebra_generators(self, str_keys=False): r""" Return the Chevalley Lie algebra generators of ``self``. INPUT: - ``str_keys`` -- (default: ``False``) set to ``True`` to have the indices indexed by strings instead of simple (co)roots EXAMPLES:: sage: L = LieAlgebra(QQ, cartan_type=['A', 1]) sage: L.lie_algebra_generators() Finite family {alpha[1]: E[alpha[1]], -alpha[1]: E[-alpha[1]], alphacheck[1]: h1} sage: L.lie_algebra_generators(True) Finite family {'e1': E[alpha[1]], 'f1': E[-alpha[1]], 'h1': h1} """ index_set = self._cartan_type.index_set() alpha = self._Q.simple_roots() alphacheck = self._Q.simple_coroots() B = self.basis() ret = {} if str_keys: for i in index_set: al = alpha[i] ret['e{}'.format(i)] = B[al] ret['f{}'.format(i)] = B[-al] ret['h{}'.format(i)] = B[alphacheck[i]] keys = (['e{}'.format(i) for i in index_set] + ['f{}'.format(i) for i in index_set] + ['h{}'.format(i) for i in index_set]) else: for i in index_set: al = alpha[i] ret[al] = B[al] ret[-al] = B[-al] ret[alphacheck[i]] = B[alphacheck[i]] keys = ([alpha[i] for i in index_set] + [-alpha[i] for i in index_set] + [alphacheck[i] for i in index_set]) return Family(keys, ret.__getitem__)
def group_generators(self): """ Return group generators for ``self``. This default implementation calls :meth:`gens`, for backward compatibility. EXAMPLES:: sage: A = AlternatingGroup(4) sage: A.group_generators() Family ((2,3,4), (1,2,3)) """ from sage.sets.family import Family try: return Family(self.gens()) except AttributeError: raise NotImplementedError( "no generators are implemented for this group")
def basis(self): """ Return a basis of ``self``. EXAMPLES:: sage: d = lie_algebras.VirasoroAlgebra(QQ) sage: B = d.basis(); B Lazy family (basis map(i))_{i in Disjoint union of Family ({'c'}, Integer Ring)} sage: B['c'] c sage: B[3] d[3] sage: B[-15] d[-15] """ I = DisjointUnionEnumeratedSets([Set(['c']), ZZ]) return Family(I, self.monomial, name='basis map')
def basis(self): """ Return a basis of ``self``. The basis returned begins with the unity of `R` and continues with the standard basis of `M`. EXAMPLES:: sage: m = matrix([[0,1],[1,1]]) sage: J = JordanAlgebra(m) sage: J.basis() Family (1 + (0, 0), 0 + (1, 0), 0 + (0, 1)) """ R = self.base_ring() ret = (self.element_class(self, R.one(), self._M.zero()), ) ret += tuple( self.element_class(self, R.zero(), x) for x in self._M.basis()) return Family(ret)
def basis(self): r""" Return a basis of ``self``. EXAMPLES: A basis of a subalgebra:: sage: sc = {('a','b'): {'c': 1}, ('a','c'): {'d': 1}} sage: L.<a,b,c,d> = LieAlgebra(QQ, sc) sage: L.subalgebra([a + b, c + d]).basis() Family (a + b, c, d) A basis of an ideal:: sage: sc = {('x','y'): {'z': 1}, ('x','z'): {'w': 1}} sage: L.<x,y,z,w> = LieAlgebra(QQ, sc) sage: L.ideal([x + y + z + w]).basis() Family (w, x + y, z) """ L = self.ambient() B = [self._to_m(X) for X in L.basis()] m = L.module() sm = m.submodule([self._to_m(X) for X in self.gens()]) d = 0 while sm.dimension() > d: d = sm.dimension() SB = sm.basis() if not self._is_ideal: B = SB brackets = [self._to_m(L.bracket(self._from_m(v), self._from_m(w))) for v in B for w in SB] sm = m.submodule(sm.basis() + brackets) basis = [self.element_class(self, self._from_m(v)) for v in sm.echelonized_basis()] sortkey = lambda X: self.lift(X).leading_support(key=self._order) return Family(sorted(basis, key=sortkey))
def simple_reflections(self): r""" Return the simple reflections `(s_i)_{i\in I}` of ``self`` as a family indexed by :meth:`index_set`. .. SEEALSO:: - :meth:`simple_reflection` - :meth:`index_set` EXAMPLES: For the symmetric group, we recognize the simple transpositions:: sage: W = SymmetricGroup(4); W Symmetric group of order 4! as a permutation group sage: s = W.simple_reflections() sage: s Finite family {1: (1,2), 2: (2,3), 3: (3,4)} sage: s[1] (1,2) sage: s[2] (2,3) sage: s[3] (3,4) Here are the simple reflections for a colored symmetric group and a reflection group:: sage: W = ColoredPermutations(1,3) sage: W.simple_reflections() Finite family {1: [[0, 0, 0], [2, 1, 3]], 2: [[0, 0, 0], [1, 3, 2]]} sage: W = ReflectionGroup((1,1,3), index_set=['a','b']) # optional - gap3 sage: W.simple_reflections() # optional - gap3 Finite family {'a': (1,4)(2,3)(5,6), 'b': (1,3)(2,5)(4,6)} This default implementation uses :meth:`.index_set` and :meth:`.simple_reflection`. """ from sage.sets.family import Family return Family(self.index_set(), self.simple_reflection)
def __init__(self, modules, **options): CombinatorialFreeModule.Tensor.__init__(self, modules, **options) # with sage 5.7.beta4 the basis has the wrong category if False: try: from sage.sets.family import Family cc = list(self.basis().keys()).cc # the CartesianProduct self._indices = Family(cc, lambda u: tuple(u)) def contains(self, x): return x in cc import types self._indices._contains_ = types.MethodType(contains, self.indices()) self.basis.clear_cache() except AttributeError: pass self._fix_basis_tests()
def basis(self): """ Return a basis of ``self``. EXAMPLES:: sage: W.<x,y> = DifferentialWeylAlgebra(QQ) sage: B = W.basis() sage: it = iter(B) sage: [it.next() for i in range(20)] [1, x, y, dx, dy, x^2, x*y, x*dx, x*dy, y^2, y*dx, y*dy, dx^2, dx*dy, dy^2, x^3, x^2*y, x^2*dx, x^2*dy, x*y^2] """ n = self._n I = IntegerListsLex(NonNegativeIntegers(), length=n * 2) one = self.base_ring().one() f = lambda x: self.element_class(self, { (tuple(x[:n]), tuple(x[n:])): one }) return Family(I, f, name="basis map")
def alpha(self): r""" Returns the family `(\alpha_i)_{i\in I}` of the simple roots, with the extra feature that, for simple irreducible root systems, `\alpha_0` yields the opposite of the highest root. EXAMPLES:: sage: alpha = RootSystem(["A",2]).root_lattice().alpha() sage: alpha[1] alpha[1] sage: alpha[0] -alpha[1] - alpha[2] """ if self.root_system.is_finite() and self.root_system.is_irreducible(): return Family(self.index_set(), self.simple_root, \ hidden_keys = [0], hidden_function = lambda i: - self.highest_root()) else: return self.simple_roots()
def basis(self): """ Return a basis of ``self``. EXAMPLES:: sage: from sage.geometry.linear_expression import LinearExpressionModule sage: L = LinearExpressionModule(QQ, ('x', 'y', 'z')) sage: list(L.basis()) [x + 0*y + 0*z + 0, 0*x + y + 0*z + 0, 0*x + 0*y + z + 0, 0*x + 0*y + 0*z + 1] """ from sage.sets.family import Family gens = self.gens() d = {i: g for i, g in enumerate(gens)} d['b'] = self.element_class(self, self.ambient_module().zero(), self.base_ring().one()) return Family(list(range(len(gens))) + ['b'], lambda i: d[i])
def algebra_generators(self): r""" Return the generators of this algebra. These are the rooted trees with just one vertex. EXAMPLES:: sage: A = algebras.FreePreLie(ZZ, 'fgh'); A Free PreLie algebra on 3 generators ['f', 'g', 'h'] over Integer Ring sage: list(A.algebra_generators()) [B[f[]], B[g[]], B[h[]]] sage: A = algebras.FreePreLie(QQ, ['x1','x2']) sage: list(A.algebra_generators()) [B[x1[]], B[x2[]]] """ Trees = self.basis().keys() return Family(self._alphabet, lambda a: self.monomial(Trees([], a)))
def algebra_generators(self): r""" Return generators of this group algebra (as an algebra). EXAMPLES:: sage: GroupAlgebras(QQ).example(AlternatingGroup(10)).algebra_generators() Finite family {(1,2,3,4,5,6,7,8,9): B[(1,2,3,4,5,6,7,8,9)], (8,9,10): B[(8,9,10)]} .. NOTE:: This function is overloaded for SymmetricGroupAlgebras to return Permutations and not Elements of the symmetric group:: sage: GroupAlgebras(QQ).example(SymmetricGroup(10)).algebra_generators() [[2, 1, 3, 4, 5, 6, 7, 8, 9, 10], [2, 3, 4, 5, 6, 7, 8, 9, 10, 1]] """ from sage.sets.family import Family return Family(self.group().gens(), self.term)
def gens(self): """ Return a family of generators of ``self``. OUTPUT: - a :class:`~sage.sets.family.Family`, indexed by the positive integers, whose `n`-th element is ``self.gen(n)``. EXAMPLES:: sage: from sage.rings.algebraic_closure_finite_field import AlgebraicClosureFiniteField sage: F = AlgebraicClosureFiniteField(GF(5), 'z') sage: g = F.gens(); g Lazy family (...(i))_{i in Positive integers} sage: g[3] z3 """ from sage.sets.family import Family from sage.sets.positive_integers import PositiveIntegers return Family(PositiveIntegers(), self.gen)
def example(self): """ EXAMPLES:: sage: M = Semigroups().SetsWithAction().example() sage: TestSuite(M).run(skip = ["_test_pickling"]) """ from sage.monoids.representations import SetWithAction from sage.rings.finite_rings.integer_mod_ring import IntegerModRing from sage.sets.finite_set_maps import FiniteSetMaps from sage.combinat.automatic_monoid import AutomaticMonoid from sage.sets.family import Family from sage.combinat.j_trivial_monoids import SubFiniteMonoidsOfFunctions Z = IntegerModRing(10) ambient = FiniteSetMaps(Z) S = AutomaticMonoid(Family({2: ambient(lambda x: 2*x), 3: ambient(lambda x: 3*x) }), category=SubFiniteMonoidsOfFunctions()) M = SetWithAction(S, Z, action = lambda f,m: f(m)) M.rename("Representation of the monoid generated by <2,3> acting on Z/10 Z by multiplication") return M
def algebra_generators(self): r""" Return the algebra generators of ``self``. EXAMPLES:: sage: AW = algebras.AskeyWilson(QQ) sage: G = AW.algebra_generators() sage: G['A'] A sage: G['a'] a sage: list(G) [A, B, C, a, b, g] """ A = self.variable_names() def build_monomial(g): exp = [0] * 6 exp[A.index(g)] = 1 return self.monomial(self._indices(exp)) return Family(A, build_monomial)
def algebra_generators(self): """ Return the algebra generators of ``self``. EXAMPLES:: sage: Y = algebras.YokonumaHecke(5, 3) sage: dict(Y.algebra_generators()) {'g1': g[1], 'g2': g[2], 't1': t1, 't2': t2, 't3': t3} """ one = self._Pn.one() zero = [0] * self._n d = {} for i in range(self._n): r = list(zero) # Make a copy r[i] = 1 d['t%s' % (i + 1)] = self.monomial((tuple(r), one)) G = self._Pn.group_generators() for i in range(1, self._n): d['g%s' % i] = self.monomial((tuple(zero), G[i])) return Family(sorted(d), lambda i: d[i])
def basis(self): """ Return the basis of ``self``. EXAMPLES:: sage: L = lie_algebras.Heisenberg(QQ, oo) sage: L.basis() Lazy family (basis map(i))_{i in Disjoint union of Family ({'z'}, The Cartesian product of (Positive integers, {'p', 'q'}))} sage: L.basis()['z'] z sage: L.basis()[(12, 'p')] p12 """ S = cartesian_product([PositiveIntegers(), ['p','q']]) I = DisjointUnionEnumeratedSets([Set(['z']), S]) def basis_elt(x): if isinstance(x, str): return self.monomial(x) return self.monomial(x[1] + str(x[0])) return Family(I, basis_elt, name="basis map")
def IntegerListsNN(**kwds): """ Lists of nonnegative integers with constraints. This function returns the union of ``IntegerListsLex(n, **kwds)`` where `n` ranges over all nonnegative integers. EXAMPLES:: sage: from sage.combinat.integer_lists.nn import IntegerListsNN sage: L = IntegerListsNN(max_length=3, max_slope=-1) sage: L Disjoint union of Lazy family (<lambda>(i))_{i in Non negative integer semiring} sage: it = iter(L) sage: for _ in range(20): ....: print(next(it)) [] [1] [2] [3] [2, 1] [4] [3, 1] [5] [4, 1] [3, 2] [6] [5, 1] [4, 2] [3, 2, 1] [7] [6, 1] [5, 2] [4, 3] [4, 2, 1] [8] """ return DisjointUnionEnumeratedSets( Family(NN, lambda i: IntegerListsLex(i, **kwds)))
def __init__(self, poset): self.poset = poset support = poset.list() ambient_monoid = FiniteSetMaps(support, action="right") def genij(ij): (i, j) = ij return ambient_monoid.from_dict( {k: i if k == j else k for k in support}) index = map(tuple, poset.cover_relations()) # index elems must be hashable self.pi = Family(index, genij) category = Monoids().JTrivial().Finite() & Monoids().Transformation( ).Subobjects() AutomaticMonoid.__init__(self, self.pi, ambient_monoid, one=ambient_monoid.one(), mul=operator.mul, category=category)
def basis(self): """ Return a basis of ``self``. EXAMPLES:: sage: W.<x,y> = DifferentialWeylAlgebra(QQ) sage: B = W.basis() sage: it = iter(B) sage: [next(it) for i in range(20)] [1, x, y, dx, dy, x^2, x*y, x*dx, x*dy, y^2, y*dx, y*dy, dx^2, dx*dy, dy^2, x^3, x^2*y, x^2*dx, x^2*dy, x*y^2] """ n = self._n # TODO in #17927: use IntegerVectors(length=2*n) from sage.combinat.integer_list import IntegerListsNN I = IntegerListsNN(length=n * 2) one = self.base_ring().one() f = lambda x: self.element_class(self, { (tuple(x[:n]), tuple(x[n:])): one }) return Family(I, f, name="basis map")
def __init__(self, monoid, generators=None): r""" EXAMPLES:: sage: from sage_semigroups.monoids.karnofsky_rhodes_expansion import KarnofskyRhodesExpansion sage: from sage_semigroups.monoids.free_left_regular_band import FreeLeftRegularBand sage: F = FreeLeftRegularBand(2); F Free left regular band generated by ('a', 'b') sage: K = KarnofskyRhodesExpansion(F); K Karnofsky-Rhodes expansion of Free left regular band generated by ('a', 'b') sage: TestSuite(K).run() """ self._underlying_monoid = monoid if generators is None: generators = monoid.semigroup_generators() self._words = Words(generators) self._underlying_monoid_generators = Family(generators) Parent.__init__(self, category=Monoids().Finite().FinitelyGenerated()) self._representatives = {self._canonicalize(self.one()): self.one()} # TODO: do we really want to do this?! # force the generation of all the elements self.list()
class KarnofskyRhodesExpansion(UniqueRepresentation, Parent): def __init__(self, monoid, generators=None): r""" EXAMPLES:: sage: from sage_semigroups.monoids.karnofsky_rhodes_expansion import KarnofskyRhodesExpansion sage: from sage_semigroups.monoids.free_left_regular_band import FreeLeftRegularBand sage: F = FreeLeftRegularBand(2); F Free left regular band generated by ('a', 'b') sage: K = KarnofskyRhodesExpansion(F); K Karnofsky-Rhodes expansion of Free left regular band generated by ('a', 'b') sage: TestSuite(K).run() """ self._underlying_monoid = monoid if generators is None: generators = monoid.semigroup_generators() self._words = Words(generators) self._underlying_monoid_generators = Family(generators) Parent.__init__(self, category=Monoids().Finite().FinitelyGenerated()) self._representatives = {self._canonicalize(self.one()): self.one()} # TODO: do we really want to do this?! # force the generation of all the elements self.list() def succ_generators(self, side="twosided"): def fcn(x): pgens = [] if side == "right" or side == "twosided": pgens += [self(x.value * g.value) for g in self.semigroup_generators()] if side == "left" or side == "twosided": pgens += [self(g.value * x.value) for g in self.semigroup_generators()] gens = [] for gen in pgens: mT = self._canonicalize(gen) if mT not in self._representatives: gens.append(gen) self._representatives[mT] = gen elif self._representatives[mT] == gen: gens.append(gen) return gens return fcn def _repr_(self): return "Karnofsky-Rhodes expansion of %s"%(self._underlying_monoid,) @cached_method def one(self): return self(self._words()) def _canonicalize(self,x): m = self.projection(x) T = self.path_transition_edges(x) return (m,T) def representative(self, x): return self._representatives[self._canonicalize(x)] def product(self, x, y): return self.representative(self(x.value * y.value)) return self.representative(self.representative(x).value * self.representative(y).value) def semigroup_generators(self): return Family([self(self._words((i,))) for i in self._underlying_monoid_generators]) def an_element(self): return self.one() @lazy_attribute def _underlying_monoid_cayley_graph(self): return self._underlying_monoid.cayley_graph(side="right", generators=self._underlying_monoid_generators) @lazy_attribute def _transition_edges(self): G = self._underlying_monoid_cayley_graph d = {} for (i,C) in enumerate(G.strongly_connected_components()): for v in C: d[v] = i transition_edges = Set([(a,b,l) for (a,b,l) in G.edges() if d[a] != d[b]]) return transition_edges def projection(self, w): return self._underlying_monoid.prod(w.value) @lazy_attribute def _transition_dictionary(self): t = {} for (u,v,l) in self._underlying_monoid_cayley_graph.edge_iterator(): t[u, l] = v return t def _read_path(self, w): t = self._transition_dictionary i = self._underlying_monoid_generators.inverse_family() v = self._underlying_monoid.one() path = [] for a in w.value: w = t[v,i[a]] path.append((v,w,i[a])) v = w return path def path_transition_edges(self, w): tedges = self._transition_edges output = [] for (a,b,l) in self._read_path(w): if (a,b,l) in tedges: output.append((a,b,l)) return Set(output) def are_equivalent(self, u, v): assert u in self assert v in self return self.projection(u) == self.projection(v) and \ self.path_transition_edges(u) == self.path_transition_edges(v) def __iter__(self): from sage.combinat.backtrack import TransitiveIdeal return TransitiveIdeal(self.succ_generators(side = "right"), [self.one()]).__iter__() class Element (ElementWrapper): wrapped_class = FiniteWord_class __lt__ = ElementWrapper._lt_by_value def _eq_(self, other): return self.parent().are_equivalent(self, other)
def __init__(self, *args): r""" The constructor either acts as a copy constructor for a finite surface, or you can pass two options: polygons and identifications. INPUT: - ``polygons`` - a family of polygons (might be a list, a dictionnary label -> polygon or more generally a Family) - ``identifications`` - the identification of the edges. A list of pairs ((p0,e0),(p1,e1)). """ if len(args)==2: polygons=args[0] identifications=args[1] self._polygons = Family(polygons) if self._polygons.cardinality() == 0: raise ValueError("there should be at least one polygon") self._field = self._polygons.an_element().parent().field() n = 0 for p in self._polygons: if p.parent().field() != self._field: raise ValueError("the field must be the same for all polygons") n += 1 if n > 10: # the number of polygons may be infinite... break if isinstance(identifications, (list,dict)): edge_identifications = {} if isinstance(identifications, dict): it = identifications.iteritems() else: it = iter(identifications) for e0,e1 in it: edge_identifications[e0] = e1 # Check that e0 makes sense. assert e0[1]>=0 and e0[1]<self._polygons[e0[0]].num_edges() # Check that e1 makes sense. assert e1[1]>=0 and e1[1]<self._polygons[e1[0]].num_edges() if e1 in edge_identifications: assert edge_identifications[e1] == e0 else: edge_identifications[e1] = e0 else: edge_identifications = identifications self._edge_identifications = edge_identifications elif len(args)==1: # Copy constructor for finite surface. s = args[0] if not s.is_finite(): raise ValueError("Can only copy finite surface.") polygons = {} edge_identifications = {} for label,polygon in s.label_polygon_iterator(): polygons[label]=polygon for edge in xrange(polygon.num_edges()): edge_identifications[(label,edge)]=s.opposite_edge(label,edge) self._field = s.base_ring() self._polygons=Family(polygons) self._edge_identifications = edge_identifications else: raise ValueError("Can only be called with one or two arguments.")
class Surface_polygons_and_gluings(Surface): r""" Similarity surface build from a list of polygons and gluings. """ def __init__(self, *args): r""" The constructor either acts as a copy constructor for a finite surface, or you can pass two options: polygons and identifications. INPUT: - ``polygons`` - a family of polygons (might be a list, a dictionnary label -> polygon or more generally a Family) - ``identifications`` - the identification of the edges. A list of pairs ((p0,e0),(p1,e1)). """ if len(args)==2: polygons=args[0] identifications=args[1] self._polygons = Family(polygons) if self._polygons.cardinality() == 0: raise ValueError("there should be at least one polygon") self._field = self._polygons.an_element().parent().field() n = 0 for p in self._polygons: if p.parent().field() != self._field: raise ValueError("the field must be the same for all polygons") n += 1 if n > 10: # the number of polygons may be infinite... break if isinstance(identifications, (list,dict)): edge_identifications = {} if isinstance(identifications, dict): it = identifications.iteritems() else: it = iter(identifications) for e0,e1 in it: edge_identifications[e0] = e1 # Check that e0 makes sense. assert e0[1]>=0 and e0[1]<self._polygons[e0[0]].num_edges() # Check that e1 makes sense. assert e1[1]>=0 and e1[1]<self._polygons[e1[0]].num_edges() if e1 in edge_identifications: assert edge_identifications[e1] == e0 else: edge_identifications[e1] = e0 else: edge_identifications = identifications self._edge_identifications = edge_identifications elif len(args)==1: # Copy constructor for finite surface. s = args[0] if not s.is_finite(): raise ValueError("Can only copy finite surface.") polygons = {} edge_identifications = {} for label,polygon in s.label_polygon_iterator(): polygons[label]=polygon for edge in xrange(polygon.num_edges()): edge_identifications[(label,edge)]=s.opposite_edge(label,edge) self._field = s.base_ring() self._polygons=Family(polygons) self._edge_identifications = edge_identifications else: raise ValueError("Can only be called with one or two arguments.") # display everything by default # I don't think we should be doing this by default whenever we create # a surface. -Pat #adj = [] #todo = [self.base_label()] #labs = set(self.polygon_labels()) #labs.remove(self.base_label()) #while todo: # p1 = todo.pop() # for e1 in range(self.polygon(p1).num_edges()): # p2,e2 = self.opposite_edge(p1,e1) # if p2 in labs: # labs.remove(p2) # adj.append((p1,e1)) #self._plot_options = {'adjacencies': adj} def is_finite(self): r""" Return whether or not the surface is finite. """ from sage.rings.infinity import Infinity return self._polygons.cardinality() != Infinity def base_ring(self): return self._field def polygon_labels(self): from sage.combinat.words.alphabet import build_alphabet return build_alphabet(self._polygons.keys()) def base_label(self): return self.polygon_labels().an_element() def polygon(self, lab): r""" Return the polygon with label ``lab``. """ return self._polygons[lab] def opposite_edge(self, p, e): if (p,e) not in self._edge_identifications: e = e % self._polygons[p].num_edges() if (p,e) not in self._edge_identifications: raise ValueError("The pair"+str((p,e))+" is not a valid edge identifier.") return self._edge_identifications[(p,e)]
def __classcall_private__(cls, generators, ambient=None, one=None, mul=operator.mul, category=None): """ Parse and straighten the arguments; figure out the category. TESTS:: sage: from sage.monoids.automatic_semigroup import AutomaticSemigroup sage: R = IntegerModRing(9) sage: M = AutomaticSemigroup((), one=R.one()) sage: M.ambient() == R True sage: AutomaticSemigroup((0,)).category() Join of Category of finitely generated semigroups and Category of subquotients of semigroups and Category of commutative magmas and Category of subobjects of sets sage: AutomaticSemigroup((0,), one=1).category() Join of Category of subquotients of monoids and Category of commutative monoids and Category of finitely generated semigroups and Category of subobjects of sets sage: AutomaticSemigroup((0,), one=0).category() Join of Category of commutative monoids and Category of finitely generated semigroups and Category of subquotients of semigroups and Category of subobjects of sets sage: AutomaticSemigroup((0,), mul=operator.add).category() Join of Category of semigroups and Category of subobjects of sets sage: AutomaticSemigroup((0,), one=0, mul=operator.add).category() Join of Category of monoids and Category of subobjects of sets sage: S5 = SymmetricGroup(5) sage: AutomaticSemigroup([S5((1,2))]).category() Join of Category of finite groups and Category of subquotients of monoids and Category of finite finitely generated semigroups and Category of subquotients of finite sets and Category of subobjects of sets .. TODO:: One would want a subsemigroup of a group to be automatically a subgroup (in ``Groups().Subobjects()``). """ generators = Family(generators) if ambient is None: # Try to guess the ambient semigroup from the generators or the unit if generators.cardinality() > 0: ambient = generators.first().parent() elif one is not None: ambient = one.parent() else: raise ValueError( "AutomaticSemigroup requires at least one generator or `one` to determine the ambient space" ) elif ambient not in Sets: raise ValueError("ambient (=%s) should be a set" % ambient) # if mul is not operator.mul and category.is_subcategory(Monoids().Subobjects()) error if one is None and category is not None: if category.is_subcategory(Monoids().Subobjects()): one = ambient.one() elif category.is_subcategory(Monoids()): raise ValueError("For a monoid which is just a subsemigroup, the unit should be specified") # Try to determine the most specific category # This logic should be in the categories if mul is operator.mul: default_category = Semigroups().FinitelyGenerated() if one is not None and one == ambient.one(): default_category = default_category.Unital() if ambient in Semigroups().Commutative(): default_category = default_category.Commutative() if ambient in Groups().Finite(): default_category = default_category & Groups() else: default_category = Sets() if ambient in Sets().Finite(): default_category = default_category.Finite() default_category = default_category.Subobjects() & Semigroups() if one is not None: default_category = default_category.Unital() cls = AutomaticMonoid if category is None: category = default_category else: category = default_category & category return super(AutomaticSemigroup, cls).__classcall__( cls, generators, ambient=ambient, one=one, mul=mul, category=category )