def __cast_input(X, parent): if (__check_input(X, parent)): return Matrix(parent, X) raise TypeError("The input %s can not be seen as a matrix of %s" % (X, parent))
def verify_algebraically_PS(g, P0, alpha, trace_and_norm, verbose=True): # input: # * P0 (only necessary to shift the series) # * [trace_numerator, trace_denominator, norm_numerator, norm_denominator] # output: # a boolean if verbose: print "verify_algebraically()" L = P0.base_ring() assert alpha.base_ring() is L L_poly = PolynomialRing(L, "xL") xL = L_poly.gen() # shifting the series makes our life easier trace_numerator, trace_denominator, norm_numerator, norm_denominator = [ L_poly(coeff)(L_poly.gen() + P0[0]) for coeff in trace_and_norm ] L_fpoly = L_poly.fraction_field() trace = L_fpoly(trace_numerator) / L_fpoly(trace_denominator) norm = L_fpoly(norm_numerator) / L_fpoly(norm_denominator) Xpoly = L_poly([norm(0), -trace(0), 1]) if verbose: print "xpoly = %s" % Xpoly if Xpoly.is_irreducible(): M = Xpoly.root_field("c") else: # this avoids bifurcation later on in the code M = NumberField(xL, "c") if verbose: print M xi_degree = max( [elt.degree() for elt in [trace_denominator, norm_denominator]]) D = 2 * xi_degree hard_bound = D + (4 + 2) soft_bound = hard_bound + 5 M_ps = PowerSeriesRing(M, "T", default_prec=soft_bound) T = M_ps.gen() Tsub = T + P0[0] trace_M = M_ps(trace) norm_M = M_ps(norm) sqrtdisc = sqrt(trace_M**2 - 4 * norm_M) x1 = (trace_M - sqrtdisc) / 2 x2 = (trace_M + sqrtdisc) / 2 y1 = sqrt(g(x1)) y2 = sqrt(g(x2)) iy = 1 / sqrt(g(Tsub)) dx1 = x1.derivative(T) dx2 = x2.derivative(T) dx1_y1 = dx1 / y1 dx2_y2 = dx2 / y2 eq1 = Matrix([[-2 * M_ps(alpha.row(0).list())(Tsub) * iy, dx1_y1, dx2_y2]]) eq2 = Matrix( [[-2 * M_ps(alpha.row(1).list())(Tsub) * iy, x1 * dx1_y1, x2 * dx2_y2]]) branches = Matrix([[1, 1, 1], [1, 1, -1], [1, -1, 1], [1, -1, -1]]).transpose() meq1 = eq1 * branches meq2 = eq2 * branches algzero = False for j in range(4): if meq1[0, j] == 0 and meq2[0, j] == 0: algzero = True break if verbose: print "Done, verify_algebraically() = %s" % algzero return algzero
def invariant_generators(self): r""" Return invariant ring generators. Computes generators for the polynomial ring `F[x_1,\ldots,x_n]^G`, where `G` in `GL(n,F)` is a finite matrix group. In the "good characteristic" case the polynomials returned form a minimal generating set for the algebra of `G`-invariant polynomials. In the "bad" case, the polynomials returned are primary and secondary invariants, forming a not necessarily minimal generating set for the algebra of `G`-invariant polynomials. ALGORITHM: Wraps Singular's ``invariant_algebra_reynolds`` and ``invariant_ring`` in ``finvar.lib``. EXAMPLES:: sage: F = GF(7); MS = MatrixSpace(F,2,2) sage: gens = [MS([[0,1],[-1,0]]),MS([[1,1],[2,3]])] sage: G = MatrixGroup(gens) sage: G.invariant_generators() [x1^7*x2 - x1*x2^7, x1^12 - 2*x1^9*x2^3 - x1^6*x2^6 + 2*x1^3*x2^9 + x2^12, x1^18 + 2*x1^15*x2^3 + 3*x1^12*x2^6 + 3*x1^6*x2^12 - 2*x1^3*x2^15 + x2^18] sage: q = 4; a = 2 sage: MS = MatrixSpace(QQ, 2, 2) sage: gen1 = [[1/a,(q-1)/a],[1/a, -1/a]]; gen2 = [[1,0],[0,-1]]; gen3 = [[-1,0],[0,1]] sage: G = MatrixGroup([MS(gen1),MS(gen2),MS(gen3)]) sage: G.cardinality() 12 sage: G.invariant_generators() [x1^2 + 3*x2^2, x1^6 + 15*x1^4*x2^2 + 15*x1^2*x2^4 + 33*x2^6] sage: F = CyclotomicField(8) sage: z = F.gen() sage: a = z+1/z sage: b = z^2 sage: MS = MatrixSpace(F,2,2) sage: g1 = MS([[1/a, 1/a], [1/a, -1/a]]) sage: g2 = MS([[-b, 0], [0, b]]) sage: G=MatrixGroup([g1,g2]) sage: G.invariant_generators() [x1^4 + 2*x1^2*x2^2 + x2^4, x1^5*x2 - x1*x2^5, x1^8 + 28/9*x1^6*x2^2 + 70/9*x1^4*x2^4 + 28/9*x1^2*x2^6 + x2^8] AUTHORS: - David Joyner, Simon King and Martin Albrecht. REFERENCES: - Singular reference manual - [Stu1993]_ - S. King, "Minimal Generating Sets of non-modular invariant rings of finite groups", :arxiv:`math/0703035`. """ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.interfaces.singular import singular gens = self.gens() singular.LIB("finvar.lib") n = self.degree() # len((gens[0].matrix()).rows()) F = self.base_ring() q = F.characteristic() # test if the field is admissible if F.gen() == 1: # we got the rationals or GF(prime) FieldStr = str(F.characteristic()) elif hasattr(F, 'polynomial'): # we got an algebraic extension if len(F.gens()) > 1: raise NotImplementedError( "can only deal with finite fields and (simple algebraic extensions of) the rationals" ) FieldStr = '(%d,%s)' % (F.characteristic(), str(F.gen())) else: # we have a transcendental extension FieldStr = '(%d,%s)' % (F.characteristic(), ','.join( str(p) for p in F.gens())) # Setting Singular's variable names # We need to make sure that field generator and variables get different names. if str(F.gen())[0] == 'x': VarStr = 'y' else: VarStr = 'x' VarNames = '(' + ','.join( (VarStr + str(i) for i in range(1, n + 1))) + ')' # The function call and affectation below have side-effects. Do not remove! # (even if pyflakes say so) R = singular.ring(FieldStr, VarNames, 'dp') if hasattr(F, 'polynomial') and F.gen() != 1: # we have to define minpoly singular.eval('minpoly = ' + str(F.polynomial()).replace('x', str(F.gen()))) A = [singular.matrix(n, n, str((x.matrix()).list())) for x in gens] Lgens = ','.join((x.name() for x in A)) PR = PolynomialRing(F, n, [VarStr + str(i) for i in range(1, n + 1)]) if q == 0 or (q > 0 and self.cardinality() % q): from sage.all import Matrix try: elements = [g.matrix() for g in self.list()] except (TypeError, ValueError): elements if elements is not None: ReyName = 't' + singular._next_var_name() singular.eval('matrix %s[%d][%d]' % (ReyName, self.cardinality(), n)) for i in range(1, self.cardinality() + 1): M = Matrix(F, elements[i - 1]) D = [{} for foobar in range(self.degree())] for x, y in M.dict().items(): D[x[0]][x[1]] = y for row in range(self.degree()): for t in D[row].items(): singular.eval('%s[%d,%d]=%s[%d,%d]+(%s)*var(%d)' % (ReyName, i, row + 1, ReyName, i, row + 1, repr(t[1]), t[0] + 1)) IRName = 't' + singular._next_var_name() singular.eval('matrix %s = invariant_algebra_reynolds(%s)' % (IRName, ReyName)) else: ReyName = 't' + singular._next_var_name() singular.eval('list %s=group_reynolds((%s))' % (ReyName, Lgens)) IRName = 't' + singular._next_var_name() singular.eval('matrix %s = invariant_algebra_reynolds(%s[1])' % (IRName, ReyName)) OUT = [ singular.eval(IRName + '[1,%d]' % (j)) for j in range(1, 1 + int(singular('ncols(' + IRName + ')'))) ] return [PR(gen) for gen in OUT] if self.cardinality() % q == 0: PName = 't' + singular._next_var_name() SName = 't' + singular._next_var_name() singular.eval('matrix %s,%s=invariant_ring(%s)' % (PName, SName, Lgens)) OUT = [ singular.eval(PName + '[1,%d]' % (j)) for j in range(1, 1 + singular('ncols(' + PName + ')')) ] OUT += [ singular.eval(SName + '[1,%d]' % (j)) for j in range(2, 1 + singular('ncols(' + SName + ')')) ] return [PR(gen) for gen in OUT]
def charpoly_frobenius(frob_matrix, charpoly_prec, p, weight, a = 1): """ INPUT: - ``frob_matrix`` -- a matrix representing the p-power Frobenius lift to Z_q up to some precision - ``charpoly_prec`` -- a vector ai, such that, frob_matrix.change_ring(ZZ).charpoly()[i] will be correct mod p^ai, this can be easily deduced from the hodge numbers and knowing the q-adic precision of frob_matrix - ``p`` -- prime p - ``weight`` -- weight of the motive - ``a`` -- q = q^a OUTPUT: a list of integers corresponding to the characteristic polynomial of the Frobenius action Examples: sage: M = Matrix([[O(17), 8 + O(17)], [O(17), 15 + O(17)]]) sage: charpoly_frobenius(M, [2, 1, 1], 17, 1, 1) [17, 2, 1] sage: R = Zq(17 ** 2 , names=('a',)); sage: M = Matrix(R, [[8*17 + 16*17**2 + O(17**3), 8 + 11*17 + O(17**2)], [7*17**2 + O(17**3), 15 + 8*17 + O(17**2)]]) sage: charpoly_frobenius(M, [3, 2, 2], 17, 1, 2) [289, 30, 1] sage: M = Matrix([[8*31 + 8*31**2 + O(31**3), O(31**3), O(31**3), O(31**3)], [O(31**3), 23*31 + 22*31**2 + O(31**3), O(31**3), O(31**3)], [O(31**3), O(31**3), 27 + 7*31 + O(31**3), O(31**3)], [O(31**3), O(31**3), O(31**3), 4 + 23*31 + O(31**3)]]) sage: charpoly_frobenius(M, [4, 3, 2, 2, 2], 31, 1, 1) [961, 0, 46, 0, 1] sage: M = Matrix([[4*43**2 + O(43**3), 17*43 + 11*43**2 + O(43**3), O(43**3), O(43**3), 17 + 37*43 + O(43**3), O(43**3)], [30*43 + 23*43**2 + O(43**3), 5*43 + O(43**3), O(43**3), O(43**3), 3 + 38*43 + O(43**3), O(43**3)], [O(43**3), O(43**3), 9*43 + 32*43**2 + O(43**3), 13 + 25*43 + O(43**3), O(43**3), 17 + 18*43 + O(43**3)], [O(43**3), O(43**3), 22*43 + 25*43**2 + O(43**3), 11 + 24*43 + O(43**3), O(43**3), 36 + 5*43 + O(43**3)], [42*43 + 15*43**2 + O(43**3), 22*43 + 8*43**2 + O(43**3), O(43**3), O(43**3), 29 + 4*43 + O(43**3), O(43**3)], [O(43**3), O(43**3), 6*43 + 19*43**2 + O(43**3), 8 + 24*43 + O(43**3), O(43**3), 31 + 42*43 + O(43**3)]]) sage: charpoly_frobenius(M, [5, 4, 3, 2, 2, 2, 2], 43, 1, 1) [79507, 27735, 6579, 1258, 153, 15, 1] """ if a > 1: F = frob_matrix sigmaF = F; for _ in range(a - 1): sigmaF = Matrix(F.base_ring(), [[elt.frobenius() for elt in row] for row in sigmaF.rows()]) F = F * sigmaF F = F.change_ring(ZZ) else: F = frob_matrix.change_ring(ZZ) cp = F.charpoly().list(); assert len(charpoly_prec) == len(cp) assert cp[-1] == 1; # reduce cp mod prec degree = F.nrows() mod = [0] * (degree + 1) for i in range(degree): mod[i] = p**charpoly_prec[i] cp[i] = cp[i] % mod[i] # figure out the sign if weight % 2 == 1: # for odd weight the sign is always 1 # it's the charpoly of a USp matrix # and charpoly of a symplectic matrix is reciprocal sign = 1 else: for i in range(degree/2): p_power = p ** min( charpoly_prec[i], charpoly_prec[degree - i] + (a*(degree - 2*i)*weight/2)); # Note: degree*weight = 0 mod 2 if cp[i] % p_power != 0 and cp[degree-i] % p_power != 0: if 0 == (cp[i] + cp[degree - i] * p**(a*(degree-2*i)*weight/2)) % p_power: sign = -1; else: sign = 1; assert 0 == (-sign*cp[i] + cp[degree - i] * p**(a*(degree-2*i)*weight/2)) % p_power; break; cp[0] = sign * p**(a*degree*weight/2) #calculate the i-th power sum of the roots and correct cp allong the way halfdegree = ceil(degree/2) + 1; e = [None]*(halfdegree) for k in range(halfdegree): e[k] = cp[degree - k] if (k%2 ==0) else -cp[degree - k] if k > 0: # verify if p^charpoly_prec[degree - k] > 2*degree/k * q^(w*k/2) assert log(k)/log(p) + charpoly_prec[degree - k] > log(2*degree)/log(p) + a*0.5*weight*k, "log(k)/log(p) + charpoly_prec[degree - k] <= log(2*degree)/log(p) + a*0.5*weight*k, k = %d" % k #e[k] = \sum x_{i_1} x_{i_2} ... x_{i_k} # where x_* are eigenvalues # and i_1 < i_2 ... < i_k #s[k] = \sum x_i ^k for i>0 s = [None]*(halfdegree) for k in range(1, halfdegree): # assume that s[i] and e[i] are correct for i < k # e[k] correct modulo mod[degree - k] # S = sum (-1)^i e[k-i] * s[i] # s[k] = (-1)^(k-1) (k*e[k] + S) ==> (-1)^(k-1) s[k] - S = k*e[k] S = sum( (-1)**i * e[k-i] * s[i] for i in range(1,k)) s[k] = (-1)**(k - 1) * (S + k*e[k]) #hence s[k] is correct modulo k*mod[degree - k] localmod = k*mod[degree - k] s[k] = s[k] % localmod # |x_i| = p^(w*0.5) # => s[k] <= degree*p^(a*w*k*0.5) # recall, 2*degree*p^(a*w*k*0.5) /k < mod[degree - k] if s[k] > degree*p**(a*weight*k*0.5) : s[k] = -(-s[k] % localmod); #now correct e[k] with: # (-1)^(k-1) s[k] - S = k*e[k] e[k] = (-S + (-1)**(k - 1) * s[k])//k; assert (-S + (-1)**(k - 1) * s[k])%k == 0 cp[degree - k] = e[k] if k % 2 == 0 else -e[k] cp[k] = sign*cp[degree - k]*p**(a*(degree-2*k)*weight/2) return cp
def bound_rosati(alpha_geo): H = Matrix(4, 4) H[0, 2] = H[1, 3] = -1 H[2, 0] = H[3, 1] = 1 return (alpha_geo * H * alpha_geo.transpose() * H.inverse()).trace() / 2
def integer_bivariate(p, k, X, Y, early_return=True): """ Computes small integer roots of a bivariate polynomial. More information: Coron J., "Finding Small Roots of Bivariate Integer Polynomial Equations: a Direct Approach" :param p: the polynomial :param k: the amount of shifts to use :param X: an approximate bound on the x roots :param Y: an approximate bound on the y roots :param early_return: try to return as early as possible (default: true) :return: a generator generating small roots (tuples of x and y roots) of the polynomial """ x, y = p.parent().gens() delta = max(p.degrees()) (i0, j0), W = max(map(lambda kv: (kv[0], abs(kv[1])), p(x * X, y * Y).dict().items()), key=lambda kv: kv[1]) logging.debug("Calculating n...") S = Matrix(k**2) for a in range(k): for b in range(k): s = x**a * y**b * p for i in range(k): for j in range(k): S[a * k + b, i * k + j] = s.coefficient([i0 + i, j0 + j]) n = abs(S.det()) logging.debug(f"Found n = {n}") # Monomials are collected in "left" and "right" lists, which determine where the columns are in relation to eachother # This partition ensures the Hermite form will set desired monomial coefficients to zero logging.debug("Generating monomials...") left_monomials = [] right_monomials = [] for i in range(k + delta): for j in range(k + delta): if 0 <= i - i0 < k and 0 <= j - j0 < k: left_monomials.append(x**i * y**j) else: right_monomials.append(x**i * y**j) assert len(left_monomials) == k**2 monomials = left_monomials + right_monomials L = Matrix(k**2 + (k + delta)**2, (k + delta)**2) row = 0 logging.debug("Generating s shifts...") for a in range(k): for b in range(k): s = x**a * y**b * p s = s(x * X, y * Y) for col, monomial in enumerate(monomials): L[row, col] = s.monomial_coefficient(monomial) row += 1 logging.debug("Generating additional shifts...") for col, monomial in enumerate(monomials): r = monomial * n r = r(x * X, y * Y) L[row, col] = r.monomial_coefficient(monomial) row += 1 logging.debug(f"Lattice size: {L.nrows()} x {L.ncols()}") logging.debug("Generating Echelon form...") L = L.echelon_form(algorithm="pari0") logging.debug( f"Executing the LLL algorithm on the sublattice ({k ** 2} x {k ** 2})..." ) L2 = L.submatrix(k**2, k**2, (k + delta)**2 - k**2).LLL() new_polynomials = [] logging.debug("Reconstructing polynomials...") for row in range(L2.nrows()): new_polynomial = 0 # Only use right monomials now (corresponding the the sublattice) for col, monomial in enumerate(right_monomials): new_polynomial += L2[row, col] * monomial if new_polynomial.is_constant(): continue new_polynomial = new_polynomial(x / X, y / Y).change_ring(ZZ) new_polynomials.append(new_polynomial) logging.debug("Calculating resultants...") for h in new_polynomials: res = p.resultant(h, y) if res.is_constant(): continue for x0, _ in res.univariate_polynomial().roots(): h_ = p.subs(x=x0) if h_.is_constant(): continue for y0, _ in h_.univariate_polynomial().roots(): yield int(x0), int(y0) if early_return: # Assuming that the first "good" polynomial in the lattice doesn't provide roots, we return. return
def E(n, i, j, ring=QQ): A = Matrix(ring, n) A[i, j] = 1 return A
def qrp(A, rank = None, extra_prec = 16): m = A.nrows() n = A.ncols() s = min(m,n) if rank is not None: #s = min(s, rank + 1); pass; base_prec = A.base_ring().precision(); base_field = A.base_ring() if is_ComplexField(base_field): field = ComplexField(base_prec + extra_prec + m); else: field = RealField(base_prec + extra_prec + m); # field = base_field; R = copy(A); A = A.change_ring(field); P = [ j for j in xrange(n) ]; Q = identity_matrix(field,m) # print A.base_ring() # print Q.base_ring() normbound = field(2)**(-0.5*field.precision()); colnorms2 = [ float_sum([ R[k,j].norm() for k in range(m)]) for j in range(n) ]; for j in xrange(s): # find column with max norm maxnorm2 = colnorms2[j]; p = j; for k in xrange(j, n): if colnorms2[k] > maxnorm2: p = k; maxnorm2 = colnorms2[k]; #it is worth it to recompute the maxnorm xnorm2 = float_sum([ R[i,p].norm() for i in range(j, m)]) xnorm = sqrt(xnorm2); # swap j and p in A, P and colnorms if j != p: P[j], P[p] = P[p], P[j] colnorms2[j], colnorms2[p] = colnorms2[p], colnorms2[j] for i in xrange(0, m): R[i, j], R[i, p] = R[i, p], R[i, j]; # compute householder vector u = [ R[k,j] for k in xrange(j, m) ]; alpha = R[j,j]; # alphanorm = alpha.abs(); if alpha.real() < 0: u[0] -= xnorm z = -1 else: u[0] += xnorm z = 1 beta = xnorm*(xnorm + alpha * z); if beta.abs() < normbound: beta = float_sum( [u[i].conjugate() * R[i+j,j] for i in range(m - j)]) if beta == field(0): break; beta = 1/beta; # H = I - beta * u * u^* # Apply householder matrix #update R R[j,j] = -z * xnorm; for i in xrange(j+1,m): R[i,j] = 0 for k in xrange(j+1, n): lambdR = beta * float_sum( [u[l - j].conjugate() * R[l, k] for l in xrange(j, m)] ); for i in xrange(j, m): R[i, k] -= lambdR * u[i - j]; #update Q for k in xrange(m): lambdQ = float_sum( [u[l - j].conjugate() * Q[l, k] for l in xrange(j, m)] ); lambdQ *= beta for i in xrange(j,m): Q[i, k] -= lambdQ * u[i - j]; for k in xrange(j + 1, n): square = R[j,k].norm(); colnorms2[k] -= square; # sometimes is better to just recompute the norm if True: #colnorms2[k] < normbound or colnorms2[k] < square*normbound: colnorms2[k] = float_sum([ R[i,k].norm() for i in range(j + 1, m)]) # compute P matrix Pmatrix = Matrix(ZZ, n); for i in xrange(n): Pmatrix[ P[i], i] = 1; return Q.conjugate_transpose().change_ring(base_field), R.change_ring(base_field), Pmatrix;
def to_sage(field,A): return Matrix(field, [[ field(A[i,j].real, A[i,j].imag) for j in range(A.cols)] for i in range(A.rows)])
def _get_element_nullspace(self, M): ## We take the Conversion System R = self.__conversion ## We assume the matrix is in the correct space M = Matrix(R.poly_field(), [[R.simplify(el.poly()) for el in row] for row in M]) ## We clean denominators to lie in the polynomial ring lcms = [self.__get_lcm([el.denominator() for el in row]) for row in M] M = Matrix(R.poly_ring(), [[el * lcms[i] for el in M[i]] for i in range(M.nrows())]) f = lambda p: R(p).is_zero() try: assert (f(1) == False) except Exception: raise ValueError("The method to check membership is not correct") ## Computing the kernell of the matrix if (len(R.map_of_vars()) > 0): bareiss_algorithm = BareissAlgorithm( R.poly_ring(), M, f, R._ConversionSystem__relations) ker = bareiss_algorithm.syzygy().transpose() ## If some relations are found during this process, we add it to the conversion system R.add_relations(bareiss_algorithm.relations()) else: ker = [v for v in M.right_kernel_matrix()] ## If the nullspace has high dimension, we try to reduce the final vector computing zeros at the end aux = [row for row in ker] i = 1 while (len(aux) > 1): new = [] current = None for j in range(len(aux)): if (aux[j][-(i)] == 0): new += [aux[j]] elif (current is None): current = j else: new += [ aux[current] * aux[j][-(i)] - aux[j] * aux[current][-(i)] ] current = j aux = [el / gcd(el) for el in new] i = i + 1 ## When exiting the loop, aux has just one vector sol = aux[0] sol = [R.simplify(a) for a in sol] ## Just to be sure everything is as simple as possible, we divide again by the gcd of all our ## coefficients and simplify them using the relations known. fin_gcd = gcd(sol) finalSolution = [a / fin_gcd for a in sol] finalSolution = [R.simplify(a) for a in finalSolution] ## We transform our polynomial into elements of our destiny domain realSolution = [R.to_real(a) for a in finalSolution] for i in range(len(realSolution)): try: realSolution[i].built = ("polynomial", (finalSolution[i], { str(key): R.map_of_vars()[str(key)] for key in finalSolution[i].variables() })) except AttributeError: pass return vector(R.base(), realSolution)
def reduce_matrix_split_relative(M, p_root, relative_root): return Matrix([[ reduce_constant_split_relative(x, p_root, relative_root) for x in row ] for row in M.rows()]);
def swap_cols(M, c1, c2): r''' Method for performing the swap of two columns in Gauss-Jordan elimination. This method takes a matrix `M`, two column indices and returns a new matrix where the elements in the columns have been swapped. This is one of the basic steps while performing Gauss-Jordan elimination on a matrix to compute its determinant/nullspace/solution space/rank etc. INPUT: * ``M``: the original matrix with entries `m_{i,j}`. * ``c1``: first index of the column to be swapped. * ``c2``: second index of the column to be swapped. OUTPUT: A new matrix where the elements of the columns indexed by ``c1`` and ``c2`` have been exchanged. EXAMPLES:: sage: from ajpastor.misc.matrix import * sage: M = Matrix(QQ, [[1,2],[3,4]]) sage: swap_cols(M, 0, 1) [2 1] [4 3] sage: N = block_matrix(QQ, [[M, 0],[0, 1]]) sage: swap_cols(N, 0, 1) [2 1 0] [4 3 0] [0 0 1] sage: swap_cols(N, 1, 2) [1 0 2] [3 0 4] [0 1 0] This method raises a ValueError when the index is not valid:: sage: swap_cols(N, -1, 1) Traceback (most recent call last): ... ValueError: The first column index is not valid sage: swap_cols(N, 1, 10) Traceback (most recent call last): ... ValueError: The second column index is not valid Also, if the input for the matrix is not such, the method raises a TypeError:: sage: swap_cols([[1,2,0],[3,4,0],[0,0,1]], 2, 10) Traceback (most recent call last): ... TypeError: First argument must be a matrix. ... ''' try: if (0 > c1 or c1 >= M.ncols()): raise ValueError("The first column index is not valid") if (0 > c2 or c2 >= M.ncols()): raise ValueError("The second column index is not valid") ## We make a copy of the matrix new_rows = [] for i in range(M.nrows()): new_rows += [[M[i][j] for j in range(M.ncols())]] ## We make the current operation for i in range(M.nrows()): new_rows[i][c1] = M[i][c2] new_rows[i][c2] = M[i][c1] return Matrix(M.parent().base(), new_rows) except AttributeError: raise TypeError("First argument must be a matrix. Given %s" % M)
def scal_col(M, d_c, el): r''' Method for performing the rescale of a column in Gauss-Jordan elimination. This method takes a matrix `M`, a column index and an element from the parent ring of `M` and returns a new matrix where the elements in the column have been scaled by the element. This is one of the basic steps while performing Gauss-Jordan elimination on a matrix to compute its determinant/nullspace/solution space/rank etc. INPUT: * ``M``: the original matrix with entries `m_{i,j}`. * ``d_c``: index of the column where the scaling will be performed. * ``el``: element in the base ring of ``M`` that will be used in the scaling. OUTPUT: A new matrix `\tilde{M}` with entries `\tilde{m}_{i,j}` such that: * If ``j != d_c``: `\tilde{m}_{i,j} = m_{i,j}`. * Else: `\tilde{m}_{i,d_c} = (el)m_{i,d_c}`. EXAMPLES:: sage: from ajpastor.misc.matrix import * sage: M = Matrix(QQ, [[1,2],[3,4]]) sage: scal_col(M, 0, 2) [2 2] [6 4] sage: scal_col(M, 1, -1) [ 1 -2] [ 3 -4] sage: N = block_matrix(QQ, [[M, 0],[0, 1]]) sage: scal_col(N, 2, 10) [ 1 2 0] [ 3 4 0] [ 0 0 10] sage: scal_col(N, 1, 1) [1 2 0] [3 4 0] [0 0 1] This method raises a ValueError when the index is not valid:: sage: scal_col(N, -1, 1) Traceback (most recent call last): ... ValueError: The column index is not valid sage: scal_col(N, 10, 1) Traceback (most recent call last): ... ValueError: The column index is not valid Also, if the input for the matrix is not such, the method raises a TypeError:: sage: scal_col([[1,2,0],[3,4,0],[0,0,1]], 2, 10) Traceback (most recent call last): ... TypeError: First argument must be a matrix. ... It is interesting to remark that this method is equivalent to the application of the method :func:`op_cols` with the same destiny and source indices and the element being ``-element+1``:: sage: scal_col(N, 2, 10) == op_cols(N, 2, 2, -9) True ''' try: if (0 > d_c or d_c >= M.ncols()): raise ValueError("The column index is not valid") ## We make a copy of the matrix new_rows = [] for i in range(M.nrows()): new_rows += [[M[i][j] for j in range(M.ncols())]] ## We make the current operations for i in range(M.nrows()): new_rows[i][d_c] = el * M[i][d_c] return Matrix(M.parent().base(), new_rows) except AttributeError: raise TypeError("First argument must be a matrix. Given %s" % M)
def op_rows(M, s_r, d_r, el): r''' Method for performing the row substraction in Gauss-Jordan elimination. This method takes a matrix `M`, two rows indices and an element from the parent ring of `M` and returns a new matrix where the elements in the second row have been "reduced" by the elements in the first row times such element. This is one of the basic steps while performing Gauss-Jordan elimination on a matrix to compute its determinant/nullspace/solution space/rank etc. INPUT: * ``M``: the original matrix with entries `m_{i,j}`. * ``s_r``: index of the row that will be used in the reduction. * ``d_r``: index of the row where the reduction will be performed. * ``el``: element in the base ring of ``M`` that will be used in the reduction. OUTPUT: A new matrix `\tilde{M}` with entries `\tilde{m}_{i,j}` such that: * If ``i != d_r``: `\tilde{m}_{i,j} = m_{i,j}`. * Else: `\tilde{m}_{d_r,j} = m_{d_r,j} - (el)m_{s_r,j}`. EXAMPLES:: sage: from ajpastor.misc.matrix import * sage: M = Matrix(QQ, [[1,2],[3,4]]) sage: op_rows(M, 0, 1, 2) [1 2] [1 0] sage: op_rows(M, 1, 0, -1) [4 6] [3 4] sage: N = block_matrix(QQ, [[M, 0],[0, 1]]) sage: op_rows(N, 2, 0, 10) [ 1 2 -10] [ 3 4 0] [ 0 0 1] sage: op_rows(N, 1, 2, 1) [ 1 2 0] [ 3 4 0] [-3 -4 1] This method raises a ValueError when the indices are not valid:: sage: op_rows(N, -1, 2, 1) Traceback (most recent call last): ... ValueError: The source row index is not valid sage: op_rows(N, 1, -2, 1) Traceback (most recent call last): ... ValueError: The destiny row index is not valid Also, if the input for the matrix is not such, the method raises a TypeError:: sage: op_rows([[1,2,0],[3,4,0],[0,0,1]], 1, 2, 1) Traceback (most recent call last): ... TypeError: First argument must be a matrix. ... ''' try: if (0 > s_r or s_r >= M.nrows()): raise ValueError("The source row index is not valid") if (0 > d_r or d_r >= M.nrows()): raise ValueError("The destiny row index is not valid") ## We make a copy of the matrix new_rows = [] for i in range(M.nrows()): new_rows += [[M[i][j] for j in range(M.ncols())]] ## We make the current operations for i in range(M.ncols()): new_rows[d_r][i] = M[d_r][i] - el * M[s_r][i] return Matrix(M.parent().base(), new_rows) except AttributeError: raise TypeError("First argument must be a matrix. Given %s" % M)
def make_class(self): # Create a list of the curves in the class from the database self.db_curves = list( db.ec_nfcurves.search({ 'field_label': self.field_label, 'conductor_norm': self.conductor_norm, 'conductor_label': self.conductor_label, 'iso_nlabel': self.iso_nlabel })) # Rank or bounds try: self.rk = web_latex(self.db_curves[0]['rank']) except KeyError: self.rk = "?" try: self.rk_bnds = "%s...%s" % tuple(self.db_curves[0]['rank_bounds']) except KeyError: self.rank_bounds = [0, Infinity] self.rk_bnds = "not recorded" # Extract the isogeny degree matrix from the database if not hasattr(self, 'isogeny_matrix'): # this would happen if the class is initiated with a curve # which is not #1 in its class: self.isogeny_matrix = self.db_curves[0].isogeny_matrix self.isogeny_matrix = Matrix(self.isogeny_matrix) self.one_deg = ZZ(self.class_deg).is_prime() # Create isogeny graph: self.graph = make_graph(self.isogeny_matrix) P = self.graph.plot(edge_labels=True) self.graph_img = encode_plot(P) self.graph_link = '<img src="%s" width="200" height="150"/>' % self.graph_img self.isogeny_matrix_str = latex(Matrix(self.isogeny_matrix)) self.field = FIELD(self.field_label) self.field_name = field_pretty(self.field_label) self.field_knowl = nf_display_knowl(self.field_label, self.field_name) def curve_url(c): return url_for(".show_ecnf", nf=c['field_label'], conductor_label=c['conductor_label'], class_label=c['iso_label'], number=c['number']) self.curves = [[ c['short_label'], curve_url(c), web_ainvs(self.field_label, c['ainvs']) ] for c in self.db_curves] self.urls = {} self.urls['class'] = url_for(".show_ecnf_isoclass", nf=self.field_label, conductor_label=self.conductor_label, class_label=self.iso_label) self.urls['conductor'] = url_for(".show_ecnf_conductor", nf=self.field_label, conductor_label=self.conductor_label) self.urls['field'] = url_for('.show_ecnf1', nf=self.field_label) sig = self.signature totally_real = sig[1] == 0 imag_quadratic = sig == [0, 1] if totally_real: self.hmf_label = "-".join( [self.field_label, self.conductor_label, self.iso_label]) self.urls['hmf'] = url_for('hmf.render_hmf_webpage', field_label=self.field_label, label=self.hmf_label) if sig[0] <= 2: self.urls['Lfunction'] = url_for( "l_functions.l_function_ecnf_page", field_label=self.field_label, conductor_label=self.conductor_label, isogeny_class_label=self.iso_label) elif self.abs_disc**2 * self.conductor_norm < 40000: # we shouldn't trust the Lfun computed on the fly for large conductor self.urls['Lfunction'] = url_for( "l_functions.l_function_hmf_page", field=self.field_label, label=self.hmf_label, character='0', number='0') if imag_quadratic: self.bmf_label = "-".join( [self.field_label, self.conductor_label, self.iso_label]) self.bmf_url = url_for('bmf.render_bmf_webpage', field_label=self.field_label, level_label=self.conductor_label, label_suffix=self.iso_label) self.urls['Lfunction'] = url_for( "l_functions.l_function_ecnf_page", field_label=self.field_label, conductor_label=self.conductor_label, isogeny_class_label=self.iso_label) self.friends = [] if totally_real: self.friends += [('Hilbert Modular Form ' + self.hmf_label, self.urls['hmf'])] if imag_quadratic: #self.friends += [('Bianchi Modular Form %s not available' % self.bmf_label, '')] self.friends += [('Bianchi Modular Form %s' % self.bmf_label, self.bmf_url)] if 'Lfunction' in self.urls: self.friends += [('L-function', self.urls['Lfunction'])] else: self.friends += [('L-function not available', "")] self.properties = [('Base field', self.field_name), ('Label', self.class_label), (None, self.graph_link), ('Conductor', '%s' % self.conductor_label)] if self.rk != '?': self.properties += [('Rank', '%s' % self.rk)] else: if self.rk_bnds == 'not recorded': self.properties += [('Rank', '%s' % self.rk_bnds)] else: self.properties += [('Rank bounds', '%s' % self.rk_bnds)] self.bread = [('Elliptic Curves ', url_for(".index")), (self.field_label, self.urls['field']), (self.conductor_label, self.urls['conductor']), ('isogeny class %s' % self.short_label, self.urls['class'])]
def list_of_basis(N, weight, prec=501, tol=1e-20, sv_min=1E-1, sv_max=1E15, set_dim=None): r""" Returns a list of pairs (r,D) forming a basis """ # First we find the smallest Discriminant for each of the components if set_dim != None and set_dim > 0: dim = set_dim else: dim = dimension_jac_cusp_forms(int(weight + 0.5), N, -1) basislist = dict() num_gotten = 0 co_tmp = dict() num_gotten = 0 C0 = 1 RF = RealField(prec) if (silent > 1): print("N={0}".format(N)) print("dim={0}".format(dim)) print("sv_min={0}".format(sv_min)) print("sv_max={0}".format(sv_max)) Aold = Matrix(RF, 1) tol0 = 1E-20 #tol # we start with the first discriminant, then the second etc. Z2N = IntegerModRing(2 * N) ZZ4N = IntegerModRing(4 * N) for Dp in [1..max(1000, 100 * dim)]: D = -Dp # we use the dual of the Weil representation D4N = ZZ4N(D) if (not (is_square(D4N))): continue for r in my_modsqrt(D4N, N): # I want to make sure that P_{(D,r)} is independent from the previously computed functions # The only sure way to do this is to compute all submatrices (to a much smaller precision than what we want at the end) # The candidate is [D,r] and we need to compute the vector of [D,r,D',r'] # for all D',r' already in the list ltmp1 = dict() ltmp2 = dict() j = 0 for [Dp, rp] in basislist.values(): ltmp1[j] = [D, r, Dp, rp] ltmp2[j] = [Dp, rp, D, r] j = j + 1 ltmp1[j] = [D, r, D, r] #print "Checking: D,r,D,r=",ltmp1 ctmp1 = ps_coefficients_holomorphic_vec(N, weight, ltmp1, tol0) # print "ctmp1=",ctmp1 if (j > 0): #print "Checking: D,r,Dp,rp=",ltmp2 # Data is ok?: {0: True} ctmp2 = ps_coefficients_holomorphic_vec(N, weight, ltmp2, tol0) # print "ctmp2=",ctmp2 #print "num_gotten=",num_gotten A = matrix(RF, num_gotten + 1) # The old matrixc with the elements that are already added to the basis # print "Aold=\n",A,"\n" # print "num_gotten=",num_gotten # print "Aold=\n",Aold,"\n" for k in range(Aold.nrows()): for l in range(Aold.ncols()): A[k, l] = Aold[k, l] # endfor # print "A set by old=\n",A,"\n" # Add the (D',r',D,r) for each D',r' in the list tmp = RF(1.0) for l in range(num_gotten): # we do not use the scaling factor when # determining linear independence # mm=RF(abs(ltmp2[l][0]))/N4 # tmp=RF(mm**(weight-one)) A[num_gotten, l] = ctmp2['data'][l] * tmp # Add the (D,r,D',r') for each D',r' in the list # print "ctmp1.keys()=",ctmp1.keys() for l in range(num_gotten + 1): #mm=RF(abs(ltmp1[l][2]))/4N #tmp=RF(mm**(weight-one)) # print "scaled with=",tmp.n(200) A[l, num_gotten] = ctmp1['data'][l] * tmp #[d,B]=mat_inverse(A) # d=det(A) #if(silent>1): #d=det(A) #print "det A = ",d # Now we have to determine whether we have a linearly independent set or not dold = mpmath.mp.dps mpmath.mp.dps = int(prec / 3.3) AInt = mpmath.matrix(int(A.nrows()), int(A.ncols())) AMp = mpmath.matrix(int(A.nrows()), int(A.ncols())) if (silent > 0): print("tol0={0}".format(tol0)) for ir in range(A.nrows()): for ik in range(A.ncols()): AInt[ir, ik] = mpmath.mp.mpi(A[ir, ik] - tol0, A[ir, ik] + tol0) AMp[ir, ik] = mpmath.mpf(A[ir, ik]) d = mpmath.det(AMp) di = mpmath.mp.mpi(mpmath.mp.det(AInt)) #for ir in range(A.nrows()): # for ik in range(A.ncols()): # #print "A.d=",AInt[ir,ik].delta if (silent > 0): print("mpmath.mp.dps={0}".format(mpmath.mp.dps)) print("det(A)={0}".format(d)) print("det(A-as-interval)={0}".format(di)) print("d.delta={0}".format(di.delta)) #if(not mpmath.mpi(d) in di): # raise ArithmeticError," Interval determinant not ok?" #ANP=A.numpy() #try: # u,s,vnp=svd(ANP) # s are the singular values # sl=s.tolist() # mins=min(sl) # the smallest singular value # maxs=max(sl) # if(silent>1): # print "singular values = ",s #except LinAlgError: # if(silent>0): # print "could not compute SVD!" # print "using abs(det) instead" # mins=abs(d) # maxs=abs(d) #if((mins>sv_min and maxs< sv_max)): zero = mpmath.mpi(0) if (zero not in di): if (silent > 1): print("Adding D,r={0}, {1}".format(D, r)) basislist[num_gotten] = [D, r] num_gotten = num_gotten + 1 if (num_gotten >= dim): return basislist else: #print "setting Aold to A" Aold = A else: if (silent > 1): print(" do not use D,r={0}, {1}".format(D, r)) # endif mpmath.mp.dps = dold # endfor if (num_gotten < dim): raise ValueError( " did not find enough good elements for a basis list!")
def make_class(self): # Create a list of the curves in the class from the database self.db_curves = list( db.ec_nfcurves.search({ 'field_label': self.field_label, 'conductor_norm': self.conductor_norm, 'conductor_label': self.conductor_label, 'iso_nlabel': self.iso_nlabel })) # Rank or bounds try: self.rk = web_latex(self.db_curves[0]['rank']) except KeyError: self.rk = "?" try: self.rk_bnds = "%s...%s" % tuple(self.db_curves[0]['rank_bounds']) except KeyError: self.rank_bounds = [0, Infinity] self.rk_bnds = "not recorded" # Extract the isogeny degree matrix from the database if not hasattr(self, 'isogeny_matrix'): # this would happen if the class is initiated with a curve # which is not #1 in its class: self.isogeny_matrix = self.db_curves[0].isogeny_matrix self.isogeny_matrix = Matrix(self.isogeny_matrix) self.one_deg = ZZ(self.class_deg).is_prime() # Create isogeny graph: self.graph = make_graph(self.isogeny_matrix) P = self.graph.plot(edge_labels=True) self.graph_img = encode_plot(P) self.graph_link = '<img src="%s" width="200" height="150"/>' % self.graph_img self.isogeny_matrix_str = latex(Matrix(self.isogeny_matrix)) self.field = FIELD(self.field_label) self.field_name = field_pretty(self.field_label) self.field_knowl = nf_display_knowl(self.field_label, self.field_name) def curve_url(c): return url_for(".show_ecnf", nf=c['field_label'], conductor_label=c['conductor_label'], class_label=c['iso_label'], number=c['number']) self.curves = [[ c['short_label'], curve_url(c), web_ainvs(self.field_label, c['ainvs']) ] for c in self.db_curves] self.urls = {} self.urls['class'] = url_for(".show_ecnf_isoclass", nf=self.field_label, conductor_label=self.conductor_label, class_label=self.iso_label) self.urls['conductor'] = url_for(".show_ecnf_conductor", nf=self.field_label, conductor_label=self.conductor_label) self.urls['field'] = url_for('.show_ecnf1', nf=self.field_label) sig = self.signature totally_real = sig[1] == 0 imag_quadratic = sig == [0, 1] if totally_real: self.hmf_label = "-".join( [self.field_label, self.conductor_label, self.iso_label]) self.urls['hmf'] = url_for('hmf.render_hmf_webpage', field_label=self.field_label, label=self.hmf_label) lfun_url = url_for("l_functions.l_function_ecnf_page", field_label=self.field_label, conductor_label=self.conductor_label, isogeny_class_label=self.iso_label) origin_url = lfun_url.lstrip('/L/').rstrip('/') if sig[0] <= 2 and db.lfunc_instances.exists({'url': origin_url}): self.urls['Lfunction'] = lfun_url elif self.abs_disc**2 * self.conductor_norm < 40000: # we shouldn't trust the Lfun computed on the fly for large conductor self.urls['Lfunction'] = url_for( "l_functions.l_function_hmf_page", field=self.field_label, label=self.hmf_label, character='0', number='0') if imag_quadratic: self.bmf_label = "-".join( [self.field_label, self.conductor_label, self.iso_label]) self.bmf_url = url_for('bmf.render_bmf_webpage', field_label=self.field_label, level_label=self.conductor_label, label_suffix=self.iso_label) lfun_url = url_for("l_functions.l_function_ecnf_page", field_label=self.field_label, conductor_label=self.conductor_label, isogeny_class_label=self.iso_label) origin_url = lfun_url.lstrip('/L/').rstrip('/') if db.lfunc_instances.exists({'url': origin_url}): self.urls['Lfunction'] = lfun_url # most of this code is repeated in WebEllipticCurve.py # and should be refactored self.friends = [] if totally_real and not 'Lfunction' in self.urls: self.friends += [('Hilbert Modular Form ' + self.hmf_label, self.urls['hmf'])] if imag_quadratic: if "CM" in self.label: self.friends += [('Bianchi modular Form is not cuspidal', '')] elif not 'Lfunction' in self.urls: if db.bmf_forms.label_exists(self.bmf_label): self.friends += [ ('Bianchi modular Form %s' % self.bmf_label, self.bmf_url) ] else: self.friends += [ ('(Bianchi modular Form %s)' % self.bmf_label, '') ] if 'Lfunction' in self.urls: Lfun = get_lfunction_by_url( self.urls['Lfunction'].lstrip('/L').rstrip('/'), projection=['degree', 'trace_hash', 'Lhash']) instances = get_instances_by_Lhash_and_trace_hash( Lfun['Lhash'], Lfun['degree'], Lfun.get('trace_hash')) exclude = { elt[1].rstrip('/').lstrip('/') for elt in self.friends if elt[1] } exclude.add(lfun_url.lstrip('/L/').rstrip('/')) self.friends += names_and_urls(instances, exclude=exclude) self.friends += [('L-function', self.urls['Lfunction'])] else: self.friends += [('L-function not available', "")] self.properties = [('Base field', self.field_name), ('Label', self.class_label), (None, self.graph_link), ('Conductor', '%s' % self.conductor_label)] if self.rk != '?': self.properties += [('Rank', '%s' % self.rk)] else: if self.rk_bnds == 'not recorded': self.properties += [('Rank', '%s' % self.rk_bnds)] else: self.properties += [('Rank bounds', '%s' % self.rk_bnds)] self.bread = [('Elliptic Curves ', url_for(".index")), (self.field_label, self.urls['field']), (self.conductor_label, self.urls['conductor']), ('isogeny class %s' % self.short_label, self.urls['class'])]
def cuspidal_integral_to_rational_basis(M): """ Returns the base change matrix for the cuspidal subspace of M """ S=M.cuspidal_subspace() return Matrix([S.coordinate_vector(i) for i in cuspidal_integral_structure_matrix(M)])
def _get_element_nullspace(self, M): ## We take the domain where our elements will lie ## conversion->lazy domain->domain parent = self.__conversion.base().base() ## We assume the matrix is in the correct space M = self.__conversion.simplify(M) ## We clean denominators to lie in the polynomial ring lcms = [lcm([el.denominator() for el in row]) for row in M] R = M.parent().base().base() M = Matrix(R, [[el * lcms[i] for el in M[i]] for i in range(M.nrows())]) f = lambda p: self.__smart_is_null(p) try: assert (f(1) == False) except Exception: raise ValueError("The method to check membership is not correct") from sage.rings.polynomial.polynomial_ring import is_PolynomialRing as isUniPolynomial from sage.rings.polynomial.multi_polynomial_ring import is_MPolynomialRing as isMPolynomial ## Computing the kernell of the matrix if (isUniPolynomial(R) or isMPolynomial(R)): bareiss_algorithm = BareissAlgorithm(R, M, f) ker = bareiss_algorithm.syzygy().transpose() ## If some relations are found during this process, we add it to the conversion system self.__conversion.add_relations(bareiss_algorithm.relations()) else: ker = [v for v in M.right_kernel_matrix()] ## If the nullspace has high dimension, we try to reduce the final vector computing zeros at the end aux = [row for row in ker] i = 1 while (len(aux) > 1): new = [] current = None for j in range(len(aux)): if (aux[j][-(i)] == 0): new += [aux[j]] elif (current is None): current = j else: new += [ aux[current] * aux[j][-(i)] - aux[j] * aux[current][-(i)] ] current = j aux = [el / gcd(el) for el in new] i = i + 1 ## When exiting the loop, aux has just one vector sol = aux[0] sol = [self.__conversion.simplify(a) for a in sol] ## This steps for clearing denominator are no longer needed #lazyLcm = lcm([a.denominator() for a in sol]) #finalLazySolution = [a.numerator()*(lazyLcm/(a.denominator())) for a in sol] ## Just to be sure everything is as simple as possible, we divide again by the gcd of all our ## coefficients and simplify them using the relations known. fin_gcd = gcd(sol) finalSolution = [a / fin_gcd for a in sol] finalSolution = [self.__conversion.simplify(a) for a in finalSolution] ## We transform our polynomial into elements of our destiny domain finalSolution = [ self.__conversion.to_real(a).raw() for a in finalSolution ] return vector(parent, finalSolution)
def invariant_generators(self): """ Wraps Singular's invariant_algebra_reynolds and invariant_ring in finvar.lib, with help from Simon King and Martin Albrecht. Computes generators for the polynomial ring `F[x_1,\ldots,x_n]^G`, where G in GL(n,F) is a finite matrix group. In the "good characteristic" case the polynomials returned form a minimal generating set for the algebra of G-invariant polynomials. In the "bad" case, the polynomials returned are primary and secondary invariants, forming a not necessarily minimal generating set for the algebra of G-invariant polynomials. EXAMPLES:: sage: F = GF(7); MS = MatrixSpace(F,2,2) sage: gens = [MS([[0,1],[-1,0]]),MS([[1,1],[2,3]])] sage: G = MatrixGroup(gens) sage: G.invariant_generators() [x1^7*x2 - x1*x2^7, x1^12 - 2*x1^9*x2^3 - x1^6*x2^6 + 2*x1^3*x2^9 + x2^12, x1^18 + 2*x1^15*x2^3 + 3*x1^12*x2^6 + 3*x1^6*x2^12 - 2*x1^3*x2^15 + x2^18] sage: q = 4; a = 2 sage: MS = MatrixSpace(QQ, 2, 2) sage: gen1 = [[1/a,(q-1)/a],[1/a, -1/a]]; gen2 = [[1,0],[0,-1]]; gen3 = [[-1,0],[0,1]] sage: G = MatrixGroup([MS(gen1),MS(gen2),MS(gen3)]) sage: G.cardinality() 12 sage: G.invariant_generators() [x1^2 + 3*x2^2, x1^6 + 15*x1^4*x2^2 + 15*x1^2*x2^4 + 33*x2^6] sage: F = GF(5); MS = MatrixSpace(F,2,2) sage: gens = [MS([[1,2],[-1,1]]),MS([[1,1],[-1,1]])] sage: G = MatrixGroup(gens) sage: G.invariant_generators() # long time (67s on sage.math, 2012) [x1^20 + x1^16*x2^4 + x1^12*x2^8 + x1^8*x2^12 + x1^4*x2^16 + x2^20, x1^20*x2^4 + x1^16*x2^8 + x1^12*x2^12 + x1^8*x2^16 + x1^4*x2^20] sage: F=CyclotomicField(8) sage: z=F.gen() sage: a=z+1/z sage: b=z^2 sage: MS=MatrixSpace(F,2,2) sage: g1=MS([[1/a,1/a],[1/a,-1/a]]) sage: g2=MS([[1,0],[0,b]]) sage: g3=MS([[b,0],[0,1]]) sage: G=MatrixGroup([g1,g2,g3]) sage: G.invariant_generators() # long time (12s on sage.math, 2011) [x1^8 + 14*x1^4*x2^4 + x2^8, x1^24 + 10626/1025*x1^20*x2^4 + 735471/1025*x1^16*x2^8 + 2704156/1025*x1^12*x2^12 + 735471/1025*x1^8*x2^16 + 10626/1025*x1^4*x2^20 + x2^24] AUTHORS: - David Joyner, Simon King and Martin Albrecht. REFERENCES: - Singular reference manual - B. Sturmfels, "Algorithms in invariant theory", Springer-Verlag, 1993. - S. King, "Minimal Generating Sets of non-modular invariant rings of finite groups", arXiv:math.AC/0703035 """ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.interfaces.singular import singular gens = self.gens() singular.LIB("finvar.lib") n = self.degree() #len((gens[0].matrix()).rows()) F = self.base_ring() q = F.characteristic() ## test if the field is admissible if F.gen() == 1: # we got the rationals or GF(prime) FieldStr = str(F.characteristic()) elif hasattr(F, 'polynomial'): # we got an algebraic extension if len(F.gens()) > 1: raise NotImplementedError, "can only deal with finite fields and (simple algebraic extensions of) the rationals" FieldStr = '(%d,%s)' % (F.characteristic(), str(F.gen())) else: # we have a transcendental extension FieldStr = '(%d,%s)' % (F.characteristic(), ','.join( [str(p) for p in F.gens()])) ## Setting Singular's variable names ## We need to make sure that field generator and variables get different names. if str(F.gen())[0] == 'x': VarStr = 'y' else: VarStr = 'x' VarNames = '(' + ','.join( (VarStr + str(i + 1) for i in range(n))) + ')' R = singular.ring(FieldStr, VarNames, 'dp') if hasattr(F, 'polynomial') and F.gen() != 1: # we have to define minpoly singular.eval('minpoly = ' + str(F.polynomial()).replace('x', str(F.gen()))) A = [singular.matrix(n, n, str((x.matrix()).list())) for x in gens] Lgens = ','.join((x.name() for x in A)) PR = PolynomialRing(F, n, [VarStr + str(i) for i in range(1, n + 1)]) if q == 0 or (q > 0 and self.cardinality() % q != 0): from sage.all import Integer, Matrix try: gapself = gap(self) # test whether the backwards transformation works as well: for i in range(self.ngens()): if Matrix(gapself.gen(i + 1), F) != self.gen(i).matrix(): raise ValueError except (TypeError, ValueError): gapself is None if gapself is not None: ReyName = 't' + singular._next_var_name() singular.eval('matrix %s[%d][%d]' % (ReyName, self.cardinality(), n)) En = gapself.Enumerator() for i in range(1, self.cardinality() + 1): M = Matrix(En[i], F) D = [{} for foobar in range(self.degree())] for x, y in M.dict().items(): D[x[0]][x[1]] = y for row in range(self.degree()): for t in D[row].items(): singular.eval('%s[%d,%d]=%s[%d,%d]+(%s)*var(%d)' % (ReyName, i, row + 1, ReyName, i, row + 1, repr(t[1]), t[0] + 1)) foobar = singular(ReyName) IRName = 't' + singular._next_var_name() singular.eval('matrix %s = invariant_algebra_reynolds(%s)' % (IRName, ReyName)) else: ReyName = 't' + singular._next_var_name() singular.eval('list %s=group_reynolds((%s))' % (ReyName, Lgens)) IRName = 't' + singular._next_var_name() singular.eval('matrix %s = invariant_algebra_reynolds(%s[1])' % (IRName, ReyName)) OUT = [ singular.eval(IRName + '[1,%d]' % (j)) for j in range(1, 1 + singular('ncols(' + IRName + ')')) ] return [PR(gen) for gen in OUT] if self.cardinality() % q == 0: PName = 't' + singular._next_var_name() SName = 't' + singular._next_var_name() singular.eval('matrix %s,%s=invariant_ring(%s)' % (PName, SName, Lgens)) OUT = [ singular.eval(PName + '[1,%d]' % (j)) for j in range(1, 1 + singular('ncols(' + PName + ')')) ] + [ singular.eval(SName + '[1,%d]' % (j)) for j in range(2, 1 + singular('ncols(' + SName + ')')) ] return [PR(gen) for gen in OUT]
def invariants_eps(FQM, TM, use_reduction = True, proof = False, debug = 0): r""" Computes the invariants of a direct summand in the decomposition for weight one-half modular forms. Such a summand is of the form \[ \mathbb{C}[(\mathbb{Z}/2N\mathbb{Z}, -x^2/4N)]^\epsilon \otimes \mathbb{C}[M], \] where $M$ is a given finite quadratic module and $\epsilon$ acts on the first factor via $\mathfrak{e}_\mu \mapsto \mathfrak{e}_{-\mu}$. INPUT: - FQM: A given finite quadratic module, referred to as $M$ above - TM: A cyclic module of the form as given abve (the first factor) NOTE: We do not check that TM is of the stated form. The function is an auxiliary function usually only called by ``weight_one_half_dim``. EXAMPLES: NONE """ eps = True if TM != None and FQM != None: TMM = TM+FQM elif TM != None: TMM = TM else: TMM = FQM eps = False debug2 = 0 if debug > 1: print("FQM = {0}, TM = {1}, TMM = {2}".format(FQM, TM, TMM)) debug2 = 1 if debug > 2: debug2=debug inv = invariants(TMM, use_reduction, proof=proof, debug=debug2) if debug > 1: print(inv) if type(inv) in [list,tuple]: V = inv[1] else: V = inv d = [0,0] if V.dimension() != 0: el = list() M = Matrix(V.base_ring(), V.ambient_module().dimension()) if eps: f = 1 if TMM.signature() % 4 == 0 else -1 for v in inv[0]: #Get the coordinate of this isotropic element vv = v.c_list() #change the first coordinate to its negative (the eps-action) vv[0] = -vv[0] vv = TMM(vv,can_coords=True) #since the isotropic elements are taken up to the action of +-1, we need to check #if we have this element (vv) or its negative (-vv) in the list #we append the index of the match, together with a sign to the list `el`, #where the sign is -1 if -vv is in inv[0] and the signature is 2 mod 4 #(i.e. the std generator of the center acts as -1) if inv[0].count(vv) > 0: el.append((inv[0].index(vv),1)) else: el.append((inv[0].index(-vv),f)) #We create the entries of the matrix M #which acts as eps on the space spanned by the isotropic vectors (mod +-1) for i in range(len(el)): M[el[i][0],i] = el[i][1] #import pdb; pdb.set_trace() if debug > 1: print("M={0}, V={1}".format(M, V)) try: KM = (M-M.parent().one()).kernel_on(V) if debug > 1: print("KM for ev 1 = {0}".format(KM)) d[0] = KM.dimension() KM = (M+M.parent().one()).kernel_on(V) if debug > 1: print("KM for ev -1 = {0}".format(KM)) d[1] = KM.dimension() except Exception as e: raise RuntimeError("Error occurred for {0}, {1}".format(FQM.jordan_decomposition().genus_symbol(), e), M, V) else: d = [V.dimension(), 0] if debug > 1: print(d) return d
def add_vertex(self, g): self.M = self.M.stack(Matrix(1, self.M.ncols())) self.M[-1, 0] = g
def coeff_reduce(C, B, SB, Detail=0, debug=False): """B is a list of lists of rationals holding an invertible dxd matrix C is a list of lists of rationals holding an nxd matrix C*B remains invariant SB = Sturm bound: the first SB rows of C should span the whole row space over Z Computes invertible U and returns (C', B') = (C*U, U^(-1)*B), so that the entries of C' are integers and 'small' """ t0 = time.time() if Detail>1: print("Before LLL, coefficients:\n{}".format(C)) # convert to actual matrices: d = len(B) nan = len(C) B=Matrix(B) C=Matrix(C) if debug: print("B has size {}".format(B.dimensions())) #print("B={}".format(B)) print("C has size {}".format(C.dimensions())) #print("C={}".format(C)) CB=C*B # Make integral: C1, den = C._clear_denom() B1 = B/den t1 = time.time() if Detail: print("Cleared denominator = {} in {:0.3f}".format(den,t1-t0)) # Make primitive: if debug: print("C1 is in {}".format(C1.parent())) #print("C1 = {}".format(C1)) C1 = pari(C1) # if debug: # print("B1={} in {}".format(B1, B1.parent())) B1 = pari(B1) V1V2S = C1.matsnf(1) t2 = time.time() V2 = V1V2S[1] S = pari_row_slice(nan-d+1,nan)(V1V2S[2]) if Detail: print("Computed Smith form in {:0.3f}".format(t2-t1)) if debug: print("V1V2S[2] = {}".format(V1V2S[2])) print("S = {}".format(S)) print("About to invert matrix of size {}".format(V2.matsize())) C1 *= V2 B1 = V2**(-1)*B1 if debug: assert CB==C1*B1 scales = [S[i,i] for i in range(d)] if not all(s==1 for s in scales): if Detail: print("scale factors {}".format(scales)) C1 *= S**(-1) B1 = S*B1 if debug: assert CB==C1*B1 # LLL-reduce U = C1.qflll() t3 = time.time() if Detail: print("LLL done in {:0.3f}".format(t3-t2)) if debug: assert U.matdet() in [1,-1] C1 *= U B1 = U**(-1)*B1 if debug: assert CB==C1*B1 print("found Bred, Cred") # Convert back to lists of lists Cred = [ci.list() for ci in C1.mattranspose()] Bred = [bi.list() for bi in B1.mattranspose()] if Detail>1: print("After LLL, coefficients:\n{}\nbasis={}".format(Cred,Bred)) return Cred, Bred
def split_vertex(self, i, row1, row2): self.M = self.M.stack(Matrix(2, self.M.ncols(), row1 + row2)) self.add_edge(self.M.nrows() - 2, self.M.nrows() - 1) self.del_vertex(i)
def verify_algebraically_GB(g, P0, alpha, trace_and_norm, verbose=True): # input: # * P0 (only necessary to shift the series) # * [trace_numerator, trace_denominator, norm_numerator, norm_denominator] # output: # a boolean if verbose: print "verify_algebraically()" L = P0.base_ring() assert alpha.base_ring() is L L_poly = PolynomialRing(L, "xL") xL = L_poly.gen() # shifting the series avoids makes our life easier trace_numerator, trace_denominator, norm_numerator, norm_denominator = [ L_poly(coeff)(L_poly.gen() + P0[0]) for coeff in trace_and_norm ] L_fpoly = L_poly.fraction_field() trace = L_fpoly(trace_numerator) / L_fpoly(trace_denominator) norm = L_fpoly(norm_numerator) / L_fpoly(norm_denominator) L_fpoly_Z2 = PolynomialRing(L_fpoly, "z") z = L_fpoly_Z2.gen() gP0 = g(L_poly.gen() + P0[0]) disc = (trace**2 - 4 * norm) IsqrtD = L_fpoly_Z2.ideal([z**2 - disc.numerator()]) R0 = L_fpoly_Z2.quotient_ring(IsqrtD) iz = z / R0(z**2).lift() x1 = R0((trace - z / sqrt(disc.denominator())) / 2).lift() x2 = R0((trace + z / sqrt(disc.denominator())) / 2).lift() assert x1 + x2 == trace, "x1 + x2" assert R0(x1 * x2) == norm, "x1 * x2" dx1 = R0( (trace.derivative(xL) - iz * sqrt(disc.denominator()) * disc.derivative(xL) / 2) / 2).lift() dx2 = R0( (trace.derivative(xL) + iz * sqrt(disc.denominator()) * disc.derivative(xL) / 2) / 2).lift() assert R0(dx1 + dx2) == R0(trace.derivative(xL)), "dx1 + dx2" gx1 = R0(g(x1)).lift() gx2 = R0(g(x2)).lift() igx1 = R0(gx2).lift() / R0(gx2 * gx1).lift() assert R0(gx1 * igx1) == 1, "gx1 * igx1" igx2 = R0(gx1).lift() / R0(gx1 * gx2).lift() assert R0(gx2 * igx2) == 1, "gx2 * igx2" square = gx1.numerator() if verbose: print "Simplifying sqrt( g(x1).numerator() )" a1, a2, d1, d2 = simplify_sqrt(square.constant_coefficient().numerator(), square.monomial_coefficient(z).numerator(), disc.numerator()) assert (d1.numerator() // gP0) in L or (d2.numerator() // gP0) in L, "d1 or d2" L_fpoly_Z = PolynomialRing(L_fpoly, 3, "z, y, w") z, y, w = L_fpoly_Z.gens() Isqrt = L_fpoly_Z.ideal([z - y * w, y**2 - d1, w**2 - d2]) R = L_fpoly_Z.quotient_ring(Isqrt) iz = z / (d1 * d2) assert R(z * iz) == 1 iw = w / R(w**2).lift() assert R(w * iw) == 1 iy = y / R(y**2).lift() assert R(iy * y) == 1 sgx1 = R(a1 * y + a2 * w).lift() / R(sqrt(gx1.denominator())).lift() sgx2 = R(a1 * y - a2 * w).lift() / R(sqrt(gx1.denominator())).lift() assert R(sgx1**2) == gx1, "sgx1**2" assert R(sgx2**2) == gx2, "sgx2**2" isgx1 = R(sgx2).lift() / R(sgx1 * sgx2).lift() isgx2 = R(sgx1).lift() / R(sgx1 * sgx2).lift() assert R(sgx1 * isgx1) == 1, "sgx1 * isgx1" assert R(sgx2 * isgx2) == 1, "sgx2 * isgx2" if verbose: print "adjusting to d1//g(x) or d1/g(x) in L" if R(w**2 / gP0).lift().degree() == 0: ct = iw * sqrt(R(w**2 / gP0).lift().constant_coefficient()) else: assert R(w**2 / gP0).lift().degree() == 0 ct = iy * sqrt(R(y**2 / gP0).lift().constant_coefficient()) # else: # assert R(y**2/gP0).lift() in L, "\n%s\n%s\n%s\n%s\n" % ( R(y**2/gP0).lift(), R(y**2/gP0).lift() in L, R(w**2/gP0).lift(), R(w**2/gP0).lift() in L, ) # ct = iy * sqrt(L(R(y**2/gP0).lift())) eq1 = Matrix([[ -2 * L_poly(alpha.row(0).list())(L_poly.gen() + P0[0]) * ct, R(dx1 * isgx1), R(dx2 * isgx2) ]]) eq2 = Matrix([[ -2 * L_poly(alpha.row(1).list())(L_poly.gen() + P0[0]) * ct, R(x1 * dx1 * isgx1), R(x2 * dx2 * isgx2) ]]) branches = Matrix( R, [[1, 1, 1], [1, 1, -1], [1, -1, 1], [1, -1, 1]]).transpose() meq1 = eq1 * branches meq2 = eq2 * branches algzero = False for j in range(4): if meq1[0, j] == 0 and meq2[0, j] == 0: algzero = True break if verbose: print "Done, verify_algebraically() = %s" % algzero return algzero
def symmetric_block_diagonalize(N1): r"""Returns matrices `H` and `Q` such that `N1 = Q*H*Q.T` and `H` is block diagonal. The algorithm used here is as follows. Whenever a row operation is performed (via multiplication on the left by a transformation matrix `q`) the corresponding symmetric column operation is also performed via multiplication on the right by `q^T`. For each column `j` of `N1`: 1. If column `j` consists only of zeros then swap with the last column with non-zero entries. 2. If there is a `1` in position `j` of the column (i.e. a `1` lies on the diagonal in this column) then eliminate further entries below as in standard Gaussian elimination. 3. Otherwise, if there is a `1` in the column, but not in position `j` then rows are swapped in a way that it appears in the position `j+1` of the column. Eliminate further entries below as in standard Gaussian elimination. 4. After elimination, if `1` lies on the diagonal in column `j` then increment `j` by one. If instead the block matrix `[0 1 \\ 1 0]` lies along the diagonal then eliminate under the `(j,j+1)` element (the upper right element) of this `2 x 2` block and increment `j` by two. 5. Repeat until `j` passes the final column or until further columns consists of all zeros. 6. Finally, perform the appropriate transformations such that all `2 x 2` blocks in `H` appear first in the diagonalization. (Uses the `diagonal_locations` helper function.) Parameters ---------- N1 : GF(2) matrix Returns ------- H : GF(2) matrix Symmetric `g x g` matrix where the diagonal elements consist of either a "1" or a `2 x 2` block matrix `[0 1 \\ 1 0]`. Q : GF(2) matrix The corresponding transformation matrix. """ g = N1.nrows() H = zero_matrix(GF(2), g) Q = identity_matrix(GF(2), g) # if N1 is the zero matrix the H is also the zero matrix (and Q is the # identity transformation) if (N1 % 2) == 0: return H,Q # perform the "modified gaussian elimination" B = Matrix(GF(2),[[0,1],[1,0]]) H = N1.change_ring(GF(2)) j = 0 while (j < g) and (H[:,j:] != 0): # if the current column is zero then swap with the last non-zero column if H.column(j) == 0: last_non_zero_col = max(k for k in range(j,g) if H.column(k) != 0) Q.swap_columns(j,last_non_zero_col) H = Q.T*N1*Q # if the current diagonal element is 1 then gaussian eliminate as # usual. otherwise, swap rows so that a "1" appears in H[j+1,j] and # then eliminate from H[j+1,j] if H[j,j] == 1: rows_to_eliminate = (r for r in range(g) if H[r,j] == 1 and r != j) for r in rows_to_eliminate: Q.add_multiple_of_column(r,j,1) H = Q.T*N1*Q else: # find the first non-zero element in the column after the diagonal # element and swap rows with this element first_non_zero = min(k for k in range(j,g) if H[k,j] != 0) Q.swap_columns(j+1,first_non_zero) H = Q.T*N1*Q # eliminate *all* other ones in the column, including those above # the element (j,j+1) rows_to_eliminate = (r for r in range(g) if H[r,j] == 1 and r != j+1) for r in rows_to_eliminate: Q.add_multiple_of_column(r,j+1,1) H = Q.T*N1*Q # increment the column based on the diagonal element if H[j,j] == 1: j += 1 elif H[j:(j+2),j:(j+2)] == B: # in the block diagonal case, need to eliminate below the j+1 term rows_to_eliminate = (r for r in range(g) if H[r,j+1] == 1 and r != j) for r in rows_to_eliminate: Q.add_multiple_of_column(r,j,1) H = Q.T*N1*Q j += 2 # finally, check if there are blocks of "special" form. that is, shift all # blocks such that they occur first along the diagonal of H index_one, index_B = diagonal_locations(H) while index_one < index_B: j = index_B Qtilde = zero_matrix(GF(2), g) Qtilde[0,0] = 1 Qtilde[j,0] = 1; Qtilde[j+1,0] = 1 Qtilde[0,j] = 1; Qtilde[0,j+1] = 1 Qtilde[j:(j+2),j:(j+2)] = B Q = Q*Qtilde H = Q.T*N1*Q # continue until none are left index_one, index_B = diagonal_locations(H) # above, we used Q to store column operations on N1. switch to rows # operations on H so that N1 = Q*H*Q.T Q = Q.T.inverse() return H,Q
def pade_sage(input_series, m, n): r""" Returns the Pade approximant of ``self`` of index `(m, n)`. The Pade approximant of index `(m, n)` of a formal power series `f` is the quotient `Q/P` of two polynomials `Q` and `P` such that `\deg(Q)\leq m`, `\deg(P)\leq n` and .. MATH:: f(z) - Q(z)/P(z) = O(z**{m+n+1}). The formal power series `f` must be known up to order `n + m + 1`. See :wikipedia:`Pade\_approximant` INPUT: - ``m``, ``n`` -- integers, describing the degrees of the polynomials OUTPUT: a ratio of two polynomials .. WARNING:: The current implementation uses a very slow algorithm and is not suitable for high orders. ALGORITHM: This method uses the formula as a quotient of two determinants. .. SEEALSO:: * :mod:`sage.matrix.berlekamp_massey`, * :meth:`sage.rings.polynomial.polynomial_zmod_flint.Polynomial_zmod_flint.rational_reconstruct` EXAMPLES:: sage: z = PowerSeriesRing(QQ, 'z').gen() sage: exp(z).pade(4, 0) 1/24*z**4 + 1/6*z**3 + 1/2*z**2 + z + 1 sage: exp(z).pade(1, 1) (-z - 2)/(z - 2) sage: exp(z).pade(3, 3) (-z**3 - 12*z**2 - 60*z - 120)/(z**3 - 12*z**2 + 60*z - 120) sage: log(1-z).pade(4, 4) (25/6*z**4 - 130/3*z**3 + 105*z**2 - 70*z)/(z**4 - 20*z**3 + 90*z**2 - 140*z + 70) sage: sqrt(1+z).pade(3, 2) (1/6*z**3 + 3*z**2 + 8*z + 16/3)/(z**2 + 16/3*z + 16/3) sage: exp(2*z).pade(3, 3) (-z**3 - 6*z**2 - 15*z - 15)/(z**3 - 6*z**2 + 15*z - 15) TESTS: With real coefficients:: sage: R.<z> = RR[[]] sage: f = exp(2*z) sage: f.pade(3, 3) # abs tol 1e-10 (-1.0*z**3 - 6.0*z**2 - 15.0*z - 15.0)/(z**3 - 6.0*z**2 + 15.0*z - 15.0) When precision is too low:: sage: f = z + O(z**6) sage: f.pade(4, 4) Traceback (most recent call last): ... ValueError: the precision of the series is not large enough """ from sage.matrix.constructor import Matrix if input_series.precision_absolute() < n + m + 2: raise ValueError("the precision of the series is not large enough") polyring = input_series.parent()._poly_ring() z = polyring.gen() c = input_series.list() mat = Matrix(polyring, n + 1, n + 1) for i in range(1, n + 1): for j in range(n + 1): if m + i - j < len(c): mat[i, j] = c[m + i - j] for j in range(n + 1): mat[0, j] = z ** j resu_v = mat.determinant().truncate(n + 1) lead_v = resu_v.leading_coefficient() resu_v = resu_v / lead_v for j in range(n + 1): mat[0, j] = z ** j * (input_series.truncate(max(m - j + 1, 0))) resu_u = mat.determinant().truncate(m + 1) lead_u = resu_u.leading_coefficient() resu_u = resu_u / lead_u return lead_u / lead_v * resu_u / resu_v
def reduction_maps(betas, ell, verbose=False): """Input: betas: a list of d ZZ-module generators of an order O in a number field K of degree d, ell: a rational prime ell, Output: a (possibly empty) list of maps ZZ^d = GF(ell) encoding all possible ring homomorphisms from O to GF(ell). Each map is encoded as a list of d elements of GF(ell) giving the images of the order's basis (the betas). Method: convert each beta into a dxd integer matrix giving the multiplication-by-beta map from O to itself. Reduce these mod ell, and use them to define a FiniteDimensionalAlgebra A over GF(ell). Find the maximal ideals of A whose quotient field is GF(ell) (and not some extension of it). So the ideal has dimension d-1 and the required map is the inner product with a basis vector for its complement. """ K = betas[0].parent() #print("K = {}".format(K)) #print("betas = {}".format(betas)) assert betas[0]==1 Fl = GF(ell) if verbose: print("creating Hecke order mod {} from betas".format(ell)) coords = K.gen().coordinates_in_terms_of_powers() U = Matrix([coords(b) for b in betas]).inverse() structure = [(Matrix([coords(bi*bj) for bj in betas])*U).change_ring(Fl) for bi in betas] assert structure[0]==1 #print("structure = {}".format(structure)) A = FiniteDimensionalAlgebra(Fl, structure) #print("A = {}".format(A)) MM = A.maximal_ideals() d = len(betas) # dd = [M.basis_matrix().transpose().nullity() for M in MM] # print("Algebra over GF({}) of dimension {} has {} maximal ideals of residue degrees {}".format(ell,len(betas),len(MM), dd)) vv = [list(M.basis_matrix().right_kernel().basis()[0]) for M in MM if M.basis_matrix().rank()==d-1] # Taking the dot product with each of these basis vectors gives a # GF(l)-linear map from the Hecke order to GF(l), but we need it # to be a ring homomorphism, so we must scale it so 1 maps to 1. # In practice Sage will always scale the basis vector so that the # first nonzero entry (which here must be the first entry) is 1, # but we should not rely on this. for v in vv: if v[0]!=1: if verbose: print("Rescaling reduction vector v={}".format(v)) if v[0]==0: raise ValueError("reduction map defined by {} maps 1 to 0".format(v)) v0inv = 1/v[0] v = [vi*v0inv for vi in v] if verbose: print("Rescaled reduction vector v={}".format(v)) if verbose: print("{} reductions found from Hecke order".format(len(vv))) if vv: print("Reduction vector(s):") for v in vv: print(v) #return [lambda w: Fl(sum([vi*Fl(wi) for vi,wi in zip(v,w)])) for v in vv] return vv
def AddFlip(self, A, B): maxlower = self.lower; field = self.R; output_field = self.R if A.base_ring() == self.Rextraprec or B.base_ring() == self.Rextraprec: field = self.Rextraprec; if A.base_ring() == self.Rextraprec and B.base_ring() == self.Rextraprec: output_field = self.Rextraprec; # Takes H0(3D0-A), H0(3D0-B), and returns H0(3D0-C), with C s.t. A+B+C~3D0 # H0(6D0-A-B) = H0(3D0-A) * H0(3D0-B) if self.verbose: print "PicardGroup.AddFlip(self, A, B)" #### STEP 1 #### if self.verbose: print "Step 1"; AB = Matrix(field, self.nZ, 4 * self.d0 + 1 - self.g); KAB = Matrix(field, AB.nrows() - AB.ncols(), AB.nrows()); ABncols = AB.ncols(); ABnrows = AB.nrows(); rank = AB.ncols(); while True: # 5 picked at random l = 5; ABtmp = Matrix(field, self.nZ, 4 * self.d0 + 1 - self.g + l); for j in range(ABtmp.ncols()): a = random_vector_in_span(A); b = random_vector_in_span(B); for i in range(ABnrows): ABtmp[i, j] = a[i] * b[i]; Q, R, P = qrp(ABtmp, ABncols ); upper = R[rank - 1, rank - 1].abs(); lower = 0; if ABtmp.ncols() > rank and ABnrows > rank: lower = R[rank, rank ].abs(); if self.threshold_check(upper, lower): # the first AB.cols columns are a basis for H0(6D0-A-B) = H0(3D0-A) * H0(3D0-B) # v in AB iff KAB * v = 0 for i in range(ABnrows): for j in range(ABncols): AB[i, j] = Q[i, j] for j in range(ABnrows - rank): KAB[j, i] = Q[i, j + rank].conjugate(); maxlower = max(maxlower, lower); break; if self.verbose: print "Warning: In AddFlip, not full rank at step 1, retrying." print "upper = %s lower = %s" % (RealField(35)(upper), RealField(35)(lower)) #### STEP 2 #### # H0(3D0-A-B) = {s in V : s*V c H0(6D0-A-B)} if self.verbose: print "Step 2" #### Prec Test #### if self.verbose: a = random_vector_in_span(A); b = random_vector_in_span(B); ab = Matrix(field, self.nZ, 1); for i in range(self.nZ): ab[i, 0] = a[i] * b[i]; print field print "maxlower = %s ,lower = %s " % (RealField(15)(maxlower), RealField(15)(lower)) print "Precision of the input of MakAddflip: %s"% (RealField(15)(max(map(lambda x: RR(x[0].abs()), list(KAB * ab)))),) KABnrows = KAB.nrows(); KABncols = KAB.ncols(); while True: # columns = equations for H0(3D0-A-B) = {s in V : s*V c H0(6D0-A-B)} \subset H0(6D0-A-B) l = 2; KABred = copy(KAB); KABred = KABred.stack( Matrix(field, l*KABnrows, KABncols ) ); for m in range(l): # Attempt of IGS of V v = random_vector_in_span( self.V ) for j in xrange(KABncols): for i in range( KABnrows ): KABred[i + m * KABnrows + KABnrows , j] = KAB[i, j] * v[j] ABred, upper, lower = Kernel(KABred, KABred.ncols() - (self.d0 + 1 - self.g)); if self.threshold_check(upper, lower): maxlower = max(maxlower, lower); break; if self.verbose: print "Warning: In AddFlip, failed IGS at step 2, retrying." print "upper = %s lower = %s" % (RealField(35)(upper), RealField(35)(lower)) #### STEP 3 #### # H0(6D0-A-B-C) = f*H0(3D0), where f in H0(30-A-B) if self.verbose: print "Step 3" # fv represents f*H0(3D0) fV= copy(self.V) for j in xrange(3*self.d0 + 1 - self.g): for i in xrange(self.nZ): fV[i,j] *= ABred[i,0] KfV, upper, lower = EqnMatrix( fV, 3 * self.d0 + 1 - self.g ) # Equations for f*V assert self.threshold_check(upper, lower), "upper = %s lower = %s" % (RealField(35)(upper), RealField(35)(lower)) maxlower = max(maxlower, lower); KfVnrows = KfV.nrows(); KfVncols = KfV.ncols(); ### STEP 4 ### # H0(3D0-C) = {s in V : s*H0(3D0-A-B) c H0(6D0-A-B-C)} = {s in V : s*H0(3D0-A-B) c f*V} if self.verbose: print "Step 4" while True: # Equations for H0(3D0-C) #expand KC l = 2; KC = self.KV.stack(Matrix(field, l * KfV.nrows(), self.KV.ncols())); KC_old_rows = self.KV.nrows(); for m in range(l): # Attempt of IGS of H0(3D0-A-B) v = random_vector_in_span(ABred) for i in range(KfVnrows): for j in range(KfVncols): KC[i + m * KfVnrows + KC_old_rows, j] = v[j] * KfV[i, j]; output, upper, lower = Kernel(KC, KC.ncols() - (2 * self.d0 + 1 - self.g) ); if self.threshold_check(upper, lower): maxlower = max(maxlower, lower); break; if self.verbose: print "Warning: In MakAddFlip, failed IGS at step 4, retrying." print "upper = %s lower = %s" % (RealField(35)(upper), RealField(35)(lower)) return output.change_ring(output_field), maxlower;
def block_matrix(parent, rows, constant_or_identity=True): ''' Method that build a matrix using as blocks the elements of rows. This method allows the user to build a matrix defining its blocks. There are two options for the inputs: * A matrix (that will be directly used as a block * Elements alone: will build a constant matrix or a diagonal matrix depending on the argument ``constant_or_identity``. This method checks that the size of the input is correct, i.e., all rows have the same amount of columns and that all matrices within a row provide the same amount of rows. INPUT: * ``parent``: the desired parent for the final matrix. All elements mast be inside this parent. * ``rows``: a list of arguments representing the rows of the matrix. Each row is another list where the elements may be matrices (indicating the number of rows for this block) or elements in ``parent`` that will create constant or diagonal matrices with such element. * ``constant_or_identity``: this argument decides whether the elements create a diagonal matrix (``True``) or a constant matrix (``False``). OUTPUT: A matrix with the corresponding struture. If any of the sizes does not match, a :class:`~ajpastor.misc.exceptions.SizeMatrixError` will be raised. EXAMPLES:: sage: from ajpastor.misc.matrix import * sage: M = Matrix(QQ,[[1,2],[3,4]]); I = identity_matrix(QQ, 3) sage: block_matrix(QQ,[[M,0],[0,I]]) [1 2 0 0 0] [3 4 0 0 0] [0 0 1 0 0] [0 0 0 1 0] [0 0 0 0 1] sage: block_matrix(QQ, [[I, 1],[2, M]]) [1 0 0 1 0] [0 1 0 0 1] [0 0 1 0 0] [2 0 0 1 2] [0 2 0 3 4] sage: block_matrix(QQ, [[I, 1],[2, M]], False) [1 0 0 1 1] [0 1 0 1 1] [0 0 1 1 1] [2 2 2 1 2] [2 2 2 3 4] This method also works with non-square matrices:: sage: N = Matrix(QQ, [[1,2,3],[4,5,6]]) sage: block_matrix(QQ, [[N,1],[0,M]]) [1 2 3 1 0] [4 5 6 0 1] [0 0 0 1 2] [0 0 0 3 4] sage: block_matrix(QQ, [[N,M],[1,2]], False) [1 2 3 1 2] [4 5 6 3 4] [1 1 1 2 2] However, the sizes of the columns and rows has to fit:: sage: block_matrix(QQ, [[N, 1], [M, 0]]) Traceback (most recent call last): ... SizeMatrixError: The col has not the proper format -- different size This method also allows matrices in list format:: sage: block_matrix(QQ, [[N, [[0, 1],[-1,0]]], [1, M]]) [ 1 2 3 0 1] [ 4 5 6 -1 0] [ 1 0 0 1 2] [ 0 1 0 3 4] sage: block_matrix(QQ, [[N, [[0, 1],[1,0],[1,1]]],[1, M]]) Traceback (most recent call last): ... SizeMatrixError: The row has not the proper format -- different size And also, if all entries are matrices, we do not need to have in the input all the rows with the same length:: sage: L = Matrix(QQ, [[5, 4, 3, 2, 1]]) sage: block_matrix(QQ, [[M, N],[L]]) [1 2 1 2 3] [3 4 4 5 6] [5 4 3 2 1] sage: block_matrix(QQ, [[N, I[:2]], [L,[[9]]]]) [1 2 3 1 0 0] [4 5 6 0 1 0] [5 4 3 2 1 9] sage: block_matrix(QQ, [[N, I[:2]], [L]]) Traceback (most recent call last): ... SizeMatrixError: The given rows have different column size Finally, this method also allows the user to create a Matrix in a normal fashion (providing each of its elements):: sage: block_matrix(QQ, [[1,2],[3,4]]) == M True ''' ## We have two different ways of seeing the input: either all the provided rows are of the same size, ## allowing to have input in the parent ring, or the sizes must match perfectly between the rows. # Checking the first case: if any element is in parent if (any(any((el in parent) for el in row) for row in rows)): d = len(rows[0]) for i in range(1, len(rows)): if (d != len(rows[i])): raise SizeMatrixError( "The rows provided can not be seen as a matrix") ## We check the sizes rows_hights = [__check_row(row, parent) for row in rows] cols_widths = [ __check_col([rows[i][j] for i in range(len(rows))], parent) for j in range(len(rows[0])) ] rows_with_matrix = [] for i in range(len(rows)): row_with_matrix = [] for j in range(len(rows[0])): if (rows[i][j] in parent): if (constant_or_identity): row_with_matrix += [ Matrix(parent, [[ rows[i][j] * kronecker_delta(k, l) for l in range(cols_widths[j]) ] for k in range(rows_hights[i])]) ] else: row_with_matrix += [ Matrix( parent, [[rows[i][j] for l in range(cols_widths[j])] for k in range(rows_hights[i])]) ] else: row_with_matrix += [Matrix(parent, rows[i][j])] rows_with_matrix += [row_with_matrix] else: # Second case: all elements are matrices, hence they must fit exactly ## We check the sizes rows_hights = [__check_row(row, parent) for row in rows] cols_widths = [sum(__ncols(el) for el in row) for row in rows] if (any(el != cols_widths[0] for el in cols_widths)): raise SizeMatrixError("The given rows have different column size") rows_with_matrix = [] for i in range(len(rows)): row_with_matrix = [] for j in range(len(rows[i])): row_with_matrix += [Matrix(parent, rows[i][j])] rows_with_matrix += [row_with_matrix] ## At this point the variable "row_with_matrix" has all the entries for the final matrix ## No checks are needed final_rows = [] for i in range(len(rows_with_matrix)): for j in range(rows_hights[i]): final_rows += [ reduce(lambda p, q: p + q, [list(el[j]) for el in rows_with_matrix[i]]) ] return Matrix(parent, final_rows)