def _construct_UEA(self): """ Construct the universal enveloping algebra of ``self``. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: UEA = L._construct_UEA(); UEA Noncommutative Multivariate Polynomial Ring in b0, b1, b2 over Rational Field, nc-relations: {} sage: UEA.relations(add_commutative=True) {b1*b0: b0*b1, b2*b0: b0*b2, b2*b1: b1*b2} :: sage: L.<x,y,z> = LieAlgebra(QQ, {('x','y'):{'z':1}, ('y','z'):{'x':1}, ('z','x'):{'y':1}}) sage: UEA = L._construct_UEA(); UEA Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {...} sage: sorted(UEA.relations().items(), key=str) [(y*x, x*y - z), (z*x, x*z + y), (z*y, y*z - x)] """ # Create the UEA relations # We need to get names for the basis elements, not just the generators I = self._basis_ordering try: names = [str(x) for x in I] def names_map(x): return x F = FreeAlgebra(self.base_ring(), names) except ValueError: names = ['b{}'.format(i) for i in range(self.dimension())] self._UEA_names_map = {g: names[i] for i, g in enumerate(I)} names_map = self._UEA_names_map.__getitem__ F = FreeAlgebra(self.base_ring(), names) # ``F`` is the free algebra over the basis of ``self``. The # universal enveloping algebra of ``self`` will be constructed # as a quotient of ``F``. d = F.gens_dict() rels = {} S = self.structure_coefficients(True) # Construct the map from indices to names of the UEA def get_var(g): return d[names_map(g)] # The function ``get_var`` sends an element of the basis of # ``self`` to the corresponding element of ``F``. for k in S.keys(): g0 = get_var(k[0]) g1 = get_var(k[1]) if g0 < g1: rels[g1 * g0] = g0 * g1 - F.sum(val * get_var(g) for g, val in S[k]) else: rels[g0 * g1] = g1 * g0 + F.sum(val * get_var(g) for g, val in S[k]) return F.g_algebra(rels)
def __init__(self, field, relations, names): """ The finitely presented algebra equivalent to the free algebra over `field` generated by `names` modulo the ideal generated by `relations`. """ if field not in Fields: raise TypeError('Base ring must be a field.') if type(relations) == str: relations = tuple(relations.split(',')) elif type(relations) == list: relations = tuple(relations) elif not isinstance(relations, tuple): raise TypeError( 'Relations must be given as a list, tuple, or string.') if type(names) == str: names = tuple(names.split(',')) elif type(names) == list: names = tuple(names) elif not isinstance(names, tuple): raise TypeError( 'Generators must be given as a list, tuple, or string.') self._ngens = len(names) self._nrels = len(relations) self._free_alg = FreeAlgebra(field, self._ngens, names) self._ideal = self._free_alg.ideal(relations) self._reduce = [] for f in self._ideal.gens(): mons = f.monomials() if len(mons) == 1: self._reduce.append([_to_word(f), Word(''), field.zero()]) if len(mons) == 2: coeffs = f.coefficients() if mons[0] < mons[1]: self._reduce.append([ _to_word(mons[1]), _to_word(mons[0]), -coeffs[0] * coeffs[1]**(-1) ]) elif mons[0] > mons[1]: self._reduce.append([ _to_word(mons[0]), _to_word(mons[1]), -coeffs[1] * coeffs[0]**(-1) ]) QuotientRing_nc.__init__(self, self._free_alg, self._ideal, names, category=AlgebrasWithBasis(field))
def hamilton_quatalg(R): """ Hamilton quaternion algebra over the commutative ring R, constructed as a free algebra quotient. INPUT: - R -- a commutative ring OUTPUT: - Q -- quaternion algebra - gens -- generators for Q EXAMPLES:: sage: H, (i,j,k) = sage.algebras.free_algebra_quotient.hamilton_quatalg(ZZ) sage: H Free algebra quotient on 3 generators ('i', 'j', 'k') and dimension 4 over Integer Ring sage: i^2 -1 sage: i in H True Note that there is another vastly more efficient models for quaternion algebras in Sage; the one here is mainly for testing purposes:: sage: R.<i,j,k> = QuaternionAlgebra(QQ,-1,-1) # much fast than the above """ n = 3 from sage.algebras.free_algebra import FreeAlgebra from sage.matrix.all import MatrixSpace A = FreeAlgebra(R, n, 'i') F = A.monoid() i, j, k = F.gens() mons = [F(1), i, j, k] M = MatrixSpace(R, 4) mats = [ M([0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0]), M([0, 0, 1, 0, 0, 0, 0, 1, -1, 0, 0, 0, 0, -1, 0, 0]), M([0, 0, 0, 1, 0, 0, -1, 0, 0, 1, 0, 0, -1, 0, 0, 0]) ] H3 = FreeAlgebraQuotient(A, mons, mats, names=('i', 'j', 'k')) return H3, H3.gens()
def _construct_UEA(self): """ Construct the universal enveloping algebra of ``self``. EXAMPLES:: sage: L.<x, y> = LieAlgebra(QQ) sage: L._construct_UEA() Free Algebra on 2 generators (x, y) over Rational Field """ # TODO: Pass the index set along once FreeAlgebra accepts it return FreeAlgebra(self.base_ring(), len(self._names), self._names)
def hamilton_quatalg(R): """ Hamilton quaternion algebra over the commutative ring R, constructed as a free algebra quotient. INPUT: - R -- a commutative ring OUTPUT: - Q -- quaternion algebra - gens -- generators for Q EXAMPLES:: sage: H, (i,j,k) = sage.algebras.free_algebra_quotient.hamilton_quatalg(ZZ) sage: H Free algebra quotient on 3 generators ('i', 'j', 'k') and dimension 4 over Integer Ring sage: i^2 -1 sage: i in H True Note that there is another vastly more efficient models for quaternion algebras in Sage; the one here is mainly for testing purposes:: sage: R.<i,j,k> = QuaternionAlgebra(QQ,-1,-1) # much fast than the above """ n = 3 from sage.algebras.free_algebra import FreeAlgebra from sage.matrix.all import MatrixSpace A = FreeAlgebra(R, n, 'i') F = A.monoid() i, j, k = F.gens() mons = [ F(1), i, j, k ] M = MatrixSpace(R,4) mats = [M([0,1,0,0, -1,0,0,0, 0,0,0,-1, 0,0,1,0]), M([0,0,1,0, 0,0,0,1, -1,0,0,0, 0,-1,0,0]), M([0,0,0,1, 0,0,-1,0, 0,1,0,0, -1,0,0,0]) ] H3 = FreeAlgebraQuotient(A,mons,mats, names=('i','j','k')) return H3, H3.gens()
def _construct_UEA(self): """ Construct the universal enveloping algebra of ``self``. EXAMPLES:: sage: L.<x, y> = LieAlgebra(QQ) sage: L._construct_UEA() Free Algebra on 2 generators (x, y) over Rational Field sage: L.<x> = LieAlgebra(QQ) sage: L._construct_UEA() Free Algebra on 1 generators (x,) over Rational Field """ return FreeAlgebra(self.base_ring(), len(self._names), self._names)
def diff_alg(self): r""" Return the algebra of differential operators (over QQ) which is used on rational functions representing elements of ``self``. EXAMPLES:: sage: from graded_ring import ModularFormsRing sage: ModularFormsRing().diff_alg() Noncommutative Multivariate Polynomial Ring in X, Y, Z, dX, dY, dZ over Rational Field, nc-relations: {dY*Y: Y*dY + 1, dZ*Z: Z*dZ + 1, dX*X: X*dX + 1} sage: from space import CuspForms sage: CuspForms(k=12, base_ring=AA).diff_alg() Noncommutative Multivariate Polynomial Ring in X, Y, Z, dX, dY, dZ over Rational Field, nc-relations: {dY*Y: Y*dY + 1, dZ*Z: Z*dZ + 1, dX*X: X*dX + 1} """ # We only use two operators for now which do not involve 'd', so for performance # reason we choose FractionField(base_ring) instead of self.coeff_ring(). free_alg = FreeAlgebra(FractionField(ZZ),6,'X,Y,Z,dX,dY,dZ') (X,Y,Z,dX,dY,dZ) = free_alg.gens() diff_alg = free_alg.g_algebra({dX*X:1+X*dX,dY*Y:1+Y*dY,dZ*Z:1+Z*dZ}) return diff_alg
def _construct_UEA(self): """ Construct the universal enveloping algebra of ``self``. EXAMPLES:: sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() sage: UEA = L._construct_UEA(); UEA Noncommutative Multivariate Polynomial Ring in b0, b1, b2 over Rational Field, nc-relations: {} sage: UEA.relations(add_commutative=True) {b1*b0: b0*b1, b2*b0: b0*b2, b2*b1: b1*b2} :: sage: L.<x,y,z> = LieAlgebra(QQ, {('x','y'):{'z':1}, ('y','z'):{'x':1}, ('z','x'):{'y':1}}) sage: UEA = L._construct_UEA(); UEA Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {...} sage: sorted(UEA.relations().items(), key=str) [(y*x, x*y - z), (z*x, x*z + y), (z*y, y*z - x)] """ # Create the UEA relations # We need to get names for the basis elements, not just the generators I = self._basis_ordering try: names = [str(x) for x in I] def names_map(x): return x F = FreeAlgebra(self.base_ring(), names) except ValueError: names = ['b{}'.format(i) for i in range(self.dimension())] self._UEA_names_map = {g: names[i] for i,g in enumerate(I)} names_map = self._UEA_names_map.__getitem__ F = FreeAlgebra(self.base_ring(), names) # ``F`` is the free algebra over the basis of ``self``. The # universal enveloping algebra of ``self`` will be constructed # as a quotient of ``F``. d = F.gens_dict() rels = {} S = self.structure_coefficients(True) # Construct the map from indices to names of the UEA def get_var(g): return d[names_map(g)] # The function ``get_var`` sends an element of the basis of # ``self`` to the corresponding element of ``F``. for k in S.keys(): g0 = get_var(k[0]) g1 = get_var(k[1]) if g0 < g1: rels[g1*g0] = g0*g1 - F.sum(val*get_var(g) for g, val in S[k]) else: rels[g0*g1] = g1*g0 + F.sum(val*get_var(g) for g, val in S[k]) return F.g_algebra(rels)
def __classcall_private__(cls, R=None, arg0=None, arg1=None, names=None, index_set=None, abelian=False, **kwds): """ Select the correct parent based upon input. TESTS:: sage: LieAlgebra(QQ, abelian=True, names='x,y,z') Abelian Lie algebra on 3 generators (x, y, z) over Rational Field sage: LieAlgebra(QQ, {('e','h'): {'e':-2}, ('f','h'): {'f':2}, ....: ('e','f'): {'h':1}}, names='e,f,h') Lie algebra on 3 generators (e, f, h) over Rational Field """ # Parse associative algebra input # ----- assoc = kwds.get("associative", None) if assoc is not None: return LieAlgebraFromAssociative(assoc, names=names, index_set=index_set) # Parse input as a Cartan type # ----- ct = kwds.get("cartan_type", None) if ct is not None: from sage.combinat.root_system.cartan_type import CartanType ct = CartanType(ct) if ct.is_affine(): from sage.algebras.lie_algebras.affine_lie_algebra import AffineLieAlgebra return AffineLieAlgebra(R, cartan_type=ct, kac_moody=kwds.get("kac_moody", True)) if not ct.is_finite(): raise NotImplementedError("non-finite types are not implemented yet, see trac #14901 for details") rep = kwds.get("representation", "bracket") if rep == 'bracket': from sage.algebras.lie_algebras.classical_lie_algebra import LieAlgebraChevalleyBasis return LieAlgebraChevalleyBasis(R, ct) if rep == 'matrix': from sage.algebras.lie_algebras.classical_lie_algebra import ClassicalMatrixLieAlgebra return ClassicalMatrixLieAlgebra(R, ct) raise ValueError("invalid representation") # Parse the remaining arguments # ----- if R is None: raise ValueError("invalid arguments") check_assoc = lambda A: (isinstance(A, (Ring, MatrixSpace)) or A in Rings() or A in Algebras(R).Associative()) if arg0 in ZZ or check_assoc(arg1): # Check if we need to swap the arguments arg0, arg1 = arg1, arg0 # Parse the first argument # ----- if isinstance(arg0, dict): if not arg0: from sage.algebras.lie_algebras.abelian import AbelianLieAlgebra return AbelianLieAlgebra(R, names, index_set) elif isinstance(next(iter(arg0.keys())), (list, tuple)): # We assume it is some structure coefficients arg1, arg0 = arg0, arg1 if isinstance(arg0, (list, tuple)): if all(isinstance(x, str) for x in arg0): # If they are all strings, then it is a list of variables names = tuple(arg0) if isinstance(arg0, str): names = tuple(arg0.split(',')) elif isinstance(names, str): names = tuple(names.split(',')) # Parse the second argument if isinstance(arg1, dict): # Assume it is some structure coefficients from sage.algebras.lie_algebras.structure_coefficients import LieAlgebraWithStructureCoefficients return LieAlgebraWithStructureCoefficients(R, arg1, names, index_set, **kwds) # Otherwise it must be either a free or abelian Lie algebra if arg1 in ZZ: if isinstance(arg0, str): names = arg0 if names is None: index_set = list(range(arg1)) else: if isinstance(names, str): names = tuple(names.split(',')) if arg1 != 1 and len(names) == 1: names = tuple('{}{}'.format(names[0], i) for i in range(arg1)) if arg1 != len(names): raise ValueError("the number of names must equal the" " number of generators") if abelian: from sage.algebras.lie_algebras.abelian import AbelianLieAlgebra return AbelianLieAlgebra(R, names, index_set) # Otherwise it is the free Lie algebra rep = kwds.get("representation", "bracket") if rep == "polynomial": # Construct the free Lie algebra from polynomials in the # free (associative unital) algebra # TODO: Change this to accept an index set once FreeAlgebra accepts one from sage.algebras.free_algebra import FreeAlgebra F = FreeAlgebra(R, names) if index_set is None: index_set = F.variable_names() # TODO: As part of #16823, this should instead construct a # subclass with specialized methods for the free Lie algebra return LieAlgebraFromAssociative(F, F.gens(), names=names, index_set=index_set) raise NotImplementedError("the free Lie algebra has only been" " implemented using polynomials in the" " free algebra, see trac ticket #16823")
def __classcall_private__(cls, R=None, arg0=None, arg1=None, names=None, index_set=None, abelian=False, **kwds): """ Select the correct parent based upon input. TESTS:: sage: LieAlgebra(QQ, abelian=True, names='x,y,z') Abelian Lie algebra on 3 generators (x, y, z) over Rational Field sage: LieAlgebra(QQ, {('e','h'): {'e':-2}, ('f','h'): {'f':2}, ....: ('e','f'): {'h':1}}, names='e,f,h') Lie algebra on 3 generators (e, f, h) over Rational Field """ # Parse associative algebra input # ----- assoc = kwds.get("associative", None) if assoc is not None: return LieAlgebraFromAssociative(assoc, names=names, index_set=index_set) # Parse input as a Cartan type # ----- ct = kwds.get("cartan_type", None) if ct is not None: from sage.combinat.root_system.cartan_type import CartanType ct = CartanType(ct) if ct.is_affine(): from sage.algebras.lie_algebras.affine_lie_algebra import AffineLieAlgebra return AffineLieAlgebra(R, cartan_type=ct, kac_moody=kwds.get("kac_moody", True)) if not ct.is_finite(): raise NotImplementedError( "non-finite types are not implemented yet, see trac #14901 for details" ) rep = kwds.get("representation", "bracket") if rep == 'bracket': from sage.algebras.lie_algebras.classical_lie_algebra import LieAlgebraChevalleyBasis return LieAlgebraChevalleyBasis(R, ct) if rep == 'matrix': from sage.algebras.lie_algebras.classical_lie_algebra import ClassicalMatrixLieAlgebra return ClassicalMatrixLieAlgebra(R, ct) raise ValueError("invalid representation") # Parse the remaining arguments # ----- if R is None: raise ValueError("invalid arguments") check_assoc = lambda A: (isinstance(A, (Ring, MatrixSpace)) or A in Rings() or A in Algebras(R).Associative()) if arg0 in ZZ or check_assoc(arg1): # Check if we need to swap the arguments arg0, arg1 = arg1, arg0 # Parse the first argument # ----- if isinstance(arg0, dict): if not arg0: from sage.algebras.lie_algebras.abelian import AbelianLieAlgebra return AbelianLieAlgebra(R, names, index_set) elif isinstance(next(iter(arg0.keys())), (list, tuple)): # We assume it is some structure coefficients arg1, arg0 = arg0, arg1 if isinstance(arg0, (list, tuple)): if all(isinstance(x, str) for x in arg0): # If they are all strings, then it is a list of variables names = tuple(arg0) if isinstance(arg0, str): names = tuple(arg0.split(',')) elif isinstance(names, str): names = tuple(names.split(',')) # Parse the second argument if isinstance(arg1, dict): # Assume it is some structure coefficients from sage.algebras.lie_algebras.structure_coefficients import LieAlgebraWithStructureCoefficients return LieAlgebraWithStructureCoefficients(R, arg1, names, index_set, **kwds) # Otherwise it must be either a free or abelian Lie algebra if arg1 in ZZ: if isinstance(arg0, str): names = arg0 if names is None: index_set = list(range(arg1)) else: if isinstance(names, str): names = tuple(names.split(',')) if arg1 != 1 and len(names) == 1: names = tuple('{}{}'.format(names[0], i) for i in range(arg1)) if arg1 != len(names): raise ValueError("the number of names must equal the" " number of generators") if abelian: from sage.algebras.lie_algebras.abelian import AbelianLieAlgebra return AbelianLieAlgebra(R, names, index_set) # Otherwise it is the free Lie algebra rep = kwds.get("representation", "bracket") if rep == "polynomial": # Construct the free Lie algebra from polynomials in the # free (associative unital) algebra # TODO: Change this to accept an index set once FreeAlgebra accepts one from sage.algebras.free_algebra import FreeAlgebra F = FreeAlgebra(R, names) if index_set is None: index_set = F.variable_names() # TODO: As part of #16823, this should instead construct a # subclass with specialized methods for the free Lie algebra return LieAlgebraFromAssociative(F, F.gens(), names=names, index_set=index_set) raise NotImplementedError("the free Lie algebra has only been" " implemented using polynomials in the" " free algebra, see trac ticket #16823")
def has_irred_rep(self, n, gen_set=None, restrict=None, force=False): """ Returns `True` if there exists an `n`-dimensional irreducible representation of `self`, and `False` otherwise. Of course, this function runs `has_rep(n, restrict)` to verify there is a representation in the first place, and returns `False` if not. The argument `restrict` may be used equivalenty to its use in `has_rep()`. The argument `gen_set` may be set to `'PBW'` or `'pbw'`, if `self` has an algebra basis similar to that of a Poincaré-Birkhoff-Witt basis. Alternatively, an explicit generating set for the algorithm implemented by this function can be given, as a tuple or array of `FreeAlgebraElements`. This is only useful if the package cannot reduce the elements of `self`, but they can be reduced in theory. Use `force=True` if the function does not recognize the base field as computable, but the field is computable. """ if (not force and self.base_field() not in NumberFields and self.base_field() not in FiniteFields): raise TypeError( 'Base field must be computable. If %s is computable' % self.base_field() + ' then use force=True to bypass this.') if n not in ZZ or n < 1: raise ValueError('Dimension must be a positive integer.') if not self.has_rep(n, restrict): return False from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.groups.all import SymmetricGroup import math B = PolynomialRing(self.base_field(), (self.ngens() * n**2 + 1), 'x', order='deglex') M = MatrixSpace(B, n, sparse=True) gen_matrix = list() if not isinstance(restrict, (tuple, list)): restrict = [None for i in range(self.ngens())] if len(restrict) != self.ngens(): raise ValueError( 'Length of restrict does not match number of generators.') for i in range(self.ngens()): ith_gen_matrix = [] for j in range(n): for k in range(n): if restrict[i] == 'upper' and j > k: ith_gen_matrix.append(B.zero()) elif restrict[i] == 'lower' and j < k: ith_gen_matrix.append(B.zero()) elif restrict[i] == 'diagonal' and j != k: ith_gen_matrix.append(B.zero()) else: ith_gen_matrix.append(B.gen(j + (j + 1) * k + i * n**2)) gen_matrix.append(M(ith_gen_matrix)) relB = list() for i in range(self.nrels()): relB += self._to_matrix(self.rel(i), M, gen_matrix).list() Z = FreeAlgebra(ZZ, 2 * n - 2, 'Y') standard_poly = Z(0) for s in SymmetricGroup(2 * n - 2).list(): standard_poly += s.sign() * reduce( lambda x, y: x * y, [Z('Y%s' % (i - 1)) for i in s.tuple()]) if n <= 6 and is_NumberField(self.base_field()): p = 2 * n else: p = int( math.floor(n * math.sqrt(2 * n**2 / float(n - 1) + 1 / float(4)) + n / float(2) - 3)) if isinstance(gen_set, (tuple, list)): try: gen_set = [ self._to_matrix(elt, M, gen_matrix) for elt in gen_set ] except (NameError, TypeError) as error: print(error) if gen_set == None: word_gen_set = list(self._create_rep_gen_set(n, p)) gen_set = [ self._to_matrix(_to_element(self, [[word, self.one()]]), M, gen_matrix) for word in word_gen_set ] elif gen_set == 'pbw' or gen_set == 'PBW': word_gen_set = list(self._create_pbw_rep_gen_set(n, p)) gen_set = [ self._to_matrix(_to_element(self, [[word, self.one()]]), M, gen_matrix) for word in word_gen_set ] else: raise TypeError('Invalid generating set.') ordering = [i for i in range(2 * n - 2)] max_ordering = [ len(gen_set) - (2 * n - 2) + i for i in range(2 * n - 2) ] ordering.insert(0, 0) max_ordering.insert(0, len(gen_set)) rep_exists = False z = B.gen(B.ngens() - 1) while ordering[0] != max_ordering[0]: y = gen_set[ordering[0]].trace_of_product( standard_poly.subs({ Z('Y%s' % (j - 1)): gen_set[ordering[j]] for j in range(1, 2 * n - 1) })) radB_test = relB + [B(1) - z * y] if B.one() not in B.ideal(radB_test): rep_exists = True break for i in range(2 * n - 2, -1, -1): if i != 0 and ordering[i] != max_ordering[i]: ordering[i] += 1 break elif i == 0: ordering[i] += 1 if ordering[i] != max_ordering[i]: for j in range(1, 2 * n - 1): ordering[j] = j - 1 return rep_exists
class FinitelyPresentedAlgebra(QuotientRing_nc, Algebra): """ A class for finitely presented algebras. """ Element = FinitelyPresentedAlgebraElement def __init__(self, field, relations, names): """ The finitely presented algebra equivalent to the free algebra over `field` generated by `names` modulo the ideal generated by `relations`. """ if field not in Fields: raise TypeError('Base ring must be a field.') if type(relations) == str: relations = tuple(relations.split(',')) elif type(relations) == list: relations = tuple(relations) elif not isinstance(relations, tuple): raise TypeError( 'Relations must be given as a list, tuple, or string.') if type(names) == str: names = tuple(names.split(',')) elif type(names) == list: names = tuple(names) elif not isinstance(names, tuple): raise TypeError( 'Generators must be given as a list, tuple, or string.') self._ngens = len(names) self._nrels = len(relations) self._free_alg = FreeAlgebra(field, self._ngens, names) self._ideal = self._free_alg.ideal(relations) self._reduce = [] for f in self._ideal.gens(): mons = f.monomials() if len(mons) == 1: self._reduce.append([_to_word(f), Word(''), field.zero()]) if len(mons) == 2: coeffs = f.coefficients() if mons[0] < mons[1]: self._reduce.append([ _to_word(mons[1]), _to_word(mons[0]), -coeffs[0] * coeffs[1]**(-1) ]) elif mons[0] > mons[1]: self._reduce.append([ _to_word(mons[0]), _to_word(mons[1]), -coeffs[1] * coeffs[0]**(-1) ]) QuotientRing_nc.__init__(self, self._free_alg, self._ideal, names, category=AlgebrasWithBasis(field)) def _repr_(self): """ Returns a text representation of `self`. """ return 'Finitely presented algebra over {} with presentation <{} | {}>'.format( self.base_field(), ', '.join([str(self.gen(i)) for i in range(self.ngens())]), ', '.join([str(self.rel(i)) for i in range(self.nrels())])) def _latex_(self): """ Returns a LaTeX representation of `self`. """ return '{} \\langle {} \\mid {} \\rangle'.format( self.base_field()._latex_(), ', '.join(self.free_algebra().latex_variable_names()), ', '.join([self.rel(i)._latex_() for i in range(self.nrels())])) def _element_constructor_(self, f, coerce=True): """ Converts `f` into an element of `self`. """ if isinstance(f, FinitelyPresentedAlgebraElement): if f.parent() is self: return f f = f.lift() if coerce: A = self.free_algebra() f = A(f) return self.element_class(self, f) def ngens(self): """ Returns the number of generations of `self`. """ return self._ngens def nrels(self): """ Returns the number of relations of `self`. """ return self._nrels def base_ring(self): """ Returns the number of relations of `self`. """ return self.free_algebra().base_ring() def base_field(self): """ Same functionality as `base_ring()`. """ return self.free_algebra().base_ring() def free_algebra(self): """ Returns the base free algebra of `self`, which is a `FreeAlgebra` object over its base field with generators matching the generators of `self`. """ return self._free_alg def gen(self, i): """ Returns the i-th generator of `self`, as a `FreeAlgebraElement`. """ return self.free_algebra().gen(i) def gens(self): """ Returns the generators of `self`, as a tuple. """ return self.free_algebra().gens() def rel(self, i): """ Returns the i-th relation of `self`, as a `FreeAlgebraElement`. """ return self.defining_ideal().gen(i) def rels(self): """ Returns the relations of `self`, as a tuple. """ return self.defining_ideal().gens() def one(self): """ Returns the multiplicative identity of `self`, which is equal to the multiplicative identity of its base field. """ return self.element_class(self, self.free_algebra().one()) def zero(self): """ Returns the additive identity of `self`, which is equal to the additive identity of its base field. """ return self.element_class(self, self.free_algebra().zero()) def monoid(self): """ Returns the free monoid on the generators of `self`. """ return self.free_algebra().monoid() def has_rep(self, n, restrict=None, force=False): """ Returns `True` if there exists an `n`-dimensional representation of `self`, and `False` otherwise. The optional argument `restrict` may be used to restrict the possible images of the generators. To do so, `restrict` must be a tuple with entries of `None`, `'diagonal'`, `'lower'`, or `'upper'`. Its length must match the number of generators of `self`. Use `force=True` if the function does not recognize the base field as computable, but the field is computable. """ if (not force and self.base_field() not in NumberFields and self.base_field() not in FiniteFields): raise TypeError( 'Base field must be computable. If %s is computable' % self.base_field() + ' then use force=True to bypass this.') if n not in ZZ or n < 1: raise ValueError('Dimension must be a positive integer.') from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing import math B = PolynomialRing(self.base_field(), (self.ngens() * n**2 + 1), 'x', order='deglex') M = MatrixSpace(B, n, sparse=True) gen_matrix = list() if not isinstance(restrict, (tuple, list)): restrict = [None for i in range(self.ngens())] if len(restrict) != self.ngens(): raise ValueError( 'Length of restrict does not match number of generators.') for i in range(self.ngens()): ith_gen_matrix = [] for j in range(n): for k in range(n): if restrict[i] == 'upper' and j > k: ith_gen_matrix.append(B.zero()) elif restrict[i] == 'lower' and j < k: ith_gen_matrix.append(B.zero()) elif restrict[i] == 'diagonal' and j != k: ith_gen_matrix.append(B.zero()) else: ith_gen_matrix.append(B.gen(j + (j + 1) * k + i * n**2)) gen_matrix.append(M(ith_gen_matrix)) relB_list = list() for i in range(self.nrels()): relB_list += self._to_matrix(self.rel(i), M, gen_matrix).list() relB = B.ideal(relB_list) if relB.dimension() == -1: return False else: return True def has_irred_rep(self, n, gen_set=None, restrict=None, force=False): """ Returns `True` if there exists an `n`-dimensional irreducible representation of `self`, and `False` otherwise. Of course, this function runs `has_rep(n, restrict)` to verify there is a representation in the first place, and returns `False` if not. The argument `restrict` may be used equivalenty to its use in `has_rep()`. The argument `gen_set` may be set to `'PBW'` or `'pbw'`, if `self` has an algebra basis similar to that of a Poincaré-Birkhoff-Witt basis. Alternatively, an explicit generating set for the algorithm implemented by this function can be given, as a tuple or array of `FreeAlgebraElements`. This is only useful if the package cannot reduce the elements of `self`, but they can be reduced in theory. Use `force=True` if the function does not recognize the base field as computable, but the field is computable. """ if (not force and self.base_field() not in NumberFields and self.base_field() not in FiniteFields): raise TypeError( 'Base field must be computable. If %s is computable' % self.base_field() + ' then use force=True to bypass this.') if n not in ZZ or n < 1: raise ValueError('Dimension must be a positive integer.') if not self.has_rep(n, restrict): return False from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.groups.all import SymmetricGroup import math B = PolynomialRing(self.base_field(), (self.ngens() * n**2 + 1), 'x', order='deglex') M = MatrixSpace(B, n, sparse=True) gen_matrix = list() if not isinstance(restrict, (tuple, list)): restrict = [None for i in range(self.ngens())] if len(restrict) != self.ngens(): raise ValueError( 'Length of restrict does not match number of generators.') for i in range(self.ngens()): ith_gen_matrix = [] for j in range(n): for k in range(n): if restrict[i] == 'upper' and j > k: ith_gen_matrix.append(B.zero()) elif restrict[i] == 'lower' and j < k: ith_gen_matrix.append(B.zero()) elif restrict[i] == 'diagonal' and j != k: ith_gen_matrix.append(B.zero()) else: ith_gen_matrix.append(B.gen(j + (j + 1) * k + i * n**2)) gen_matrix.append(M(ith_gen_matrix)) relB = list() for i in range(self.nrels()): relB += self._to_matrix(self.rel(i), M, gen_matrix).list() Z = FreeAlgebra(ZZ, 2 * n - 2, 'Y') standard_poly = Z(0) for s in SymmetricGroup(2 * n - 2).list(): standard_poly += s.sign() * reduce( lambda x, y: x * y, [Z('Y%s' % (i - 1)) for i in s.tuple()]) if n <= 6 and is_NumberField(self.base_field()): p = 2 * n else: p = int( math.floor(n * math.sqrt(2 * n**2 / float(n - 1) + 1 / float(4)) + n / float(2) - 3)) if isinstance(gen_set, (tuple, list)): try: gen_set = [ self._to_matrix(elt, M, gen_matrix) for elt in gen_set ] except (NameError, TypeError) as error: print(error) if gen_set == None: word_gen_set = list(self._create_rep_gen_set(n, p)) gen_set = [ self._to_matrix(_to_element(self, [[word, self.one()]]), M, gen_matrix) for word in word_gen_set ] elif gen_set == 'pbw' or gen_set == 'PBW': word_gen_set = list(self._create_pbw_rep_gen_set(n, p)) gen_set = [ self._to_matrix(_to_element(self, [[word, self.one()]]), M, gen_matrix) for word in word_gen_set ] else: raise TypeError('Invalid generating set.') ordering = [i for i in range(2 * n - 2)] max_ordering = [ len(gen_set) - (2 * n - 2) + i for i in range(2 * n - 2) ] ordering.insert(0, 0) max_ordering.insert(0, len(gen_set)) rep_exists = False z = B.gen(B.ngens() - 1) while ordering[0] != max_ordering[0]: y = gen_set[ordering[0]].trace_of_product( standard_poly.subs({ Z('Y%s' % (j - 1)): gen_set[ordering[j]] for j in range(1, 2 * n - 1) })) radB_test = relB + [B(1) - z * y] if B.one() not in B.ideal(radB_test): rep_exists = True break for i in range(2 * n - 2, -1, -1): if i != 0 and ordering[i] != max_ordering[i]: ordering[i] += 1 break elif i == 0: ordering[i] += 1 if ordering[i] != max_ordering[i]: for j in range(1, 2 * n - 1): ordering[j] = j - 1 return rep_exists def is_rep(self, image, n, force=False): """ Returns `True` if the map generated by mapping the generators to the matrices defined in `image` is an `n`-dimensional representation of `self`, and `False` otherwise. The entries of `image` must be `n`-by-`n` matrices with entries in the algebraic closure of the base field of `self`. Its length must match the number of generators of `self.` Use `force=True` if the function does not recognize the base field as computable, but the field is computable. """ if (not force and self.base_field() not in NumberFields and self.base_field() not in FiniteFields): raise TypeError('Base field must be computable. If %s is' % self.base_field() + ' computable then use force=True to bypass this.') if n not in ZZ or n < 1: raise ValueError('Dimension must be a positive integer.') M = MatrixSpace(self.base_field().algebraic_closure(), n, sparse=True) if len(image) != self.ngens(): raise ValueError( 'Length of image does not match number of generators.') if False in {mat in M for mat in image}: raise TypeError('Improper image, must contain elements of %s.' % M._repr_()) image = [M(image[i]) for i in range(len(image))] for rel in self.rels(): if self._to_matrix(rel, M, image) != M.zero(): return False return True def is_irred_rep(self, image, n, force=False): """ Returns `True` if the map generated by mapping the generators to the matrices defined in `image` is an `n`-dimensional irreducible representation of `self`, and `False` otherwise. Like above, the entries of `image` must be `n`-by-`n` matrices with entries in the algebraic closure of the base field of `self`. Its length must match the number of generators of `self.` Use `force=True` if the function does not recognize the base field as computable, but the field is computable. """ if (not force and self.base_field() not in NumberFields and self.base_field() not in FiniteFields): raise TypeError( 'Base field must be computable. If %s is computable' % self.base_field() + ' then use force=True to bypass this.') if n not in ZZ or n < 1: raise ValueError('Dimension must be a positive integer.') if not self.is_rep(image, n): return False from sage.matrix.all import Matrix from sage.rings.number_field.number_field import is_NumberField import math M = MatrixSpace(self.base_field().algebraic_closure(), n, sparse=True) image = [M(image[i]).list() for i in range(len(image))] if n <= 6 and is_NumberField(self.base_field()): p = 2 * n else: p = int( math.floor(n * math.sqrt(2 * n**2 / float(n - 1) + 1 / float(4)) + n / float(2) - 3)) prod_set = list(self._create_prod_set(image, n, p)) prod_set.append(M.one()) vector = [mat.list() for mat in prod_set] return 0 not in Matrix(vector).echelon_form().diagonal() def _create_rep_gen_set(self, n, p): """ A helper function. Creates the gen_set for has_irred_rep(). """ import itertools alphabet = [_to_word(self.gen(i)) for i in range(self.ngens())] power_word = [alphabet[i]**n for i in range(self.ngens())] for i in range(1, p + 1): for words in itertools.product(alphabet, repeat=i): yield_word = True word = Word('') for w in words: word = word * w rewrite = [] empty_word = Word('') for rel in self._reduce: if rel[0].is_factor(word): yield_word = False break if yield_word: for pw in power_word: if pw.is_factor(word): yield_word = False break if yield_word: yield word def _create_pbw_rep_gen_set(self, n, p): """ A helper function. Creates the `gen_set` for `has_irred_rep()` when `gen_set='pbw'`. """ import itertools alphabet = [_to_word(self.gen(i)) for i in range(self.ngens())] power_word = [alphabet[i]**n for i in range(self.ngens())] commuter = [] for w in alphabet: for v in alphabet: if w > v: commuter.append(w * v) for i in range(1, p + 1): for words in itertools.product(alphabet, repeat=i): yield_word = True word = Word('') for w in words: word = word * w for c in commuter: if c.is_factor(word): yield_word = False for pw in power_word: if pw.is_factor(word): yield_word = False if yield_word: yield word def _create_prod_set(self, image, n, p): """ A helper function. Creates all products of matrices up to and including length `p`. """ import itertools M = MatrixSpace(self.base_field().algebraic_closure(), n, sparse=True) for i in range(1, p + 1): for matrices in itertools.product(image, repeat=i): result = M.one() for m in matrices: result = result * M(m) yield result def _to_matrix(self, f, M, image): """ A helper function. Converts a `FinitelyPresentedAlgebraElement` into a matrix. """ f_string = str(f).replace('^', '**') for i in range(self.ngens()): f_string = f_string.replace(str(self.gen(i)), '$' + str(self.gen(i))) for j in range(self.ngens()): f_string = f_string.replace('$' + str(self.gen(j)), 'image[%s]' % j) return M(eval(f_string))