diff --git a/as_covers/as_polyforms.sage b/as_covers/as_polyforms.sage new file mode 100644 index 0000000..e448bf7 --- /dev/null +++ b/as_covers/as_polyforms.sage @@ -0,0 +1,109 @@ +class as_polyform: + def __init__(self, form, mult): + self.form = form + self.curve = form.curve + self.mult = mult + + def __add__(self, other): + return as_polyform(self.form + other.form, self.mult) + + def __repr__(self): + return str(self.form) + ' dx⊗' + str(self.mult) + + def expansion_at_infty(self): + return self.form.expansion_at_infty()*(self.curve.dx.expansion_at_infty())^(self.mult) + + def coordinates(self, basis = 0): + """Find coordinates of the given holomorphic form self in terms of the basis forms in a list holo.""" + AS = self.curve + if basis == 0: + basis = AS.holomorphic_differentials_basis() + RxyzQ, Rxyz, x, y, z = AS.fct_field + # We need to have only polynomials to use monomial_coefficients in linear_representation_polynomials, + # and sometimes basis elements have denominators. Thus we multiply by them. + denom = LCM([denominator(omega.form.function) for omega in basis]) + basis = [denom*omega.form.function for omega in basis] + self_with_no_denominator = denom*self.form.function + return linear_representation_polynomials(Rxyz(self_with_no_denominator), [Rxyz(omega) for omega in basis]) + + +def as_holo_polydifferentials_basis(AS, mult, threshold = 8): + v = AS.dx.valuation() + result = AS.at_most_poles(mult*v, threshold=threshold) + return [as_polyform(omega, mult) for omega in result] + +as_cover.holo_polydifferentials_basis = as_holo_polydifferentials_basis + +def as_canonical_ideal(AS, threshold=8): + B0 = AS.holomorphic_differentials_basis(threshold=threshold) + F = AS.base_ring + g = AS.genus() + B1 = [(B0[i], B0[j]) for i in range(g) for j in range(g) if i <= j] + B2 = AS.holo_polydifferentials_basis(2, threshold = threshold) + g = AS.genus() + r = len(B2) + M = matrix(F, g^2, r) + for i in range(0, len(B1)): + (a, b) = B1[i] + c = as_function(AS, a.form*b.form) + c = as_reduction(AS, c) + c = as_function(AS, c) + c = as_polyform(c, 2) + #return M, c.coordinates(basis=B2) + M[i, :] = vector(c.coordinates(basis=B2)) + K = M.kernel().basis() + result = [] + for v in K: + coeffs = {b : 0 for b in B1} + for i in range(r): + if v[i] != 0: + coeffs[B1[i]] += v[i] + result += [as_tensor_product_forms(B1, coeffs)] + return result + +as_cover.canonical_ideal = as_canonical_ideal + +class as_tensor_product_forms: + def __init__(self, pairs_of_forms, coeffs): + self.pairs = pairs_of_forms + self.coeffs = coeffs #dictionary + self.curve = pairs_of_forms[0][0].curve + + def coordinates(self, basis = 0): + AS = self.curve + g = AS.genus() + F = AS.base_ring + if basis == 0: + basis = AS.holomorphic_differentials_basis() + result = matrix(F, g, g) + for (omega1, omega2) in self.pairs: + c = omega1.coordinates(basis = basis) + d = omega2.coordinates(basis = basis) + for i in range(g): + for j in range(g): + result[i, j] += self.coeffs[(omega1, omega2)]*c[i]*d[j] + return result + + def __repr__(self): + result = '' + for (omega1, omega2) in self.pairs: + if self.coeffs[(omega1, omega2)] !=0: + result += str(self.coeffs[(omega1, omega2)]) + ' * ' + str(omega1) + '⊗' + str(omega2) + ' + ' + return result + + def polynomial(self): + AS = self.curve + F = AS.base_ring + g = AS.genus() + M = self.coordinates() + Rg = PolynomialRing(F, 'X', g) + X = Rg.gens() + return sum(M[i, j] * X[i]*X[j] for i in range(g) for j in range(g)) + + def group_action(self, elt): + p = self.base_ring.characteristic() + n = self.height + elt1 = [p^n - a for a in elt] + pairs_of_forms2 = [(a.group_action(elt), b.group_action(elt1)) for (a, b) in pairs_of_forms] + coeffs2 = {(a.group_action(elt), b.group_action(elt1)) : self.coeffs[(a, b)] for (a, b) in pairs_of_forms} + return as_tensor_product_forms(pairs_of_forms2, self.coeffs) \ No newline at end of file diff --git a/as_covers/as_reduction.sage b/as_covers/as_reduction.sage index 2cafe16..343e444 100644 --- a/as_covers/as_reduction.sage +++ b/as_covers/as_reduction.sage @@ -3,6 +3,7 @@ def as_reduction(AS, fct): n = AS.height F = AS.base_ring RxyzQ, Rxyz, x, y, z = AS.fct_field + p = F.characteristic() ff = AS.functions ff = [RxyzQ(F.function) for F in ff] fct = RxyzQ(fct) diff --git a/as_drw/as_compability.sage b/as_drw/as_compability.sage index d93d38b..f967ae5 100644 --- a/as_drw/as_compability.sage +++ b/as_drw/as_compability.sage @@ -2,6 +2,8 @@ def teichmuller(self, n): AS = self.curve return as_witt([self] + n*[0*AS.x]) +as_function.teichmuller = teichmuller + def Vd(self, n, m = 1): '''Return V^m([f] d[x]) in W_n Omega''' AS = self.curve @@ -9,11 +11,45 @@ def Vd(self, n, m = 1): return as_witt_form([self] + n*[0*AS.x], (n+1)*[0*AS.x]) return as_witt_form(m*[0*AS.x] + [self] + (n-m)*[0*AS.x], (n+1)*[0*AS.x]) +as_function.Vd = Vd + def dV(self, n, m = 1): '''Return dV^m([f] d[x]) in W_n Omega''' AS = self.curve return as_witt_form((n+1)*[0*AS.x], m*[0*AS.x] + [self] + (n-m)*[0*AS.x]) -as_function.teichmuller = teichmuller -as_function.Vd = Vd -as_function.dV = dV \ No newline at end of file +as_function.dV = dV + + +def witt_diffn(self, n): + RxyzQ, Rxyz, x, y, z = AS.fct_field + F = Rxyz.base_ring + Rx. = PolynomialRing(F) + return 0 + +as_function.witt_diffn = witt_diffn + +def auxilliary_multiplication(fct, i, form_fct, j, n): + '''Return V^i(fct)*V^j(form_fct dx) in W_n Omega''' + AS = fct.curve + p = AS.base_ring.characteristic() + if i == 0: + return as_witt_form(j*[0*AS.x] + [fct^(p^j) * form_fct] + (n-j)*[0*AS.x], (n+1)*[0*AS.x]) + if j == 0: + return auxilliary_multiplication(fct, i-1, form_fct^p * AS.x^(p-1), j, n).verschiebung() + return p*auxilliary_multiplication(fct, i-1, form_fct, j-1, n).verschiebung() + +def auxilliary_multiplication2(fct1, i, fct2, j, n): + '''Return V^i(fct1)*dV^j(fct2) in W_n Omega''' + AS = fct.curve + p = AS.base_ring.characteristic() + if i > 0 and j > 0: + return auxilliary_multiplication2(fct1, i, fct2, j, n).verschiebung() + if i == 0: + aux_Vs = j*[0*AS.x] * [fct2 * fct1^(p^j - 1) * fct1.diffn()/AS.dx] + (n - j)*[0*AS.x] + aux = as_witt_form(aux_Vs, (n+1)*[0*AS.x]) + return as_witt_form((n+1)*[0*AS.x], j*[0*AS.x] + [fct1^(p^j) * fct2] + (n-j)*[0*AS.x]) - aux + if j == 0: + aux_Vs = j*[0*AS.x] * [fct1 * fct2^(p^j - 1) * fct2.diffn()/AS.dx] + (n - j)*[0*AS.x] + aux = as_witt_form(aux_Vs, (n+1)*[0*AS.x]) + return aux \ No newline at end of file diff --git a/as_drw/as_witt.sage b/as_drw/as_witt.sage index 37a0d4f..9e3ee5f 100644 --- a/as_drw/as_witt.sage +++ b/as_drw/as_witt.sage @@ -38,17 +38,23 @@ class as_witt: result = [as_function(C, a) for a in result] return as_witt(result) - def verschiebung(self): + def verschiebung(self, i = 1): AS = self.curve - return as_witt([0*AS.x] + self.f) + return as_witt(i*[0*AS.x] + self.f[i:]) def R(self): n = self.n return as_witt(self.f[:n]) - def frobenius(self): + def frobenius(self, i = 1): n= self.n p = self.curve.base_ring.characteristic() result = self.f[:n] - result = [a^p for a in result] - return as_witt(result) \ No newline at end of file + result = [a^(p^i) for a in result] + return as_witt(result) + + def diffn(self): + n = self.n + AS = self.curve + f0 = self.f[0] + return f[0].witt_diffn(n) + as_witt_form((n+1)*[0*AS.x], [0*AS.x] + self.f[1:]) \ No newline at end of file diff --git a/as_drw/as_witt_form.sage b/as_drw/as_witt_form.sage index e062ec4..af5b4bc 100644 --- a/as_drw/as_witt_form.sage +++ b/as_drw/as_witt_form.sage @@ -25,7 +25,7 @@ class as_witt_form: return result def __add__(self, other): - print(self, other) + #print(self, other) n = self.n AS = self.curve ######## @@ -33,7 +33,7 @@ class as_witt_form: while(i <= n and self.Vs[i].function == 0 and other.Vs[i].function == 0): i+=1 if i<=n: - print('a') + #print('a') if (i <= n and self.Vs[i].function != 0 and other.Vs[i].function != 0): self1 = self.cutVs(i) other1 = other.cutVs(i) @@ -42,7 +42,7 @@ class as_witt_form: other_first = other.Vs[i].teichmuller(n) aux = self_first + other_first for j in range(0, n+1 - i): - print(i, j) + #print(i, j) result += aux.f[j].Vd(n, i+j) return result if (i <= n and self.Vs[i].function != 0 and other.Vs[i].function == 0): @@ -62,7 +62,7 @@ class as_witt_form: while(i <= n and self.dVs[i].function == 0 and other.dVs[i].function == 0): i+=1 if i<=n: - print('b', i) + #print('b', i) if (i <= n and self.dVs[i].function != 0 and other.dVs[i].function != 0): self1 = self.cutdVs(i) other1 = other.cutdVs(i) @@ -87,6 +87,25 @@ class as_witt_form: return result return (0*AS.x).Vd(n) + def __rmul__(self, other): + n = self.n + AS = self.curve + + if parent(other) == ZZ or isinstance(other, int): + if other == 0: + return as_witt_form((n+1)*[0*AS.x], (n+1)*[0*AS.x]) + if other < 0: + return -(-other)*self + return self + (other - 1)*self + + if isinstance(other, as_witt): + result = as_witt_form((n+1)*[0*AS.x], (n+1)*[0*AS.x]) + for i in range(0, n+1): + for j in range(0, n+1): + result += auxilliary_multiplication(other.f[i], i, self.Vs[j], j, n) + result += auxilliary_multiplication(other.f[i], i, self.dVs[j], j, n) + return result + def verschiebung(self, i = 1): AS = self.curve p = AS.base_ring.characteristic() diff --git a/init.sage b/init.sage index a004224..4d1acbe 100644 --- a/init.sage +++ b/init.sage @@ -13,6 +13,7 @@ load('as_covers/dual_element.sage') load('as_covers/ith_magical_component.sage') load('as_covers/combination_components.sage') load('as_covers/group_action_matrices.sage') +load('as_covers/as_polyforms.sage') load('superelliptic_drw/second_patch.sage') load('superelliptic_drw/decomposition_into_g0_g8.sage') load('superelliptic_drw/superelliptic_witt.sage') @@ -30,6 +31,11 @@ load('as_drw/witt_polynomials.sage') load('as_drw/as_witt.sage') load('as_drw/as_witt_form.sage') load('as_drw/as_compability.sage') +load('quaternion_covers/quaternion_covers.sage') +load('quaternion_covers/quaternion_function_class.sage') +load('quaternion_covers/quaternion_form_class.sage') +load('quaternion_covers/quaternion_polyforms.sage') +load('quaternion_covers/quaternion_reduction.sage') ############## ############## def init(lista, tests = False, init=True): diff --git a/quaternion_covers/quaternion_covers.sage b/quaternion_covers/quaternion_covers.sage new file mode 100644 index 0000000..427c5be --- /dev/null +++ b/quaternion_covers/quaternion_covers.sage @@ -0,0 +1,435 @@ +class quaternion_cover: + def __init__(self, C, list_of_fcts, branch_points = [], prec = 10): + self.quotient = C + self.functions = list_of_fcts + self.height = len(list_of_fcts) + F = C.base_ring + self.base_ring = F + p = C.characteristic + self.characteristic = p + self.prec = prec + #group acting + n = self.height + from itertools import product + pr = [list(GF(p)) for _ in range(n)] + self.group = QuaternionGroup() + ######### + f = C.polynomial + m = C.exponent + r = f.degree() + delta = GCD(m, r) + self.nb_of_pts_at_infty = delta + self.branch_points = list(range(delta)) + branch_points + Rxy. = PolynomialRing(F, 2) + Rt. = LaurentSeriesRing(F, default_prec=prec) + + all_x_series = {} + all_y_series = {} + all_z_series = {} + all_dx_series = {} + all_jumps = {} + + for pt in self.branch_points: + x_series = superelliptic_function(C, x).expansion(pt=pt, prec=prec) + y_series = superelliptic_function(C, y).expansion(pt=pt, prec=prec) + z_series = [] + jumps = [] + n = len(list_of_fcts) + list_of_power_series = [g.expansion(pt=pt, prec=prec) for g in list_of_fcts] + for j in range(n): + if j == 2: + list_of_power_series[j] += z_series[0]*list_of_power_series[0] + z_series[1]*(list_of_power_series[0] + list_of_power_series[1]) + power_series = list_of_power_series[j] + jump, correction, t_old, z = artin_schreier_transform(power_series, prec = prec) + x_series = x_series(t = t_old) + y_series = y_series(t = t_old) + z_series = [zi(t = t_old) for zi in z_series] + z_series += [z] + jumps += [jump] + list_of_power_series = [g(t = t_old) for g in list_of_power_series] + + all_jumps[pt] = jumps + all_x_series[pt] = x_series + all_y_series[pt] = y_series + all_z_series[pt] = z_series + all_dx_series[pt] = x_series.derivative() + self.jumps = all_jumps + self.x_series = all_x_series + self.y_series = all_y_series + self.z_series = all_z_series + self.dx_series = all_dx_series + ############## + #Function field + variable_names = 'x, y' + for i in range(n): + variable_names += ', z' + str(i) + Rxyz = PolynomialRing(F, n+2, variable_names) + x, y = Rxyz.gens()[:2] + z = Rxyz.gens()[2:] + RxyzQ = FractionField(Rxyz) + self.fct_field = (RxyzQ, Rxyz, x, y, z) + self.x = quaternion_function(self, x) + self.y = quaternion_function(self, y) + self.z = [quaternion_function(self, z[j]) for j in range(n)] + self.dx = quaternion_form(self, 1) + self.one = quaternion_function(self, 1) + + + def __repr__(self): + n = self.height + p = self.characteristic + if n==1: + return "(Z/p)-cover of " + str(self.quotient)+" with the equation:\n z^" + str(p) + " - z = " + str(self.functions[0]) + + result = "Q8-cover of " + str(self.quotient)+" with the equations:\n" + for i in range(n): + if i !=2: + result += 'z' + str(i) + "^" + str(p) + " - z" + str(i) + " = " + str(self.functions[i]) + "\n" + else: + result += 'z2^2 - z2 = ' + str(self.functions[2]) + ' + ' + 'z0 * ' + str(self.functions[0]) + ' + z1 * (' + str(self.functions[0] + self.functions[1]) + ')' + return result + + def genus(self): + jumps = self.jumps + gY = self.quotient.genus() + n = self.height + branch_pts = self.branch_points + p = self.characteristic + return p^n*gY + (p^n - 1)*(len(branch_pts) - 1) + sum(p^(n-j-1)*(jumps[pt][j]-1)*(p-1)/2 for j in range(n) for pt in branch_pts) + + def exponent_of_different(self, place = 0): + jumps = self.jumps + n = self.height + delta = self.nb_of_pts_at_infty + p = self.characteristic + return sum(p^(n-j-1)*(jumps[place][j]+1)*(p-1) for j in range(n)) + + def exponent_of_different_prim(self, place = 0): + jumps = self.jumps + n = self.height + delta = self.nb_of_pts_at_infty + p = self.characteristic + return sum(p^(n-j-1)*(jumps[place][j])*(p-1) for j in range(n)) + + def holomorphic_differentials_basis(self, threshold = 8): + from itertools import product + x_series = self.x_series + y_series = self.y_series + z_series = self.z_series + dx_series = self.dx_series + delta = self.nb_of_pts_at_infty + p = self.characteristic + n = self.height + prec = self.prec + C = self.quotient + F = self.base_ring + m = C.exponent + r = C.polynomial.degree() + RxyzQ, Rxyz, x, y, z = self.fct_field + Rt. = LaurentSeriesRing(F, default_prec=prec) + #Tworzymy zbiór S form z^i x^j y^k dx/y o waluacji >= waluacja z^(p-1)*dx/y + S = [] + pr = [list(GF(p)) for _ in range(n)] + for i in range(0, threshold*r): + for j in range(0, m): + for k in product(*pr): + eta = quaternion_form(self, x^i * prod(z[i1]^(k[i1]) for i1 in range(n))/y^j) + eta_exp = eta.expansion(pt=self.branch_points[0]) + S += [(eta, eta_exp)] + + forms = holomorphic_combinations(S) + + for pt in self.branch_points[1:]: + forms = [(omega, omega.expansion(pt=pt)) for omega in forms] + forms = holomorphic_combinations(forms) + + if len(forms) < self.genus(): + print("I haven't found all forms, only ", len(forms), " of ", self.genus()) + return holomorphic_differentials_basis(self, threshold = threshold + 1) + if len(forms) > self.genus(): + raise ValueError("Increase precision.") + return forms + + def cartier_matrix(self, prec=50): + g = self.genus() + F = self.base_ring + M = matrix(F, g, g) + for i, omega in enumerate(self.holomorphic_differentials_basis()): + M[:, i] = vector(omega.cartier().coordinates()) + return M + + def at_most_poles(self, pole_order, threshold = 8): + """ Find fcts with pole order in infty's at most pole_order. Threshold gives a bound on powers of x in the function. + If you suspect that you haven't found all the functions, you may increase it.""" + from itertools import product + x_series = self.x_series + y_series = self.y_series + z_series = self.z_series + delta = self.nb_of_pts_at_infty + p = self.characteristic + n = self.height + prec = self.prec + C = self.quotient + F = self.base_ring + m = C.exponent + r = C.polynomial.degree() + RxyzQ, Rxyz, x, y, z = self.fct_field + F = C.base_ring + Rt. = LaurentSeriesRing(F, default_prec=prec) + #Tworzymy zbiór S form z^i x^j y^k dx/y o waluacji >= waluacja z^(p-1)*dx/y + S = [] + RQxyz = FractionField(Rxyz) + pr = [list(GF(p)) for _ in range(n)] + for i in range(0, threshold*r): + for j in range(0, m): + for k in product(*pr): + eta = quaternion_function(self, x^i * prod(z[i1]^(k[i1]) for i1 in range(n))*y^j) + eta_exp = eta.expansion_at_infty() + S += [(eta, eta_exp)] + + forms = holomorphic_combinations_fcts(S, pole_order) + + for i in range(1, delta): + forms = [(omega, omega.expansion_at_infty(place = i)) for omega in forms] + forms = holomorphic_combinations_fcts(forms, pole_order) + + return forms + + def magical_element(self, threshold = 8): + list_of_elts = self.at_most_poles(self.exponent_of_different_prim(), threshold) + result = [] + for a in list_of_elts: + if a.trace().function != 0: + result += [a] + return result + + def pseudo_magical_element(self, threshold = 8): + list_of_elts = self.at_most_poles(self.exponent_of_different(), threshold) + result = [] + for a in list_of_elts: + if a.trace().function != 0: + result += [a] + return result + + def at_most_poles_forms(self, pole_order, threshold = 8): + """Find forms with pole order in all the points at infty equat at most to pole_order. Threshold gives a bound on powers of x in the form. + If you suspect that you haven't found all the functions, you may increase it.""" + from itertools import product + x_series = self.x_series + y_series = self.y_series + z_series = self.z_series + delta = self.nb_of_pts_at_infty + p = self.characteristic + n = self.height + prec = self.prec + C = self.quotient + F = self.base_ring + m = C.exponent + r = C.polynomial.degree() + RxyzQ, Rxyz, x, y, z = self.fct_field + Rt. = LaurentSeriesRing(F, default_prec=prec) + #Tworzymy zbiór S form z^i x^j y^k dx/y o waluacji >= waluacja z^(p-1)*dx/y + S = [] + RQxyz = FractionField(Rxyz) + pr = [list(GF(p)) for _ in range(n)] + for i in range(0, threshold*r): + for j in range(0, m): + for k in product(*pr): + eta = quaternion_form(self, x^i * prod(z[i1]^(k[i1]) for i1 in range(n))/y^j) + eta_exp = eta.expansion_at_infty() + S += [(eta, eta_exp)] + + forms = holomorphic_combinations_forms(S, pole_order) + + for pt in self.branch_points[1:]: + forms = [(omega, omega.expansion(pt=pt)) for omega in forms] + forms = holomorphic_combinations_forms(forms, pole_order) + + return forms + + def uniformizer(self, place = 0): + '''Return uniformizer of curve self at place-th place at infinity.''' + p = self.characteristic + n = self.height + F = self.base_ring + RxyzQ, Rxyz, x, y, z = self.fct_field + fx = quaternion_function(self, x) + z = [quaternion_function(self, zi) for zi in z] + # We create a list of functions. We add there all variables... + list_of_fcts = [fx]+z + vfx = fx.valuation(place) + vz = [zi.valuation(place) for zi in z] + + # Then we subtract powers of variables with the same valuation (so that 1/t^(kp) cancels) and add to this list. + for j1 in range(n): + for j2 in range(n): + if j1>j2: + a = gcd(vz[j1] , vz[j2]) + vz1 = vz[j1]/a + vz2 = vz[j2]/a + for b in F: + if (z[j1]^(vz2) - b*z[j2]^(vz1)).valuation(place) > (z[j2]^(vz1)).valuation(place): + list_of_fcts += [z[j1]^(vz2) - b*z[j2]^(vz1)] + for j1 in range(n): + a = gcd(vz[j1], vfx) + vzj = vz[j1] /a + vfx = vfx/a + for b in F: + if (fx^(vzj) - b*z[j1]^(vfx)).valuation(place) > (z[j1]^(vfx)).valuation(place): + list_of_fcts += [fx^(vzj) - b*z[j1]^(vfx)] + #Finally, we check if on the list there are two elements with the same valuation. + for f1 in list_of_fcts: + for f2 in list_of_fcts: + d, a, b = xgcd(f1.valuation(place), f2.valuation(place)) + if d == 1: + return f1^a*f2^b + raise ValueError("My method of generating fcts with relatively prime valuation failed.") + + + def ith_ramification_gp(self, i, place = 0): + '''Find ith ramification group at place at infty of nb place.''' + G = self.group + t = self.uniformizer(place) + Gi = [G[0]] + for g in G: + if g != G[0]: + tg = t.group_action(g) + v = (tg - t).valuation(place) + if v >= i+1: + Gi += [g] + return Gi + + def ramification_jumps(self, place = 0): + '''Return list of lower ramification jumps at at place at infty of nb place.''' + G = self.group + ramification_jps = [] + i = 0 + while len(G) > 1: + Gi = self.ith_ramification_gp(i+1, place) + if len(Gi) < len(G): + ramification_jps += [i] + G = Gi + i+=1 + return ramification_jps + + def a_number(self): + g = self.genus() + return g - self.cartier_matrix().rank() + + def cohomology_of_structure_sheaf_basis(self, holo_basis = 0, threshold = 8): + if holo_basis == 0: + holo_basis = self.holomorphic_differentials_basis(threshold = threshold) + from itertools import product + x_series = self.x_series + y_series = self.y_series + z_series = self.z_series + delta = self.nb_of_pts_at_infty + p = self.characteristic + n = self.height + prec = self.prec + C = self.quotient + F = self.base_ring + m = C.exponent + r = C.polynomial.degree() + RxyzQ, Rxyz, x, y, z = self.fct_field + Rt. = LaurentSeriesRing(F, default_prec=prec) + #Tworzymy zbiór S form z^i x^j y^k dx/y o waluacji >= waluacja z^(p-1)*dx/y + result_fcts = [] + V = VectorSpace(F,self.genus()) + S = V.subspace([]) + RQxyz = FractionField(Rxyz) + pr = [list(GF(p)) for _ in range(n)] + i = 0 + while len(result_fcts) < self.genus(): + for j in range(0, m): + for k in product(*pr): + f = quaternion_function(self, prod(z[i1]^(k[i1]) for i1 in range(n))/x^i*y^j) + f_products = [omega.serre_duality_pairing(f) for omega in holo_basis] + if vector(f_products) not in S: + S = S+V.subspace([V(f_products)]) + result_fcts += [f] + i += 1 + return result_fcts + + def lift_to_de_rham(self, fct, threshold = 30): + '''Given function fct, find form eta regular on affine part such that eta - d(fct) is regular in infty. (Works for one place at infty now)''' + from itertools import product + x_series = self.x_series + y_series = self.y_series + z_series = self.z_series + dx_series = self.dx_series + delta = self.nb_of_pts_at_infty + p = self.characteristic + n = self.height + prec = self.prec + C = self.quotient + F = self.base_ring + m = C.exponent + r = C.polynomial.degree() + RxyzQ, Rxyz, x, y, z = self.fct_field + Rt. = LaurentSeriesRing(F, default_prec=prec) + #Tworzymy zbiór S form z^i x^j y^k dx/y o waluacji >= waluacja z^(p-1)*dx/y + S = [(fct.diffn(), fct.diffn().expansion_at_infty())] + pr = [list(GF(p)) for _ in range(n)] + holo = self.holomorphic_differentials_basis(threshold = threshold) + for i in range(0, threshold*r): + for j in range(0, m): + for k in product(*pr): + eta = quaternion_form(self, x^i*prod(z[i1]^(k[i1]) for i1 in range(n))/y^j) + eta_exp = eta.expansion_at_infty() + S += [(eta, eta_exp)] + forms = holomorphic_combinations(S) + if len(forms) <= self.genus(): + raise ValueError("Increase threshold!") + for omega in forms: + for a in F: + if (a*omega + fct.diffn()).is_regular_on_U0(): + return a*omega + fct.diffn() + raise ValueError("Unknown.") + + def de_rham_basis(self, holo_basis = 0, cohomology_basis = 0, threshold = 30): + if holo_basis == 0: + holo_basis = self.holomorphic_differentials_basis(threshold = threshold) + if cohomology_basis == 0: + cohomology_basis = self.cohomology_of_structure_sheaf_basis(holo_basis = holo_basis, threshold = threshold) + result = [] + for omega in holo_basis: + result += [quaternion_cech(self, omega, quaternion_function(self, 0))] + for f in cohomology_basis: + omega = self.lift_to_de_rham(f, threshold = threshold) + result += [quaternion_cech(self, omega, f)] + return result + +def holomorphic_combinations(S): + """Given a list S of pairs (form, corresponding Laurent series at some pt), find their combinations holomorphic at that pt.""" + C_AS = S[0][0].curve + p = C_AS.characteristic + F = C_AS.base_ring + prec = C_AS.prec + Rt. = LaurentSeriesRing(F, default_prec=prec) + RtQ = FractionField(Rt) + minimal_valuation = min([g[1].valuation() for g in S]) + if minimal_valuation >= 0: + return [s[0] for s in S] + list_of_lists = [] #to będzie lista złożona z list współczynników część nieholomorficznych rozwinięcia form z S + for eta, eta_exp in S: + a = -minimal_valuation + eta_exp.valuation() + list_coeffs = a*[0] + eta_exp.list() + (-minimal_valuation)*[0] + list_coeffs = list_coeffs[:-minimal_valuation] + list_of_lists += [list_coeffs] + M = matrix(F, list_of_lists) + V = M.kernel() #chcemy wyzerować części nieholomorficzne, biorąc kombinacje form z S + + + # Sprawdzamy, jakim formom odpowiadają elementy V. + forms = [] + for vec in V.basis(): + forma_holo = quaternion_form(C_AS, 0) + forma_holo_power_series = Rt(0) + for vec_wspolrzedna, elt_S in zip(vec, S): + eta = elt_S[0] + #eta_exp = elt_S[1] + forma_holo += vec_wspolrzedna*eta + #forma_holo_power_series += vec_wspolrzedna*eta_exp + forms += [forma_holo] + return forms \ No newline at end of file diff --git a/quaternion_covers/quaternion_form_class.sage b/quaternion_covers/quaternion_form_class.sage new file mode 100644 index 0000000..8badd01 --- /dev/null +++ b/quaternion_covers/quaternion_form_class.sage @@ -0,0 +1,303 @@ +class quaternion_form: + def __init__(self, C, g): + self.curve = C + n = C.height + F = C.base_ring + variable_names = 'x, y' + for i in range(n): + variable_names += ', z' + str(i) + Rxyz = PolynomialRing(F, n+2, variable_names) + x, y = Rxyz.gens()[:2] + z = Rxyz.gens()[2:] + RxyzQ = FractionField(Rxyz) + self.form = RxyzQ(g) + + def __repr__(self): + return "(" + str(self.form)+") * dx" + + def expansion_at_infty(self, place = 0): + C = self.curve + delta = C.nb_of_pts_at_infty + F = C.base_ring + x_series = C.x_series[place] + y_series = C.y_series[place] + z_series = C.z_series[place] + dx_series = C.dx_series[place] + n = C.height + variable_names = 'x, y' + for j in range(n): + variable_names += ', z' + str(j) + Rxyz = PolynomialRing(F, n+2, variable_names) + x, y = Rxyz.gens()[:2] + z = Rxyz.gens()[2:] + RxyzQ = FractionField(Rxyz) + prec = C.prec + Rt. = LaurentSeriesRing(F, default_prec=prec) + g = self.form + sub_list = {x : x_series, y : y_series} | {z[j] : z_series[j] for j in range(n)} + return g.substitute(sub_list)*dx_series + + def expansion(self, pt = 0): + '''Same code as expansion_at_infty.''' + C = self.curve + F = C.base_ring + x_series = C.x_series[pt] + y_series = C.y_series[pt] + z_series = C.z_series[pt] + dx_series = C.dx_series[pt] + n = C.height + variable_names = 'x, y' + for j in range(n): + variable_names += ', z' + str(j) + Rxyz = PolynomialRing(F, n+2, variable_names) + x, y = Rxyz.gens()[:2] + z = Rxyz.gens()[2:] + RxyzQ = FractionField(Rxyz) + prec = C.prec + Rt. = LaurentSeriesRing(F, default_prec=prec) + g = self.form + sub_list = {x : x_series, y : y_series} | {z[j] : z_series[j] for j in range(n)} + return g.substitute(sub_list)*dx_series + + def __add__(self, other): + C = self.curve + g1 = self.form + g2 = other.form + return quaternion_form(C, g1 + g2) + + def __sub__(self, other): + C = self.curve + g1 = self.form + g2 = other.form + return quaternion_form(C, g1 - g2) + + def __neg__(self): + C = self.curve + g = self.form + return quaternion_form(C, -g) + + def __rmul__(self, constant): + C = self.curve + omega = self.form + return quaternion_form(C, constant*omega) + + def group_action(self, ZN_tuple): + C = self.curve + n = C.height + RxyzQ, Rxyz, x, y, z = C.fct_field + sub_list = {x : x, y : y} | {z[j] : z[j]+ZN_tuple[j] for j in range(n)} + g = self.form + return quaternion_form(C, g.substitute(sub_list)) + + def coordinates(self, basis = 0): + """Find coordinates of the given holomorphic form self in terms of the basis forms in a list holo.""" + C = self.curve + if basis == 0: + basis = C.holomorphic_differentials_basis() + RxyzQ, Rxyz, x, y, z = C.fct_field + # We need to have only polynomials to use monomial_coefficients in linear_representation_polynomials, + # and sometimes basis elements have denominators. Thus we multiply by them. + denom = LCM([denominator(omega.form) for omega in basis]) + basis = [denom*omega for omega in basis] + self_with_no_denominator = denom*self + return linear_representation_polynomials(Rxyz(self_with_no_denominator.form), [Rxyz(omega.form) for omega in basis]) + + def trace(self): + C = self.curve + C_super = C.quotient + n = C.height + F = C.base_ring + variable_names = 'x, y' + for j in range(n): + variable_names += ', z' + str(j) + Rxyz = PolynomialRing(F, n+2, variable_names) + x, y = Rxyz.gens()[:2] + z = Rxyz.gens()[2:] + RxyzQ = FractionField(Rxyz) + result = quaternion_form(C, 0) + G = C.group + for a in G: + result += self.group_action(a) + result = result.form + Rxy. = PolynomialRing(F, 2) + Qxy = FractionField(Rxy) + result = quaternion_reduction(AS, result) + return superelliptic_form(C_super, Qxy(result)) + + def residue(self, place=0): + return self.expansion_at_infty(place = place).residue() + + def valuation(self, place=0): + return self.expansion_at_infty(place = place).valuation() + + def serre_duality_pairing(self, fct): + AS = self.curve + return sum((fct*self).residue(place = _) for _ in range(AS.nb_of_pts_at_infty)) + + def cartier(self): + C = self.curve + F = C.base_ring + n = C.height + ff = C.functions + p = F.characteristic() + C_super = C.quotient + (RxyzQ, Rxyz, x, y, z) = C.fct_field + fct = self.form + Rxy. = PolynomialRing(F, 2) + RxyQ = FractionField(Rxy) + x, y = Rxyz.gens()[0], Rxyz.gens()[1] + z = Rxyz.gens()[2:] + num = Rxyz(fct.numerator()) + den = Rxyz(fct.denominator()) + result = RxyzQ(0) + #return (num, den, z, fct) + if den in Rxy: + sub_list = {x : x, y : y} | {z[j] : (z[j]^p - RxyzQ(ff[j].function)) for j in range(n)} + num = RxyzQ(num.substitute(sub_list)) + den1 = Rxyz(num.denominator()) + num = Rxyz(num*den1^p) + for monomial in Rxyz(num).monomials(): + degrees = [monomial.degree(z[i]) for i in range(n)] + product_of_z = prod(z[i]^(degrees[i]) for i in range(n)) + monomial_divided_by_z = monomial/product_of_z + product_of_z_no_p = prod(z[i]^(degrees[i]/p) for i in range(n)) + aux_form = superelliptic_form(C_super, RxyQ(monomial_divided_by_z/den)) + aux_form = aux_form.cartier() + result += product_of_z_no_p * Rxyz(num).monomial_coefficient(monomial) * aux_form.form/den1 + return quaternion_form(C, result) + raise ValueError("Please present first your form as sum z^i omega_i, where omega_i are forms on quotient curve.") + + def is_regular_on_U0(self): + AS = self.curve + C = AS.quotient + m = C.exponent + RxyzQ, Rxyz, x, y, z = AS.fct_field + if y^(m-1)*self.form in Rxyz: + return True + return False + +def artin_schreier_transform(power_series, prec = 10): + """Given a power_series, find correction such that power_series - (correction)^p +correction has valuation + -jump non divisible by p. Also, express t (the variable) in terms of the uniformizer at infty on the curve + z^p - z = power_series, where z = 1/t_new^(jump) and express z in terms of the new uniformizer.""" + correction = 0 + F = power_series.parent().base() + p = F.characteristic() + Rt. = LaurentSeriesRing(F, default_prec=prec) + RtQ = FractionField(Rt) + power_series = RtQ(power_series) + if power_series.valuation() == +Infinity: + raise ValueError("Precision is too low.") + if power_series.valuation() >= 0: + # THIS IS WRONG - THERE ARE SEVERAL PLACES OVER THIS PLACE, AND IT DEPENDS + aux = t^p - t + z = new_reverse(aux, prec = prec) + z = z(t = power_series) + return(0, 0, t, z) + + while(power_series.valuation() % p == 0 and power_series.valuation() < 0): + M = -power_series.valuation()/p + coeff = power_series.list()[0] #wspolczynnik a_(-p) w f_AS + correction += coeff.nth_root(p)*t^(-M) + power_series = power_series - (coeff*t^(-p*M) - coeff.nth_root(p)*t^(-M)) + jump = max(-(power_series.valuation()), 0) + try: + T = nth_root2((power_series)^(-1), jump, prec=prec) #T is defined by power_series = 1/T^m + except: + print("no ", str(jump), "-th root; divide by", power_series.list()[0]) + return (jump, power_series.list()[0]) + T_rev = new_reverse(T, prec = prec) + t_old = T_rev(t^p/nth_root2(1 - t^((p-1)*jump), jump, prec=prec)) + z = 1/t^(jump) + Rt(correction)(t = t_old) + return(jump, correction, t_old, z) + + +def are_forms_linearly_dependent(set_of_forms): + from sage.rings.polynomial.toy_variety import is_linearly_dependent + C = set_of_forms[0].curve + F = C.base_ring + n = C.height + variable_names = 'x, y' + for i in range(n): + variable_names += ', z' + str(i) + Rxyz = PolynomialRing(F, n+2, variable_names) + denominators = prod(denominator(omega.form) for omega in set_of_forms) + return is_linearly_dependent([Rxyz(denominators*omega.form) for omega in set_of_forms]) + +def holomorphic_combinations_fcts(S, pole_order): + '''given a set S of (form, corresponding Laurent series at some pt), find their combinations holomorphic at that pt''' + C_AS = S[0][0].curve + p = C_AS.characteristic + F = C_AS.base_ring + prec = C_AS.prec + Rt. = LaurentSeriesRing(F, default_prec=prec) + RtQ = FractionField(Rt) + minimal_valuation = min([Rt(g[1]).valuation() for g in S]) + if minimal_valuation >= -pole_order: + return [s[0] for s in S] + list_of_lists = [] #to będzie lista złożona z list współczynników część nieholomorficznych rozwinięcia form z S + for eta, eta_exp in S: + a = -minimal_valuation + Rt(eta_exp).valuation() + list_coeffs = a*[0] + Rt(eta_exp).list() + (-minimal_valuation)*[0] + list_coeffs = list_coeffs[:-minimal_valuation - pole_order] + list_of_lists += [list_coeffs] + M = matrix(F, list_of_lists) + V = M.kernel() #chcemy wyzerować części nieholomorficzne, biorąc kombinacje form z S + + + # Sprawdzamy, jakim formom odpowiadają elementy V. + forms = [] + for vec in V.basis(): + forma_holo = quaternion_function(C_AS, 0) + forma_holo_power_series = Rt(0) + for vec_wspolrzedna, elt_S in zip(vec, S): + eta = elt_S[0] + #eta_exp = elt_S[1] + forma_holo += vec_wspolrzedna*eta + #forma_holo_power_series += vec_wspolrzedna*eta_exp + forms += [forma_holo] + return forms + +#given a set S of (form, corresponding Laurent series at some pt), find their combinations holomorphic at that pt +def holomorphic_combinations_forms(S, pole_order): + C_AS = S[0][0].curve + p = C_AS.characteristic + F = C_AS.base_ring + prec = C_AS.prec + Rt. = LaurentSeriesRing(F, default_prec=prec) + RtQ = FractionField(Rt) + minimal_valuation = min([Rt(g[1]).valuation() for g in S]) + if minimal_valuation >= -pole_order: + return [s[0] for s in S] + list_of_lists = [] #to będzie lista złożona z list współczynników część nieholomorficznych rozwinięcia form z S + for eta, eta_exp in S: + a = -minimal_valuation + Rt(eta_exp).valuation() + list_coeffs = a*[0] + Rt(eta_exp).list() + (-minimal_valuation)*[0] + list_coeffs = list_coeffs[:-minimal_valuation - pole_order] + list_of_lists += [list_coeffs] + M = matrix(F, list_of_lists) + V = M.kernel() #chcemy wyzerować części nieholomorficzne, biorąc kombinacje form z S + + + # Sprawdzamy, jakim formom odpowiadają elementy V. + forms = [] + for vec in V.basis(): + forma_holo = quaternion_form(C_AS, 0) + forma_holo_power_series = Rt(0) + for vec_wspolrzedna, elt_S in zip(vec, S): + eta = elt_S[0] + #eta_exp = elt_S[1] + forma_holo += vec_wspolrzedna*eta + #forma_holo_power_series += vec_wspolrzedna*eta_exp + forms += [forma_holo] + return forms + +#print only forms that are log at the branch pts, but not holomorphic +def only_log_forms(C_AS): + list1 = AS.at_most_poles_forms(0) + list2 = AS.at_most_poles_forms(1) + result = [] + for a in list2: + if not(are_forms_linearly_dependent(list1 + result + [a])): + result += [a] + return result diff --git a/quaternion_covers/quaternion_function_class.sage b/quaternion_covers/quaternion_function_class.sage new file mode 100644 index 0000000..6090656 --- /dev/null +++ b/quaternion_covers/quaternion_function_class.sage @@ -0,0 +1,207 @@ +class quaternion_function: + def __init__(self, C, g): + self.curve = C + F = C.base_ring + n = C.height + variable_names = 'x, y' + for i in range(n): + variable_names += ', z' + str(i) + Rxyz = PolynomialRing(F, n+2, variable_names) + x, y = Rxyz.gens()[:2] + z = Rxyz.gens()[2:] + RxyzQ = FractionField(Rxyz) + self.function = RxyzQ(g) + #self.function = quaternion_reduction(AS, RxyzQ(g)) + + def __repr__(self): + return str(self.function) + + def __eq__(self, other): + AS = self.curve + RxyzQ, Rxyz, x, y, z = AS.fct_field + aux = self - other + aux = RxyzQ(aux.function) + aux = aux.numerator() + aux = quaternion_function(AS, aux) + aux = aux.expansion_at_infty() + if aux.valuation() >= 1: + return True + return False + + def __add__(self, other): + C = self.curve + g1 = self.function + g2 = other.function + return quaternion_function(C, g1 + g2) + + def __sub__(self, other): + C = self.curve + g1 = self.function + g2 = other.function + return quaternion_function(C, g1 - g2) + + def __rmul__(self, constant): + C = self.curve + g = self.function + return quaternion_function(C, constant*g) + + def __neg__(self): + C = self.curve + g = self.function + return quaternion_function(C, -g) + + def __mul__(self, other): + if isinstance(other, quaternion_function): + C = self.curve + g1 = self.function + g2 = other.function + return quaternion_function(C, g1*g2) + if isinstance(other, quaternion_form): + C = self.curve + g1 = self.function + g2 = other.form + return quaternion_form(C, g1*g2) + + def __truediv__(self, other): + C = self.curve + g1 = self.function + g2 = other.function + return quaternion_function(C, g1/g2) + + def __pow__(self, exponent): + C = self.curve + g1 = self.function + return quaternion_function(C, g1^(exponent)) + + def expansion_at_infty(self, place = 0): + C = self.curve + delta = C.nb_of_pts_at_infty + F = C.base_ring + x_series = C.x_series[place] + y_series = C.y_series[place] + z_series = C.z_series[place] + n = C.height + variable_names = 'x, y' + for j in range(n): + variable_names += ', z' + str(j) + Rxyz = PolynomialRing(F, n+2, variable_names) + x, y = Rxyz.gens()[:2] + z = Rxyz.gens()[2:] + RxyzQ = FractionField(Rxyz) + prec = C.prec + Rt. = LaurentSeriesRing(F, default_prec=prec) + g = self.function + g = RxyzQ(g) + sub_list = {x : x_series, y : y_series} | {z[j] : z_series[j] for j in range(n)} + return g.substitute(sub_list) + + def expansion(self, pt = 0): + C = self.curve + delta = C.nb_of_pts_at_infty + F = C.base_ring + x_series = C.x_series[pt] + y_series = C.y_series[pt] + z_series = C.z_series[pt] + n = C.height + variable_names = 'x, y' + for j in range(n): + variable_names += ', z' + str(j) + Rxyz = PolynomialRing(F, n+2, variable_names) + x, y = Rxyz.gens()[:2] + z = Rxyz.gens()[2:] + RxyzQ = FractionField(Rxyz) + prec = C.prec + Rt. = LaurentSeriesRing(F, default_prec=prec) + g = self.function + g = RxyzQ(g) + sub_list = {x : x_series, y : y_series} | {z[j] : z_series[j] for j in range(n)} + return g.substitute(sub_list) + + def group_action(self, elt): + Q8 = QuaternionGroup() + Qi, Qj = Q8.gens() + AS = self.curve + RxyzQ, Rxyz, x, y, z = AS.fct_field + if elt == Qi^4: + sub_list = {x : x, y : y} | {z[0] : z[0], z[1] : z[1], z[2]: z[2]} + if elt == Qi: + sub_list = {x : x, y : y} | {z[0] : z[0]+1, z[1] : z[1], z[2]: z[2] + z[0]} + if elt == Qj: + sub_list = {x : x, y : y} | {z[0] : z[0], z[1] : z[1] + 1, z[2]: z[2] + z[1] + z[0]} + if elt == Qi^2: + sub_list = {x : x, y : y} | {z[0] : z[0], z[1] : z[1], z[2]: z[2] + 1} + if elt == Qi * Qj: + sub_list = {x : x, y : y} | {z[0] : z[0] + 1, z[1] : z[1] + 1, z[2]: z[2] + z[1] + 1} + if elt == Qj * Qi: + sub_list = {x : x, y : y} | {z[0] : z[0] + 1, z[1] : z[1] + 1, z[2]: z[2] + z[1]} + if elt == Qi^3: + sub_list = {x : x, y : y} | {z[0] : z[0]+1, z[1] : z[1], z[2]: z[2] + z[0] + 1} + if elt == Qj^3: + sub_list = {x : x, y : y} | {z[0] : z[0], z[1] : z[1] + 1, z[2]: z[2] + z[1] + z[0] + 1} + g = self.function + return quaternion_function(AS, g.substitute(sub_list)) + + def trace(self): + C = self.curve + C_super = C.quotient + n = C.height + F = C.base_ring + variable_names = 'x, y' + for j in range(n): + variable_names += ', z' + str(j) + Rxyz = PolynomialRing(F, n+2, variable_names) + x, y = Rxyz.gens()[:2] + z = Rxyz.gens()[2:] + RxyzQ = FractionField(Rxyz) + result = quaternion_function(C, 0) + G = C.group + for a in G: + result += self.group_action(a) + result = result.function + Rxy. = PolynomialRing(F, 2) + Qxy = FractionField(Rxy) + result = quaternion_reduction(AS, result) + return superelliptic_function(C_super, Qxy(result)) + + def coordinates(self, prec = 100, basis = 0): + "Return coordinates in H^1(X, OX)." + AS = self.curve + if basis == 0: + basis = [AS.holomorphic_differentials_basis(), AS.cohomology_of_structure_sheaf_basis()] + holo_diffs = basis[0] + coh_basis = basis[1] + f_products = [] + for f in coh_basis: + f_products += [[omega.serre_duality_pairing(f) for omega in holo_diffs]] + product_of_fct_and_omegas = [] + product_of_fct_and_omegas = [omega.serre_duality_pairing(self) for omega in holo_diffs] + + V = (F^(AS.genus())).span_of_basis([vector(a) for a in f_products]) + coh_coordinates = V.coordinates(product_of_fct_and_omegas) + return coh_coordinates + + def diffn(self): + C = self.curve + C_super = C.quotient + n = C.height + RxyzQ, Rxyz, x, y, z = C.fct_field + fcts = C.functions + f = self.function + y_super = superelliptic_function(C_super, y) + dy_super = y_super.diffn().form + dz = [] + for i in range(n): + dfct = fcts[i].diffn().form + dz += [-dfct] + result = f.derivative(x) + result += f.derivative(y)*dy_super + for i in range(n): + result += f.derivative(z[i])*dz[i] + return quaternion_form(C, result) + + def valuation(self, place = 0): + '''Return valuation at i-th place at infinity.''' + C = self.curve + F = C.base_ring + Rt. = LaurentSeriesRing(F) + return Rt(self.expansion_at_infty(place = place)).valuation() diff --git a/quaternion_covers/quaternion_reduction.sage b/quaternion_covers/quaternion_reduction.sage new file mode 100644 index 0000000..c71c893 --- /dev/null +++ b/quaternion_covers/quaternion_reduction.sage @@ -0,0 +1,33 @@ +def quaternion_reduction(AS, fct): + '''Simplify rational function fct as a function in the function field of AS, so that z[i] appear in powers