def make_function_field(k): r""" Return the function field corresponding to this field. INPUT: - ``k`` -- the residue field of a discrete valuation on a function field. OUTPUT: the field `k` as a function field; this is rather experimental.. """ from sage.rings.function_field.function_field import is_FunctionField if is_FunctionField(k): return k if hasattr(k, "base_field"): # it seems that k is an extension of a rational function field k0 = k.base_field() f0 = FunctionField(k0.base_ring(), k0.base().variable_name()) G = k.modulus().change_ring(f0) # G *should* be irreducible, but unfortunately this is sometimes # not true, due to a bug in Sage's factoring assert G.is_irreducible(), "G must be irreducible! This problem is probably caused by a bug in Sage's factoring." return f0.extension(G, 'y') else: # it seems that k is simply a rational function field return FunctionField(k.base_ring(), k.variable_name())
def __init__(self, f, n, name='y'): R = f.parent() assert R.variable_name() != name, "variable names must be distinct" k = R.base_ring() assert k.characteristic() == 0 or ZZ(n).gcd(k.characteristic( )) == 1, "the characteristic of the base field must be prime to n" ff = f.factor() assert gcd( [m for g, m in ff] + [n]) == 1, "the equation y^n=f(x) must be absolutely irreducible" self._n = n self._f = f self._ff = ff self._name = name FX = FunctionField(k, R.variable_name()) S = PolynomialRing(FX, 'T') T = S.gen() FY = FX.extension(T**n - FX(f), name) self._function_field = FY self._constant_base_field = k self._extra_extension_degree = ZZ(1) self._covering_degree = n self._coordinate_functions = self.coordinate_functions() self._field_of_constants_degree = ZZ(1) self._is_separable = True
def Deltoid(cls, par_type='rational'): r""" Return a deltoid motion. """ if par_type == 'rational': FF = FunctionField(QQ, 't') t = FF.gen() C = { _sage_const_0 : vector((_sage_const_0 , _sage_const_0 )), _sage_const_1 : vector((_sage_const_1 , _sage_const_0 )), _sage_const_2 : vector((_sage_const_4 *(t**_sage_const_2 - _sage_const_2 )/(t**_sage_const_2 + _sage_const_4 ), _sage_const_12 *t/(t**_sage_const_2 + _sage_const_4 ))), _sage_const_3 : vector(((t**_sage_const_4 - _sage_const_13 *t**_sage_const_2 + _sage_const_4 )/(t**_sage_const_4 + _sage_const_5 *t**_sage_const_2 + _sage_const_4 ), _sage_const_6 *(t**_sage_const_3 - _sage_const_2 *t)/(t**_sage_const_4 + _sage_const_5 *t**_sage_const_2 + _sage_const_4 ))) } G = FlexRiGraph([[0, 1], [1, 2], [2, 3], [0, 3]]) return GraphMotion.ParametricMotion(G, C, 'rational', sampling_type='tan', check=False) elif par_type == 'symbolic': t = var('t') C = { _sage_const_0 : vector((_sage_const_0 , _sage_const_0 )), _sage_const_1 : vector((_sage_const_1 , _sage_const_0 )), _sage_const_2 : vector((_sage_const_4 *(t**_sage_const_2 - _sage_const_2 )/(t**_sage_const_2 + _sage_const_4 ), _sage_const_12 *t/(t**_sage_const_2 + _sage_const_4 ))), _sage_const_3 : vector(((t**_sage_const_4 - _sage_const_13 *t**_sage_const_2 + _sage_const_4 )/(t**_sage_const_4 + _sage_const_5 *t**_sage_const_2 + _sage_const_4 ), _sage_const_6 *(t**_sage_const_3 - _sage_const_2 *t)/(t**_sage_const_4 + _sage_const_5 *t**_sage_const_2 + _sage_const_4 ))) } G = FlexRiGraph([[0, 1], [1, 2], [2, 3], [0, 3]]) return ParametricGraphMotion.ParametricMotion(G, C, 'symbolic', sampling_type='tan', check=False) else: raise exceptions.ValueError('Deltoid with par_type ' + str(par_type) + ' is not supported.')
def _test(self): from sage.all import QQ, PolynomialRing, GaussValuation, FunctionField R = PolynomialRing(QQ, 'x') x = R.gen() v = GaussValuation(R, QQ.valuation(2)) K = FunctionField(QQ, 'x') x = K.gen() v = K.valuation(v) K.valuation((v, K.hom(x/2), K.hom(2*x)))
def _test(self): from sage.all import FunctionField, QQ, PolynomialRing K = FunctionField(QQ, 'x') x = K.gen() R = PolynomialRing(K, 'y') y = R.gen() L = K.extension(y**3 - 1 / x**3 * y + 2 / x**4, 'y') v = K.valuation(x) v.extensions(L)
def lower_components(self, u=Infinity): r""" Return the lower components relative to a given extension of the base field. INPUT: - ``u`` -- an integer, or ``Infinity`` (default: ``Infinity``) OUTPUT: the list of lower components of the model of the reduction tree lying over this base component. If `u=\infty` then these components are computed over the splitting field of the base component. Otherwise, `u` is assumed to be a break in the ramification filtration of the splitting field, and then we use the corresponding subfield. The entries of the list correspond to the irreducible components of the special fiber of the `v_L`-model `\mathcal{X}` (the normalization of `\mathcal{X}_0`) lying over the given inertial component. """ if u in self._lower_components.keys(): return self._lower_components[u] # we have already computed this before! L = self.splitting_field() if u == Infinity: vL = L.valuation() else: L = L.ramification_subfield(u) vL = L.valuation() FX = self.berkovich_line().function_field() L = vL.domain() # actually, this is the number field underlying L FXL = FunctionField(L, FX.variable_name()) XL = BerkovichLine(FXL, vL) f, s = self.type_II_point().discoid() f = FXL(f) v0 = self.valuation() F0 = self.function_field() x0 = FXL(v0.lift(v0.residue_field().gen())) k0 = F0.constant_base_field() lower_valuations = [xi.valuation() for xi in XL.points_from_inequality(f, s)] lower_components = [] for v in lower_valuations: F1 = make_function_field(v.residue_field()) # we need to find the correct inclusion of F0 into F1 if k0.is_prime_field(): phi = F0.hom(F1(v.reduce(x0))) else: k1 = F1.constant_base_field() theta0 = FXL(v0.lift(k0.gen())) psi = k0.hom([k1(F1(v.reduce(theta0)))]) phi = F0.hom(F1(v.reduce(x0)), psi) lower_components.append(LowerComponent(self, vL, v, phi)) self._lower_components[u] = lower_components return lower_components
def time_is_semistable(self): K = FunctionField(QQ, 'x') x = K.gen() R = PolynomialRing(K, 'T') T = R.gen() f = 64 * x**3 * T - 64 * x**3 + 36 * x**2 * T**2 + 208 * x**2 * T + 192 * x**2 + 9 * x * T**3 + 72 * x * T**2 + 240 * x * T + 64 * x + T**4 + 9 * T**3 + 52 * T**2 + 48 * T L = K.extension(f, 'y') Y = SmoothProjectiveCurve(L) v = QQ.valuation(13) M = SemistableModel(Y, v) return M.is_semistable()
def _test(self): from sage.all import GF, FunctionField, PolynomialRing k = GF(4) a = k.gen() R = PolynomialRing(k, 'b') b = R.gen() l = k.extension(b**2 + b + a, 'b') K = FunctionField(l, 'x') x = K.gen() R = PolynomialRing(K, 't') t = R.gen() F = t * x F.factor(proof=False)
def _test(self): from sage.all import PolynomialRing, QQ, NumberField, GaussValuation, FunctionField R = PolynomialRing(QQ, 'x') x = R.gen() K = NumberField(x**6 + 126 * x**3 + 126, 'pi') v = K.valuation(2) R = PolynomialRing(K, 'x') x = R.gen() v = GaussValuation(R, v).augmentation(x, QQ(2) / 3) F = FunctionField(K, 'x') x = F.gen() v = F.valuation(v) S = PolynomialRing(F, 'y') y = S.gen() w0 = GaussValuation(S, v) G = y**2 - x**3 - 3 w1 = w0.mac_lane_step(G)[0] w1.mac_lane_step(G)
def __init__(self, Y, vK): p = vK.residue_field().characteristic() assert p == Y.covering_degree() assert isinstance(Y, SuperellipticCurve) f = Y.polynomial() R = f.parent() assert R.base_ring() is vK.domain( ), "the domain of vK must be the base field of f" assert p == vK.residue_field().characteristic( ), "the exponent p must be the residue characteristic of vK" assert not p.divides(f.degree()), "the degree of f must be prime to p" self._p = p self._base_valuation = vK v0 = GaussValuation(R, vK) phi, psi, f1 = v0.monic_integral_model(f) # now f1 = phi(f).monic() if f1 != f.monic(): print( "We make the coordinate change (x --> %s) in order to work with an integral polynomial f" % phi(R.gen())) self._f = f1 a = phi(f).leading_coefficient() pi = vK.uniformizer() m = (vK(a) / p).floor() a = pi**(-p * m) * a self._a = a FX = FunctionField(vK.domain(), R.variable_name()) S = PolynomialRing(FX, 'T') T = S.gen() FY = FX.extension(T**p - FX(a * f1), 'y') self._FX = FX self._FY = FY Y = SmoothProjectiveCurve(FY) self._curve = Y else: self._f = f.monic() self._a = vK.domain().one() self._FY = Y.function_field() self._FX = Y.rational_function_field() self._curve = Y X = BerkovichLine(self._FX, vK) self._X = X
def __init__(self, f, name='y'): R = f.parent() assert R.variable_name() != name, "variable names must be distinct" k = R.base_ring() assert k.characteristic() != 3,\ "the characteristic of the base field must not be 3" assert f.gcd(f.derivative()).is_one(), "f must be separable" self._n = 3 self._f = f self._ff = f.factor() self._name = name FX = FunctionField(k, R.variable_name()) S = PolynomialRing(FX, 'T') T = S.gen() FY = FX.extension(T**3 - FX(f), name) self._function_field = FY self._constant_base_field = k self._extra_extension_degree = ZZ(1) self._covering_degree = 3 self._coordinate_functions = self.coordinate_functions() self._field_of_constants_degree = ZZ(1) self._is_separable = True
def base_change_of_function_field(F, L): r""" Return the base change of a function field with respect to an extension of the base field. INPUT: - ``F`` -- a function field over a field `K` - ``L`` -- a finite field extension of `K` OUTPUT: the function field `F_L:= F\otimes_K L`. It is not checked whether the result is really a function field. """ F0 = F.base() F0L = FunctionField(L, F0.variable_name()) if F0 == F: # F is a rational function field return F0L else: return F0L.extension(F.polynomial().change_ring(F0L), F.variable_name())
def make_function_field(K): r""" Return the function field isomorphic to this field, an isomorphism, and its inverse. INPUT: - ``K`` -- a field OUTPUT: A triple `(F,\phi,\psi)`, where `F` is a rational function field, `\phi:K\to F` is a field isomorphism and `\psi` the inverse of `\phi`. It is assumed that `K` is either the fraction field of a polynomial ring over a finite field `k`, or a finite simple extension of such a field. In the first case, `F=k_1(x)` is a rational function field over a finite field `k_1`, where `k_1` as an *absolute* finite field isomorphic to `k`. In the second case, `F` is a finite simple extension of a rational function field as in the first case. """ from mclf.curves.smooth_projective_curves import make_finite_field from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.categories.function_fields import FunctionFields if hasattr(K, "modulus") or hasattr(K, "polynomial"): # we hope that K is a simple finite extension of a field which is # isomorphic to a rational function field K_base = K.base_field() F_base, phi_base, psi_base = make_function_field(K_base) if hasattr(K, "modulus"): G = K.modulus() else: G = K.polynomial() R = G.parent() R_new = PolynomialRing(F_base, R.variable_name()) G_new = R_new([phi_base(c) for c in G.list()]) assert G_new.is_irreducible(), "G must be irreducible!" # F = F_base.extension(G_new, R.variable_name()) F = F_base.extension(G_new, 'y') # phi0 = R.hom(F.gen(), F) # to construct phi:K=K_0[x]/(G) --> F=F_0[y]/(G), # we first 'map' from K to K_0[x] phi = K.hom(R.gen(), R, check=False) # then from K_0[x] to F_0[y] psi = R.hom(phi_base, R_new) # then from F_0[y] to F = F_0[y]/(G) phi = phi.post_compose(psi.post_compose(R_new.hom(F.gen(), F))) psi = F.hom(K.gen(), psi_base) return F, phi, psi else: # we hope that K is isomorphic to a rational function field over a # finite field if K in FunctionFields(): # K is already a function field k = K.constant_base_field() k_new, phi_base, psi_base = make_finite_field(k) F = FunctionField(k_new, K.variable_name()) phi = K.hom(F.gen(), phi_base) psi = F.hom(K.gen(), psi_base) return F, phi, psi elif hasattr(K, "function_field"): F1 = K.function_field() phi1 = F1.coerce_map_from(K) psi1 = F1.hom(K.gen()) F, phi2, psi2 = make_function_field(F1) phi = phi1.post_compose(phi2) psi = psi2.post_compose(psi1) return F, phi, psi else: raise NotImplementedError
def lower_components(self, u=Infinity): r""" Return the lower components relative to a given extension of the base field. INPUT: - ``u`` -- an integer, or ``Infinity`` (default: ``Infinity``) OUTPUT: the list of lower components of the model of the reduction tree lying over this base component. If `u=\infty` then these components are computed over the splitting field of the base component. Otherwise, `u` is assumed to be a break in the ramification filtration of the splitting field, and then we use the corresponding subfield. The entries of the list correspond to the irreducible components of the special fiber of the `v_L`-model `\mathcal{X}` (the normalization of `\mathcal{X}_0`) lying over the given inertial component. By definition, the constant base field of these components is the residue field of `v_L` (and it may differ from its field of constants). """ if u in self._lower_components.keys(): return self._lower_components[u] # we have already computed this before! L = self.splitting_field() if u == Infinity: vL = L.valuation() else: L = L.ramification_subfield(u) vL = L.valuation() # we construct the base change of the underlying Berkovich line # to L: FX = self.berkovich_line().function_field() L = vL.domain() # actually, this is the number field underlying L FXL = FunctionField(L, FX.variable_name()) # test that FX is a subring of FXL assert FX.is_subring(FXL) # hence there is a natural coercion morphism XL = BerkovichLine(FXL, vL) # the representation of xi as a discoid on X, which is defined # by an inequality v(f) >= s: f, s = self.type_II_point().discoid() # the lower components correspon to the connected components of # the base change to L of the discoid defining the inertial component: f = FXL(f) lower_valuations = [xi.valuation() for xi in XL.points_from_inequality(f, s)] # some preparation: v0 = self.valuation() F0 = self.function_field() x0 = FXL(v0.lift(v0.residue_field().gen())) # x0 is a lift to FXL of the canonical coordinate on the # inertial component; we need it to find the natural map from the # lower components to the inertial component. k0 = F0.constant_base_field() theta0 = FXL(v0.lift(k0.gen())) # now we construct the lower components: lower_components = [] for v in lower_valuations: F1, to_F1, _ = make_function_field(v.residue_field()) # we need to find the correct inclusion phi of F0 into F1 if k0.is_prime_field(): # we don't have to worry about the right embedding of the # constant base field phi = F0.hom(to_F1(v.reduce(x0))) else: k1 = F1.constant_base_field() # we have to be careful about the correct embedding of k0 into k1 phi_base = k0.hom([k1(to_F1(v.reduce(theta0)))]) # now phi is determined by phi_base and the image of the # natural coordinate of F0 phi = F0.hom(to_F1(v.reduce(x0)), phi_base) lower_components.append(LowerComponent(self, vL, v, phi)) self._lower_components[u] = lower_components return lower_components
def make_function_field(K): r""" Return the function field isomorphic to this field, an isomorphism, and its inverse. INPUT: - ``K`` -- a field OUTPUT: A triple `(F,\phi,\psi)`, where `F` is a rational function field, `\phi:K\to F` is a field isomorphism and `\psi` the inverse of `\phi`. It is assumed that `K` is either the fraction field of a polynomial ring over a finite field `k`, or a finite simple extension of such a field. In the first case, `F=k_1(x)` is a rational function field over a finite field `k_1`, where `k_1` as an *absolute* finite field isomorphic to `k`. In the second case, `F` is a finite simple extension of a rational function field as in the first case. .. NOTE:: this command seems to be partly superflous by now, because the residue of a valuation is already of type "function field" whenever this makes sense. However, even if `K` is a function field over a finite field, it is not guaranteed that the constant base field is a 'true' finite field, and then it is important to change that. """ from mclf.curves.smooth_projective_curves import make_finite_field from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.categories.function_fields import FunctionFields from sage.rings.function_field.function_field import is_FunctionField if is_FunctionField(K): k = K.constant_base_field() if k.is_finite() and hasattr(k, "base_field"): # k seems to be finite, but not a true finite field # we construct a true finite field k1 isomorphic to k # and F isomorphic to K with constant base field k1 k1, phi, psi = make_finite_field(k) if hasattr(K, "polynomial"): # K is an extension of a rational function field K0 = K.rational_function_field() F0, phi0, psi0 = make_function_field(K0) f = K.polynomial().change_ring(phi0) F = F0.extension(f) return F, K.hom(F.gen(), phi0), F.hom(K.gen(), psi0) else: F = FunctionField(k1, K.variable_name()) return F, K.hom(F.gen(), phi), F.hom(K.gen(), psi) else: return K, K.Hom(K).identity(), K.Hom(K).identity() if hasattr(K, "modulus") or hasattr(K, "polynomial"): # we hope that K is a simple finite extension of a field which is # isomorphic to a rational function field K_base = K.base_field() F_base, phi_base, psi_base = make_function_field(K_base) if hasattr(K, "modulus"): G = K.modulus() else: G = K.polynomial() R = G.parent() R_new = PolynomialRing(F_base, R.variable_name()) G_new = R_new([phi_base(c) for c in G.list()]) assert G_new.is_irreducible(), "G must be irreducible!" # F = F_base.extension(G_new, R.variable_name()) F = F_base.extension(G_new, 'y') # phi0 = R.hom(F.gen(), F) # to construct phi:K=K_0[x]/(G) --> F=F_0[y]/(G), # we first 'map' from K to K_0[x] phi = K.hom(R.gen(), R, check=False) # then from K_0[x] to F_0[y] psi = R.hom(phi_base, R_new) # then from F_0[y] to F = F_0[y]/(G) phi = phi.post_compose(psi.post_compose(R_new.hom(F.gen(), F))) psi = F.hom(K.gen(), psi_base) return F, phi, psi else: # we hope that K is isomorphic to a rational function field over a # finite field if K in FunctionFields(): # K is already a function field k = K.constant_base_field() k_new, phi_base, psi_base = make_finite_field(k) F = FunctionField(k_new, K.variable_name()) phi = K.hom(F.gen(), phi_base) psi = F.hom(K.gen(), psi_base) return F, phi, psi elif hasattr(K, "function_field"): F1 = K.function_field() phi1 = F1.coerce_map_from(K) psi1 = F1.hom(K.gen()) F, phi2, psi2 = make_function_field(F1) phi = phi1.post_compose(phi2) psi = psi2.post_compose(psi1) return F, phi, psi else: raise NotImplementedError
def _dimension_Gamma_2(wt_range, j, group='Gamma(2)'): """ Return the dict {(k-> partition -> [ d(k), e(k), c(k)] for k in wt_range]}, where d(k), e(k), c(k) are the dimensions of the $p$-canonical part of $M_{k,j}(\Gamma(2))$ and its subspaces of Non-cusp forms and Cusp forms. """ partitions = [ u'6', u'51', u'42', u'411', u'33', u'321', u'3111', u'222', u'2211', u'21111', u'111111' ] latex_names = { 'Gamma(2)': '\\Gamma(2)', 'Gamma0(2)': '\\Gamma_0(2)', 'Gamma1(2)': '\\Gamma_1(2)', 'Sp4(Z)': '\\mathrm{Sp}(4,\mathbb{Z})' } if is_odd(j): dct = dict( (k, dict((h, [0, 0, 0]) for h in partitions)) for k in wt_range) for k in dct: dct[k]['All'] = [0, 0, 0] partitions.insert(0, 'All') return partitions, dct if 'Sp4(Z)' == group and 2 == j and wt_range[0] < 4: wt_range1 = [k for k in wt_range if k < 4] wt_range2 = [k for k in wt_range if k >= 4] if wt_range2 != []: headers, dct = _dimension_Gamma_2(wt_range2, j, group) else: headers, dct = ['Total', 'Non cusp', 'Cusp'], {} for k in wt_range1: dct[k] = dict([(h, 0) for h in headers]) return headers, dct if j >= 2 and wt_range[0] < 4: raise NotImplementedError( "Dimensions of \(M_{k,j}(%s)\) for <span style='color:black'>\(k<4\)</span> and <span style='color:black'>\(j\ge 2\)</span> not implemented" % latex_names.get(group, group)) query = {'sym_power': str(j), 'group': 'Gamma(2)', 'space': 'total'} db_total = smf_db_dimensions().find_one(query) if not db_total: raise NotImplementedError( 'Dimensions of \(M_{k,j}\) for \(j=%d\) not implemented' % j) query['space'] = 'cusp' db_cusp = smf_db_dimensions().find_one(query) if not db_cusp: raise NotImplementedError( 'Dimensions of \(M_{k,j}\) for \(j=%d\) not implemented' % j) P = PowerSeriesRing(ZZ, default_prec=wt_range[-1] + 1, names=('t')) Qt = FunctionField(QQ, names=('t')) total = dict() cusp = dict() for p in partitions: f = Qt(str(db_total[p])) total[p] = P(f.numerator()) / P(f.denominator()) f = Qt(str(db_cusp[p])) cusp[p] = P(f.numerator()) / P(f.denominator()) if 'Gamma(2)' == group: dct = dict( (k, dict((p, [total[p][k], total[p][k] - cusp[p][k], cusp[p][k]]) for p in partitions)) for k in wt_range) for k in dct: dct[k]['All'] = [ sum(dct[k][p][i] for p in dct[k]) for i in range(3) ] partitions.insert(0, 'All') headers = partitions elif 'Gamma1(2)' == group: ps = { '3': ['6', '42', '222'], '21': ['51', '42', '321'], '111': ['411', '33'] } dct = dict((k, dict((p, [ sum(total[q][k] for q in ps[p]), sum(total[q][k] - cusp[q][k] for q in ps[p]), sum(cusp[q][k] for q in ps[p]), ]) for p in ps)) for k in wt_range) for k in dct: dct[k]['All'] = [ sum(dct[k][p][i] for p in dct[k]) for i in range(3) ] headers = ps.keys() headers.sort(reverse=True) headers.insert(0, 'All') elif 'Gamma0(2)' == group: headers = ['Total', 'Non cusp', 'Cusp'] ps = ['6', '42', '222'] dct = dict((k, { 'Total': sum(total[p][k] for p in ps), 'Non cusp': sum(total[p][k] - cusp[p][k] for p in ps), 'Cusp': sum(cusp[p][k] for p in ps) }) for k in wt_range) elif 'Sp4(Z)' == group: headers = ['Total', 'Non cusp', 'Cusp'] p = '6' dct = dict((k, { 'Total': total[p][k], 'Non cusp': total[p][k] - cusp[p][k], 'Cusp': cusp[p][k] }) for k in wt_range) else: raise NotImplementedError('Dimension for %s not implemented' % group) return headers, dct
# Utility functions: # # - pp as generator of function field over QQ # - substitution in ratonal function, star involution # - lists of monic and non-monic polys and forms over any base F # - roots of a polynomial or binary form # - affine linear combinations from sage.all import (FunctionField, QQ, PolynomialRing, ProjectiveSpace, VectorSpace, infinity) from collections import Counter Qp = FunctionField(QQ, 'p') pp = Qp.gen() def subs(f, p): """Substitute p for the variable of the rational function f. """ if f in QQ: return f n = f.numerator()(p) d = f.denominator()(p) if d: return n / d else: return infinity def star(r): return r if r in QQ else subs(r, 1 / pp)