def solve_ideal_equation(gamma, I, D, N, O): """Find mu_0 in Rj such that (O* gamma / NO)[mu_0] = I / NO. Args: gamma: An element of O. I: A left O-ideal. D: The index [O : R + Rj]. N: The norm of I. Must be prime. O: An order in a rational quaternion algebra containing 1, i, j, k. Returns: mu_0 in Rj such that 0 != gamma * mu_0 in I. """ assert N == I.norm() assert is_prime(N) # d = D*c + N*_ d, c, _ = xgcd(D, N) assert d == 1 a, b = [Integer(x) for x in O.quaternion_algebra().invariants()] F = GF(N) # The suffix _ff means that this element is over a finite field, not the # rationals. B_ff = QuaternionAlgebra(F, a, b) i_ff, j_ff, k_ff = B_ff.gens() # phi is homomorphism O -> (a, b | F) with kernel NO. def phi(alpha): # alpha + NO = alpha_prime + NO. alpha_prime = D * c * alpha t, x, y, z = [ Integer(coeff) for coeff in alpha_prime.coefficient_tuple() ] return t + x * i_ff + y * j_ff + z * k_ff gamma_ff = phi(gamma) gamma_ff_mat = gamma_ff.matrix(action="left") I_basis_ff = [phi(alpha).coefficient_tuple() for alpha in I.basis()] # Only use 2nd and 3rd rows of gamma_ff_mat so solution is in Rj. lin_system = matrix(F, [gamma_ff_mat[2], gamma_ff_mat[3]] + I_basis_ff) sol = lin_system.left_kernel().basis()[0] y, z = sol[0], sol[1] mu_ff = y * j_ff + z * k_ff assert vector(F, (gamma_ff * mu_ff).coefficient_tuple()) in span( I_basis_ff, F) B = O.quaternion_algebra() i, j, k = B.gens() mu_0 = sum( Integer(coeff) * elem for coeff, elem in zip(mu_ff.coefficient_tuple(), [1, i, j, k])) assert 0 != mu_0 assert gamma * mu_0 in I return mu_0
def complete_column(a, c, i=1): """ Completes a column a,c with gcd(a,c) = 1 to an SL_2(Z)-matrix. INPUT: - a - int, the top entry of the column. - c - int, the bottom entry of the column. - i - int. OUTPUT: - A matrix in SL_2(Z) with i-th column [a,c] """ if gcd(a, c) > 1: raise ValueError('Input needs to be coprime') _, d, b = xgcd(a, c) b = -b if i == 1: return Matrix([[a, b], [c, d]]) elif i == 2: return Matrix([[-b, a], [-d, c]])
def __add__(self, other): if isinstance(other, Integer): other *= IdentityEndomorphismMod(self.E, self.psi) if isinstance(other, ZeroEndomorphismMod): return self if self.fx == other.fx: if self.fy == other.fy: m = (3 * self.fx ** 2 + self.A) / (2 * self.fy * self.f) else: return ZeroEndomorphismMod(self.E, self.psi) else: div, inv, _ = xgcd(self.fx.parent().lift(self.fx - other.fx), (self.psi)) if div.is_constant(): m = (self.fy - other.fy) * inv else: raise ZeroDivisionError(div) fx = m ** 2 * self.f - self.fx - other.fx fy = m * (self.fx - fx) - self.fy return EndomorphismMod(self.E, fx, fy, self.psi)
def AL(N, S): r""" INPUT: - N - a positive integer - S - a maximal divisor of N or a set of primes OUPUT: - A matrix representing the partial Atkin-Lehner operator W_S """ try: S = prod([p**(N.valuation(p)) for p in S]) except TypeError: if gcd(S, N / S) > 1: S = S.prime_divisors() S = prod([p**(N.valuation(p)) for p in S]) _, w, z = xgcd(S, N / S) z = -z return Matrix([[S, 1], [N * z, S * w]])
def find_monic_replacements(self, p, t, pt_generators, prev_nu): r""" Replace possibly non-monic generators of `N_{(p^t)}(B)` by monic generators. INPUT: - ``p`` -- a prime element of `D` - ``t`` -- a non-negative integer - ``pt_generators`` -- a list `(g_1, \ldots, g_s)` of polynomials in `D[X]` such that `N_{(p^t)}(B) = (g_1, \ldots, g_s) + pN_{(p^{t-1})}(B)` - ``prev_nu`` -- a `(p^{t-1})`-minimal polynomial of `B` OUTPUT: A list `(h_1, \ldots, h_r)` of monic polynomials such that `N_{(p^t)}(B) = (h_1, \ldots, h_r) + pN_{(p^{t-1})}(B)`. EXAMPLES:: sage: from sage.matrix.compute_J_ideal import ComputeMinimalPolynomials sage: B = matrix(ZZ, [[1, 0, 1], [1, -2, -1], [10, 0, 0]]) sage: C = ComputeMinimalPolynomials(B) sage: x = polygen(ZZ, 'x') sage: nu_1 = x^2 + x sage: generators_4 = [2*x^2 + 2*x, x^2 + 3*x + 2] sage: C.find_monic_replacements(2, 2, generators_4, nu_1) [x^2 + 3*x + 2] TESTS:: sage: C.find_monic_replacements(2, 3, generators_4, nu_1) Traceback (most recent call last): ... ValueError: [2*x^2 + 2*x, x^2 + 3*x + 2] not in N_{(2^3)}(B) sage: C.find_monic_replacements(2, 2, generators_4, x^2) Traceback (most recent call last): ... ValueError: x^2 not in N_{(2^1)}(B) ALGORITHM: [HR2016]_, Algorithms 2 and 3. """ from sage.arith.misc import xgcd if not all((g(self._B) % p**t).is_zero() for g in pt_generators): raise ValueError("%s not in N_{(%s^%s)}(B)" % (pt_generators, p, t)) if not (prev_nu(self._B) % p**(t - 1)).is_zero(): raise ValueError("%s not in N_{(%s^%s)}(B)" % (prev_nu, p, t - 1)) (X, ) = self._DX.gens() replacements = [] for f in pt_generators: g = f p_prt = p_part(g, p) while g != p * p_prt: r = p_prt.quo_rem(prev_nu)[1] g1 = g - p * p_prt d, u, v = xgcd(g1.leading_coefficient(), p) h = u * (p * r + g1) + v * p * prev_nu * X**(g1.degree() - prev_nu.degree()) replacements.append(h % p**t) #reduce coefficients mod p^t to keep coefficients small g = g.quo_rem(h)[1] p_prt = p_part(g, p) replacements = list(set(replacements)) assert all(g.is_monic() for g in replacements),\ "Something went wrong in find_monic_replacements" return replacements
def find_monic_replacements(self, p, t, pt_generators, prev_nu): r""" Replace possibly non-monic generators of `N_{(p^t)}(B)` by monic generators. INPUT: - ``p`` -- a prime element of `D` - ``t`` -- a non-negative integer - ``pt_generators`` -- a list `(g_1, \ldots, g_s)` of polynomials in `D[X]` such that `N_{(p^t)}(B) = (g_1, \ldots, g_s) + pN_{(p^{t-1})}(B)` - ``prev_nu`` -- a `(p^{t-1})`-minimal polynomial of `B` OUTPUT: A list `(h_1, \ldots, h_r)` of monic polynomials such that `N_{(p^t)}(B) = (h_1, \ldots, h_r) + pN_{(p^{t-1})}(B)`. EXAMPLES:: sage: from sage.matrix.compute_J_ideal import ComputeMinimalPolynomials sage: B = matrix(ZZ, [[1, 0, 1], [1, -2, -1], [10, 0, 0]]) sage: C = ComputeMinimalPolynomials(B) sage: x = polygen(ZZ, 'x') sage: nu_1 = x^2 + x sage: generators_4 = [2*x^2 + 2*x, x^2 + 3*x + 2] sage: C.find_monic_replacements(2, 2, generators_4, nu_1) [x^2 + 3*x + 2] TESTS:: sage: C.find_monic_replacements(2, 3, generators_4, nu_1) Traceback (most recent call last): ... ValueError: [2*x^2 + 2*x, x^2 + 3*x + 2] not in N_{(2^3)}(B) sage: C.find_monic_replacements(2, 2, generators_4, x^2) Traceback (most recent call last): ... ValueError: x^2 not in N_{(2^1)}(B) ALGORITHM: [HR2016]_, Algorithms 2 and 3. """ from sage.arith.misc import xgcd if not all((g(self._B) % p**t).is_zero() for g in pt_generators): raise ValueError("%s not in N_{(%s^%s)}(B)" % (pt_generators, p, t)) if not (prev_nu(self._B) % p**(t-1)).is_zero(): raise ValueError("%s not in N_{(%s^%s)}(B)" % (prev_nu, p, t-1)) (X,) = self._DX.gens() replacements = [] for f in pt_generators: g = f p_prt = p_part(g, p) while g != p*p_prt: r = p_prt.quo_rem(prev_nu)[1] g1 = g - p*p_prt d, u, v = xgcd(g1.leading_coefficient(), p) h = u*(p*r + g1) + v*p*prev_nu*X**(g1.degree()-prev_nu.degree()) replacements.append(h % p**t) #reduce coefficients mod p^t to keep coefficients small g = g.quo_rem(h)[1] p_prt = p_part(g, p) replacements = list(set(replacements)) assert all(g.is_monic() for g in replacements),\ "Something went wrong in find_monic_replacements" return replacements