def _check_generic(self, cones1, cones2, v): """ Returns pairs of cones which intersect with respect to `v`. For every pair of cones `c1`, `c2` from respective lists, check whether they intersect generically with respect to `v`. Return list of ordered pairs of cones which intersect with respect to `v`. This means the intersection of `c1` and `c2+v` is non-empty. EXAMPLES:: sage: A = MW(toric_varieties.P2().fan()) sage: cones = A._fan(1) sage: v = (.1,.3) sage: A._check_generic(cones,cones,v) [(1-d cone of Rational polyhedral fan in 2-d lattice N, 1-d cone of Rational polyhedral fan in 2-d lattice N)] """ good_pairs = [] for c1 in cones1: for c2 in cones2: # check whether v is in c1 - c2 (i.e. mink sum of c1 and -c2) C = Cone(rays=[r for r in c1.rays()] + [-1 * r for r in c2.rays()]) if C.contains(v): good_pairs.append((c1, c2)) return good_pairs
def cone_in_homology(tri, angle): rays = taut_rays(tri, angle) if len(rays) == 0: return [] else: A = projection_to_homology(tri,angle) projectedRays = [A*v for v in rays] C = Cone(projectedRays) return [ray for ray in C.rays()]
def experiment(): rank = 5 level = 4 num_points = 11 client = cbc.CBClient() liealg = cbd.TypeALieAlgebra(rank, store_fusion=True, exact=True) rays = [] wts = [] ranks = [] for wt in liealg.get_weights(level): if wt == tuple([0 for x in range(0, rank)]): continue cbb = cbd.SymmetricConformalBlocksBundle(client, liealg, wt, num_points, level) if cbb.get_rank() == 0: continue divisor = cbb.get_symmetrized_divisor() if divisor == [ sage.Rational(0) for x in range(0, num_points // 2 - 1) ]: continue rays = rays + [divisor] wts = wts + [wt] ranks = ranks + [cbb.get_rank()] #print(wt, cbb.get_rank(), divisor) p = Polyhedron(rays=rays, base_ring=RationalField(), backend="cdd") extremal_rays = [list(v.vector()) for v in p.Vrepresentation()] c = Cone(p) print("Extremal rays:") for i in range(0, len(rays)): ray = rays[i] wt = wts[i] rk = ranks[i] if ray in extremal_rays: print(rk, wt, ray)
def fan_isomorphism_generator(fan1, fan2): """ Iterate over the isomorphisms from ``fan1`` to ``fan2``. ALGORITHM: The :meth:`sage.geometry.fan.Fan.vertex_graph` of the two fans is compared. For each graph isomorphism, we attempt to lift it to an actual isomorphism of fans. INPUT: - ``fan1``, ``fan2`` -- two fans. OUTPUT: Yields the fan isomorphisms as matrices acting from the right on rays. EXAMPLES:: sage: fan = toric_varieties.P2().fan() sage: from sage.geometry.fan_isomorphism import fan_isomorphism_generator sage: tuple( fan_isomorphism_generator(fan, fan) ) ( [1 0] [0 1] [ 1 0] [ 0 1] [-1 -1] [-1 -1] [0 1], [1 0], [-1 -1], [-1 -1], [ 1 0], [ 0 1] ) sage: m1 = matrix([(1, 0), (0, -5), (-3, 4)]) sage: m2 = matrix([(3, 0), (1, 0), (-2, 1)]) sage: m1.elementary_divisors() == m2.elementary_divisors() == [1,1,0] True sage: fan1 = Fan([Cone([m1*vector([23, 14]), m1*vector([ 3,100])]), ... Cone([m1*vector([-1,-14]), m1*vector([-100, -5])])]) sage: fan2 = Fan([Cone([m2*vector([23, 14]), m2*vector([ 3,100])]), ... Cone([m2*vector([-1,-14]), m2*vector([-100, -5])])]) sage: next(fan_isomorphism_generator(fan1, fan2)) [18 1 -5] [ 4 0 -1] [ 5 0 -1] sage: m0 = identity_matrix(ZZ, 2) sage: m1 = matrix([(1, 0), (0, -5), (-3, 4)]) sage: m2 = matrix([(3, 0), (1, 0), (-2, 1)]) sage: m1.elementary_divisors() == m2.elementary_divisors() == [1,1,0] True sage: fan0 = Fan([Cone([m0*vector([1,0]), m0*vector([1,1])]), ... Cone([m0*vector([1,1]), m0*vector([0,1])])]) sage: fan1 = Fan([Cone([m1*vector([1,0]), m1*vector([1,1])]), ... Cone([m1*vector([1,1]), m1*vector([0,1])])]) sage: fan2 = Fan([Cone([m2*vector([1,0]), m2*vector([1,1])]), ... Cone([m2*vector([1,1]), m2*vector([0,1])])]) sage: tuple(fan_isomorphism_generator(fan0, fan0)) ( [1 0] [0 1] [0 1], [1 0] ) sage: tuple(fan_isomorphism_generator(fan1, fan1)) ( [1 0 0] [ -3 -20 28] [0 1 0] [ -1 -4 7] [0 0 1], [ -1 -5 8] ) sage: tuple(fan_isomorphism_generator(fan1, fan2)) ( [18 1 -5] [ 6 -3 7] [ 4 0 -1] [ 1 -1 2] [ 5 0 -1], [ 2 -1 2] ) sage: tuple(fan_isomorphism_generator(fan2, fan1)) ( [ 0 -1 1] [ 0 -1 1] [ 1 -7 2] [ 2 -2 -5] [ 0 -5 4], [ 1 0 -3] ) """ if not fan_isomorphic_necessary_conditions(fan1, fan2): return graph1 = fan1.vertex_graph() graph2 = fan2.vertex_graph() graph_iso = graph1.is_isomorphic(graph2, edge_labels=True, certify=True) if not graph_iso[0]: return graph_iso = graph_iso[1] # Pick a basis of rays in fan1 max_cone = fan1(fan1.dim())[0] fan1_pivot_rays = max_cone.rays() fan1_basis = fan1_pivot_rays + fan1.virtual_rays() # A QQ-basis for N_1 fan1_pivot_cones = [fan1.embed(Cone([r])) for r in fan1_pivot_rays] # The fan2 cones as set(set(ray indices)) fan2_cones = frozenset( frozenset(cone.ambient_ray_indices()) for cone in fan2.generating_cones()) # iterate over all graph isomorphisms graph1 -> graph2 for perm in graph2.automorphism_group(edge_labels=True): # find a candidate m that maps fan1_basis to the image rays under the graph isomorphism fan2_pivot_cones = [perm(graph_iso[c]) for c in fan1_pivot_cones] fan2_pivot_rays = fan2.rays( [c.ambient_ray_indices()[0] for c in fan2_pivot_cones]) fan2_basis = fan2_pivot_rays + fan2.virtual_rays() try: m = matrix(ZZ, fan1_basis).solve_right(matrix(ZZ, fan2_basis)) m = m.change_ring(ZZ) except (ValueError, TypeError): continue # no solution # check that the candidate m lifts the vertex graph homomorphism graph_image_ray_indices = [ perm(graph_iso[c]).ambient_ray_indices()[0] for c in fan1(1) ] try: matrix_image_ray_indices = [ fan2.rays().index(r * m) for r in fan1.rays() ] except ValueError: continue if graph_image_ray_indices != matrix_image_ray_indices: continue # check that the candidate m maps generating cone to generating cone image_cones = frozenset( # The image(fan1) cones as set(set(integers) frozenset(graph_image_ray_indices[i] for i in cone.ambient_ray_indices()) for cone in fan1.generating_cones()) if image_cones == fan2_cones: m.set_immutable() yield m
def borcherds_input_Qbasis(self, pole_order, prec, verbose=False): r""" Compute a Q-basis of input functions into the Borcherds lift with pole order in infinity up to pole_order. This method computes a list of Borcherds lift inputs F_0, ..., F_d which is a Q-basis in the following sense: it is minimal with the property that every modular form with pole order at most pole_order whose Borcherds lift is holomorphic can be expressed in the form (k_0 F_0 + ... + k_d F_d) where k_i are nonnegative rational numbers. INPUT: - ``pole_order`` -- positive number (does not need to be an integer) - ``prec`` -- precision of the output OUTPUT: WeilRepModularFormsBasis """ S = self.gram_matrix() w = self.weilrep() K = w.base_field() d = K.discriminant() wt = self.input_wt() M, p, X = self._borcherds_product_polyhedron(pole_order, prec, verbose=verbose) try: b = Matrix(Cone(p).rays()) if verbose: print('I will now try to find Borcherds product inputs.') try: u = M.solve_left(b) Y = [v * X for v in u.rows()] Y = WeilRepModularFormsBasis(wt, Y, w) except ValueError: Y = WeilRepModularFormsBasis(wt, [], w) except IndexError: Y = WeilRepModularFormsBasis(wt, [], w) if wt >= 0: X = deepcopy( w.basis_vanishing_to_order(wt, max(0, -pole_order), prec)) if X: X.extend(Y) Y = X X = Y._WeilRepModularFormsBasis__basis if d >= -4: if d == -4: f = w.multiplication_by_i() n = 2 else: f = w.multiplication_by_zeta() f2 = f * f n = 3 for i, x in enumerate(X): try: j = X.index(f(x)) if j == i: X[i] = x / n else: del X[j] except ValueError: pass if n == 3: try: j2 = X.index(f2(x)) del X[j2] except ValueError: pass X.sort(key=lambda x: x.fourier_expansion()[0][2][0]) return WeilRepModularFormsBasis(wt, X, w)
def trivial(ambient_dim=None, lattice=None): r""" The trivial cone with no nonzero generators in ``ambient_dim`` dimensions, or living in ``lattice``. INPUT: - ``ambient_dim`` -- a nonnegative integer (default: ``None``); the dimension of the ambient space - ``lattice`` -- a toric lattice (default: ``None``); the lattice in which the cone will live If ``ambient_dim`` is omitted, then it will be inferred from the rank of ``lattice``. If the ``lattice`` is omitted, then the default lattice of rank ``ambient_dim`` will be used. A ``ValueError`` is raised if neither ``ambient_dim`` nor ``lattice`` are specified. It is also a ``ValueError`` to specify both ``ambient_dim`` and ``lattice`` unless the rank of ``lattice`` is equal to ``ambient_dim``. OUTPUT: A :class:`.ConvexRationalPolyhedralCone` representing the trivial cone with no nonzero generators living in ``lattice``, with ambient dimension ``ambient_dim``. A ``ValueError`` can be raised if the inputs are incompatible or insufficient. See the INPUT documentation for details. EXAMPLES: Construct the trivial cone, containing only the origin, in three dimensions:: sage: cones.trivial(3) 0-d cone in 3-d lattice N If a ``lattice`` is given, the trivial cone will live in that lattice:: sage: L = ToricLattice(3, 'M') sage: cones.trivial(3, lattice=L) 0-d cone in 3-d lattice M TESTS: We can construct the trivial cone in a trivial ambient space:: sage: cones.trivial(0) 0-d cone in 0-d lattice N An error is raised if the rank of the lattice disagrees with ``ambient_dim``:: sage: L = ToricLattice(1, 'M') sage: cones.trivial(3, lattice=L) Traceback (most recent call last): ... ValueError: lattice rank=1 and ambient_dim=3 are incompatible We also get an error if no arguments are given:: sage: cones.trivial() Traceback (most recent call last): ... ValueError: either the ambient dimension or the lattice must be specified """ from sage.geometry.cone import Cone (ambient_dim, lattice) = _preprocess_args(ambient_dim, lattice) return Cone([], lattice)
def schur(ambient_dim=None, lattice=None): r""" The Schur cone in ``ambient_dim`` dimensions, or living in ``lattice``. The Schur cone in `n` dimensions induces the majorization ordering on the ambient space. If `\left\{e_{1}, e_{2}, \ldots, e_{n}\right\}` is the standard basis for the space, then its generators are `\left\{e_{i} - e_{i+1}\ |\ 1 \le i \le n-1\right\}`. Its dual is the downward monotonic cone. INPUT: - ``ambient_dim`` -- a nonnegative integer (default: ``None``); the dimension of the ambient space - ``lattice`` -- a toric lattice (default: ``None``); the lattice in which the cone will live If ``ambient_dim`` is omitted, then it will be inferred from the rank of ``lattice``. If the ``lattice`` is omitted, then the default lattice of rank ``ambient_dim`` will be used. A ``ValueError`` is raised if neither ``ambient_dim`` nor ``lattice`` are specified. It is also a ``ValueError`` to specify both ``ambient_dim`` and ``lattice`` unless the rank of ``lattice`` is equal to ``ambient_dim``. OUTPUT: A :class:`.ConvexRationalPolyhedralCone` representing the Schur cone living in ``lattice``, with ambient dimension ``ambient_dim``. Each generating ray has the integer ring as its base ring. A ``ValueError`` can be raised if the inputs are incompatible or insufficient. See the INPUT documentation for details. REFERENCES: - [GS2010]_, Section 3.1 - [IS2005]_, Example 7.3 - [SS2016]_, Example 7.4 EXAMPLES: Verify the claim [SS2016]_ that the maximal angle between any two generators of the Schur cone and the nonnegative orthant in dimension five is `\left(3/4\right)\pi`:: sage: P = cones.schur(5) sage: Q = cones.nonnegative_orthant(5) sage: G = ( g.change_ring(QQbar).normalized() for g in P ) sage: H = ( h.change_ring(QQbar).normalized() for h in Q ) sage: actual = max(arccos(u.inner_product(v)) for u in G for v in H) sage: expected = 3*pi/4 sage: abs(actual - expected).n() < 1e-12 True The dual of the Schur cone is the "downward monotonic cone" [GS2010]_, whose elements' entries are in non-increasing order:: sage: set_random_seed() sage: ambient_dim = ZZ.random_element(10) sage: K = cones.schur(ambient_dim).dual() sage: x = K.random_element() sage: all( x[i] >= x[i+1] for i in range(ambient_dim-1) ) True TESTS: We get the trivial cone when ``ambient_dim`` is zero:: sage: cones.schur(0).is_trivial() True The Schur cone induces the majorization ordering, as in Iusem and Seeger's [IS2005]_ Example 7.3:: sage: set_random_seed() sage: def majorized_by(x,y): ....: return (all(sum(x[0:i]) <= sum(y[0:i]) ....: for i in range(x.degree()-1)) ....: and sum(x) == sum(y)) sage: ambient_dim = ZZ.random_element(10) sage: V = VectorSpace(QQ, ambient_dim) sage: S = cones.schur(ambient_dim) sage: majorized_by(V.zero(), S.random_element()) True sage: x = V.random_element() sage: y = V.random_element() sage: majorized_by(x,y) == ( (y-x) in S ) True If a ``lattice`` was given, it is actually used:: sage: L = ToricLattice(3, 'M') sage: cones.schur(3, lattice=L) 2-d cone in 3-d lattice M Unless the rank of the lattice disagrees with ``ambient_dim``:: sage: L = ToricLattice(1, 'M') sage: cones.schur(3, lattice=L) Traceback (most recent call last): ... ValueError: lattice rank=1 and ambient_dim=3 are incompatible We also get an error if no arguments are given:: sage: cones.schur() Traceback (most recent call last): ... ValueError: either the ambient dimension or the lattice must be specified """ from sage.geometry.cone import Cone from sage.matrix.constructor import matrix from sage.rings.integer_ring import ZZ (ambient_dim, lattice) = _preprocess_args(ambient_dim, lattice) def _f(i, j): if i == j: return 1 elif j - i == 1: return -1 else: return 0 # The "max" below catches the trivial case where ambient_dim == 0. S = matrix(ZZ, max(0, ambient_dim - 1), ambient_dim, _f) return Cone(S.rows(), lattice)
def rearrangement(p, ambient_dim=None, lattice=None): r""" The rearrangement cone of order ``p`` in ``ambient_dim`` dimensions, or living in ``lattice``. The rearrangement cone of order ``p`` in ``ambient_dim`` dimensions consists of all vectors of length ``ambient_dim`` whose smallest ``p`` components sum to a nonnegative number. For example, the rearrangement cone of order one has its single smallest component nonnegative. This implies that all components are nonnegative, and that therefore the rearrangement cone of order one is the nonnegative orthant in its ambient space. When ``p`` and ``ambient_dim`` are equal, all components of the cone's elements must sum to a nonnegative number. In other words, the rearrangement cone of order ``ambient_dim`` is a half-space. INPUT: - ``p`` -- a nonnegative integer; the number of components to "rearrange", between ``1`` and ``ambient_dim`` inclusive - ``ambient_dim`` -- a nonnegative integer (default: ``None``); the dimension of the ambient space - ``lattice`` -- a toric lattice (default: ``None``); the lattice in which the cone will live If ``ambient_dim`` is omitted, then it will be inferred from the rank of ``lattice``. If the ``lattice`` is omitted, then the default lattice of rank ``ambient_dim`` will be used. A ``ValueError`` is raised if neither ``ambient_dim`` nor ``lattice`` are specified. It is also a ``ValueError`` to specify both ``ambient_dim`` and ``lattice`` unless the rank of ``lattice`` is equal to ``ambient_dim``. It is also a ``ValueError`` to specify a non-integer ``p``. OUTPUT: A :class:`.ConvexRationalPolyhedralCone` representing the rearrangement cone of order ``p`` living in ``lattice``, with ambient dimension ``ambient_dim``. Each generating ray has the integer ring as its base ring. A ``ValueError`` can be raised if the inputs are incompatible or insufficient. See the INPUT documentation for details. ALGORITHM: Suppose that the ambient space is of dimension `n`. The extreme directions of the rearrangement cone for `1 \le p \le n-1` are given by [Jeong2017]_ Theorem 5.2.3. When `2 \le p \le n-2` (that is, if we ignore `p = 1` and `p = n-1`), they consist of - the standard basis `\left\{e_{1},e_{2},\ldots,e_{n}\right\}` for the ambient space, and - the `n` vectors `\left(1,1,\ldots,1\right)^{T} - pe_{i}` for `i = 1,2,\ldots,n`. Special cases are then given for `p = 1` and `p = n-1` in the theorem. However in SageMath we don't need conically-independent extreme directions. We only need a generating set, because the :func:`Cone` function will eliminate any redundant generators. And one can easily verify that the special-case extreme directions for `p = 1` and `p = n-1` are contained in the conic hull of the `2n` generators just described. The half space resulting from `p = n` is also covered by this set of generators, so for all valid `p` we simply take the conic hull of those `2n` vectors. REFERENCES: - [GJ2016]_, Section 4 - [HS2010]_, Example 2.21 - [Jeong2017]_, Section 5.2 EXAMPLES: The rearrangement cones of order one are nonnegative orthants:: sage: orthant = cones.nonnegative_orthant(6) sage: cones.rearrangement(1,6).is_equivalent(orthant) True When ``p`` and ``ambient_dim`` are equal, the rearrangement cone is a half-space, so we expect its lineality to be one less than ``ambient_dim`` because it will contain a hyperplane but is not the entire space:: sage: cones.rearrangement(5,5).lineality() 4 Jeong's Proposition 5.2.1 [Jeong2017]_ states that all rearrangement cones are proper when ``p`` is less than ``ambient_dim``:: sage: all( cones.rearrangement(p, ambient_dim).is_proper() ....: for ambient_dim in range(10) ....: for p in range(1, ambient_dim) ) True Jeong's Corollary 5.2.4 [Jeong2017]_ states that if `p = n-1` in an `n`-dimensional ambient space, then the Lyapunov rank of the rearrangement cone is `n`, and that for all other `p > 1` its Lyapunov rank is one:: sage: all( cones.rearrangement(p, ambient_dim).lyapunov_rank() ....: == ....: ambient_dim ....: for ambient_dim in range(2, 10) ....: for p in [ ambient_dim-1 ] ) True sage: all( cones.rearrangement(p, ambient_dim).lyapunov_rank() == 1 ....: for ambient_dim in range(3, 10) ....: for p in range(2, ambient_dim-1) ) True TESTS: Jeong's Proposition 5.2.1 [Jeong2017]_ states that rearrangement cones are permutation-invariant:: sage: ambient_dim = ZZ.random_element(2,10).abs() sage: p = ZZ.random_element(1, ambient_dim) sage: K = cones.rearrangement(p, ambient_dim) sage: P = SymmetricGroup(ambient_dim).random_element().matrix() sage: all( K.contains(P*r) for r in K ) True The smallest ``p`` components of every element of the rearrangement cone should sum to a nonnegative number. In other words, the generators really are what we think they are:: sage: set_random_seed() sage: def _has_rearrangement_property(v,p): ....: return sum( sorted(v)[0:p] ) >= 0 sage: all( ....: _has_rearrangement_property( ....: cones.rearrangement(p, ambient_dim).random_element(), ....: p ....: ) ....: for ambient_dim in range(2, 10) ....: for p in range(1, ambient_dim+1) ....: ) True The rearrangement cone of order ``p`` is, almost by definition, contained in the rearrangement cone of order ``p + 1``:: sage: set_random_seed() sage: ambient_dim = ZZ.random_element(2,10) sage: p = ZZ.random_element(1, ambient_dim) sage: K1 = cones.rearrangement(p, ambient_dim) sage: K2 = cones.rearrangement(p+1, ambient_dim) sage: all( x in K2 for x in K1 ) True Jeong's Proposition 5.2.1 [Jeong2017]_ states that the rearrangement cone of order ``p`` is linearly isomorphic to the rearrangement cone of order ``ambient_dim - p`` when ``p`` is less than ``ambient_dim``:: sage: set_random_seed() sage: ambient_dim = ZZ.random_element(2,10) sage: p = ZZ.random_element(1, ambient_dim) sage: K1 = cones.rearrangement(p, ambient_dim) sage: K2 = cones.rearrangement(ambient_dim-p, ambient_dim) sage: Mp = ((1/p)*matrix.ones(QQ, ambient_dim) ....: - matrix.identity(QQ, ambient_dim)) sage: Cone( (Mp*K2.rays()).columns() ).is_equivalent(K1) True The order ``p`` should be an integer between ``1`` and ``ambient_dim``, inclusive:: sage: cones.rearrangement(0,3) Traceback (most recent call last): ... ValueError: order p=0 should be an integer between 1 and ambient_dim=3, inclusive sage: cones.rearrangement(5,3) Traceback (most recent call last): ... ValueError: order p=5 should be an integer between 1 and ambient_dim=3, inclusive sage: cones.rearrangement(3/2, 3) Traceback (most recent call last): ... ValueError: order p=3/2 should be an integer between 1 and ambient_dim=3, inclusive If a ``lattice`` was given, it is actually used:: sage: L = ToricLattice(3, 'M') sage: cones.rearrangement(2, 3, lattice=L) 3-d cone in 3-d lattice M Unless the rank of the lattice disagrees with ``ambient_dim``:: sage: L = ToricLattice(1, 'M') sage: cones.rearrangement(2, 3, lattice=L) Traceback (most recent call last): ... ValueError: lattice rank=1 and ambient_dim=3 are incompatible We also get an error if neither the ambient dimension nor lattice are specified:: sage: cones.rearrangement(3) Traceback (most recent call last): ... ValueError: either the ambient dimension or the lattice must be specified """ from sage.geometry.cone import Cone from sage.matrix.constructor import matrix from sage.rings.integer_ring import ZZ (ambient_dim, lattice) = _preprocess_args(ambient_dim, lattice) if p < 1 or p > ambient_dim or p not in ZZ: raise ValueError("order p=%s should be an integer between 1 " "and ambient_dim=%d, inclusive" % (p, ambient_dim)) I = matrix.identity(ZZ, ambient_dim) M = matrix.ones(ZZ, ambient_dim) - p * I G = matrix.identity(ZZ, ambient_dim).rows() + M.rows() return Cone(G, lattice=lattice)
def nonnegative_orthant(ambient_dim=None, lattice=None): r""" The nonnegative orthant in ``ambient_dim`` dimensions, or living in ``lattice``. The nonnegative orthant consists of all componentwise-nonnegative vectors. It is the convex-conic hull of the standard basis. INPUT: - ``ambient_dim`` -- a nonnegative integer (default: ``None``); the dimension of the ambient space - ``lattice`` -- a toric lattice (default: ``None``); the lattice in which the cone will live If ``ambient_dim`` is omitted, then it will be inferred from the rank of ``lattice``. If the ``lattice`` is omitted, then the default lattice of rank ``ambient_dim`` will be used. A ``ValueError`` is raised if neither ``ambient_dim`` nor ``lattice`` are specified. It is also a ``ValueError`` to specify both ``ambient_dim`` and ``lattice`` unless the rank of ``lattice`` is equal to ``ambient_dim``. OUTPUT: A :class:`.ConvexRationalPolyhedralCone` living in ``lattice`` and having ``ambient_dim`` standard basis vectors as its generators. Each generating ray has the integer ring as its base ring. A ``ValueError`` can be raised if the inputs are incompatible or insufficient. See the INPUT documentation for details. REFERENCES: - Chapter 2 in [BV2009]_ (Examples 2.4, 2.14, and 2.23 in particular) EXAMPLES:: sage: cones.nonnegative_orthant(3).rays() N(1, 0, 0), N(0, 1, 0), N(0, 0, 1) in 3-d lattice N TESTS: We can construct the trivial cone as the nonnegative orthant in a trivial vector space:: sage: cones.nonnegative_orthant(0) 0-d cone in 0-d lattice N The nonnegative orthant is a proper cone:: sage: set_random_seed() sage: ambient_dim = ZZ.random_element(10) sage: K = cones.nonnegative_orthant(ambient_dim) sage: K.is_proper() True If a ``lattice`` was given, it is actually used:: sage: L = ToricLattice(3, 'M') sage: cones.nonnegative_orthant(lattice=L) 3-d cone in 3-d lattice M Unless the rank of the lattice disagrees with ``ambient_dim``:: sage: L = ToricLattice(1, 'M') sage: cones.nonnegative_orthant(3, lattice=L) Traceback (most recent call last): ... ValueError: lattice rank=1 and ambient_dim=3 are incompatible We also get an error if no arguments are given:: sage: cones.nonnegative_orthant() Traceback (most recent call last): ... ValueError: either the ambient dimension or the lattice must be specified """ from sage.geometry.cone import Cone from sage.matrix.constructor import matrix from sage.rings.integer_ring import ZZ (ambient_dim, lattice) = _preprocess_args(ambient_dim, lattice) I = matrix.identity(ZZ, ambient_dim) return Cone(I.rows(), lattice)
def borcherds_input_Qbasis(self, pole_order, prec, verbose=False): r""" Compute a Q-basis of input functions into the Borcherds lift with pole order in infinity up to pole_order. This method computes a list of Borcherds lift inputs F_0, ..., F_d which is a Q-basis in the following sense: it is minimal with the property that every modular form with pole order at most pole_order whose Borcherds lift is holomorphic can be expressed in the form (k_0 F_0 + ... + k_d F_d) where k_i are nonnegative rational numbers. INPUT: - ``pole_order`` -- positive number (does not need to be an integer) - ``prec`` -- precision of the output OUTPUT: WeilRepModularFormsBasis EXAMPLES:: sage: from weilrep import * sage: m = ParamodularForms(5) sage: m.borcherds_input_Qbasis(1/4, 5) [(0), 10 + 50*q + 260*q^2 + 1030*q^3 + 3500*q^4 + O(q^5)] [(1/10), 6*q^(-1/20) + 16*q^(19/20) + 82*q^(39/20) + 304*q^(59/20) + 1046*q^(79/20) + 3120*q^(99/20) + O(q^(119/20))] [(1/5), q^(-1/5) - 24*q^(4/5) - 143*q^(9/5) - 622*q^(14/5) - 2204*q^(19/5) - 6876*q^(24/5) + O(q^(29/5))] [(3/10), -16*q^(11/20) - 102*q^(31/20) - 448*q^(51/20) - 1650*q^(71/20) - 5248*q^(91/20) + O(q^(111/20))] [(2/5), -q^(1/5) + 14*q^(6/5) + 92*q^(11/5) + 386*q^(16/5) + 1318*q^(21/5) + O(q^(26/5))] [(1/2), 20*q^(3/4) + 160*q^(7/4) + 700*q^(11/4) + 2560*q^(15/4) + 8000*q^(19/4) + O(q^(23/4))] [(3/5), -q^(1/5) + 14*q^(6/5) + 92*q^(11/5) + 386*q^(16/5) + 1318*q^(21/5) + O(q^(26/5))] [(7/10), -16*q^(11/20) - 102*q^(31/20) - 448*q^(51/20) - 1650*q^(71/20) - 5248*q^(91/20) + O(q^(111/20))] [(4/5), q^(-1/5) - 24*q^(4/5) - 143*q^(9/5) - 622*q^(14/5) - 2204*q^(19/5) - 6876*q^(24/5) + O(q^(29/5))] [(9/10), 6*q^(-1/20) + 16*q^(19/20) + 82*q^(39/20) + 304*q^(59/20) + 1046*q^(79/20) + 3120*q^(99/20) + O(q^(119/20))] ------------------------------------------------------------ [(0), 36 + 586*q + 3708*q^2 + 17694*q^3 + 68852*q^4 + O(q^5)] [(1/10), -5*q^(-1/20) - 270*q^(19/20) - 1935*q^(39/20) - 10275*q^(59/20) - 43245*q^(79/20) - 157545*q^(99/20) + O(q^(119/20))] [(1/5), 5*q^(-1/5) + 90*q^(4/5) + 1035*q^(9/5) + 6130*q^(14/5) + 28460*q^(19/5) + 109260*q^(24/5) + O(q^(29/5))] [(3/10), -185*q^(11/20) - 1595*q^(31/20) - 8505*q^(51/20) - 36145*q^(71/20) - 131485*q^(91/20) + O(q^(111/20))] [(2/5), 65*q^(1/5) + 630*q^(6/5) + 4100*q^(11/5) + 18940*q^(16/5) + 74070*q^(21/5) + O(q^(26/5))] [(1/2), 7*q^(-1/4) - 54*q^(3/4) - 747*q^(7/4) - 5026*q^(11/4) - 23859*q^(15/4) - 94960*q^(19/4) + O(q^(23/4))] [(3/5), 65*q^(1/5) + 630*q^(6/5) + 4100*q^(11/5) + 18940*q^(16/5) + 74070*q^(21/5) + O(q^(26/5))] [(7/10), -185*q^(11/20) - 1595*q^(31/20) - 8505*q^(51/20) - 36145*q^(71/20) - 131485*q^(91/20) + O(q^(111/20))] [(4/5), 5*q^(-1/5) + 90*q^(4/5) + 1035*q^(9/5) + 6130*q^(14/5) + 28460*q^(19/5) + 109260*q^(24/5) + O(q^(29/5))] [(9/10), -5*q^(-1/20) - 270*q^(19/20) - 1935*q^(39/20) - 10275*q^(59/20) - 43245*q^(79/20) - 157545*q^(99/20) + O(q^(119/20))] """ S = self.gram_matrix() w = self.weilrep() wt = self.input_wt() M, p, X = self._borcherds_product_polyhedron(pole_order, prec, verbose=verbose) try: b = matrix(Cone(p).rays()) if verbose: print('I will now try to find Borcherds product inputs.') try: u = M.solve_left(b) Y = [v * X for v in u.rows()] Y.sort(key=lambda x: x.fourier_expansion()[0][2][0]) Y = WeilRepModularFormsBasis(wt, Y, w) except ValueError: Y = WeilRepModularFormsBasis(wt, [], w) except IndexError: Y = WeilRepModularFormsBasis(wt, [], w) if wt >= 0: X = X = deepcopy( w.basis_vanishing_to_order(wt, max(0, -pole_order), prec)) if X: X.extend(Y) return X return Y
def borcherds_input_basis(self, pole_order, prec, verbose=False): r""" Compute a basis of input functions into the Borcherds lift with pole up to pole_order. This method computes a list of Borcherds lift inputs F_0, ..., F_d which is a basis in the following sense: it is minimal with the property that every modular form with pole order at most pole_order whose Borcherds lift is holomorphic can be expressed in the form (k_0 F_0 + ... + k_d F_d) where k_i are nonnegative integers. WARNING: this can take a long time, and the output list might be longer than you expect!! INPUT: - ``pole_order`` -- positive number (does not need to be an integer) - ``prec`` -- precision of the output OUTPUT: WeilRepModularFormsBasis EXAMPLES:: sage: from weilrep import * sage: m = ParamodularForms(5) sage: m.borcherds_input_basis(1/4, 5) [(0), 8 + 98*q + 604*q^2 + 2822*q^3 + 10836*q^4 + O(q^5)] [(1/10), q^(-1/20) - 34*q^(19/20) - 253*q^(39/20) - 1381*q^(59/20) - 5879*q^(79/20) - 21615*q^(99/20) + O(q^(119/20))] [(1/5), q^(-1/5) + 6*q^(4/5) + 107*q^(9/5) + 698*q^(14/5) + 3436*q^(19/5) + 13644*q^(24/5) + O(q^(29/5))] [(3/10), -31*q^(11/20) - 257*q^(31/20) - 1343*q^(51/20) - 5635*q^(71/20) - 20283*q^(91/20) + O(q^(111/20))] [(2/5), 9*q^(1/5) + 94*q^(6/5) + 612*q^(11/5) + 2816*q^(16/5) + 10958*q^(21/5) + O(q^(26/5))] [(1/2), q^(-1/4) - 2*q^(3/4) - 61*q^(7/4) - 518*q^(11/4) - 2677*q^(15/4) - 11280*q^(19/4) + O(q^(23/4))] [(3/5), 9*q^(1/5) + 94*q^(6/5) + 612*q^(11/5) + 2816*q^(16/5) + 10958*q^(21/5) + O(q^(26/5))] [(7/10), -31*q^(11/20) - 257*q^(31/20) - 1343*q^(51/20) - 5635*q^(71/20) - 20283*q^(91/20) + O(q^(111/20))] [(4/5), q^(-1/5) + 6*q^(4/5) + 107*q^(9/5) + 698*q^(14/5) + 3436*q^(19/5) + 13644*q^(24/5) + O(q^(29/5))] [(9/10), q^(-1/20) - 34*q^(19/20) - 253*q^(39/20) - 1381*q^(59/20) - 5879*q^(79/20) - 21615*q^(99/20) + O(q^(119/20))] ------------------------------------------------------------ [(0), 10 + 50*q + 260*q^2 + 1030*q^3 + 3500*q^4 + O(q^5)] [(1/10), 6*q^(-1/20) + 16*q^(19/20) + 82*q^(39/20) + 304*q^(59/20) + 1046*q^(79/20) + 3120*q^(99/20) + O(q^(119/20))] [(1/5), q^(-1/5) - 24*q^(4/5) - 143*q^(9/5) - 622*q^(14/5) - 2204*q^(19/5) - 6876*q^(24/5) + O(q^(29/5))] [(3/10), -16*q^(11/20) - 102*q^(31/20) - 448*q^(51/20) - 1650*q^(71/20) - 5248*q^(91/20) + O(q^(111/20))] [(2/5), -q^(1/5) + 14*q^(6/5) + 92*q^(11/5) + 386*q^(16/5) + 1318*q^(21/5) + O(q^(26/5))] [(1/2), 20*q^(3/4) + 160*q^(7/4) + 700*q^(11/4) + 2560*q^(15/4) + 8000*q^(19/4) + O(q^(23/4))] [(3/5), -q^(1/5) + 14*q^(6/5) + 92*q^(11/5) + 386*q^(16/5) + 1318*q^(21/5) + O(q^(26/5))] [(7/10), -16*q^(11/20) - 102*q^(31/20) - 448*q^(51/20) - 1650*q^(71/20) - 5248*q^(91/20) + O(q^(111/20))] [(4/5), q^(-1/5) - 24*q^(4/5) - 143*q^(9/5) - 622*q^(14/5) - 2204*q^(19/5) - 6876*q^(24/5) + O(q^(29/5))] [(9/10), 6*q^(-1/20) + 16*q^(19/20) + 82*q^(39/20) + 304*q^(59/20) + 1046*q^(79/20) + 3120*q^(99/20) + O(q^(119/20))] ------------------------------------------------------------ [(0), 22 + 342*q + 2156*q^2 + 10258*q^3 + 39844*q^4 + O(q^5)] [(1/10), -2*q^(-1/20) - 152*q^(19/20) - 1094*q^(39/20) - 5828*q^(59/20) - 24562*q^(79/20) - 89580*q^(99/20) + O(q^(119/20))] [(1/5), 3*q^(-1/5) + 48*q^(4/5) + 571*q^(9/5) + 3414*q^(14/5) + 15948*q^(19/5) + 61452*q^(24/5) + O(q^(29/5))] [(3/10), -108*q^(11/20) - 926*q^(31/20) - 4924*q^(51/20) - 20890*q^(71/20) - 75884*q^(91/20) + O(q^(111/20))] [(2/5), 37*q^(1/5) + 362*q^(6/5) + 2356*q^(11/5) + 10878*q^(16/5) + 42514*q^(21/5) + O(q^(26/5))] [(1/2), 4*q^(-1/4) - 28*q^(3/4) - 404*q^(7/4) - 2772*q^(11/4) - 13268*q^(15/4) - 53120*q^(19/4) + O(q^(23/4))] [(3/5), 37*q^(1/5) + 362*q^(6/5) + 2356*q^(11/5) + 10878*q^(16/5) + 42514*q^(21/5) + O(q^(26/5))] [(7/10), -108*q^(11/20) - 926*q^(31/20) - 4924*q^(51/20) - 20890*q^(71/20) - 75884*q^(91/20) + O(q^(111/20))] [(4/5), 3*q^(-1/5) + 48*q^(4/5) + 571*q^(9/5) + 3414*q^(14/5) + 15948*q^(19/5) + 61452*q^(24/5) + O(q^(29/5))] [(9/10), -2*q^(-1/20) - 152*q^(19/20) - 1094*q^(39/20) - 5828*q^(59/20) - 24562*q^(79/20) - 89580*q^(99/20) + O(q^(119/20))] ------------------------------------------------------------ [(0), 36 + 586*q + 3708*q^2 + 17694*q^3 + 68852*q^4 + O(q^5)] [(1/10), -5*q^(-1/20) - 270*q^(19/20) - 1935*q^(39/20) - 10275*q^(59/20) - 43245*q^(79/20) - 157545*q^(99/20) + O(q^(119/20))] [(1/5), 5*q^(-1/5) + 90*q^(4/5) + 1035*q^(9/5) + 6130*q^(14/5) + 28460*q^(19/5) + 109260*q^(24/5) + O(q^(29/5))] [(3/10), -185*q^(11/20) - 1595*q^(31/20) - 8505*q^(51/20) - 36145*q^(71/20) - 131485*q^(91/20) + O(q^(111/20))] [(2/5), 65*q^(1/5) + 630*q^(6/5) + 4100*q^(11/5) + 18940*q^(16/5) + 74070*q^(21/5) + O(q^(26/5))] [(1/2), 7*q^(-1/4) - 54*q^(3/4) - 747*q^(7/4) - 5026*q^(11/4) - 23859*q^(15/4) - 94960*q^(19/4) + O(q^(23/4))] [(3/5), 65*q^(1/5) + 630*q^(6/5) + 4100*q^(11/5) + 18940*q^(16/5) + 74070*q^(21/5) + O(q^(26/5))] [(7/10), -185*q^(11/20) - 1595*q^(31/20) - 8505*q^(51/20) - 36145*q^(71/20) - 131485*q^(91/20) + O(q^(111/20))] [(4/5), 5*q^(-1/5) + 90*q^(4/5) + 1035*q^(9/5) + 6130*q^(14/5) + 28460*q^(19/5) + 109260*q^(24/5) + O(q^(29/5))] [(9/10), -5*q^(-1/20) - 270*q^(19/20) - 1935*q^(39/20) - 10275*q^(59/20) - 43245*q^(79/20) - 157545*q^(99/20) + O(q^(119/20))] """ S = self.gram_matrix() w = self.weilrep() wt = self.input_wt() M, p, X = self._borcherds_product_polyhedron(pole_order, prec, verbose=verbose) if verbose: print('I will now try to find a Hilbert basis.') try: b = matrix(Cone(p).Hilbert_basis()) try: u = M.solve_left(b) Y = [v * X for v in u.rows()] Y.sort(key=lambda x: x.fourier_expansion()[0][2][0]) Y = WeilRepModularFormsBasis(wt, Y, w) except ValueError: Y = WeilRepModularFormsBasis(wt, [], w) except IndexError: Y = WeilRepModularFormsBasis(wt, [], w) if wt >= 0: X = deepcopy( w.basis_vanishing_to_order(wt, max(0, -pole_order), prec)) if X: X.extend(Y) return X return Y
def cluster_fan(self, depth=infinity): from sage.geometry.cone import Cone from sage.geometry.fan import Fan seeds = self.seeds(depth=depth, mutating_F=False) cones = map(lambda s: Cone(s.g_vectors()), seeds) return Fan(cones)