def coerce_ff( self, elt ): ''' Some symbolic methods in sage are not available in the polynomial ring over a number field, but are available in the polynomial ring over a fraction field. INPUT: - "elt" -- String of an symbolic expression with polynomials in "self.pol_ring". OUTPUT: - Elements in a polynomial ring R with generators "self.gens()". The ground field of R is the fraction field of a polynomial ring whose generators corresponds to the roots in the number field "PolyRing.num_field". For example R=FF[x,y,z] where FF=FractionField( QQ[a0,a1,a2,a3] ). ''' eval_dct = {} # construct fraction field FF FF = sage_QQ if PolyRing.num_field != sage_QQ: ngens = PolyRing.num_field.gens_dict().keys() # (a0,a1,...) FF = sage_FractionField( sage_PolynomialRing( sage_QQ, ngens ) ) eval_dct.update( FF.gens_dict() ) # construct polynomial ring R over FF pgens = self.pol_ring.gens_dict().keys() R = sage_PolynomialRing( FF, pgens ) eval_dct.update( R.gens_dict() ) # coerce "elt" to R return sage__eval( str( elt ), eval_dct )
def get_S1xS1_pmz(xyvw_pmz_lst): ''' Attributes ---------- xyvw_pmz_lst: list<sage_POLY> A list of bi-homogeneous polynomials in QQ[x,y;v,w] that represent a parametric map P^1xP^1 ---> P^n. Returns ------- list<sage_POLY> Returns a list of polynomials in QQ[c0,s0,s1,c1] / <c0^2+s0^2-1,c1^2+s1^2-1> that represents the composition of the input parametric map with a birational map: S^1xS^1 ---> P^1xP^1 (1:c0:s0)x(1:c1:s1) |--> (1-s0:c0)x(1-s1:c1) ''' R = sage_PolynomialRing(sage_QQ, 'x,y,v,w') x, y, v, w = R.gens() xyvw_pmz_lst = sage__eval(str(xyvw_pmz_lst), R.gens_dict()) c0, s0, c1, s1 = OrbRing.coerce('c0,s0,c1,s1') sub_dct = {x: 1 - s0, y: c0, v: 1 - s1, w: c1} pmz_lst = [ OrbRing.coerce(xyvw_pmz.subs(sub_dct)) for xyvw_pmz in xyvw_pmz_lst ] return pmz_lst
def set_gens( self, new_gens ): ''' INPUT: - "new_gens" -- set of generators of PolynomialRing. OUTPUT: - Modifies "self.pol_ring" to be a PolynomialRing with generators "new_gens" over the same number field as before. ''' self.pol_ring = sage_PolynomialRing( self.get_num_field(), new_gens ) self.__update_ring_dct()
def __init__( self, var_lst = 'x,y,z', reset_num_field = False ): ''' Constructor. INPUT: - "var_lst" -- A string of characters separated by comma's. The characters represent the generators of a polynomial ring over "Poly_Ring.num_field". - "reset_num_field" -- If True then the base field is reset to QQ. Otherwise, the base field remembers previously added roots. ''' # reset static num_field variable # if reset_num_field: PolyRing.reset_base_field() # Polynomial ring over an algebraic number field. # self.pol_ring = sage_PolynomialRing( PolyRing.num_field, var_lst ) # Usage: sage__eval('<element in pol_ring>', ring_dct) # self.ring_dct = {} # update dictionary # self.ring_dct['t'] = sage_PolynomialRing( PolyRing.num_field, 't' ).gens()[0] self.__update_ring_dct() # Unlike in PARI/GP, class group computations in Sage do not by default # assume the Generalized Riemann Hypothesis. To do class groups computations # not provably correctly we set proof.number_field(False). It can easily take # 1000 times longer to do computations with proof (the default). # sage_proof.number_field( False )
def hilbert_poly(X, base=sage_QQ): ''' Computes the Hilbert polynomial of an ideal. Parameters ---------- X : string(sage_POLY) A polynomial in the variables x=(x0,...,xn) or y=(y0,...,yn). base : sage_RING Ground field of polynomials. Returns ------- sage_POLY Hilbert polynomial of ideal. ''' # make sure that the input are strings X = str(X) # if the input are polynomials in x then the output # is a list of poynomials in y (vx, vy) = ('x', 'y') if 'x' in X else ('y', 'x') # detect the number of x-variables occurring n_lst = [] n = 0 while n < 50: if vx + str(n) in X: n_lst += [n] n = n + 1 n = max(n_lst) x_lst = [vx + str(i) for i in range(n + 1)] ring = sage_PolynomialRing(base, x_lst) x_lst = ring.gens() dct = ring_dict(ring) X = sage__eval(X, dct) return sage_ideal(X).hilbert_polynomial()
def get_implicit_image(ls): ''' INPUT: - "ls" -- LinearSeries object. Elements in "ls.pol_lst" should be homogeneous polynomials over QQ. These polynomials represent a map F between projective spaces. We assume that the polynomials are co-prime. OUTPUT - A list of polynomials in QQ[x0,...,xn]. These polynomials represent the ideal of the image of the map F in projective n-space, where n is "len(ls.pol_lst)-1". This method might not terminate within reasonable time. ''' # QQ[x0,...,xn] vx_lst = ['x' + str(i) for i in range(len(ls.pol_lst))] ring = sage_PolynomialRing(sage_QQ, vx_lst + list(ls.ring.gens())) x_lst = ring.gens()[0:len(vx_lst)] v_lst = ring.gens()[len(vx_lst):] # coerce "ls.pol_lst"" p_lst = [sage__eval(str(pol), ring.gens_dict()) for pol in ls.pol_lst] # construct ideal s_lst = [x_lst[i] - p_lst[i] for i in range(len(p_lst))] s_ideal = ring.ideal(s_lst) LSTools.p(len(s_lst), s_lst) # eliminate all variables except for the xi's e_lst = list(s_ideal.elimination_ideal(v_lst).gens()) LSTools.p(len(e_lst), e_lst) # test dct = {x_lst[i]: p_lst[i] for i in range(len(p_lst))} r_lst = [e.subs(dct) for e in e_lst] LSTools.p('test:', sum(r_lst) == 0) return e_lst
def get_deg_surf(imp_lst, emb_dim): ''' Attributes ---------- imp_lst: list<sage_POLY> A list of polynomials in QQ[x0,...,xn] defining a surface where n==emb_dim. emb_dim: int A non-negative integer, representing the embedding dimension. Returns ------- Degree of surface defined by "imp_lst". ''' ring = sage_PolynomialRing(sage_QQ, ['x' + str(i) for i in range(emb_dim + 1)]) imp_lst = sage__eval(str(imp_lst), ring.gens_dict()) hpol = ring.ideal(imp_lst).hilbert_polynomial() return hpol.diff().diff()
def euclidean_type_form(X, base=sage_QQ): ''' Outputs the equation of a hypersurface in P^n in a form so that the intersection with the hyperplane at infinity becomes apparent. Parameters ---------- X : string(sage_POLY) A polynomial in the variables x=(x0,...,xn) or y=(y0,...,yn). base : sage_RING Ground field of polynomials. Returns ------- string A string of the equations in the normal form X1 + X2 where X1 = sage_factor( X.subs({x0:0}) ) X2 = sage_factor( X-X1 ) If #variables is equal to 3, then also include the form in x,y and z variables. ''' # make sure that the input are strings X = str(X) # if the input are polynomials in x then the output # is a list of poynomials in y (vx, vy) = ('x', 'y') if 'x' in X else ('y', 'x') # detect the number of x-variables occurring n_lst = [] n = 0 while n < 50: if vx + str(n) in X: n_lst += [n] n = n + 1 n = max(n_lst) x_lst = [vx + str(i) for i in range(n + 1)] ring = sage_PolynomialRing(base, x_lst) x_lst = ring.gens() dct = ring_dict(ring) X = sage__eval(X, dct) XA = sage_factor(X.subs({x_lst[0]: 0})) XB = sage_factor(X - XA) assert X == XA + XB OrbTools.p('X =', XA, '+', XB) out = str(XA) + ' + ' + str(XB) if n == 3: x0, x1, x2, x3 = ring.gens() x, y, z = sage_var('x,y,z') W = X.subs({x1: x, x2: y, x3: z}) WA = W.subs({x0: 0}) WB = W - WA assert W == WA + WB WA = sage_factor(WA.subs({x0: 1})) WB = sage_factor(WB.subs({x0: 1})) OrbTools.p('W =', WA, '+', WB) out += '\n' + str(WA) + ' + ' + str(WB) return out
def invert_map(f, X, base=sage_QQ): ''' Computes the inverse of a map defined by polynomials. If the parameters f and X are not strings, then they are automatically converted to strings. Parameters ---------- f : string(list<sage_POLY>) A string of a list of m+1 polynomials in x=(x0,...,xn) or y=(y0,...,yn) with n<=50 that define a projective map: F: X ---> P^m where P^m denotes projective n-space. X : string(list<sage_POLY>) A string of a list of polynomials in the same variables as parameter f that define the ideal of a variety X in projective space. base : sage_RING Ground field of polynomials. Returns ------- list<sage_POLY> Suppose that the input were polynomials in x. The output is of the form [ p0(x)-r0(y) ,..., pn(x)-rn(y) ] were ri(y) are rational functions and pi(x) are polynomials. If f is a birational map, then pi(x)=xi. The polynomials live in the polynomial ring R[x] where R=B(y) and base ring B is defined by the base parameter. ''' # make sure that the input are strings f = str(f) X = str(X) # if the input are polynomials in x then the output # is a list of poynomials in y (vx, vy) = ('x', 'y') if 'x' in f else ('y', 'x') # detect the number of x-variables occurring n_lst = [] n = 0 while n < 50: if vx + str(n) in f or vx + str(n) in X: n_lst += [n] n = n + 1 n = max(n_lst) # construct ring B(y0,...ym)[x0,...,xn] # over fraction field B(y0,...ym) # where m is the number of elements in map f and B equals base # # For example B is rationals sage_QQ so that: # xring = sage_PolynomialRing( sage_QQ, 'x0,x1,x2,x3,x4') # yfield = sage_FractionField( sage_PolynomialRing( sage_QQ, 'y0,y1,y2,y3' ) ) # ring = sage_PolynomialRing( yfield, 'x0,x1,x2,x3,x4') # x_lst = [vx + str(i) for i in range(n + 1)] y_lst = [vy + str(i) for i in range(len(f.split(',')))] yfield = sage_FractionField(sage_PolynomialRing(base, y_lst)) ring = sage_PolynomialRing(yfield, x_lst) y_lst = yfield.gens() x_lst = ring.gens() dct = ring_dict(ring) X = sage__eval(X, dct) f = sage__eval(f, dct) map = [y_lst[i] - f[i] for i in range(len(f))] gb_lst = list(sage_ideal(X + map).groebner_basis()) OrbTools.p(gb_lst) return gb_lst
def compose_maps(g, f, base=sage_QQ): ''' Computes the composition of polynomial maps. Both parameters and return value are all polynomials in either x=(x0,...,xn) or y=(y0,...,yn) with n<=50, but not both. The input parameters are explicitly converted to strings, in case they are not strings. Parameters ---------- f : string(list<sage_POLY>) A string of a list of b+1 polynomials that defines a projective map: f: P^a ---> P^b where P^a denotes projective n-space. If the value is not a string, then it will be converted to a string. g : string(list<sage_POLY>) A string of a list of c+1 polynomials that defines a projective map: g: P^b ---> P^c. If the value is not a string, then it will be converted to a string. base : sage_RING Ground field of polynomials. Returns ------- list<sage_POLY> The composition of the maps f o g: P^a ---> P^c. ''' # make sure that the input are strings g = str(g) f = str(f) # check variables (vf, vg) = ('x', 'y') if 'x' in f else ('y', 'x') if vg not in g: g = g.replace(vf, vg) # detect the number of vf-variables occurring n_lst = [] n = 0 while n < 50: if vf + str(n) in f or vg + str(n) in g: n_lst += [n] n = n + 1 n = max(n_lst) # construct the ring v_lst = [] v_lst += [vf + str(i) for i in range(n + 1)] v_lst += [vg + str(i) for i in range(n + 1)] ring = sage_PolynomialRing(base, v_lst) vg_lst = ring.gens()[n + 1:] dct = ring_dict(ring) g_lst = sage__eval(g, dct) f_lst = sage__eval(f, dct) # compose the maps for i in range(len(f_lst)): g_lst = [g.subs({vg_lst[i]: f_lst[i]}) for g in g_lst] OrbTools.p(g_lst) return g_lst
def preimage_map(f, X, Y, base=sage_QQ): ''' Computes the preimage f^{-1}(Y) of a variety Y under a map f: X ---> P^m, defined by polynomials, where the domain X is a variety and P^m denotes projective space. If the parameters f, X and Y are not strings, then they are automatically converted to strings. Parameters ---------- f : string(list<sage_POLY>) A string of a list of m+1 polynomials in x=(x0,...,xn) or y=(y0,...,yn) with n<=50 that define a projective map: f: X ---> P^m where P^m denotes projective n-space. X : string(list<sage_POLY>) A string of a list of polynomials in the same variables as parameter f that define the ideal of a variety X in projective space. Y : string(list<sage_POLY>) A string of a list of polynomials in y if f consists of polynomials in x (and vice versa). Polynomials define the generators of an ideal of a variety Y in projective space. base : sage_RING Ground field of polynomials. Returns ------- list(list<sage_POLY>) A list of lists of polynomials. Each list of polynomials defines a component in the primary decomposition of the ideal of the preimage f^{-1}(Y). The polynomials are in x or y if parameter f represents polynomials in x respectively y. Note that some of the components in the primary decomposition are not part of the ideal, but correspond to the locus where the map f is not defined. ''' # make sure that the input are strings f = str(f) X = str(X) Y = str(Y) # if the input are polynomials in x then the output # is a list of poynomials in y (vx, vy) = ('x', 'y') if 'x' in f else ('y', 'x') # detect the number of x-variables occurring n_lst = [] n = 0 while n < 50: if vx + str(n) in f or vx + str(n) in X: n_lst += [n] n = n + 1 n = max(n_lst) # construct polynomial ring B[x0,...,xn,y0,...,ym] # where B is given by parameter base. x_lst = [vx + str(i) for i in range(n + 1)] y_lst = [vy + str(i) for i in range(len(f.split(',')))] mord = 'degrevlex' if base == sage_QQ else 'lex' # needed for elimination xyring = sage_PolynomialRing(base, y_lst + x_lst, order=mord) y_lst = xyring.gens()[:len(y_lst)] x_lst = xyring.gens()[len(y_lst):] # coerce into common ring with xi and yi variables dct = ring_dict(xyring) f_lst = sage__eval(f, dct) X_lst = sage__eval(X, dct) Y_lst = sage__eval(Y, dct) mf_lst = [y_lst[i] - f_lst[i] for i in range(len(f_lst))] # compute image by using groebner basis OrbTools.p(X_lst + mf_lst + Y_lst) try: img_id = sage_ideal(X_lst + mf_lst + Y_lst).elimination_ideal(y_lst) img_lst = img_id.primary_decomposition() img_lst = [img.gens() for img in img_lst] OrbTools.p(img_lst) return img_lst except Exception, e: OrbTools.p('Exception occurred:', repr(e)) gb_lst = sage_ideal(X_lst + mf_lst + Y_lst).groebner_basis() OrbTools.p(gb_lst) e_lst = [] for gb in gb_lst: if vy not in str(gb): e_lst += [gb] return e_lst
def usecase__linear_normalization__and__adjoint(): ''' In this usecase are some applications of the "linear_series" library. we show by example how to compute a linear normalization X of a surface Y, or equivalently, how to compute the completion of a linear series. Also we contruct an example of a surface Z such that X is its adjoint surface. ''' # # Linear series corresponding to the parametrization # of a cubic surface X in P^4 that is the projection # of the Veronese embedding of P^2 into P^5. # ring = PolyRing('x,y,z', True) bp_tree = BasePointTree() bp = bp_tree.add('z', (0, 0), 1) ls = LinearSeries.get([2], bp_tree) imp_lst = ls.get_implicit_image() LSTools.p('linear series =', ls.get_bp_tree()) LSTools.p('implicit image =', imp_lst) # # compute Hilbert polynomial of X in QQ[x0,...,x6] # R = sage_PolynomialRing(sage_QQ, ['x' + str(i) for i in range(len(ls.pol_lst))]) x_lst = R.gens() imp_lst = sage__eval(str(imp_lst), R.gens_dict()) hpol = R.ideal(imp_lst).hilbert_polynomial() hdeg = hpol.diff().diff() LSTools.p('Hilbert polynomial =', hpol) LSTools.p('implicit degree =', hdeg) # # projection of X in P^4 to Y in P^3, where Y is singular. # ls = LinearSeries(['x^2-x*y', 'x*z', 'y^2', 'y*z'], PolyRing('x,y,z', True)) bp_tree = ls.get_bp_tree() LSTools.p('basepoint tree of projection =', bp_tree) eqn = ls.get_implicit_image() assert len(eqn) == 1 eqn = sage_SR(eqn[0]) x0, x1, x2, x3 = sage_var('x0,x1,x2,x3') LSTools.p('eqn =', eqn) LSTools.p('D(eqn,x0) =', sage_diff(eqn, x0)) LSTools.p('D(eqn,x1) =', sage_diff(eqn, x1)) LSTools.p('D(eqn,x2) =', sage_diff(eqn, x2)) LSTools.p('D(eqn,x3) =', sage_diff(eqn, x3)) # # compute normalization X of Y # ls_norm = LinearSeries.get([2], bp_tree) LSTools.p('normalization = ', ls_norm) LSTools.p(' = ', ls_norm.get_implicit_image()) # # define pre-adjoint surface Z # ring = PolyRing('x,y,z', True) bp_tree = BasePointTree() bp_tree.add('z', (0, 0), 2) bp_tree.add('z', (0, 1), 1) ls = LinearSeries.get([5], bp_tree) LSTools.p(ls.get_bp_tree())
class OrbRing: num_field = sage_QQ vstr = '' vstr += 'x0,x1,x2,x3,x4,x5,x6,x7,x8,' vstr += 'v0,v1,v2,v3,v4,v5,v6,v7,v8,' vstr += 'c0,s0,c1,s1,' vstr += 't0,t1,t2,t3,t4,t5,t6,t7' R = sage_PolynomialRing(num_field, sage_var(vstr), order='degrevlex') @staticmethod def coerce(expr): return sage__eval(str(expr), OrbRing.R.gens_dict()) @staticmethod def random_int(val): ''' INPUT: - "val" -- An integer. OUTPUT: - A random element in the interval [-val,val] ''' return int(sage_ZZ.random_element(-val, val + 1)) @staticmethod def random_elt(lst): ''' INPUT: - "lst" -- A list. OUTPUT: - A random element in "lst". ''' idx = int(sage_ZZ.random_element(0, len(lst))) return lst[idx] @staticmethod def approx_QQ_coef(cf, ci_idx=0): ''' Parameters ---------- cf : sage_NUMBERFIELD Element of a number field. For example a coefficient of a polynomial in linear_series.PolyRing. ci_idx : int An integer specifying the complex embedding of the numberfield into the complex numbers. Returns ------- sage_QQ A rational approximation of this coefficient. ''' if cf in sage_QQ: return cf if type(cf) == int: return sage_QQ(cf) ncf = cf.complex_embeddings()[ci_idx].real_part().exact_rational() return ncf @staticmethod def approx_QQ_pol_lst(pol_lst, ci_idx=0): ''' Parameters ---------- pol_lst : list<sage_POLY> A list of polynomials with coefficients defined over a number field with a norm (thus the elements have an absolute value). ci_idx : int An integer specifying the complex embedding of the numberfield into the complex numbers. Returns ------- list<sage_POLY> An approximation of the polynomial with coefficients defined over sage_QQ. ''' out_lst = [] for pol in pol_lst: if pol in sage_QQ: out_lst += [pol] elif type(pol) == int: out_lst += [sage_QQ(pol)] elif 'NumberFieldElement' in str(type(pol)): out_lst += [OrbRing.approx_QQ_coef(pol, ci_idx)] else: out_lst += [ pol.map_coefficients( lambda cf: OrbRing.approx_QQ_coef(cf, ci_idx)) ] return out_lst
def get_implicit_projection(ls, deg): ''' This function does not work properly, since the output polynomial still contains undeterminate variables. These need to be solved. INPUT: - "ls" -- LinearSeries s.t. "ls.pol_lst" consist of polynomials in x,y,z and of the same degree. Note that the polynomials in "ls.pol_lst" define a birational map from the projective plane to a surface S in projective n-space where n equals "len(ls.pol_lst)-1". - "deg" -- An integer representing the degree of the parametrized surface S. OUTPUT: - A polynomial F(x0:x1:x2:x3) of degree at most "deg" and undetermined coefficients in (r0,r1,...). The ".parent()" of the polynomial is "SymbolicRing". The zero-set V(F) is a projection of the surface S by the map (x0:x1:...:xn) |-> (x0:x1:x2:x3) ''' p0, p1, p2, p3 = ls.pol_lst[0:4] # construct a polynomial ring c_len = len(sage_Compositions(deg + 4, length=4).list()) c_str_lst = ['c' + str(i) for i in range(c_len)] R = sage_PolynomialRing(PolyRing.num_field, ['x0', 'x1', 'x2', 'x3'] + c_str_lst + ['x', 'y', 'z'], order='lex') x0, x1, x2, x3 = R.gens()[0:4] c_lst = R.gens()[4:4 + c_len] x, y, z = R.gens()[4 + c_len:] m_lst = [] for a, b, c, d in sage_Compositions(deg + 4, length=4): m_lst += [x0**(a - 1) * x1**(b - 1) * x2**(c - 1) * x3**(d - 1)] R_dict = R.gens_dict() R_dict.update(PolyRing.num_field.gens_dict()) p0, p1, p2, p3 = sage__eval(str([p0, p1, p2, p3]), R_dict) LSTools.p(R) LSTools.p('m_lst =', m_lst) LSTools.p('(p0, p1, p2, p3) =', (p0, p1, p2, p3)) # construct polynomial in x0, x1, x2 with coefficients in c_lst F = 0 for i in range(len(m_lst)): F += c_lst[i] * m_lst[i] LSTools.p('F =', F) # compute F( p0(x,y,z):...: p3(x,y,z) ) FP = sage_expand(F.subs({ x0: p0, x1: p1, x2: p2, x3: p3 })) # expand to prevent a bug in sage. LSTools.p('FP =', FP) # obtain coefficients w.r.t. x, y and z coef_lst = [] pmzdeg = p0.total_degree() comp_lst = sage_Compositions(deg * pmzdeg + 3, length=3).list() # e.g. [1,1,2]=[0,0,1] LSTools.p('comp_lst =', comp_lst) for comp in comp_lst: coef = FP.coefficient({x: comp[0] - 1, y: comp[1] - 1, z: comp[2] - 1}) coef_lst += [coef] LSTools.p('coef_lst =', coef_lst) # compute groebner basis and reduce F w.r.t. the GB # # alternative code # ---------------- # GB = list( R.ideal( coef_lst ).groebner_basis() ) # LSTools.p( 'GB =', GB ) # red_F = F.reduce( R.ideal( GB ) ) # for g in GB: # red_F = red_F.quo_rem( g )[1] # LSTools.p( 'red_F =', red_F ) # ---------------- # scoef_lst = [sage_SR(coef) for coef in coef_lst] sc_lst = [sage_SR(c) for c in c_lst] sol_lst = sage_solve(scoef_lst, sc_lst, solution_dict=True) LSTools.p(sol_lst) red_F = sage_expand(sage_SR(F).subs(sol_lst[0])) LSTools.p('red_F =', red_F) # check the solutions sp0, sp1, sp2, sp3 = sage_SR(p0), sage_SR(p1), sage_SR(p2), sage_SR(p3) sx0, sx1, sx2, sx3 = sage_SR(x0), sage_SR(x1), sage_SR(x2), sage_SR(x3) chk_F = sage_expand(red_F.subs({sx0: sp0, sx1: sp1, sx2: sp2, sx3: sp3})) LSTools.p('chk_F =', chk_F) if chk_F != 0: warnings.warn('The polynomial red_F(p0:p1:p2:p3) does not vanish.') return red_F
def get_surf(ls, sig, coef_lst=None, prv_Q=None): ''' Constructs a surface S in a quadric with given signature. This surface is a projection of the surface that is parametrized by a map associated to a given linear series. Attributes ---------- ls: LinearSeries LinearSeries on P^1xP^1 such that its associated map parametrizes a surface S in projective n-space P^n. sig: tuple<int> A 2-tuple of positive integers (a,b). coef_lst: list<QQ> Either None or a list of coefficients over QQ of length equal to the number of generators for the ideal of S. prv_Q: sage_matrix<QQ> Either None or an full rank (m+1)*(n+1) matrix over the rationals QQ. Returns ------- dict Returns a dictionary: { 'Q': A (m+1)*(n+1) matrix over the rationals QQ corresponding to a linear projection of S in P^n to P^m where m==sum(sig)-1. Thus a quadric with signature "sig" is a hyper-quadric in projective m-space. If "prv_Q" is not equal None then Q is set to "prv_Q". 'pmz_lst': A list of m+1 polynomials in QQ[c0,s0,c1,s1]/<c0^2+s0^1-1,c1^2+s1^2-1> which represent the birational/parametric map S^1xS^1 ---> Q(S) < P^m. 'imp_lst': A list of polynomials in QQ[x0,x1,...,xm] which represent the ideal of the surface Q(S) in P^m. 'M': The symmetric matrix M associated to an hyperquadric that contains the surface image Q(S) under the linear transformation Q, or None if no such hyperquadric exists. If "coef_lst" is None then M is computed randomly and this is the default. The ideal for Q(S) is internally represented by an ordered list of polynomials in Q[x0,...,xn]. The sum of generators with given coefficients in "coef_lst" should be the equation of a quadric with signature "sig". 'UJ': A tuple (U,J) such that M == U.T*J*U and J is a diagonal matrix with entries in [0,1,-1] and signature "sig" (ie. an orthogonal diagonalization of M). The matrices U and J are defined over QQbar. The diagonal of J contains first all -1 and then all +1 values (thus the order of the 2-tuple "sig" does matter). } ''' sig_set = set([]) cur_sig = () OrbTools.p('Computing random quadrics in ideal...') while cur_sig != sig: # compute random projection of prv_Q==None # prj_dim Q, pmz_lst, imp_lst = rand_surf_prj(ls, sum(sig) - 1, prv_Q) # set coefficient list # if coef_lst != None: c_lst = coef_lst else: # c_lst = [ OrbRing.random_elt( [-1, 0, 1] ) for imp in imp_lst ] c_lst = [OrbRing.random_int(10) for imp in imp_lst] # obtain quadric in ideal from "c_lst" # i_lst = range(len(imp_lst)) M_pol = [ c_lst[i] * imp_lst[i] for i in i_lst if imp_lst[i].total_degree() == 2 ] M_pol = sum(M_pol) if M_pol == 0: # No quadratic forms in random projection. Possibly signature sig does not exists. continue # eigendecomposition (M*V == V*D) # ring = sage_PolynomialRing(sage_QQ, ['x' + str(i) for i in range(sum(sig))]) x_lst = ring.gens() M = sage_invariant_theory.quadratic_form( M_pol, x_lst).as_QuadraticForm().matrix() M = sage_matrix(sage_QQ, M) D, V = M.eigenmatrix_right( ) # D has first all negative values on diagonal # determine signature of quadric # num_neg = len([d for d in D.diagonal() if d < 0]) num_pos = len([d for d in D.diagonal() if d > 0]) cur_sig = (num_neg, num_pos) if cur_sig not in sig_set: sig_set.add(cur_sig) OrbTools.p('\t\t sig =', sig, ', sig_set =', sig_set) OrbTools.p('Q =', list(Q)) OrbTools.p('pmz_lst =', pmz_lst) OrbTools.p('imp_lst =', imp_lst) OrbTools.p('c_lst =', c_lst) OrbTools.p('M_pol =', M_pol) # diagonal orthonormalization # # M == V*D*~V == W.T*D*W == U.T*J*U # where U == L*W and D == L.T*J*L # D, V = M.eigenmatrix_right() # M*V==V*D W = sage_matrix([col / col.norm() for col in V.columns()]) # transpose and norm L = sage_diagonal_matrix([d.abs().sqrt() for d in D.diagonal()]) J = sage_diagonal_matrix([d / abs(d) for d in D.diagonal()]) U = L * W OrbTools.p('M =', list(M)) OrbTools.p('U =', list(U)) OrbTools.p('J diag. =', J.diagonal()) # Return output # dct = {} dct['Q'] = Q dct['pmz_lst'] = pmz_lst dct['imp_lst'] = imp_lst dct['M'] = M dct['UJ'] = (U, J) return dct
def usecase__neron_severi_lattice(): ''' Compute NS-lattice of a linear series and the dimension of complete linear with base points. ''' # Blowup of projective plane in 3 colinear points # and 2 infinitly near points. The image of the # map associated to the linear series is a quartic # del pezzo surface with 5 families of conics. Moreover # the surface contains 8 straight lines. # ring = PolyRing('x,y,z', True) p1 = (-1, 0) p2 = (0, 0) p3 = (1, 0) p4 = (0, 1) p5 = (2, 0) bp_tree = BasePointTree() bp_tree.add('z', p1, 1) bp_tree.add('z', p2, 1) bp_tree.add('z', p3, 1) bp = bp_tree.add('z', p4, 1) bp.add('t', p5, 1) ls = LinearSeries.get([3], bp_tree) LSTools.p('ls = ', ls) LSTools.p(ls.get_bp_tree(), '\n\n ', ls.get_implicit_image()) # Detects that 3 base points lie on a line. # bp_tree = BasePointTree() bp_tree.add('z', p1, 1) bp_tree.add('z', p2, 1) bp_tree.add('z', p3, 1) ls123 = LinearSeries.get([1], bp_tree) LSTools.p('ls123 =', ls123) assert ls123.pol_lst == ring.coerce('[y]') # example of infinitly near base points # that is colinear with another simple base point. # bp_tree = BasePointTree() bp_tree.add('z', p1, 1) bp = bp_tree.add('z', p4, 1) bp.add('t', (1, 0), 1) ls1 = LinearSeries.get([1], bp_tree) LSTools.p('ls1 =', ls1) assert ls1.pol_lst == ring.coerce('[x-y+z]') # Detects that an infinitly near base points # is not colinear with other point. # for p in [p1, p2, p3]: bp_tree = BasePointTree() bp = bp_tree.add('z', p4, 1) bp.add('t', p5, 1) bp_tree.add('z', p, 1) ls45i = LinearSeries.get([1], bp_tree) assert ls45i.pol_lst == [] # line with predefined tangent # bp_tree = BasePointTree() bp = bp_tree.add('z', p4, 1) bp.add('t', p5, 1) ls45 = LinearSeries.get([1], bp_tree) LSTools.p('ls45 =', ls45) assert ls45.pol_lst == ring.coerce('[x-2*y+2*z]') # class of conics through 5 basepoints # bp_tree = BasePointTree() bp_tree.add('z', p1, 1) bp_tree.add('z', p2, 1) bp_tree.add('z', p3, 1) bp = bp_tree.add('z', p4, 1) bp.add('t', p5, 1) ls1234 = LinearSeries.get([2], bp_tree) LSTools.p('ls1234 =', ls1234) LSTools.p('\t\t', sage_factor(ls1234.pol_lst[0])) assert ring.coerce('(x - 2*y + 2*z, 1)') in ring.aux_gcd(ls1234.pol_lst)[0] assert ring.coerce('(y, 1)') in ring.aux_gcd(ls1234.pol_lst)[0] # class of conics through 4 basepoints # has a fixed component # bp_tree = BasePointTree() bp_tree.add('z', p4, 1) ls4 = LinearSeries.get([1], bp_tree) LSTools.p('ls4 =', ls4) bp_tree = BasePointTree() bp_tree.add('z', p1, 1) bp_tree.add('z', p2, 1) bp_tree.add('z', p3, 1) bp_tree.add('z', p4, 1) ls1234 = LinearSeries.get([2], bp_tree) LSTools.p('ls1234 =', ls1234) # return # Compose map associated to linear series "ls" # with the inverse stereographic projection "Pinv". # R = sage_PolynomialRing(sage_QQ, 'y0,y1,y2,y3,y4,x,y,z') y0, y1, y2, y3, y4, x, y, z = R.gens() delta = y1**2 + y2**2 + y3**2 + y4**2 Pinv = [ y0**2 + delta, 2 * y0 * y1, 2 * y0 * y2, 2 * y0 * y3, 2 * y0 * y4, -y0**2 + delta ] H = sage__eval(str(ls.pol_lst), R.gens_dict()) PinvH = [ elt.subs({ y0: H[0], y1: H[1], y2: H[2], y3: H[3], y4: H[4] }) for elt in Pinv ] LSTools.p('Pinv =', Pinv) LSTools.p('H =', H) LSTools.p('Pinv o H =', PinvH) # In order to compute the NS-lattice of the image # of "PinvH" we do a base point analysis. # ls_PinvH = LinearSeries([str(elt) for elt in PinvH], ring) LSTools.p('ls_PinvH =', ls_PinvH) LSTools.p('\t\t', ls_PinvH.get_implicit_image()) if False: # takes a long time LSTools.p(ls_PinvH.get_bp_tree())
def image_map(f, X, base=sage_QQ): ''' Computes the image f(X) of a variety X under a map f, defined by polynomials. If the parameters f and X are not strings, then they are automatically converted to strings. Parameters ---------- f : string(list<sage_POLY>) A string of a list of m+1 polynomials in x=(x0,...,xn) or y=(y0,...,yn) with n<=50 that define a projective map: f: X ---> P^m where P^m denotes projective n-space. X : string(list<sage_POLY>) A string of a list of polynomials in the same variables as parameter f that define the ideal of a variety X in projective space. base : sage_RING Ground field of polynomials. Returns ------- list<sage_POLY> A list of polynomials that define the ideal of f(X). The polynomials are in y if the input are polynomials in x, and vice versa. ''' # make sure that the input are strings f = str(f) X = str(X) # if the input are polynomials in x then the output # is a list of poynomials in y (vx, vy) = ('x', 'y') if 'x' in f else ('y', 'x') # detect the number of x-variables occurring n_lst = [] n = 0 while n < 50: if vx + str(n) in f or vx + str(n) in X: n_lst += [n] n = n + 1 n = max(n_lst) # construct polynomial ring B[x0,...,xn,y0,...,ym] # where B is given by parameter base. x_lst = [vx + str(i) for i in range(n + 1)] y_lst = [vy + str(i) for i in range(len(f.split(',')))] mord = 'degrevlex' if base == sage_QQ else 'lexdeg' # needed for elimination xyring = sage_PolynomialRing(base, x_lst + y_lst, mord) x_lst = xyring.gens()[:len(x_lst)] y_lst = xyring.gens()[len(x_lst):] # coerce into common ring with xi and yi variables dct = ring_dict(xyring) f_lst = sage__eval(f, dct) X_lst = sage__eval(X, dct) mf_lst = [y_lst[i] - f_lst[i] for i in range(len(f_lst))] # compute image by using groebner basis OrbTools.p(X_lst + mf_lst) try: # compute image by using groebner basis img_lst = sage_ideal(X_lst + mf_lst).elimination_ideal(x_lst).gens() OrbTools.p(img_lst) return img_lst except Exception, e: OrbTools.p('Exception occurred:', repr(e)) gb_lst = sage_ideal(X_lst + mf_lst).groebner_basis() OrbTools.p(gb_lst) e_lst = [] for gb in gb_lst: if vx not in str(gb): e_lst += [gb] return e_lst
def usecase__get_implicit__DP6(): ''' Construct linear series of curves in P^1xP^1, with a given tree of (infinitely near) base points. The defining polynomials of this linear series, correspond to a parametric map of an anticanonical model of a Del Pezzo surface of degree 6 in P^6. Its ideal is generated by quadratic forms. We search for a quadratic form in this ideal of signature (1,6). This quadratic form corresponds to a hyperquadric in P^6, such that the sextic Del Pezzo surface is contained in this hyperquadric. We construct a real projective isomorphism from this hyperquadric to the projectivization of the unit 5-sphere in P^6. ''' # # parametrization of degree 6 del Pezzo surface in projective 6-space. # See ".usecase__get_linear_series__P1P1_DP6()" for the construction of # this linear series. # pmz_lst = [ 'x^2*v^2 - y^2*w^2', 'x^2*v*w + y^2*v*w', 'x^2*w^2 + y^2*w^2', 'x*y*v^2 - y^2*v*w', 'x*y*v*w - y^2*w^2', 'y^2*v*w + x*y*w^2', 'y^2*v^2 + y^2*w^2' ] ls = LinearSeries(pmz_lst, PolyRing('x,y,v,w', True)) # # base points of linear series # bp_tree = ls.get_bp_tree() LSTools.p('parametrization =', ls.pol_lst) LSTools.p('base points =' + str(bp_tree)) # # implicit image in projective 6-space # of map associated to linear series # imp_lst = ls.get_implicit_image() LSTools.p('implicit equations =', imp_lst) # # compute Hilbert polynomial in QQ[x0,...,x6] # ring = sage_PolynomialRing(sage_QQ, ['x' + str(i) for i in range(7)]) x_lst = ring.gens() imp_lst = sage__eval(str(imp_lst), ring.gens_dict()) hpol = ring.ideal(imp_lst).hilbert_polynomial() hdeg = hpol.diff().diff() LSTools.p('Hilbert polynomial =', hpol) LSTools.p('implicit degree =', hdeg) # # equation of unit sphere is not in the ideal # s_pol = sum([-x_lst[0]**2] + [x**2 for x in x_lst[1:]]) LSTools.p('Inside sphere?: ', s_pol in ring.ideal(imp_lst)) # # compute random quadrics containing del Pezzo surface # until a quadric with signature (1,6) is found # or set a precomputed quadric with given "c_lst" # LSTools.p( 'Look for quadric in ideal of signature (1,6)...(may take a while)...') sig = [] while sorted(sig) != [0, 1, 6]: # set coefficient list c_lst = [] c_lst = [-1, -1, 0, 0, 0, -1, 1, 0, -1, -1, -1] # uncomment to speed up execution if c_lst == []: lst = [-1, 0, 1] for imp in imp_lst: idx = int(sage_ZZ.random_element(0, len(lst))) c_lst += [lst[idx]] # obtain quadric in ideal from "c_lst" i_lst = range(len(imp_lst)) M_pol = [ c_lst[i] * imp_lst[i] for i in i_lst if imp_lst[i].total_degree() == 2 ] M_pol = sum(M_pol) # eigendecomposition M = sage_invariant_theory.quadratic_form( M_pol, x_lst).as_QuadraticForm().matrix() M = sage_matrix(sage_QQ, M) vx = sage_vector(x_lst) D, V = M.eigenmatrix_right() # determine signature of quadric num_pos = len([d for d in D.diagonal() if d > 0]) num_neg = len([d for d in D.diagonal() if d < 0]) num_zer = len([d for d in D.diagonal() if d == 0]) sig = [num_pos, num_neg, num_zer] LSTools.p('\t sig =', sig, c_lst) # # output of M.eigenmatrix_right() ensures that # D has signature either (--- --- +) or (- +++ +++) # if num_pos < num_neg: # (--- --- +) ---> (+ --- ---) V.swap_columns(0, 6) D.swap_columns(0, 6) D.swap_rows(0, 6) # # diagonal orthonormalization # note: M == W.T*D*W == W.T*L.T*J*L*W = U.T*J*U # W = sage_matrix([col / col.norm() for col in V.columns()]) J = [] for d in D.diagonal(): if d > 0: J += [1] elif d < 0: J += [-1] J = sage_diagonal_matrix(J) L = sage_diagonal_matrix([d.abs().sqrt() for d in D.diagonal()]) U = L * W # # Do some tests # assert M_pol in ring.ideal(imp_lst) assert vx * M * vx == M_pol assert M * V == V * D # # output values # LSTools.p('quadratic form of signature (1,6) in ideal =', M_pol) LSTools.p('matrix M associated to quadratic form =', list(M)) LSTools.p('M == U.T*J*U =', list(U.T * J * U)) LSTools.p('U =', list(U)) LSTools.p('J =', list(J))