From 7247bb3ddf6b30a636836b753399a4175a34244e Mon Sep 17 00:00:00 2001 From: jgarnek Date: Thu, 8 Feb 2024 19:25:10 +0000 Subject: [PATCH] heisenberg covers added --- README.md | 7 + as_covers/group_action_matrices.sage | 20 +- .../tests/group_action_matrices_test.sage | 2 +- drafty/draft2.sage | 21 +- heisenberg_covers/heisenberg_covers.sage | 435 ++++++++++++++++++ heisenberg_covers/heisenberg_form_class.sage | 326 +++++++++++++ .../heisenberg_function_class.sage | 211 +++++++++ .../heisenberg_group_action_matrices.sage | 40 ++ heisenberg_covers/heisenberg_polyforms.sage | 209 +++++++++ heisenberg_covers/heisenberg_reduction.sage | 33 ++ .../tests/heisenberg_covers_tests.sage | 12 + init.sage | 7 + .../quaternion_group_action_matrices.sage | 40 ++ quaternion_covers/quaternion_polyforms.sage | 209 +++++++++ 14 files changed, 1551 insertions(+), 21 deletions(-) create mode 100644 heisenberg_covers/heisenberg_covers.sage create mode 100644 heisenberg_covers/heisenberg_form_class.sage create mode 100644 heisenberg_covers/heisenberg_function_class.sage create mode 100644 heisenberg_covers/heisenberg_group_action_matrices.sage create mode 100644 heisenberg_covers/heisenberg_polyforms.sage create mode 100644 heisenberg_covers/heisenberg_reduction.sage create mode 100644 heisenberg_covers/tests/heisenberg_covers_tests.sage create mode 100644 quaternion_covers/quaternion_group_action_matrices.sage create mode 100644 quaternion_covers/quaternion_polyforms.sage diff --git a/README.md b/README.md index 31c6e0c..ca57b95 100644 --- a/README.md +++ b/README.md @@ -160,6 +160,7 @@ print(AS.magical_element()) ## Polydifferential forms on abelian covers For any $(\mathbb Z/p)^n$\-cover as above, one can define a polydifferential form (i.e. a section of $\Omega^{\otimes n}$) as follows: + ``` F = GF(3) Rx. = PolynomialRing(F) @@ -170,7 +171,9 @@ omega = as_polyform(AS.x^5, 3) # the first argument is a function on AS, the sec print(omega) print(omega.expansion_at_infty()) ``` + Using the command *holo_polydifferentials_basis* one may compute the basis of $H^0(\Omega^{\otimes n})$: + ``` p = 5 F = GF(p) @@ -181,6 +184,7 @@ print(AS.holo_polydifferentials_basis(2, threshold = 15)) #we increase the thres ``` The class *as_symmetric_product_forms* may be used to define an element of $\textrm{Sym}^n \, H^0(\Omega_X)$. The command *as_symmetric_power_basis* returns a basis of $\textrm{Sym}^n \, H^0(\Omega_X)$. + ``` p = 5 F = GF(p) @@ -192,7 +196,9 @@ omega = as_symmetric_product_forms([[2, AS.dx, AS.z[0]*AS.dx], [-1, AS.x*AS.dx, print(omega) print(as_symmetric_power_basis(AS, 2)) ``` + The method *canonical_ideal* computes the elements of $\textrm{Sym}^n \, H^0(\Omega_X)$ that are in the kernel of the multiplication map with codomain in $H^0(\Omega_X^{\otimes n})$ (i.e. the n-th homogeneous part of the canonical ideal). The method *canonical_ideal_polynomials* computes the corresponding polynomials and *group_action_canonical_ideal* the matrices of the group action on the n-th homogeneous part of the canonical ideal. + ``` p = 5 F = GF(p) @@ -207,6 +213,7 @@ print(AS.group_action_canonical_ideal(2, threshold = 15)) ## Quaternion covers Some of the above methods are also implemented for quaternion covers in characteristic $2$. Those are defined by the equations $z_0^2 + z_0 = f_0, z_1^2 + z_1 = f_1, z_2^2 + z_2 = f_2 + z_0f_0 + z_1 (f_0 + f_1)$. The arguments of *quaternion_cover* are: the covered superelliptic curve $C$ and the functions $f_0$, $f_1$, $f_2$ on $C$. + ``` p = 2 F. = GF(p^2) diff --git a/as_covers/group_action_matrices.sage b/as_covers/group_action_matrices.sage index 047743e..5e0d846 100644 --- a/as_covers/group_action_matrices.sage +++ b/as_covers/group_action_matrices.sage @@ -1,4 +1,4 @@ -def group_action_matrices(F, space, list_of_group_elements, basis): +def as_group_action_matrices(F, space, list_of_group_elements, basis): n = len(list_of_group_elements) d = len(space) A = [matrix(F, d, d) for i in range(n)] @@ -9,7 +9,7 @@ def group_action_matrices(F, space, list_of_group_elements, basis): A[i][:, j] = vector(v1) return A -def group_action_matrices_holo(AS, basis=0, threshold=10): +def as_group_action_matrices_holo(AS, basis=0, threshold=10): n = AS.height generators = [] for i in range(n): @@ -19,9 +19,11 @@ def group_action_matrices_holo(AS, basis=0, threshold=10): if basis == 0: basis = AS.holomorphic_differentials_basis(threshold=threshold) F = AS.base_ring - return group_action_matrices(F, basis, generators, basis = basis) + return as_group_action_matrices(F, basis, generators, basis = basis) + +as_cover.group_action_matrices_holo = as_group_action_matrices_holo -def group_action_matrices_dR(AS, threshold=8): +def as_group_action_matrices_dR(AS, threshold=8): n = AS.height generators = [] for i in range(n): @@ -33,9 +35,11 @@ def group_action_matrices_dR(AS, threshold=8): dr_basis = AS.de_rham_basis(holo_basis = holo_basis, cohomology_basis = str_basis, threshold=threshold) F = AS.base_ring basis = [holo_basis, str_basis, dr_basis] - return group_action_matrices(F, basis[2], generators, basis = basis) + return as_group_action_matrices(F, basis[2], generators, basis = basis) -def group_action_matrices_log(AS): +as_cover.group_action_matrices_dR = as_group_action_matrices_dR + +def as_group_action_matrices_log_holo(AS): n = AS.height generators = [] for i in range(n): @@ -43,4 +47,6 @@ def group_action_matrices_log(AS): ei[i] = 1 generators += [ei] F = AS.base_ring - return group_action_matrices(F, AS.at_most_poles_forms(1), generators, basis = AS.at_most_poles_forms(1)) \ No newline at end of file + return as_group_action_matrices(F, AS.at_most_poles_forms(1), generators, basis = AS.at_most_poles_forms(1)) + +as_cover.group_action_matrices_log_holo = as_group_action_matrices_log_holo \ No newline at end of file diff --git a/as_covers/tests/group_action_matrices_test.sage b/as_covers/tests/group_action_matrices_test.sage index f5a2a7f..c8fe89f 100644 --- a/as_covers/tests/group_action_matrices_test.sage +++ b/as_covers/tests/group_action_matrices_test.sage @@ -9,7 +9,7 @@ f1 = C_super.x^2*C_super.y f2 = C_super.x^3 AS = as_cover(C_super, [f1, f2], prec=1000) -A, B = group_action_matrices_holo(AS) +A, B = AS.group_action_matrices_holo() n = A.dimensions()[0] print(A*B == B*A) print(A^p == identity_matrix(n)) diff --git a/drafty/draft2.sage b/drafty/draft2.sage index 941d642..9819a93 100644 --- a/drafty/draft2.sage +++ b/drafty/draft2.sage @@ -1,15 +1,10 @@ -p = 5 -#F. = GF(p^2, 'a') -F. = GF(p^8, 'a') -#F = GF(p).algebraic_closure() -#a = F.gen(2) +p = 3 +F = GF(3) Rx. = PolynomialRing(F) -P1 = superelliptic(x, 1) -m = 4 -s = (p^8 - 1)/(p^2 - 1) -b = a^s -C = as_cover(P1, [(P1.x)^(m), b*(P1.x)^(m)], prec=300) +P1 = superelliptic(x^2 + 1, 1) +fct1 = (P1.x)^2 +fct2 = fct1 + (P1.x)/(P1.y - P1.x) +fct3 = (P1.x)^4 +C = heisenberg_cover(P1, [fct1, fct2, fct3], prec=300) print(C) -A, B = group_action_matrices_dR(C) -print('matrices') -print(magma_module_decomposition(A, B, matrices=False)) \ No newline at end of file +a, b, c = heisenberg_group_action_matrices_holo(C) \ No newline at end of file diff --git a/heisenberg_covers/heisenberg_covers.sage b/heisenberg_covers/heisenberg_covers.sage new file mode 100644 index 0000000..78718d6 --- /dev/null +++ b/heisenberg_covers/heisenberg_covers.sage @@ -0,0 +1,435 @@ +class heisenberg_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 = [(a, b, c) for a in range(p) for b in range(p) for c in range(p)] + ######### + 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] - z_series[1])*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 = heisenberg_function(self, x) + self.y = heisenberg_function(self, y) + self.z = [heisenberg_function(self, z[j]) for j in range(n)] + self.dx = heisenberg_form(self, 1) + self.one = heisenberg_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 = "E(p^3)-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'+ "^" + str(p) + ' - z2 = ' + str(self.functions[2]) + ' + ' + '(z0 - z1) * (' + str(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 = heisenberg_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 = heisenberg_holomorphic_combinations(S) + + for pt in self.branch_points[1:]: + forms = [(omega, omega.expansion(pt=pt)) for omega in forms] + forms = heisenberg_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 = heisenberg_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 = heisenberg_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 = heisenberg_function(self, x) + z = [heisenberg_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 = heisenberg_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 = heisenberg_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 = heisenberg_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 += [heisenberg_cech(self, omega, heisenberg_function(self, 0))] + for f in cohomology_basis: + omega = self.lift_to_de_rham(f, threshold = threshold) + result += [heisenberg_cech(self, omega, f)] + return result + +def heisenberg_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 = heisenberg_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/heisenberg_covers/heisenberg_form_class.sage b/heisenberg_covers/heisenberg_form_class.sage new file mode 100644 index 0000000..186d96e --- /dev/null +++ b/heisenberg_covers/heisenberg_form_class.sage @@ -0,0 +1,326 @@ +class heisenberg_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 heisenberg_form(C, g1 + g2) + + def __sub__(self, other): + C = self.curve + g1 = self.form + g2 = other.form + return heisenberg_form(C, g1 - g2) + + def __neg__(self): + C = self.curve + g = self.form + return heisenberg_form(C, -g) + + def __rmul__(self, constant): + C = self.curve + omega = self.form + return heisenberg_form(C, constant*omega) + + def group_action(self, elt): + AS = self.curve + RxyzQ, Rxyz, x, y, z = AS.fct_field + if elt == (1, 0, 0): + sub_list = {x : x, y : y} | {z[0] : z[0] + 1, z[1] : z[1], z[2]: z[2] + z[1]} + g = self.form + return heisenberg_form(AS, g.substitute(sub_list)) + if elt == (0, 1, 0): + sub_list = {x : x, y : y} | {z[0] : z[0] + 1, z[1] : z[1] + 1, z[2]: z[2]} + g = self.form + return heisenberg_form(AS, g.substitute(sub_list)) + if elt == (0, 0, 1): + sub_list = {x : x, y : y} | {z[0] : z[0], z[1] : z[1], z[2]: z[2] - 1} + g = self.form + return heisenberg_form(AS, g.substitute(sub_list)) + + if elt[0] > 0: + elt1 = (elt[0] - 1, elt[1], elt[2]) + return self.group_action(elt1).group_action((1, 0, 0)) + + if elt[1] > 0: + elt1 = (elt[0], elt[1] - 1, elt[2]) + return self.group_action(elt1).group_action((0, 1, 0)) + + if elt[2] > 0: + elt1 = (elt[0], elt[1], elt[2] - 1) + return self.group_action(elt1).group_action((0, 0, 1)) + + 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. + fct = heisenberg_function(C, self.form) + fct = heisenberg_reduction(C, fct) + self = heisenberg_form(C, fct) + 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 = heisenberg_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 = heisenberg_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 heisenberg_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 = heisenberg_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 = heisenberg_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/heisenberg_covers/heisenberg_function_class.sage b/heisenberg_covers/heisenberg_function_class.sage new file mode 100644 index 0000000..a802c91 --- /dev/null +++ b/heisenberg_covers/heisenberg_function_class.sage @@ -0,0 +1,211 @@ +class heisenberg_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 = heisenberg_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 = heisenberg_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 heisenberg_function(C, g1 + g2) + + def __sub__(self, other): + C = self.curve + g1 = self.function + g2 = other.function + return heisenberg_function(C, g1 - g2) + + def __rmul__(self, constant): + C = self.curve + g = self.function + return heisenberg_function(C, constant*g) + + def __neg__(self): + C = self.curve + g = self.function + return heisenberg_function(C, -g) + + def __mul__(self, other): + if isinstance(other, heisenberg_function): + C = self.curve + g1 = self.function + g2 = other.function + return heisenberg_function(C, g1*g2) + if isinstance(other, heisenberg_form): + C = self.curve + g1 = self.function + g2 = other.form + return heisenberg_form(C, g1*g2) + + def __truediv__(self, other): + C = self.curve + g1 = self.function + g2 = other.function + return heisenberg_function(C, g1/g2) + + def __pow__(self, exponent): + C = self.curve + g1 = self.function + return heisenberg_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): + AS = self.curve + RxyzQ, Rxyz, x, y, z = AS.fct_field + if elt == (1, 0, 0): + sub_list = {x : x, y : y} | {z[0] : z[0] + 1, z[1] : z[1], z[2]: z[2] + z[1]} + g = self.function + return heisenberg_function(AS, g.substitute(sub_list)) + if elt == (0, 1, 0): + sub_list = {x : x, y : y} | {z[0] : z[0] + 1, z[1] : z[1] + 1, z[2]: z[2]} + g = self.function + return heisenberg_function(AS, g.substitute(sub_list)) + if elt == (0, 0, 1): + sub_list = {x : x, y : y} | {z[0] : z[0], z[1] : z[1], z[2]: z[2] - 1} + g = self.function + return heisenberg_function(AS, g.substitute(sub_list)) + + if elt[0] > 0: + elt1 = (elt[0] - 1, elt[1], elt[2]) + return self.group_action(elt1).group_action((1, 0, 0)) + + if elt[1] > 0: + elt1 = (elt[0], elt[1] - 1, elt[2]) + return self.group_action(elt1).group_action((0, 1, 0)) + + if elt[2] > 0: + elt1 = (elt[0], elt[1], elt[2] - 1) + return self.group_action(elt1).group_action((0, 0, 1)) + + 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 = heisenberg_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 = heisenberg_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 heisenberg_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/heisenberg_covers/heisenberg_group_action_matrices.sage b/heisenberg_covers/heisenberg_group_action_matrices.sage new file mode 100644 index 0000000..94dd068 --- /dev/null +++ b/heisenberg_covers/heisenberg_group_action_matrices.sage @@ -0,0 +1,40 @@ +def heisenberg_group_action_matrices(F, space, list_of_group_elements, basis): + n = len(list_of_group_elements) + d = len(space) + A = [matrix(F, d, d) for i in range(n)] + for i, g in enumerate(list_of_group_elements): + for j, omega in enumerate(space): + omega1 = omega.group_action(g) + v1 = omega1.coordinates(basis = basis) + A[i][:, j] = vector(v1) + return A + +def heisenberg_group_action_matrices_holo(AS, basis=0, threshold=10): + n = AS.height + generators = [(1, 0, 0), (0, 1, 0), (0, 0, 1)] + if basis == 0: + basis = AS.holomorphic_differentials_basis(threshold=threshold) + F = AS.base_ring + return heisenberg_group_action_matrices(F, basis, generators, basis = basis) + +heisenberg_cover.group_action_matrices_holo = heisenberg_group_action_matrices_holo + +def heisenberg_group_action_matrices_dR(AS, threshold=8): + n = AS.height + generators = [(1, 0, 0), (0, 1, 0), (0, 0, 1)] + holo_basis = AS.holomorphic_differentials_basis(threshold = threshold) + str_basis = AS.cohomology_of_structure_sheaf_basis(holo_basis = holo_basis, threshold = threshold) + dr_basis = AS.de_rham_basis(holo_basis = holo_basis, cohomology_basis = str_basis, threshold=threshold) + F = AS.base_ring + basis = [holo_basis, str_basis, dr_basis] + return heisenberg_group_action_matrices(F, basis[2], generators, basis = basis) + +heisenberg_cover.group_action_matrices_dR = heisenberg_group_action_matrices_dR + +def heisenberg_group_action_matrices_log_holo(AS): + n = AS.height + generators = [(1, 0, 0), (0, 1, 0), (0, 0, 1)] + F = AS.base_ring + return heisenberg_group_action_matrices(F, AS.at_most_poles_forms(1), generators, basis = AS.at_most_poles_forms(1)) + +heisenberg_cover.group_action_matrices_log_holo = heisenberg_group_action_matrices_log_holo \ No newline at end of file diff --git a/heisenberg_covers/heisenberg_polyforms.sage b/heisenberg_covers/heisenberg_polyforms.sage new file mode 100644 index 0000000..90b1e31 --- /dev/null +++ b/heisenberg_covers/heisenberg_polyforms.sage @@ -0,0 +1,209 @@ +class quaternion_polyform: + def __init__(self, form, mult): + self.form = form + self.curve = form.curve + self.mult = mult + + def __add__(self, other): + return quaternion_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.holo_polydifferentials_basis(mult = self.mult) + 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 quaternion_holo_polydifferentials_basis(AS, mult, threshold = 8): + v = AS.dx.valuation() + result = AS.at_most_poles(mult*v, threshold=threshold) + result = [quaternion_polyform(omega, mult) for omega in result] + if mult == 1 and len(result) < AS.genus(): + raise ValueError('Increase threshold, not all forms found.') + if mult > 1 and len(result) < (2*mult - 1)*(AS.genus() - 1): + raise ValueError('Increase threshold, not all forms found.') + return result + +quaternion_cover.holo_polydifferentials_basis = quaternion_holo_polydifferentials_basis + +def quaternion_symmetric_power_basis(AS, n, threshold = 8): + g = AS.genus() + B0 = AS.holomorphic_differentials_basis(threshold=threshold) + from itertools import product + indices = [list(range(g)) for i in range(n)] + indices_nonrepeating = [] + for i in product(*indices): + if non_decreasing(i): + indices_nonrepeating += [i] + result = [] + for i in indices_nonrepeating: + tensor_form = [B0[i[j]] for j in range(n)] + tensor_form = [1] + tensor_form + result += [tensor_form] + result = [quaternion_symmetric_product_forms([a]) for a in result] + return result + +def quaternion_canonical_ideal(AS, n, threshold=8): + B0 = AS.holomorphic_differentials_basis(threshold=threshold) + F = AS.base_ring + g = AS.genus() + RxyzQ, Rxyz, x, y, z = AS.fct_field + from itertools import product + B1 = quaternion_symmetric_power_basis(AS, n, threshold = 8) + B2 = AS.holo_polydifferentials_basis(n, threshold = threshold) + g = AS.genus() + r = len(B2) + M = matrix(F, len(B1), r) + for i in range(0, len(B1)): + c = B1[i].multiply() + #return M, c.coordinates(basis=B2) + M[i, :] = vector(c.coordinates(basis=B2)) + K = M.kernel().basis() + result = [] + for v in K: + kernel_vector = 0*B1[0] + for i in range(len(v)): + kernel_vector += v[i]*B1[i] + result += [kernel_vector] + return result + +quaternion_cover.canonical_ideal = quaternion_canonical_ideal + +def quaternion_canonical_ideal_polynomials(AS, n, threshold=8): + return [a.polynomial() for a in AS.canonical_ideal(n, threshold=threshold)] + +quaternion_cover.canonical_ideal_polynomials = quaternion_canonical_ideal_polynomials + +class quaternion_symmetric_product_forms: + def __init__(self, forms_and_coeffs): + '''Elements of forms_and_coeffs are of the form [coeff, form1, ..., formn]''' + self.n = len(forms_and_coeffs[0]) - 1 + forms_and_coeffs1 = [] + for atuple in forms_and_coeffs: + if atuple[0] != 0 and atuple[1:] not in [a[1:] for a in forms_and_coeffs1]: + forms_and_coeffs1 += [atuple] + elif atuple[1:] in [a[1:] for a in forms_and_coeffs1]: + i = [a[1:] for a in forms_and_coeffs1].index(atuple[1:]) + forms_and_coeffs1[i][0] += atuple[0] + if len(forms_and_coeffs1) == 0: + forms_and_coeffs1 = [[0] + forms_and_coeffs[0][1:]] + self.tuples = forms_and_coeffs1 + self.curve = forms_and_coeffs1[0][1].curve + + def coordinates(self, basis = 0): + AS = self.curve + g = AS.genus() + n = self.n + F = AS.base_ring + if basis == 0: + basis = AS.holomorphic_differentials_basis() + from itertools import product + indices = [list(range(g)) for i in range(self.n)] + result = {i : 0 for i in product(*indices)} + for atuple in self.tuples: + coors = [omega.coordinates(basis = basis) for omega in atuple[1:]] + for i in product(*indices): + aux_product = 1 + for j in range(n): + aux_product *= coors[j][i[j]] + result[i] += atuple[0]*aux_product + return result + + def __repr__(self): + result = '' + for atuple in self.tuples: + if atuple[0] !=0: + result += str(atuple[0]) + ' * ' + for j in range(1, self.n+1): + result += "(" + str(atuple[j]) + ")" + if j != self.n: + result + '⊗' + result += ' + ' + return result + + def __add__(self, other): + return quaternion_symmetric_product_forms(self.tuples + other.tuples) + + def __rmul__(self, other): + AS = self.curve + F = AS.base_ring + if isinstance(other, int) or other in ZZ or other in F: + aux_tuples = [] + for atuple in self.tuples: + atuple1 = [other * atuple[0]] + atuple[1:] + aux_tuples += [atuple1] + return quaternion_symmetric_product_forms(aux_tuples) + + def multiply(self): + n = self.n + AS = self.curve + RxyzQ, Rxyz, x, y, z = AS.fct_field + result = quaternion_polyform(0*AS.x, n) + for atuple in self.tuples: + aux_product = Rxyz(1) + for fct in atuple[1:]: + aux_product = aux_product * fct.form + aux_product = quaternion_function(AS, aux_product) + aux_product = quaternion_reduction(AS, aux_product) + aux_product = quaternion_function(AS, aux_product) + result += quaternion_polyform(atuple[0]*aux_product, n) + return result + + def polynomial(self): + AS = self.curve + F = AS.base_ring + g = AS.genus() + M = self.coordinates() + n = self.n + Rg = PolynomialRing(F, 'X', g) + X = Rg.gens() + from itertools import product + indices = [list(range(g)) for i in range(self.n)] + result = Rg(0) + for i in product(*indices): + aux_product = Rg(1) + for j in range(n): + aux_product *= X[i[j]] + result += M[i] * aux_product + return result + + def group_action(self, elt): + p = self.curve.base_ring.characteristic() + n = self.curve.height + aux_tuples = [] + for atuple in self.tuples: + aux_tuple = [atuple[0]] + [a.group_action(elt) for a in atuple[1:]] + aux_tuples += [aux_tuple] + return quaternion_symmetric_product_forms(aux_tuples) + +def non_decreasing(L): + return all(x<=y for x, y in zip(L, L[1:])) + +def quaternion_matrices_group_action_canonical_ideal(AS, mult, threshold = 8): + K = quaternion_canonical_ideal(AS, mult, threshold = threshold) + n = AS.height + F = AS.base_ring + K_polynomials = [a.polynomial() for a in K] + r = len(K) + matrices = [] + for i in range(2): + Qgens = QuaternionGroup().gens() + M = matrix(F, r, r) + K_group_action_polynomials = [a.group_action(Qgens[i]).polynomial() for a in K] + for i in range(r): + M[i, :] = vector(linear_representation_polynomials(K_group_action_polynomials[i], K_polynomials)) + matrices += [M] + return matrices \ No newline at end of file diff --git a/heisenberg_covers/heisenberg_reduction.sage b/heisenberg_covers/heisenberg_reduction.sage new file mode 100644 index 0000000..8665c2d --- /dev/null +++ b/heisenberg_covers/heisenberg_reduction.sage @@ -0,0 +1,33 @@ +def heisenberg_reduction(AS, fct): + '''Simplify rational function fct as a function in the function field of AS, so that z[i] appear in powers

