def __classcall_private__(cls, R, n=None, M=None, ambient=None): """ Normalize input to ensure a unique representation. EXAMPLES:: sage: from sage.categories.examples.finite_dimensional_lie_algebras_with_basis import AbelianLieAlgebra sage: A1 = AbelianLieAlgebra(QQ, n=3) sage: A2 = AbelianLieAlgebra(QQ, M=FreeModule(QQ, 3)) sage: A3 = AbelianLieAlgebra(QQ, 3, FreeModule(QQ, 3)) sage: A1 is A2 and A2 is A3 True sage: A1 = AbelianLieAlgebra(QQ, 2) sage: A2 = AbelianLieAlgebra(ZZ, 2) sage: A1 is A2 False sage: A1 = AbelianLieAlgebra(QQ, 0) sage: A2 = AbelianLieAlgebra(QQ, 1) sage: A1 is A2 False """ if M is None: M = FreeModule(R, n) else: M = M.change_ring(R) n = M.dimension() return super(AbelianLieAlgebra, cls).__classcall__(cls, R, n=n, M=M, ambient=ambient)
def __classcall_private__(cls, R, n=None, M=None, ambient=None): """ Normalize input to ensure a unique representation. EXAMPLES:: sage: from sage.categories.examples.finite_dimensional_lie_algebras_with_basis import AbelianLieAlgebra sage: A1 = AbelianLieAlgebra(QQ, n=3) sage: A2 = AbelianLieAlgebra(QQ, M=FreeModule(QQ, 3)) sage: A3 = AbelianLieAlgebra(QQ, 3, FreeModule(QQ, 3)) sage: A1 is A2 and A2 is A3 True sage: A1 = AbelianLieAlgebra(QQ, 2) sage: A2 = AbelianLieAlgebra(ZZ, 2) sage: A1 is A2 False sage: A1 = AbelianLieAlgebra(QQ, 0) sage: A2 = AbelianLieAlgebra(QQ, 1) sage: A1 is A2 False """ if M is None: M = FreeModule(R, n) else: M = M.change_ring(R) n = M.dimension() return super(AbelianLieAlgebra, cls).__classcall__(cls, R, n=n, M=M, ambient=ambient)
def normal_cone(self): r""" Return the (closure of the) normal cone of the triangulation. Recall that a regular triangulation is one that equals the "crease lines" of a convex piecewise-linear function. This support function is not unique, for example, you can scale it by a positive constant. The set of all piecewise-linear functions with fixed creases forms an open cone. This cone can be interpreted as the cone of normal vectors at a point of the secondary polytope, which is why we call it normal cone. See [GKZ]_ Section 7.1 for details. OUTPUT: The closure of the normal cone. The `i`-th entry equals the value of the piecewise-linear function at the `i`-th point of the configuration. For an irregular triangulation, the normal cone is empty. In this case, a single point (the origin) is returned. EXAMPLES:: sage: triangulation = polytopes.n_cube(2).triangulate(engine='internal') sage: triangulation (<0,1,3>, <0,2,3>) sage: N = triangulation.normal_cone(); N 4-d cone in 4-d lattice sage: N.rays() (-1, 0, 0, 0), ( 1, 0, 1, 0), (-1, 0, -1, 0), ( 1, 0, 0, -1), (-1, 0, 0, 1), ( 1, 1, 0, 0), (-1, -1, 0, 0) in Ambient free module of rank 4 over the principal ideal domain Integer Ring sage: N.dual().rays() (-1, 1, 1, -1) in Ambient free module of rank 4 over the principal ideal domain Integer Ring TESTS:: sage: polytopes.n_simplex(2).triangulate().normal_cone() 3-d cone in 3-d lattice sage: _.dual().is_trivial() True """ if not self.point_configuration().base_ring().is_subring(QQ): raise NotImplementedError("Only base rings ZZ and QQ are supported") from sage.libs.ppl import Variable, Constraint, Constraint_System, Linear_Expression, C_Polyhedron from sage.matrix.constructor import matrix from sage.misc.misc import uniq from sage.rings.arith import lcm pc = self.point_configuration() cs = Constraint_System() for facet in self.interior_facets(): s0, s1 = self._boundary_simplex_dictionary()[facet] p = set(s0).difference(facet).pop() q = set(s1).difference(facet).pop() origin = pc.point(p).reduced_affine_vector() base_indices = [i for i in s0 if i != p] base = matrix([pc.point(i).reduced_affine_vector() - origin for i in base_indices]) sol = base.solve_left(pc.point(q).reduced_affine_vector() - origin) relation = [0] * pc.n_points() relation[p] = sum(sol) - 1 relation[q] = 1 for i, base_i in enumerate(base_indices): relation[base_i] = -sol[i] rel_denom = lcm([QQ(r).denominator() for r in relation]) relation = [ZZ(r * rel_denom) for r in relation] ex = Linear_Expression(relation, 0) cs.insert(ex >= 0) from sage.modules.free_module import FreeModule ambient = FreeModule(ZZ, self.point_configuration().n_points()) if cs.empty(): cone = C_Polyhedron(ambient.dimension(), "universe") else: cone = C_Polyhedron(cs) from sage.geometry.cone import _Cone_from_PPL return _Cone_from_PPL(cone, lattice=ambient)
def normal_cone(self): r""" Return the (closure of the) normal cone of the triangulation. Recall that a regular triangulation is one that equals the "crease lines" of a convex piecewise-linear function. This support function is not unique, for example, you can scale it by a positive constant. The set of all piecewise-linear functions with fixed creases forms an open cone. This cone can be interpreted as the cone of normal vectors at a point of the secondary polytope, which is why we call it normal cone. See [GKZ1994]_ Section 7.1 for details. OUTPUT: The closure of the normal cone. The `i`-th entry equals the value of the piecewise-linear function at the `i`-th point of the configuration. For an irregular triangulation, the normal cone is empty. In this case, a single point (the origin) is returned. EXAMPLES:: sage: triangulation = polytopes.hypercube(2).triangulate(engine='internal') sage: triangulation (<0,1,3>, <1,2,3>) sage: N = triangulation.normal_cone(); N 4-d cone in 4-d lattice sage: N.rays() ( 0, 0, 0, -1), ( 0, 0, 1, 1), ( 0, 0, -1, -1), ( 1, 0, 0, 1), (-1, 0, 0, -1), ( 0, 1, 0, -1), ( 0, -1, 0, 1) in Ambient free module of rank 4 over the principal ideal domain Integer Ring sage: N.dual().rays() (1, -1, 1, -1) in Ambient free module of rank 4 over the principal ideal domain Integer Ring TESTS:: sage: polytopes.simplex(2).triangulate().normal_cone() 3-d cone in 3-d lattice sage: _.dual().is_trivial() True """ if not self.point_configuration().base_ring().is_subring(QQ): raise NotImplementedError( 'Only base rings ZZ and QQ are supported') from ppl import Constraint_System, Linear_Expression, C_Polyhedron from sage.matrix.constructor import matrix from sage.arith.all import lcm pc = self.point_configuration() cs = Constraint_System() for facet in self.interior_facets(): s0, s1 = self._boundary_simplex_dictionary()[facet] p = set(s0).difference(facet).pop() q = set(s1).difference(facet).pop() origin = pc.point(p).reduced_affine_vector() base_indices = [i for i in s0 if i != p] base = matrix([ pc.point(i).reduced_affine_vector() - origin for i in base_indices ]) sol = base.solve_left(pc.point(q).reduced_affine_vector() - origin) relation = [0] * pc.n_points() relation[p] = sum(sol) - 1 relation[q] = 1 for i, base_i in enumerate(base_indices): relation[base_i] = -sol[i] rel_denom = lcm([QQ(r).denominator() for r in relation]) relation = [ZZ(r * rel_denom) for r in relation] ex = Linear_Expression(relation, 0) cs.insert(ex >= 0) from sage.modules.free_module import FreeModule ambient = FreeModule(ZZ, self.point_configuration().n_points()) if cs.empty(): cone = C_Polyhedron(ambient.dimension(), 'universe') else: cone = C_Polyhedron(cs) from sage.geometry.cone import _Cone_from_PPL return _Cone_from_PPL(cone, lattice=ambient)
class FreeAlgebraQuotient(UniqueRepresentation, Algebra, object): @staticmethod def __classcall__(cls, A, mons, mats, names): """ Used to support unique representation. EXAMPLES:: sage: H = sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ)[0] # indirect doctest sage: H1 = sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ)[0] sage: H is H1 True """ new_mats = [] for M in mats: M = M.parent()(M) M.set_immutable() new_mats.append(M) return super(FreeAlgebraQuotient, cls).__classcall__(cls, A, tuple(mons), tuple(new_mats), tuple(names)) Element = FreeAlgebraQuotientElement def __init__(self, A, mons, mats, names): """ Returns a quotient algebra defined via the action of a free algebra A on a (finitely generated) free module. The input for the quotient algebra is a list of monomials (in the underlying monoid for A) which form a free basis for the module of A, and a list of matrices, which give the action of the free generators of A on this monomial basis. EXAMPLES: Quaternion algebra defined in terms of three generators:: sage: n = 3 sage: A = FreeAlgebra(QQ,n,'i') sage: F = A.monoid() sage: i, j, k = F.gens() sage: mons = [ F(1), i, j, k ] sage: M = MatrixSpace(QQ,4) sage: 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]) ] sage: H3.<i,j,k> = FreeAlgebraQuotient(A,mons,mats) sage: x = 1 + i + j + k sage: x 1 + i + j + k sage: x**128 -170141183460469231731687303715884105728 + 170141183460469231731687303715884105728*i + 170141183460469231731687303715884105728*j + 170141183460469231731687303715884105728*k Same algebra defined in terms of two generators, with some penalty on already slow arithmetic. :: sage: n = 2 sage: A = FreeAlgebra(QQ,n,'x') sage: F = A.monoid() sage: i, j = F.gens() sage: mons = [ F(1), i, j, i*j ] sage: r = len(mons) sage: M = MatrixSpace(QQ,r) sage: 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]) ] sage: H2.<i,j> = A.quotient(mons,mats) sage: k = i*j sage: x = 1 + i + j + k sage: x 1 + i + j + i*j sage: x**128 -170141183460469231731687303715884105728 + 170141183460469231731687303715884105728*i + 170141183460469231731687303715884105728*j + 170141183460469231731687303715884105728*i*j TEST:: sage: TestSuite(H2).run() """ if not is_FreeAlgebra(A): raise TypeError, "Argument A must be an algebra." R = A.base_ring() # if not R.is_field(): # TODO: why? # raise TypeError, "Base ring of argument A must be a field." n = A.ngens() assert n == len(mats) self.__free_algebra = A self.__ngens = n self.__dim = len(mons) self.__module = FreeModule(R, self.__dim) self.__matrix_action = mats self.__monomial_basis = mons # elements of free monoid Algebra.__init__(self, R, names, normalize=True) def __eq__(self, right): """ Return True if all defining properties of self and right match up. EXAMPLES:: sage: HQ = sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ)[0] sage: HZ = sage.algebras.free_algebra_quotient.hamilton_quatalg(ZZ)[0] sage: HQ == HQ True sage: HQ == HZ False sage: HZ == QQ False """ return type(self) == type(right) and \ self.ngens() == right.ngens() and \ self.rank() == right.rank() and \ self.module() == right.module() and \ self.matrix_action() == right.matrix_action() and \ self.monomial_basis() == right.monomial_basis() def _element_constructor_(self, x): """ EXAMPLES:: sage: H, (i,j,k) = sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ) sage: H._element_constructor_(i) is i True sage: a = H._element_constructor_(1); a 1 sage: a in H True sage: a = H._element_constructor_([1,2,3,4]); a 1 + 2*i + 3*j + 4*k """ if isinstance(x, FreeAlgebraQuotientElement) and x.parent() is self: return x return self.element_class(self, x) def _coerce_map_from_(self, S): """ EXAMPLES:: sage: H, (i,j,k) = sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ) sage: H._coerce_map_from_(H) True sage: H._coerce_map_from_(QQ) True sage: H._coerce_map_from_(GF(7)) False """ return S == self or self.__free_algebra.has_coerce_map_from(S) def _repr_(self): """ EXAMPLES:: sage: H, (i,j,k) = sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ) sage: H._repr_() "Free algebra quotient on 3 generators ('i', 'j', 'k') and dimension 4 over Rational Field" """ R = self.base_ring() n = self.__ngens r = self.__module.dimension() x = self.variable_names() return "Free algebra quotient on %s generators %s and dimension %s over %s" % ( n, x, r, R) def gen(self, i): """ The i-th generator of the algebra. EXAMPLES:: sage: H, (i,j,k) = sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ) sage: H.gen(0) i sage: H.gen(2) k An IndexError is raised if an invalid generator is requested:: sage: H.gen(3) Traceback (most recent call last): ... IndexError: Argument i (= 3) must be between 0 and 2. Negative indexing into the generators is not supported:: sage: H.gen(-1) Traceback (most recent call last): ... IndexError: Argument i (= -1) must be between 0 and 2. """ n = self.__ngens if i < 0 or not i < n: raise IndexError, "Argument i (= %s) must be between 0 and %s." % ( i, n - 1) R = self.base_ring() F = self.__free_algebra.monoid() n = self.__ngens return self.element_class(self, {F.gen(i): R(1)}) def ngens(self): """ The number of generators of the algebra. EXAMPLES:: sage: sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ)[0].ngens() 3 """ return self.__ngens def dimension(self): """ The rank of the algebra (as a free module). EXAMPLES:: sage: sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ)[0].dimension() 4 """ return self.__dim def matrix_action(self): """ EXAMPLES:: sage: sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ)[0].matrix_action() ( [ 0 1 0 0] [ 0 0 1 0] [ 0 0 0 1] [-1 0 0 0] [ 0 0 0 1] [ 0 0 -1 0] [ 0 0 0 -1] [-1 0 0 0] [ 0 1 0 0] [ 0 0 1 0], [ 0 -1 0 0], [-1 0 0 0] ) """ return self.__matrix_action def monomial_basis(self): """ EXAMPLES:: sage: sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ)[0].monomial_basis() (1, i0, i1, i2) """ return self.__monomial_basis def rank(self): """ The rank of the algebra (as a free module). EXAMPLES:: sage: sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ)[0].rank() 4 """ return self.__dim def module(self): """ The free module of the algebra. sage: H = sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ)[0]; H Free algebra quotient on 3 generators ('i', 'j', 'k') and dimension 4 over Rational Field sage: H.module() Vector space of dimension 4 over Rational Field """ return self.__module def monoid(self): """ The free monoid of generators of the algebra. EXAMPLES:: sage: sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ)[0].monoid() Free monoid on 3 generators (i0, i1, i2) """ return self.__free_algebra.monoid() def monomial_basis(self): """ The free monoid of generators of the algebra as elements of a free monoid. EXAMPLES:: sage: sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ)[0].monomial_basis() (1, i0, i1, i2) """ return self.__monomial_basis def free_algebra(self): """ The free algebra generating the algebra. EXAMPLES:: sage: sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ)[0].free_algebra() Free Algebra on 3 generators (i0, i1, i2) over Rational Field """ return self.__free_algebra
def modform_cusp_info(calc, S, l, precLimit): """ This goes through all the cusps and compares the space given by `(f|R)[S]` with the space of Elliptic modular forms expansion at those cusps. """ assert l == S.det() assert list(calc.curlS) == [S] D = calc.D HermWeight = calc.HermWeight reducedCurlFSize = calc.matrixColumnCount herm_modform_fe_expannsion = FreeModule(QQ, reducedCurlFSize) if not Integer(l).is_squarefree(): # The calculation of the cusp expansion space takes very long here, thus # we skip them for now. return None for cusp in Gamma0(l).cusps(): if cusp == Infinity: continue M = cusp_matrix(cusp) try: gamma, R, tM = solveR(M, S, space=CurlO(D)) except Exception: print (M, S) raise R.set_immutable() # for caching, we need it hashable herm_modforms = herm_modform_fe_expannsion.echelonized_basis_matrix().transpose() ell_R_denom, ell_R_order, M_R = calcMatrixTrans(calc, R) CycloDegree_R = CyclotomicField(ell_R_order).degree() print "M_R[0] nrows, ell_R_denom, ell_R_order, Cyclo degree:", \ M_R[0].nrows(), ell_R_denom, ell_R_order, CycloDegree_R # The maximum precision we can use is M_R[0].nrows(). # However, that can be quite huge (e.g. 600). ce_prec = min(precLimit, M_R[0].nrows()) ce = cuspExpansions(level=l, weight=2*HermWeight, prec=ce_prec) ell_M_denom, ell_M = ce.expansion_at(SL2Z(M)) print "ell_M_denom, ell_M nrows:", ell_M_denom, ell_M.nrows() ell_M_order = ell_R_order # not sure here. just try the one from R. toCyclPowerBase would fail if this doesn't work # CyclotomicField(l / prod(l.prime_divisors())) should also work. # Transform to same denom. denom_lcm = int(lcm(ell_R_denom, ell_M_denom)) ell_M = addRows(ell_M, denom_lcm / ell_M_denom) M_R = [addRows(M_R_i, denom_lcm / ell_R_denom) for M_R_i in M_R] ell_R_denom = ell_M_denom = denom_lcm print "new denom:", denom_lcm assert ell_R_denom == ell_M_denom # ell_M rows are the elliptic FE. M_R[i] columns are the elliptic FE. # We expect that M_R gives a higher precision for the ell FE. I'm not sure # if this is always true but we expect it here (maybe not needed, though). print "precision of M_R[0], ell_M, wanted:", M_R[0].nrows(), ell_M.ncols(), ce_prec assert ell_M.ncols() >= ce_prec prec = min(M_R[0].nrows(), ell_M.ncols()) # cut to have same precision M_R = [M_R_i[:prec,:] for M_R_i in M_R] ell_M = ell_M[:,:prec] assert ell_M.ncols() == M_R[0].nrows() == prec print "M_R[0] rank, herm rank, mult rank:", \ M_R[0].rank(), herm_modforms.rank(), (M_R[0] * herm_modforms).rank() ell_R = [M_R_i * herm_modforms for M_R_i in M_R] # I'm not sure on this. Seems to be true and it simplifies things in the following. assert ell_M_order <= ell_R_order, "{0}".format((ell_M_order, ell_R_order)) assert ell_R_order % ell_M_order == 0, "{0}".format((ell_M_order, ell_R_order)) # Transform to same Cyclomotic Field in same power base. ell_M2 = toCyclPowerBase(ell_M, ell_M_order) ell_R2 = toLowerCyclBase(ell_R, ell_R_order, ell_M_order) # We must work with the matrix. maybe we should transform hf_M instead to a # higher order field instead, if this ever fails (I'm not sure). assert ell_R2 is not None assert len(ell_M2) == len(ell_R2) # They should have the same power base & same degree now. print "ell_M2[0], ell_R2[0] rank with order %i:" % ell_M_order, ell_M2[0].rank(), ell_R2[0].rank() assert len(M_R) == len(ell_M2) for i in range(len(ell_M2)): ell_M_space = ell_M2[i].row_space() ell_R_space = ell_R2[i].column_space() merged = ell_M_space.intersection(ell_R_space) herm_modform_fe_expannsion_Ci = M_R[i].solve_right( merged.basis_matrix().transpose() ) herm_modform_fe_expannsion_Ci_module = herm_modform_fe_expannsion_Ci.column_module() herm_modform_fe_expannsion_Ci_module += M_R[i].right_kernel() extra_check_on_herm_superspace( vs=herm_modform_fe_expannsion_Ci_module, D=D, B_cF=calc.B_cF, HermWeight=HermWeight ) herm_modform_fe_expannsion = herm_modform_fe_expannsion.intersection( herm_modform_fe_expannsion_Ci_module ) print "power", i, merged.dimension(), herm_modform_fe_expannsion_Ci_module.dimension(), \ herm_modform_fe_expannsion.dimension() current_dimension = herm_modform_fe_expannsion.dimension() return herm_modform_fe_expannsion
def herm_modform_space(D, HermWeight, B_cF=10, parallelization=None, reduction_method_flags=-1): """ This calculates the vectorspace of Fourier expansions to Hermitian modular forms of weight `HermWeight` over \Gamma, where \Gamma = \Sp_2(\curlO) and \curlO is the maximal order of \QQ(\sqrt{D}). Each Fourier coefficient vector is indexed up to a precision \curlF which is given by `B_cF` such that for every [a,b,c] \in \curlF \subset \Lambda, we have 0 \le a,c \le B_cF. The function `herm_modform_indexset()` returns reduced matrices of that precision index set \curlF. """ if HermWeight % 3 != 0: raise TypeError, "the modulform is trivial/zero if HermWeight is not divisible by 3" # Transform these into native Python objects. We don't want to have # any Sage objects (e.g. Integer) here so that the cache index stays # unique. D = int(D) HermWeight = int(HermWeight) B_cF = int(B_cF) reduction_method_flags = int(reduction_method_flags) calc = C.Calc() calc.init(D = D, HermWeight = HermWeight, B_cF=B_cF) calc.calcReducedCurlF() reducedCurlFSize = calc.matrixColumnCount # Calculate the dimension of Hermitian modular form space. dim = herm_modform_space_dim(D=D, HermWeight=HermWeight) cacheIdx = (D, HermWeight, B_cF) if reduction_method_flags != -1: cacheIdx += (reduction_method_flags,) try: herm_modform_fe_expannsion, calc, curlS_denoms, pending_tasks = hermModformSpaceCache[cacheIdx] if not isinstance(calc, C.Calc): raise TypeError print "Resuming from %s" % hermModformSpaceCache._filename_for_key(cacheIdx) except (TypeError, ValueError, KeyError, EOFError): # old format or not cached or cache incomplete herm_modform_fe_expannsion = FreeModule(QQ, reducedCurlFSize) curlS_denoms = set() # the denominators of the visited matrices S pending_tasks = () current_dimension = herm_modform_fe_expannsion.dimension() verbose("current dimension: %i, wanted: %i" % (herm_modform_fe_expannsion.dimension(), dim)) if dim == 0: print "dim == 0 -> exit" return def task_iter_func(): # Iterate S \in Mat_2^T(\curlO), S > 0. while True: # Get the next S. # If calc.curlS is not empty, this is because we have recovered from a resume. if len(calc.curlS) == 0: S = calc.getNextS() else: assert len(calc.curlS) == 1 S = calc.curlS[0] l = S.det() l = toInt(l) curlS_denoms.add(l) verbose("trying S={0}, det={1}".format(S, l)) if reduction_method_flags & Method_Elliptic_reduction: yield CalcTask( func=modform_restriction_info, calc=calc, kwargs={"S":S, "l":l}) if reduction_method_flags & Method_EllipticCusp_reduction: precLimit = calcPrecisionDimension(B_cF=B_cF, S=S) yield CalcTask( func=modform_cusp_info, calc=calc, kwargs={"S":S, "l":l, "precLimit": precLimit}) calc.curlS_clearMatrices() # In the C++ internal curlS, clear previous matrices. task_iter = task_iter_func() if parallelization: parallelization.task_iter = task_iter if parallelization and pending_tasks: for func, name in pending_tasks: parallelization.exec_task(func=func, name=name) step_counter = 0 while True: if parallelization: new_task_count = 0 spaces = [] for task, exc, newspace in parallelization.get_all_ready_results(): if exc: raise exc if newspace is None: verbose("no data from %r" % task) continue if newspace.dimension() == reducedCurlFSize: verbose("no information gain from %r" % task) continue spacecomment = task assert newspace.dimension() >= dim, "%r, %r" % (task, newspace) if newspace.dimension() < herm_modform_fe_expannsion.dimension(): # Swap newspace with herm_modform_fe_expannsion. herm_modform_fe_expannsion, newspace = newspace, herm_modform_fe_expannsion current_dimension = herm_modform_fe_expannsion.dimension() if current_dimension == dim: if not isinstance(task, IntersectSpacesTask): verbose("warning: we expected IntersectSpacesTask for final dim but got: %r" % task) verbose("new dimension: %i, wanted: %i" % (current_dimension, dim)) if current_dimension <= 20: pprint(herm_modform_fe_expannsion.basis()) spacecomment = "<old base space>" spaces += [(spacecomment, newspace)] if spaces: parallelization.exec_task(IntersectSpacesTask(herm_modform_fe_expannsion, spaces)) new_task_count += 1 new_task_count += parallelization.maybe_queue_tasks() time.sleep(0.1) else: # no parallelization new_task_count = 1 if pending_tasks: # from some resuming task,_ = pending_tasks[0] pending_tasks = pending_tasks[1:] else: task = next(task_iter) newspace = task() if newspace is None: verbose("no data from %r" % task) if newspace is not None and newspace.dimension() == reducedCurlFSize: verbose("no information gain from %r" % task) newspace = None if newspace is not None: new_task_count += 1 spacecomment = task herm_modform_fe_expannsion_new = IntersectSpacesTask( herm_modform_fe_expannsion, [(spacecomment, newspace)])() if herm_modform_fe_expannsion_new is not None: herm_modform_fe_expannsion = herm_modform_fe_expannsion_new current_dimension = herm_modform_fe_expannsion.dimension() verbose("new dimension: %i, wanted: %i" % (current_dimension, dim)) if new_task_count > 0: step_counter += 1 if step_counter % 10 == 0: verbose("save state after %i steps to %s" % (step_counter, os.path.basename(hermModformSpaceCache._filename_for_key(cacheIdx)))) if parallelization: pending_tasks = parallelization.get_pending_tasks() hermModformSpaceCache[cacheIdx] = (herm_modform_fe_expannsion, calc, curlS_denoms, pending_tasks) if current_dimension == dim: verbose("finished!") break # Test for some other S with other not-yet-seen denominator. check_herm_modform_space( calc, herm_modform_space=herm_modform_fe_expannsion, used_curlS_denoms=curlS_denoms ) return herm_modform_fe_expannsion
class FreeAlgebraQuotient(UniqueRepresentation, Algebra, object): @staticmethod def __classcall__(cls, A, mons, mats, names): """ Used to support unique representation. EXAMPLES:: sage: H = sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ)[0] # indirect doctest sage: H1 = sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ)[0] sage: H is H1 True """ new_mats = [] for M in mats: M = M.parent()(M) M.set_immutable() new_mats.append(M) return super(FreeAlgebraQuotient, cls).__classcall__(cls, A, tuple(mons), tuple(new_mats), tuple(names)) Element = FreeAlgebraQuotientElement def __init__(self, A, mons, mats, names): """ Returns a quotient algebra defined via the action of a free algebra A on a (finitely generated) free module. The input for the quotient algebra is a list of monomials (in the underlying monoid for A) which form a free basis for the module of A, and a list of matrices, which give the action of the free generators of A on this monomial basis. EXAMPLES: Quaternion algebra defined in terms of three generators:: sage: n = 3 sage: A = FreeAlgebra(QQ,n,'i') sage: F = A.monoid() sage: i, j, k = F.gens() sage: mons = [ F(1), i, j, k ] sage: M = MatrixSpace(QQ,4) sage: 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]) ] sage: H3.<i,j,k> = FreeAlgebraQuotient(A,mons,mats) sage: x = 1 + i + j + k sage: x 1 + i + j + k sage: x**128 -170141183460469231731687303715884105728 + 170141183460469231731687303715884105728*i + 170141183460469231731687303715884105728*j + 170141183460469231731687303715884105728*k Same algebra defined in terms of two generators, with some penalty on already slow arithmetic. :: sage: n = 2 sage: A = FreeAlgebra(QQ,n,'x') sage: F = A.monoid() sage: i, j = F.gens() sage: mons = [ F(1), i, j, i*j ] sage: r = len(mons) sage: M = MatrixSpace(QQ,r) sage: 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]) ] sage: H2.<i,j> = A.quotient(mons,mats) sage: k = i*j sage: x = 1 + i + j + k sage: x 1 + i + j + i*j sage: x**128 -170141183460469231731687303715884105728 + 170141183460469231731687303715884105728*i + 170141183460469231731687303715884105728*j + 170141183460469231731687303715884105728*i*j TEST:: sage: TestSuite(H2).run() """ if not is_FreeAlgebra(A): raise TypeError("Argument A must be an algebra.") R = A.base_ring() # if not R.is_field(): # TODO: why? # raise TypeError, "Base ring of argument A must be a field." n = A.ngens() assert n == len(mats) self.__free_algebra = A self.__ngens = n self.__dim = len(mons) self.__module = FreeModule(R,self.__dim) self.__matrix_action = mats self.__monomial_basis = mons # elements of free monoid Algebra.__init__(self, R, names, normalize=True) def __eq__(self, right): """ Return True if all defining properties of self and right match up. EXAMPLES:: sage: HQ = sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ)[0] sage: HZ = sage.algebras.free_algebra_quotient.hamilton_quatalg(ZZ)[0] sage: HQ == HQ True sage: HQ == HZ False sage: HZ == QQ False """ return isinstance(right, FreeAlgebraQuotient) and \ self.ngens() == right.ngens() and \ self.rank() == right.rank() and \ self.module() == right.module() and \ self.matrix_action() == right.matrix_action() and \ self.monomial_basis() == right.monomial_basis() def _element_constructor_(self, x): """ EXAMPLES:: sage: H, (i,j,k) = sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ) sage: H._element_constructor_(i) is i True sage: a = H._element_constructor_(1); a 1 sage: a in H True sage: a = H._element_constructor_([1,2,3,4]); a 1 + 2*i + 3*j + 4*k """ if isinstance(x, FreeAlgebraQuotientElement) and x.parent() is self: return x return self.element_class(self,x) def _coerce_map_from_(self,S): """ EXAMPLES:: sage: H, (i,j,k) = sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ) sage: H._coerce_map_from_(H) True sage: H._coerce_map_from_(QQ) True sage: H._coerce_map_from_(GF(7)) False """ return S==self or self.__free_algebra.has_coerce_map_from(S) def _repr_(self): """ EXAMPLES:: sage: H, (i,j,k) = sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ) sage: H._repr_() "Free algebra quotient on 3 generators ('i', 'j', 'k') and dimension 4 over Rational Field" """ R = self.base_ring() n = self.__ngens r = self.__module.dimension() x = self.variable_names() return "Free algebra quotient on %s generators %s and dimension %s over %s"%(n,x,r,R) def gen(self, i): """ The i-th generator of the algebra. EXAMPLES:: sage: H, (i,j,k) = sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ) sage: H.gen(0) i sage: H.gen(2) k An IndexError is raised if an invalid generator is requested:: sage: H.gen(3) Traceback (most recent call last): ... IndexError: Argument i (= 3) must be between 0 and 2. Negative indexing into the generators is not supported:: sage: H.gen(-1) Traceback (most recent call last): ... IndexError: Argument i (= -1) must be between 0 and 2. """ n = self.__ngens if i < 0 or not i < n: raise IndexError("Argument i (= %s) must be between 0 and %s."%(i, n-1)) R = self.base_ring() F = self.__free_algebra.monoid() n = self.__ngens return self.element_class(self,{F.gen(i):R(1)}) def ngens(self): """ The number of generators of the algebra. EXAMPLES:: sage: sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ)[0].ngens() 3 """ return self.__ngens def dimension(self): """ The rank of the algebra (as a free module). EXAMPLES:: sage: sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ)[0].dimension() 4 """ return self.__dim def matrix_action(self): """ EXAMPLES:: sage: sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ)[0].matrix_action() ( [ 0 1 0 0] [ 0 0 1 0] [ 0 0 0 1] [-1 0 0 0] [ 0 0 0 1] [ 0 0 -1 0] [ 0 0 0 -1] [-1 0 0 0] [ 0 1 0 0] [ 0 0 1 0], [ 0 -1 0 0], [-1 0 0 0] ) """ return self.__matrix_action def monomial_basis(self): """ EXAMPLES:: sage: sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ)[0].monomial_basis() (1, i0, i1, i2) """ return self.__monomial_basis def rank(self): """ The rank of the algebra (as a free module). EXAMPLES:: sage: sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ)[0].rank() 4 """ return self.__dim def module(self): """ The free module of the algebra. sage: H = sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ)[0]; H Free algebra quotient on 3 generators ('i', 'j', 'k') and dimension 4 over Rational Field sage: H.module() Vector space of dimension 4 over Rational Field """ return self.__module def monoid(self): """ The free monoid of generators of the algebra. EXAMPLES:: sage: sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ)[0].monoid() Free monoid on 3 generators (i0, i1, i2) """ return self.__free_algebra.monoid() def monomial_basis(self): """ The free monoid of generators of the algebra as elements of a free monoid. EXAMPLES:: sage: sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ)[0].monomial_basis() (1, i0, i1, i2) """ return self.__monomial_basis def free_algebra(self): """ The free algebra generating the algebra. EXAMPLES:: sage: sage.algebras.free_algebra_quotient.hamilton_quatalg(QQ)[0].free_algebra() Free Algebra on 3 generators (i0, i1, i2) over Rational Field """ return self.__free_algebra