= GF(p^2) +Rx. = PolynomialRing(F) +C = superelliptic(x, 1) +Q = quaternion_cover(C, [C.x^3, a*C.x^3, 0*C.x], prec = 300) +print(Q.genus() == len(Q.holomorphic_differentials_basis())) +I2 = Q.canonical_ideal_polynomials(2) +R = I2[0].parent() #ring, to which the polynomials belong +J2 = R.ideal(I2) #ideal defined by the set I +print(J2.is_prime(), len(R.gens()) - J2.dimension() - 1) +A, B = quaternion_matrices_group_action_canonical_ideal(Q, 2) +print(A * B == B^3*A, A^2 == B^2, A^4 == identity_matrix(A.dimensions()[0])) \ No newline at end of file diff --git a/init.sage b/init.sage index 4d1acbe..0c84f76 100644 --- a/init.sage +++ b/init.sage @@ -36,6 +36,13 @@ 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') +load('quaternion_covers/quaternion_group_action_matrices.sage') +load('heisenberg_covers/heisenberg_covers.sage') +load('heisenberg_covers/heisenberg_function_class.sage') +load('heisenberg_covers/heisenberg_form_class.sage') +load('heisenberg_covers/heisenberg_polyforms.sage') +load('heisenberg_covers/heisenberg_reduction.sage') +load('heisenberg_covers/heisenberg_group_action_matrices.sage') ############## ############## def init(lista, tests = False, init=True): diff --git a/quaternion_covers/quaternion_group_action_matrices.sage b/quaternion_covers/quaternion_group_action_matrices.sage new file mode 100644 index 0000000..1aff542 --- /dev/null +++ b/quaternion_covers/quaternion_group_action_matrices.sage @@ -0,0 +1,40 @@ +def quaternion_group_action_matrices(F, space, list_of_group_elements, basis): + n = len(list_of_group_elements) + d = len(space) + A = [matrix(F, d, d) for i in range(n)] + for i, g in enumerate(list_of_group_elements): + for j, omega in enumerate(space): + omega1 = omega.group_action(g) + v1 = omega1.coordinates(basis = basis) + A[i][:, j] = vector(v1) + return A + +def quaternion_group_action_matrices_holo(AS, basis=0, threshold=10): + n = AS.height + generators = QuaternionGroup().gens() + if basis == 0: + basis = AS.holomorphic_differentials_basis(threshold=threshold) + F = AS.base_ring + return quaternion_group_action_matrices(F, basis, generators, basis = basis) + +quaternion_cover.group_action_matrices_holo = quaternion_group_action_matrices_holo + +def quaternion_group_action_matrices_dR(AS, threshold=8): + n = AS.height + generators = QuaternionGroup().gens() + holo_basis = AS.holomorphic_differentials_basis(threshold = threshold) + str_basis = AS.cohomology_of_structure_sheaf_basis(holo_basis = holo_basis, threshold = threshold) + dr_basis = AS.de_rham_basis(holo_basis = holo_basis, cohomology_basis = str_basis, threshold=threshold) + F = AS.base_ring + basis = [holo_basis, str_basis, dr_basis] + return quaternion_group_action_matrices(F, basis[2], generators, basis = basis) + +quaternion_cover.group_action_matrices_dR = quaternion_group_action_matrices_dR + +def quaternion_group_action_matrices_log_holo(AS): + n = AS.height + generators = QuaternionGroup().gens() + F = AS.base_ring + return quaternion_group_action_matrices(F, AS.at_most_poles_forms(1), generators, basis = AS.at_most_poles_forms(1)) + +quaternion_cover.group_action_matrices_log_holo = quaternion_group_action_matrices_log_holo \ No newline at end of file diff --git a/quaternion_covers/quaternion_polyforms.sage b/quaternion_covers/quaternion_polyforms.sage new file mode 100644 index 0000000..90b1e31 --- /dev/null +++ b/quaternion_covers/quaternion_polyforms.sage @@ -0,0 +1,209 @@ +class quaternion_polyform: + def __init__(self, form, mult): + self.form = form + self.curve = form.curve + self.mult = mult + + def __add__(self, other): + return quaternion_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.holo_polydifferentials_basis(mult = self.mult) + 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 quaternion_holo_polydifferentials_basis(AS, mult, threshold = 8): + v = AS.dx.valuation() + result = AS.at_most_poles(mult*v, threshold=threshold) + result = [quaternion_polyform(omega, mult) for omega in result] + if mult == 1 and len(result) < AS.genus(): + raise ValueError('Increase threshold, not all forms found.') + if mult > 1 and len(result) < (2*mult - 1)*(AS.genus() - 1): + raise ValueError('Increase threshold, not all forms found.') + return result + +quaternion_cover.holo_polydifferentials_basis = quaternion_holo_polydifferentials_basis + +def quaternion_symmetric_power_basis(AS, n, threshold = 8): + g = AS.genus() + B0 = AS.holomorphic_differentials_basis(threshold=threshold) + from itertools import product + indices = [list(range(g)) for i in range(n)] + indices_nonrepeating = [] + for i in product(*indices): + if non_decreasing(i): + indices_nonrepeating += [i] + result = [] + for i in indices_nonrepeating: + tensor_form = [B0[i[j]] for j in range(n)] + tensor_form = [1] + tensor_form + result += [tensor_form] + result = [quaternion_symmetric_product_forms([a]) for a in result] + return result + +def quaternion_canonical_ideal(AS, n, threshold=8): + B0 = AS.holomorphic_differentials_basis(threshold=threshold) + F = AS.base_ring + g = AS.genus() + RxyzQ, Rxyz, x, y, z = AS.fct_field + from itertools import product + B1 = quaternion_symmetric_power_basis(AS, n, threshold = 8) + B2 = AS.holo_polydifferentials_basis(n, threshold = threshold) + g = AS.genus() + r = len(B2) + M = matrix(F, len(B1), r) + for i in range(0, len(B1)): + c = B1[i].multiply() + #return M, c.coordinates(basis=B2) + M[i, :] = vector(c.coordinates(basis=B2)) + K = M.kernel().basis() + result = [] + for v in K: + kernel_vector = 0*B1[0] + for i in range(len(v)): + kernel_vector += v[i]*B1[i] + result += [kernel_vector] + return result + +quaternion_cover.canonical_ideal = quaternion_canonical_ideal + +def quaternion_canonical_ideal_polynomials(AS, n, threshold=8): + return [a.polynomial() for a in AS.canonical_ideal(n, threshold=threshold)] + +quaternion_cover.canonical_ideal_polynomials = quaternion_canonical_ideal_polynomials + +class quaternion_symmetric_product_forms: + def __init__(self, forms_and_coeffs): + '''Elements of forms_and_coeffs are of the form [coeff, form1, ..., formn]''' + self.n = len(forms_and_coeffs[0]) - 1 + forms_and_coeffs1 = [] + for atuple in forms_and_coeffs: + if atuple[0] != 0 and atuple[1:] not in [a[1:] for a in forms_and_coeffs1]: + forms_and_coeffs1 += [atuple] + elif atuple[1:] in [a[1:] for a in forms_and_coeffs1]: + i = [a[1:] for a in forms_and_coeffs1].index(atuple[1:]) + forms_and_coeffs1[i][0] += atuple[0] + if len(forms_and_coeffs1) == 0: + forms_and_coeffs1 = [[0] + forms_and_coeffs[0][1:]] + self.tuples = forms_and_coeffs1 + self.curve = forms_and_coeffs1[0][1].curve + + def coordinates(self, basis = 0): + AS = self.curve + g = AS.genus() + n = self.n + F = AS.base_ring + if basis == 0: + basis = AS.holomorphic_differentials_basis() + from itertools import product + indices = [list(range(g)) for i in range(self.n)] + result = {i : 0 for i in product(*indices)} + for atuple in self.tuples: + coors = [omega.coordinates(basis = basis) for omega in atuple[1:]] + for i in product(*indices): + aux_product = 1 + for j in range(n): + aux_product *= coors[j][i[j]] + result[i] += atuple[0]*aux_product + return result + + def __repr__(self): + result = '' + for atuple in self.tuples: + if atuple[0] !=0: + result += str(atuple[0]) + ' * ' + for j in range(1, self.n+1): + result += "(" + str(atuple[j]) + ")" + if j != self.n: + result + '⊗' + result += ' + ' + return result + + def __add__(self, other): + return quaternion_symmetric_product_forms(self.tuples + other.tuples) + + def __rmul__(self, other): + AS = self.curve + F = AS.base_ring + if isinstance(other, int) or other in ZZ or other in F: + aux_tuples = [] + for atuple in self.tuples: + atuple1 = [other * atuple[0]] + atuple[1:] + aux_tuples += [atuple1] + return quaternion_symmetric_product_forms(aux_tuples) + + def multiply(self): + n = self.n + AS = self.curve + RxyzQ, Rxyz, x, y, z = AS.fct_field + result = quaternion_polyform(0*AS.x, n) + for atuple in self.tuples: + aux_product = Rxyz(1) + for fct in atuple[1:]: + aux_product = aux_product * fct.form + aux_product = quaternion_function(AS, aux_product) + aux_product = quaternion_reduction(AS, aux_product) + aux_product = quaternion_function(AS, aux_product) + result += quaternion_polyform(atuple[0]*aux_product, n) + return result + + def polynomial(self): + AS = self.curve + F = AS.base_ring + g = AS.genus() + M = self.coordinates() + n = self.n + Rg = PolynomialRing(F, 'X', g) + X = Rg.gens() + from itertools import product + indices = [list(range(g)) for i in range(self.n)] + result = Rg(0) + for i in product(*indices): + aux_product = Rg(1) + for j in range(n): + aux_product *= X[i[j]] + result += M[i] * aux_product + return result + + def group_action(self, elt): + p = self.curve.base_ring.characteristic() + n = self.curve.height + aux_tuples = [] + for atuple in self.tuples: + aux_tuple = [atuple[0]] + [a.group_action(elt) for a in atuple[1:]] + aux_tuples += [aux_tuple] + return quaternion_symmetric_product_forms(aux_tuples) + +def non_decreasing(L): + return all(x<=y for x, y in zip(L, L[1:])) + +def quaternion_matrices_group_action_canonical_ideal(AS, mult, threshold = 8): + K = quaternion_canonical_ideal(AS, mult, threshold = threshold) + n = AS.height + F = AS.base_ring + K_polynomials = [a.polynomial() for a in K] + r = len(K) + matrices = [] + for i in range(2): + Qgens = QuaternionGroup().gens() + M = matrix(F, r, r) + K_group_action_polynomials = [a.group_action(Qgens[i]).polynomial() for a in K] + for i in range(r): + M[i, :] = vector(linear_representation_polynomials(K_group_action_polynomials[i], K_polynomials)) + matrices += [M] + return matrices \ No newline at end of file