Algebraic Geometry in Sage

File for the general methods concerning Algebraic Geometry

In this file we include several useful methods that can be written in a generic way involving reasoning over Algebraic Geometry. Our main concern will be Projective curves, although sometimes the methods will work with Affine varieties.

This package is based on Sage implementation of Algebraic geometry such as subschemes, coordinate rings and morphism. For further information, look to the documentation of each particular method.

AUTHORS:
  • Antonio Jimenez-Pastor (2020-04-11): initial version
TODO:
  • do the examples of the package

This package is under the terms of GNU General Public License version 3.

comb_walks.alggeo.apply_map(*args, **kwds)

Method to apply a map between projective varieties.

This method applies a morphism given by map to a point point. Since the morphism is between projective varieties, it is described by a list of polynomials. If point is not a common zero for all those polynomials, the image is just the evaluation of each polynomial on the point.

However, there are times where the point is a common zero for all the components and we still have an evaluation. In this method we focus on the particular case when the domain of map is a projective plane curve.

See method eval_at_zero_on_variety() to know exactly how this evaluation is performed in each of the projective components of the image.

INPUT:
  • map: the morphism between projective varieties.
  • point: a point on the domain of map. This point can be given as an actual point on the domain or it can also be a list of coordinates.
OUTPUT:
A point \(P\) in the codomain such that map(point) == P or a NotImplementedError in case the domain is not an algebraic curve, the curve is not smooth on the required point or the direct application does not work.

EXAMPLES:

sage: from comb_walks.alggeo import *
sage: P2 = ProjectiveSpace(2, QQ, 'xyz'); x,y,z = P2.gens(); O = P2([0,0,1])
sage: C = P2.subscheme(x^2 - y*z); O in C
True
sage: h = Hom(C,C)([z*y, z*x, x*y]); # (x, x^2) --> (1/x, 1/x^2)
sage: h(O)
Traceback (most recent call last):
...
ValueError: [0, 0, 0] does not define a valid point since all entries are 0
sage: apply_map(h, O)
(0 : 1 : 0)
sage: apply_map(h,O) in h.codomain()
True

This method allows also points on algebraic extensions of the ground field:

sage: F = NumberField(QQ['a']("a^2-2"), 'a'); a = F.gens()[0]
sage: apply_map(h, (a, 2, 1))
(1/2*a : 1/2 : 1)
sage: G = NumberField(QQ['i']("i^2+1"), 'i'); i = G.gens()[0]
sage: apply_map(h, (i, 1, 1)) # point not on the curve
Traceback (most recent call last):
...
TypeError: Coordinates [i, 1, 1] do not define a point on ...
sage: apply_map(h, (i,-1,1))
(-i : -1 : 1)

Also, this method can compute the image in each of the components of the product of Projective spaces:

sage: C = P2.subscheme((-4)*z^3 + y^2*z + (-13/4)*x*z^2 + (-7/8)*z^3)
sage: P1P1 = ProjectiveSpace(1, QQ, 'x').cartesian_product(ProjectiveSpace(1,QQ,'y')); x0,x1,y0,y1 = P1P1.gens()
sage: D = P1P1.subscheme(-x0*x1*y0^2 + x0*x1*y0*y1 - x0^2*y1^2 - x0*x1*y1^2 - x1^2*y1^2)
sage: h = Hom(C,D)([(-24)*z, 24*x + (6)*z, 12*x + 12*y + (3)*z, 24*x + (6)*z]); apply_map(h, (0,1,0))
(0 : 1 , 1 : 0)
comb_walks.alggeo.asymptotics(*args, **kwds)

Method to compute a asymptotic approximation of a rational function.

This methods allows the user to compute asymptotic information around one point on a variety. This variety must be a plane curve or the code woult not work properly.

This method will check that the given point (which can be defined in an algebraic extension field) is on the curve and then compute the local expansion of the rational function given by func around that point.

This method is equivalent to order_at_variety() when the point is the origin. This is an enhanced method that allows more flexibility on the point and take care of the changes of coordinates required to compute this asymptotic data.

INPUT:
  • variety: a hypersurface that defines a plane affine curve after its dehomogenization.
  • func: a rational function on the variety. This method will check out that it is a valid rational function. This means either it is an affine rational function in the usual sense, or both numerator and denominator need to be homogeneous of the same degree.
  • point: a point on the variety. It can have coordinates in extensions of the base ring of the variety.
OUTPUT:

A triplet containing:

  • The order of the function func at point on the variety.
  • The first non-zero coefficient of the expansion of func around point.
  • The local parameter on variety used to compute the expansion of func around point.

EXAMPLES:

sage: from comb_walks.alggeo import *
sage: P2 = ProjectiveSpace(2, QQ, 'xyz'); x,y,z = P2.gens()
sage: C = P2.subscheme(x^2 - y*z); # the usual parabola
sage: O = origin(C); P = C([0,1,0]); # origin and the point at infinity
sage: asymptotics(C, x, O)
(1, 1, x/z)
sage: asymptotics(C, y, O)
(2, 1, x/z)
sage: asymptotics(C, x/z, O) == asymptotics(C, x, O)
True
sage: asymptotics(C, x/z, P)
(-1, 1, x/y)
sage: asymptotics(C, y/z, P)
(-2, 1, x/y)
sage: asymptotics(C, y/z - (y/x)^2, P)
(+Infinity, 0, x/y)
sage: asymptotics(C, (x^2 - 3*y^2)/(7*x^3), P)
(-1, -3/7, x/y)
sage: asymptotics(C, (x^2 - 3*y^2)/(7*x^3) + (3/7)*(y/x), P)
(1, 1/7, x/y)

This method also allows to study the asymptotics around algebraic points:

sage: F = QQ.extension(QQ[x](x^2 + 1), 'i'); i = F.gens()[0]
sage: Q = point_extension([i, -1, 1], C)
sage: asymptotics(C, x^2+1, Q)
(1, (2*i), (x + (-i)*z)/z)
sage: asymptotics(C, (y^2-1)/(x^2+1), Q)
(0, -2, (x + (-i)*z)/z)
comb_walks.alggeo.decompose_at_zero(*args, **kwds)

Method that decomposes an affine curve at \((0,0)\) in a convinient way.

This method takes a plane curve that is smooth at \((0,0)\) and then computes a decomposition of the form

\[f(x,y) = y(b+h(x,y)) + x^dg(x),\]

where the following properties hold:

  • \(f(x,y)\) is the curve,
  • \(x\) is a local parameter at \((0,0)\),
  • \(y\) is the other coordinate for the curve,
  • \(b\) is a constant,
  • \(h(x,y)\) is a polynomial with \(h(0,0) = 0\),
  • \(g(x)\) is a polynomial with \(g(0)=0\).

This indicates that \(x\) has a zero of order 1 at \((0,0)\) and \(y\) has a zero of order \(d\). This can easily help to compute the order of rational functions over the curve at \((0,0)\).

This method cache the result, although different calls with different main_var arguments will lead to repeat computations sice we may have that the other variable is NOT a local parameter.

INPUT:
  • curve: a projective hypersurface or the defining polynomial in two variables of the curve.
  • main_var: optional argument to indicate which of the variables is better for considering as a local parameter on the curve.
OUTPUT:
The output of this method is the tuple \((d,g,b,h,X,Y)\) where \(d\), \(g\), \(b\), \(h\) are the described elements before, \(X\) is the local parameter chosen and \(Y\) the remaining variable.

EXAMPLES:

sage: from comb_walks.alggeo import *
sage: R.<x,y> = QQ[]
sage: f = x^4 + 2*y^2*x^2 - y; decompose_at_zero(f)
(4, 1, -1, 2*x^2*y, x, y)
sage: R.<x,y,z> = QQ[]
sage: f = x^3 - 3*x^2*y + 3*x*y^2 - y^3 + x; decompose_at_zero(f)
(3, -1, 1, x^2 - 3*x*y + 3*y^2, y, x)
sage: R.<x,y,z> = QQ.extension(QQ['i']("i^2+1"), 'i')[]; i = R.base().gens()[0]
sage: f = i*z^3 - 2*(i-1)*x^2*z + x - z; decompose_at_zero(f)
(1, 1, -1, (-2*i + 2)*x^2 + (i)*z^2, x, z)
sage: f = z + x + y; decompose_at_zero(f)
Traceback (most recent call last):
...
TypeError: The curve ''x + y + z'' is defined with an innapropriate number of variables (Expected 2, got 3)
sage: f = x^3 - x^2 - 3*y^2; decompose_at_zero(f)
Traceback (most recent call last):
...
ValueError: The curve ''x^3 - x^2 - 3*y^2'' is singular at the point (0,0)
sage: f = x^2 - 2*y + 1; decompose_at_zero(f)
Traceback (most recent call last):
...
ValueError: The curve ''x^2 - 2*y + 1'' does not go through the point (0,0)
sage: f = 3*y^3 + 2*x*y^2 + y; decompose_at_zero(f)
(+Infinity, 0, 1, 2*x*y + 3*y^2, x, y)

We can use the argument main_var to try to use that variable as a local parameter. Only if both variables are local parameters the result will be different:

sage: f = x^3 - y; decompose_at_zero(f, x)
(3, 1, -1, 0, x, y)
sage: decompose_at_zero(f, y)
(3, 1, -1, 0, x, y)
sage: f = x^3 - x - y; decompose_at_zero(f, x)
(1, x^2 - 1, -1, 0, x, y)
sage: decompose_at_zero(f, y)
(1, -1, -1, x^2, y, x)

This method can also be applied to projective varieties that defines plane curves:

sage: P1P1 = ProjectiveSpace(QQ, 1, 'x').cartesian_product(ProjectiveSpace(QQ, 1, 'y'))
sage: x0,x1,y0,y1 = P1P1.gens()
sage: C = P1P1.subscheme(x0^2*y1 - x1^2*y0); # the usual parabola
sage: decompose_at_zero(C, x0)
(2, 1, -1, 0, x0, y0)
sage: P2 = ProjectiveSpace(QQ, 2, 'xyz'); x, y, z = P2.gens()
sage: C = P2.subscheme(x^3 - 2*y^2*z + x*z^2)
sage: decompose_at_zero(C)
(2, -2, 1, x^2, y, x)
comb_walks.alggeo.dehomogenize_at_zero(variety)

Method to dehomogenize a variety defining polynomials at the origin.

This method dehomogenize the defining polynomials of an algebraic variety taking into account the different types of homogenization. Since in Projective Spaces there are (usually) several ways to dehomogenize, we take the affine chart define at the origin of the ambient space.

INPUT:
  • variety: an algebraic variety. It can be either an ambient space or a subscheme.

EXAMPLES:

sage: from comb_walks.alggeo import *
sage: P2 = ProjectiveSpace(2, QQ, 'xyz'); x, y, z = P2.gens()
sage: C = P2.subscheme(x^2 - y*z); dehomogenize_at_zero(C)
[x^2 - y]
sage: C = P2.subscheme(x^3 - 3*z*x^2 + x*y*z - y^2*z + z^3); dehomogenize_at_zero(C)
[x^3 - 3*x^2 + x*y - y^2 + 1]
sage: P1P1 = ProjectiveSpace(1, QQ, 'x').cartesian_product(ProjectiveSpace(1, QQ, 'y'))
sage: x0, x1, y0, y1 = P1P1.gens()
sage: C = P1P1.subscheme(x0^2*y1 - x1^2*y0); dehomogenize_at_zero(C)
[x0^2 - y0]
sage: C = P1P1.subscheme(x0^3*y1*y0 - y0^2*x1*x0^2 + y1^2*x1^2*x0 + y1^2*x1^3); dehomogenize_at_zero(C)
[x0^3*y0 - x0^2*y0^2 + x0 + 1]
comb_walks.alggeo.eval_at_zero_on_variety(*args, **kwds)

Method to evaluate a list of polynomials at the origin over a curve

This method computes the projective image of a list a polynomial functions at the point the origin using the information that those polynomials are defined over a hypersurface variety. This “projective” image means that not all of the values can be zero at the same time.

First, we use the equation of the curve to reduce the polynomials and, if there is a non-zero polynomial, we compute the value of the list. For doing this we require that variety is a hypersurface (i.e., it is defined with only one polynomial) and smooth at the origin.

This method is closely related to the method apply_map(). This method is allowed to receive as input both a unique polynomial defining a curve or a hypersurface (see method order_at_variety()). Essentially, any input where we can decompose the algebraic object using the method decompose_at_zero().

INPUT:
  • polys: list of polynmoials we want to evaluate at \((0,0)\).
  • variety: either a hypersurface or a polynomial defining a hypersurface.
OUTPUT:
A list of values resulting after removing the zeros of the polynomials.

EXAMPLES:

sage: from comb_walks.alggeo import *
sage: P2 = ProjectiveSpace(2, QQ, 'xyz'); x,y,z = P2.gens()
sage: C = P2.subscheme(x^2 - y*z); # usual parabola
sage: eval_at_zero_on_variety([x^2, y], C)
[1, 1]
sage: eval_at_zero_on_variety([x^2, y], x^2 - y)
[1, 1]
sage: C = P2.subscheme(x^3 - x^2*z - y^2*z)
sage: eval_at_zero_on_variety([x^2-1, y^3+2-x^2], C)
Traceback (most recent call last):
...
ValueError: The curve ''x^3 - x^2 - y^2'' is singular at the point (0,0)
sage: C = P2.subscheme(x^2 - y*z + z^2)
sage: eval_at_zero_on_variety([x^2-1, y^3+2-x^2], C)
Traceback (most recent call last):
...
ValueError: The curve ''x^2 - y + 1'' does not go through the point (0,0)
comb_walks.alggeo.expand_at_point(variety, func, point, bound)

Method to expand a rational function on a variety using a local parameter.

Given a projective variety \(A\) and a point \(P \in A\) where the variety is smooth, we can have a local parameter \(t \in C(A)\), i.e., a function that vanishes at \(P\) with order 1. This local parameter generates the ideal of all rational functions over \(A\) that vanish at \(P\).

In fact, given a rational function \(f \in C(A)\), we can compute a local expansion of \(f\) around \(P\) using this local parameter such that:

\[f = \sum_{k\geq d} f_d t^d,\]

where \(d\) is defined as the order of \(f\) at \(P\).

This method computes a local parameter \(t\) for the variety given by variety and the corresponding sequence of elements \(f_k\) for \(d \leq k < bound\).

INPUT:
  • variety: the variety \(A\) to perform the computations,
  • func: the rational function over \(A\). It can be given a an affine rational function (and we homogenize it) or as a projective rational function (i.e., a rational function defined as a quotient of two homogeneous polynomials of same degree).
  • point: a point \(P\) on the variety \(A\).
  • bound: and integer number.
OUTPUT:
A tuple (d,t) such that d[n] returns the coefficient \(f_n\) in the expansion with respect to t of func around point in variety.

EXAMPLES:

sage: from comb_walks.alggeo import *
sage: P2 = ProjectiveSpace(2, QQ, 'xyz'); x,y,z = P2.gens()
sage: C = P2.subscheme(x^3 - 3*y^2*z + x^2*z + y*z^2); O = origin(C)
sage: expand_at_point(C,1/x^3, O, 10)
({-3: 1,
  -2: 0,
  -1: 0,
  0: 0,
  1: 0,
  2: 0,
  3: 0,
  4: 0,
  5: 0,
  6: 0,
  7: 0,
  8: 0,
  9: 0},
 x/z)
sage: expand_at_point(C,x/y, O, 5)
({-1: -1, 0: 1, 1: -4, 2: 1, 3: 8, 4: 10}, x/z)
sage: all(expand_at_point(C,1/x^3 - x/y, O,10)[0].get(i,0) == expand_at_point(C,1/x^3, O,10)[0].get(i,0) - expand_at_point(C,x/y, O,10)[0].get(i,0) for i in range(-3,9))
True
TODO:
Add examples and tests
comb_walks.alggeo.homogenize_function_on_variety(func, variety)

Method to homogenize a rational function using the coordinates of a variety.

This method takes a rational function or a polynomial and computes its rational homogenize equivalent, i.e., a rational function where the numerator and the denominator are homogeneous of the same degree.

Since there are several ways to homogenize, we use the homogenization on each projective component of the variety provided. If the homogeneous variable appeared on the function, and it is not a valid rational function, we raise an error. Otherwise we add the necessary variables.

INPUT:
  • func: the rational fucntion to homogenize
  • variety: an algebraic variety used to the homogenization.

EXAMPLES:

sage: from comb_walks.alggeo import *
sage: P2 = ProjectiveSpace(2, QQ, 'xyz'); x,y,z = P2.gens()
sage: C = P2.subscheme(x^3 - 3*y*z^2 + x^2*z -z^3)
sage: homogenize_function_on_variety(x, C)
x/z
sage: homogenize_function_on_variety(y, P2)
y/z
sage: homogenize_function_on_variety(x^2 - y, C)
(x^2 - y*z)/z^2
sage: homogenize_function_on_variety((x^2 - y^2)/(x*y), C)
(x^2 - y^2)/(x*y)
sage: homogenize_function_on_variety(x*z - y^3, P2)
Traceback (most recent call last):
...
ValueError: The homogenization variable (z) appears and the function is not a rational function

This method also wors for producs of Projective spaces:

sage: P1P2 = ProjectiveSpace(1, QQ, 'x').cartesian_product(ProjectiveSpace(2, QQ, 'y'))
sage: x0, x1, y0, y1, y2 = P1P2.gens()
sage: D = P1P2.subscheme(x0^2*y2^2 - x0*x1*y0*y1 + x1^2*y0^2)
sage: homogenize_function_on_variety(x0 - y1, D)
(-x1*y1 + x0*y2)/(x1*y2)
sage: homogenize_function_on_variety(y0, P1P2)
y0/y2
sage: homogenize_function_on_variety(y2, D)
Traceback (most recent call last):
...
ValueError: The homogenization variable (y2) appears and the function is not a rational function
sage: homogenize_function_on_variety(x1*y1/y2, P1P2)
Traceback (most recent call last):
...
ValueError: The homogenization variable (x1) appears and the function is not a rational function
sage: homogenize_function_on_variety((x0 - x1)*(y0 - y1 - y2)/(x0 * y1), P1P2)
(x0*y0 - x1*y0 - x0*y1 + x1*y1 - x0*y2 + x1*y2)/(x0*y1)
sage: homogenize_function_on_variety(x1*y0/x0, D)
x1*y0/(x0*y2)
comb_walks.alggeo.is_hypersurface(variety)

Method to decide wether a variety is a hypersurface or not.

A hypersurface in affine geometry is an affine variety defined with one unique equation. This then can be extended to any type of projectivized space (multiple projectivations or standar projectivation).

EXAMPLES:

sage: from comb_walks.alggeo import *
sage: P1P1 = ProjectiveSpace(1, QQ, 'x').cartesian_product(ProjectiveSpace(1, QQ, 'y'))
sage: x0, x1, y0, y1 = P1P1.gens()
sage: is_hypersurface(P1P1)
False
sage: is_hypersurface(P1P1.subscheme(x0*y1-y0*x1))
True
sage: is_hypersurface(P1P1.subscheme(x0^3*y1^3 + 3*x0^2*x1*y1*y0^2 + 3*x0*x1^2*y1^2*y0 + x1^3*y0^3))
True
sage: is_hypersurface(P1P1.subscheme([x0, y0]))
False
sage: P3 = ProjectiveSpace(3, QQ, 'abcd'); a,b,c,d = P3.gens()
sage: is_hypersurface(P3)
False
sage: is_hypersurface(P3.subscheme(a^2*d - b^3 + c*d^2))
True
sage: is_hypersurface(P3.subscheme([a, a^2]))
False
comb_walks.alggeo.is_identity(*args, **kwds)

Method to check if a morphism is the identity morphism or not.

This method takes a morphism \(\varphi : A \rightarrow A\) and tries to deduce if it is the identity morphism. For doing so, first we rely on Sage code to check the equality and, in case it returns False, we try to go further:

  • For each projective component, we divide each coordinate with the corresponding variables. Then we check if that is the same through all the coordinates.

EXAMPLES:

sage: from comb_walks.alggeo import *
sage: P2 = ProjectiveSpace(2, QQ, 'xyz'); x,y,z = P2.gens()
sage: is_identity(P2.identity_morphism())
True
sage: is_identity(Hom(P2,P2)([x,y,z]))
True
sage: is_identity(Hom(P2,P2)([y,z,x]))
False
sage: C = P2.subscheme(x^2 - y*z); # the usual parabola
sage: h = Hom(C,C)([x*y*z, x^2*y, x^2*z]); is_identity(h)
True
sage: h = Hom(C,C)([-x,-y,-z]); is_identity(h)
True
comb_walks.alggeo.is_prod_point(point)

Boolean method to check if an object is a point on a product of projective spaces

This method checks that an element is precisely a point on an ambient space that is the product of several Projective Spaces.

INPUT:
  • point: the object to be checked.

EXAMPLES:

sage: from comb_walks.alggeo import *
sage: P2 = ProjectiveSpace(2, QQ, 'xyz'); O = origin(P2)
sage: is_prod_point(O)
False
sage: is_prod_point([0,1,1])
False
sage: P1P1 = ProjectiveSpace(1, QQ, 'x').cartesian_product(ProjectiveSpace(1, QQ, 'y'))
sage: is_prod_point(origin(P1P1))
True
comb_walks.alggeo.is_smooth_at(variety, point)

Method that considers a hypersurface and check if it is smooth at a point.

In algebraic geometry, a variety is smooth at a point if it is well approximated by an affine space near that point. For hypersurfaces, this property can be easily checked using derivatives and evaluation of the defining equation.

Since this computation using derivatives only works for affine varieties and projective spaces, we will always dehomogenize around the require point (if it is on the curve), see method lin_change_to_zero().

For convention, we say that if point is not in the variety, the variety is not smooth at that point.

EXAMPLES:

sage: from comb_walks.alggeo import *
sage: P2 = ProjectiveSpace(2, QQ, 'xyz'); x, y, z = P2.gens()
sage: C = P2.subscheme(x^2-y*z); # normal parabola
sage: O = origin(P2); P = P2([0,1,0]); Q = P2([1,0,1])
sage: is_smooth_at(C, O)
True
sage: is_smooth_at(C, P)
True
sage: is_smooth_at(C, Q)
False
sage: C = P2.subscheme(x^3 - x^2*z - y^2*z)
sage: is_smooth_at(C, O)
False
sage: is_smooth_at(C, P)
True
sage: is_smooth_at(C, Q)
True
sage: P1P1 = ProjectiveSpace(1, QQ, 'x').cartesian_product(ProjectiveSpace(1, QQ, 'y'))
sage: x0,x1,y0,y1 = P1P1.gens()
sage: C = P1P1.subscheme(x0^2*y0*y1 + y0^2*x0*x1 + x0*x1*y1^2 + y0*y1*x1^2 - x0*y0*y1*x1)
sage: O = origin(C)
sage: is_smooth_at(C, O)
True
comb_walks.alggeo.lin_change_from_zero(*args, **kwds)

Method to get a dictionary of linear change of coordinates from the origin.

This method takes a point on an algebraic scheme and computes a linear change of coordinates in its ambient space that maps the origin to point. The output is a dictionary that can be plugged into rational functions to get the pullback of this change of coordinates.

Remark: if point is the origin, this map will lead to a simple homoginization.

OUTPUT:
  • A dictionary such that pullback(change)(f) = f(**dict).numerator().

EXAMPLES:

sage: from comb_walks.alggeo import *
sage: # Test for P2 and a plane curve
sage: D = ProjectiveSpace(2, QQ, 'xyz'); x,y,z = D.gens(); O = D([0,0,1])
sage: C = D.subscheme(x^2 - y*z); # the normal parabloa
sage: O in C
True
sage: P = D([1,1,1])
sage: lin_change_from_zero(P)
{'x': (x - z)/z, 'y': (y - z)/z, 'z': 1}
sage: C.defining_polynomials()[0](**lin_change_from_zero(P)).numerator()
x^2 - 2*x*z - y*z + 2*z^2
sage: P in D.subscheme([el(**lin_change_from_zero(P)).numerator() for el in C.defining_polynomials()])
True
sage: # Testing a change with an infinity point
sage: P = D([1,0,0])
sage: lin_change_from_zero(P)
{'x': z/x, 'y': y/x, 'z': 1}
sage: P in D.subscheme([el(**lin_change_from_zero(P)).numerator() for el in C.defining_polynomials()])
True
sage: P = D([1,1,0])
sage: lin_change_from_zero(P)
{'x': (x - y)/y, 'y': z/y, 'z': 1}
sage: P in D.subscheme([el(**lin_change_from_zero(P)).numerator() for el in C.defining_polynomials()])
True
sage: # Testing with a bigger space and more complex variety
sage: P4 = ProjectiveSpace(4, QQ, 'abcde'); a,b,c,d,e = P4.gens(); O = P4([0,0,0,0,1])
sage: C = P4.subscheme([a^2+b^2+c^2+d^2, a^4-b^2*c^2+d*e^3])
sage: O in C
True
sage: P = P4([1,-1,0,1,1])
sage: lin_change_from_zero(P)
{'a': (a - e)/e, 'b': (b + e)/e, 'c': c/e, 'd': (d - e)/e, 'e': 1}
sage: P in P4.subscheme([el(**lin_change_from_zero(P)).numerator() for el in C.defining_polynomials()])
True

This method can also work with product of projective spaces:

sage: # Testing product of two projective spaces and plane curves
sage: P1P1 = ProjectiveSpace(1, QQ, 'x').cartesian_product(ProjectiveSpace(1, QQ, 'y'))
sage: x0,x1,y0,y1 = P1P1.gens(); O = P1P1([0,1,0,1])
sage: C = P1P1.subscheme(x0^2*y1 - y0*x1^2); # the usual parabola
sage: O in C
True
sage: P = P1P1([1,1,0,1]); # P = (1,0)
sage: lin_change_from_zero(P)
{'x0': (x0 - x1)/x1, 'x1': 1, 'y0': y0/y1, 'y1': 1}
sage: P in P1P1.subscheme([el(**lin_change_from_zero(P)).numerator() for el in C.defining_polynomials()])
True
sage: # Testing with plance curves and points at infinity
sage: P = P1P1([3,1,1,0]); # P = (3,inf)
sage: lin_change_from_zero(P)
{'x0': (x0 - 3*x1)/x1, 'x1': 1, 'y0': y1/y0, 'y1': 1}
sage: P in P1P1.subscheme([el(**lin_change_from_zero(P)).numerator() for el in C.defining_polynomials()])
True
sage: P = P1P1([1,0,-2,1]); # P = (inf,-2)
sage: lin_change_from_zero(P)
{'x0': x1/x0, 'x1': 1, 'y0': (y0 + 2*y1)/y1, 'y1': 1}
sage: P in P1P1.subscheme([el(**lin_change_from_zero(P)).numerator() for el in C.defining_polynomials()])
True
sage: P = P1P1([1,0,1,0]); # P = (inf,inf)
sage: lin_change_from_zero(P)
{'x0': x1/x0, 'x1': 1, 'y0': y1/y0, 'y1': 1}
sage: P in P1P1.subscheme([el(**lin_change_from_zero(P)).numerator() for el in C.defining_polynomials()])
True
comb_walks.alggeo.lin_change_to_zero(*args, **kwds)

Method to get a dictionary of linear change of coordinates to the origin.

This method takes a point in an algebraic scheme and computes a linear change of coordinates in its ambient space that maps point to the origin. The output is a dictionary that can be plugged into rational functions to get the pullback of this change of coordinates.

Remark: if point is the origin, this map will lead to a simple dehomoginization.

OUTPUT:
  • A dictionary such that pullback(change)(f) = f(**dict).
  • A list of variables equals to f(**dict).variables().

EXAMPLES:

sage: from comb_walks.alggeo import *
sage: # Test for P2 and a plane curve
sage: D = ProjectiveSpace(2, QQ, 'xyz'); x,y,z = D.gens(); O = D([0,0,1])
sage: C = D.subscheme(x^3*z - 3*x^2*y^2 + 3*x*z^3 - z^4); O in C
False
sage: P = D([1,1,1]); P in C
True
sage: lin_change_to_zero(P)
({'x': x + 1, 'y': y + 1, 'z': 1}, [x, y])
sage: O in D.subscheme([el(**lin_change_to_zero(P)[0]).homogenize(z) for el in C.defining_polynomials()])
True
sage: # Testing a change with an infinity point
sage: P = D([1,0,0])
sage: lin_change_to_zero(P)
({'x': 1, 'y': y, 'z': x}, [x, y])
sage: O in D.subscheme([el(**lin_change_to_zero(P)[0]).homogenize(z) for el in C.defining_polynomials()])
True
sage: C = D.subscheme(x^3 - y^3 + z^3); O in C
False
sage: P = D([1,1,0]); P in C
True
sage: lin_change_to_zero(P)
({'x': x + 1, 'y': 1, 'z': y}, [x, y])
sage: O in D.subscheme([el(**lin_change_to_zero(P)[0]).homogenize(z) for el in C.defining_polynomials()])
True
sage: # Testing with a bigger space and more complex variety
sage: P4 = ProjectiveSpace(4, QQ, 'abcde'); a,b,c,d,e = P4.gens(); O = P4([0,0,0,0,1])
sage: C = P4.subscheme([a^2+b^2+c^2-d^2-e^2, a^4-b^2*c^2+d*e^3]); O in C
False
sage: P = P4([-1,1,0,-1,1]); P in C
True
sage: lin_change_to_zero(P)
({'a': a - 1, 'b': b + 1, 'c': c, 'd': d - 1, 'e': 1}, [a, b, c, d])
sage: O in P4.subscheme([el(**lin_change_to_zero(P)[0]).homogenize(e) for el in C.defining_polynomials()])
True

This method can also work with product of projective spaces:

sage: # Testing product of two projective spaces and plane curves
sage: P1P1 = ProjectiveSpace(1, QQ, 'x').cartesian_product(ProjectiveSpace(1, QQ, 'y'))
sage: x0,x1,y0,y1 = P1P1.gens(); O = P1P1([0,1,0,1])
sage: C = P1P1.subscheme(x0^2*y1^2 - x0*y0*x1*y1 + x1^2*y0^2 - x1^2*y1^2); O in C
False
sage: P = P1P1([1,1,1,1]); P in C
True
sage: lin_change_to_zero(P)
({'x0': x0 + 1, 'x1': 1, 'y0': y0 + 1, 'y1': 1}, [x0, y0])
sage: O in P1P1.subscheme([el(**lin_change_to_zero(P)[0])(x0=x0/x1,y0=y0/y1).numerator() for el in C.defining_polynomials()])
True
sage: P = P1P1([0,1,-1,1]); P in C
True
sage: lin_change_to_zero(P)
({'x0': x0, 'x1': 1, 'y0': y0 - 1, 'y1': 1}, [x0, y0])
sage: O in P1P1.subscheme([el(**lin_change_to_zero(P)[0])(x0=x0/x1,y0=y0/y1).numerator() for el in C.defining_polynomials()])
True
sage: P = P1P1([1,1,0,1]); P in C
True
sage: lin_change_to_zero(P)
({'x0': x0 + 1, 'x1': 1, 'y0': y0, 'y1': 1}, [x0, y0])
sage: O in P1P1.subscheme([el(**lin_change_to_zero(P)[0])(x0=x0/x1,y0=y0/y1).numerator() for el in C.defining_polynomials()])
True
sage: # Testing with plance curves and points at infinity
sage: P = P1P1([1,0,1,0]); # P = (inf,inf)
sage: lin_change_to_zero(P)
({'x0': 1, 'x1': x0, 'y0': 1, 'y1': y0}, [x0, y0])
sage: O in P1P1.subscheme([el(**lin_change_to_zero(P)[0])(x0=x0/x1,y0=y0/y1).numerator() for el in C.defining_polynomials()])
True
sage: C = P1P1.subscheme(x0^2*y0*y1 - 3*x0^2*y1^2 + x1^2*y0^2 - x1^2*y1^2); O in C
False
sage: P = P1P1([1,0,3,1]); P in C
True
sage: lin_change_to_zero(P)
({'x0': 1, 'x1': x0, 'y0': y0 + 3, 'y1': 1}, [x0, y0])
sage: O in P1P1.subscheme([el(**lin_change_to_zero(P)[0])(x0=x0/x1,y0=y0/y1).numerator() for el in C.defining_polynomials()])
True
sage: C = P1P1.subscheme(y1^4*x1^2 - y1^2*y0^2*x0^2 + y0^4*x1*(x0 + 2*x1)); O in C
False
sage: P = P1P1([-2,1,1,0]); P in C
True
sage: lin_change_to_zero(P)
({'x0': x0 - 2, 'x1': 1, 'y0': 1, 'y1': y0}, [x0, y0])
sage: O in P1P1.subscheme([el(**lin_change_to_zero(P)[0])(x0=x0/x1,y0=y0/y1).numerator() for el in C.defining_polynomials()])
True
comb_walks.alggeo.order_at_variety(*args, **kwds)

Method to compute the order and the residue of a rational function at \((0,0)\) over an affine curve.

This method computes the order of a polynomial function over a curve at \((0,0)\). If curve defines an affine curve that is regular at \((0,0)\), then there is a local parameter \(t\). Then, any rational function can be written like

\[F(t) = \sum_{k \geq m} f_k t^k,\]

where \(f_m \neq 0\). We say that \(m\) is the order of \(F\) and \(f_m\) is its residue.

We define that the order of the zero function is \(\infty\) with residue \(0\).

INPUT:
  • rational: the polynomial function
  • variety: a hypersurface smooth at the origin or a polynomial defining an affine curve smooth at (0,0).
  • main_var: variable that will be considered local parameter if possible.
OUTPUT:

A triplet containing:

  • An integer with the order of rational at the origin over variety,
  • An element representing the residue (the first non-zero coefficient) of poly,
  • The affine local parameter used for computing the residue.

EXAMPLES:

sage: from comb_walks.alggeo import *
sage: P2 = ProjectiveSpace(QQ, 2, 'xyz'); x,y,z = P2.gens()
sage: C = P2.subscheme(x^3 - 3*x*y*z + 7*y^2*z - y*z^2)
sage: order_at_variety(x, C)
(1, 1, x)
sage: order_at_variety(y, C)
(3, 1, x)
sage: order_at_variety(7*y^2*x+ 9*y^3, C)
(7, 7, x)
sage: order_at_variety((3*x^7+2*y^2)/(3*x^6 + y^4), C)
(0, 2/3, x)
sage: order_at_variety(0, C)
(+Infinity, 0, x)

This method can also work with a defining polynomial in two variables instead of the projective variety:

sage: P1P1 = ProjectiveSpace(QQ, 1, 'x').cartesian_product(ProjectiveSpace(QQ, 1, 'y'))
sage: x0, x1, y0, y1 = P1P1.gens()
sage: C = P1P1.subscheme(x0^3*y1^2 - 3*x0*x1^2*y0*y1 + 7*y0^2*x1^3 - y0*y1*x1^3)
sage: order_at_variety(x0, C) == order_at_variety(x0, dehomogenize_at_zero(C)[0])
True
sage: order_at_variety(y0, C) == order_at_variety(y0, dehomogenize_at_zero(C)[0])
True
sage: order_at_variety((3*x0+y0^2)/x0^6, C) == order_at_variety((3*x0+y0^2)/x0^6, dehomogenize_at_zero(C)[0])
True
comb_walks.alggeo.order_morphism(*args, **kwds)

Method for computing the order of a morphism up to a bound.

Given a projective morphism \(\varphi\) with same domain and codomain, we can define its order as the minimal value of \(n\) such that \(\varphi^n = id\). If such \(n\) does not exist, then we say \(\varphi\) has infinite order.

This method checks blindly if a method has a finite order up to some power given by the argument bound. This method also cache the answer in such a way that if the bound has been reached we return (Infinity, d, m) where

  • d is the current bound that have been checked.
  • m is the current value morphism^d.

Hence if the user ask for the order again with smaller bound, the answer is inmediate and if the user ask for the order for a higher bound only new computations are done.

EXAMPLES:

sage: from comb_walks.alggeo import *
sage: P2 = ProjectiveSpace(2, QQ, 'xyz'); x,y,z = P2.gens()
sage: C = P2.subscheme(x^2-y*z)
sage: h = Hom(C,C)([-x,y,z]); order_morphism(h)
2
sage: h = Hom(C,C)([y*z, x*z, x*y]); order_morphism(h)
2
sage: C2 = P2.subscheme(x^3-3*x^2*y+3*x*y^2-y^3)
sage: h = Hom(C,C2)([x,x,z]); order_morphism(h)
+Infinity

In the package comb_walks.walkmodel, we can find several examples of morphisms that have finite order, and also some that have infinite order:

sage: from comb_walks.walkmodel import *
sage: for m in AllModels: # long time (> 15 seconds)
....:     if(order_morphism(m.iota(1,'p')) != 2):
....:         print("Error in the order of x-involution on the model %s" %m.name())
....:     if(order_morphism(m.iota(2,'p')) != 2):
....:         print("Error in the order of y-involution on the model %s" %m.name())
comb_walks.alggeo.origin(variety)

Method to retrieve the origin of an algebraic variety.

This method returns the origin point of the ambient space where a variety is. It also check if the origin belongs to that particular variety, otherwise it returns a TypeError.

The variety must be a subscheme of some projective space or a product of projective spaces. Using that information we dompute the origin for each of the components of the space and return it if it is on the variety.

INPUT:
  • variety: an algebraic subscheme to look for the origin
OUTPUT:
Either the origin point of the ambient space if it is on the variety or a TypeError.

EXAMPLES:

sage: from comb_walks.alggeo import *
sage: P1P1 = ProjectiveSpace(1, QQ, 'x').cartesian_product(ProjectiveSpace(1, QQ, 'y'))
sage: x0, x1, y0, y1 = P1P1.gens()
sage: origin(P1P1)
(0 : 1 , 0 : 1)
sage: C = P1P1.subscheme(x0^2*y1 - y0*x1^2); # usual parabola
sage: origin(C)
(0 : 1 , 0 : 1)

It is important to remark that this method returns the origin on the curve. This means that the object is different although they can be checked as equals:

sage: origin(C) is origin(P1P1)
False
sage: origin(C) == origin(P1P1)
True

This method also works with only one projective component:

sage: P2 = ProjectiveSpace(2, QQ, 'xyz'); x,y,z = P2.gens()
sage: origin(P2)
(0 : 0 : 1)
sage: C2 = P2.subscheme([x,y]); origin(C2)
(0 : 0 : 1)
sage: C2 = P2.subscheme(x^2 - 2*x*z + z^2 - y*z) # translated parabola
sage: origin(C2)
Traceback (most recent call last):
...
TypeError: The origin (0 : 0 : 1) is not on ...

And we can have even more complexes schemes:

sage: A = ProjectiveSpace(2, QQ, 'xyz').cartesian_product(ProjectiveSpace(1, QQ, 'y')).cartesian_product(ProjectiveSpace(3, QQ, 'abcd'))
sage: origin(A)
((0 : 0 : 1 , 0 : 1), (0 : 0 : 0 : 1))
comb_walks.alggeo.point_extension(*args, **kwds)

Method to create a point structure for a variety even with extensions on the field

On Sage, algebraic subschemes are based on ground fields that are, usually, not algebraically closed. This means that it is very common to have points on a variety but Sage is unable to detect that membership property since the coordinates of the point are algebraic over the field over where the curve is defined.

This method allows the user to check that membership easier doing the corresponding pushouts and extensions of the curve to bigger fields automatically.

INPUT:
  • point: a point. This point can be a point structure in some projective space or a list of coordinates. It has to be any structure such that, when we iterate through its elements (in the way for coordinate in point:...) we obtain its coordinates.
  • variety: algebraic scheme where we want to check the membership of point.
OUTPUT:
The method will return the point structure for point in the corresponding exteded variety or an Exception if the point is not in any extension of the variety.

EXAMPLES:

sage: from comb_walks.alggeo import *
sage: P1P1 = ProjectiveSpace(1, QQ, 'x').cartesian_product(ProjectiveSpace(1, QQ, 'y'))
sage: x0,x1,y0,y1 = P1P1.gens(); O = P1P1([0,1,0,1])
sage: C = P1P1.subscheme(x0^2*y1^2 - x0*y0*x1*y1 + x1^2*y0^2); O in C
True
sage: point_extension(O, C)
(0 : 1 , 0 : 1)
sage: F = QQ.extension(QQ[x](x^2-x+1), 'a'); a = F.gens()[0]; # created an algebraic element
sage: point_extension([a,1,0,1], P1P1)
(a : 1 , 0 : 1)
sage: point_extension([a,1,1,1], C)
(a : 1 , 1 : 1)

It is interesting to remark that after applying this method, the Sage membership command still returns False, but now we can recover the extended variety from the point:

sage: point_extension([a,1,1,1], C) in C
False
sage: point_extension([a,1,0,1], P1P1).scheme()
Product of projective spaces P^1 x P^1 over Number Field in a with defining polynomial x^2 - x + 1
sage: point_extension([a,1,1,1], C).scheme()
Closed subscheme of Product of projective spaces P^1 x P^1 over Number Field in a with defining polynomial x^2 - x + 1 defined by:
  x1^2*y0^2 - x0*x1*y0*y1 + x0^2*y1^2

The method returns error if the point is not valid for the variety:

sage: point_extension([1,0,a], P1P1)
Traceback (most recent call last):
...
TypeError: the list v=[1, 0, a] must have 4 components
sage: point_extension([a,1,0,1], C)
Traceback (most recent call last):
...
TypeError: Coordinates [a, 1, 0, 1] do not define a point on ...
comb_walks.alggeo.polar_part(*args, **kwds)

Method that gets the polar part at one point of a rational function.

Given a projective variety \(A\) and a point \(P \in A\) where the variety is smooth, we can have a local parameter \(t \in C(A)\), i.e., a function that vanishes at \(P\) with order 1. This local parameter generates the ideal of all rational functions over \(A\) that vanish at \(P\).

In fact, given a rational function \(f \in C(A)\), we can compute a local expansion of \(f\) around \(P\) using this local parameter such that:

\[f = \sum_{k\geq d} f_d t^d,\]

where \(d\) is defined as the order of \(f\) at \(P\). If \(d < 0\) we say that \(f\) has a pole at \(P\). And we say that the part of the expansions with negative exponents of \(t\) is the polar part of `f` at `P` w.r.t. `t`.

Since any two local parameters differs by a constant, then the polar part of \(f\) is almost uniquely defined. If we consider the polar part as the list of coefficients \([0,f_{-1}, f_{-2},\ldots]\), then this list is uniquely determined up to a multiplication termwise by the geometric sequence \([1,c, c^2, \ldots]\).

INPUT:
  • variety: variety over we are considering the function.
  • func: rational function over variety.
  • point: a point on the variety. It can be a point defined over an algebraic extenstion of the ground field of variety.
  • sequence: boolean value to determine if we return the whole polar part function or the sequence of the Laurent expansion with respect to the local parameter. In the last case, the output will be a list of values l where l[i] represents the value \(f_{-i}\).
OUTPUT:
A tuple containing the polar part and the local parameter we used to compute it.

EXAMPLES:

sage: from comb_walks.alggeo import *
sage: P2 = ProjectiveSpace(2, QQ, 'xyz'); x,y,z = P2.gens()
sage: C = P2.subscheme(x^3 - 3*y^2*z + x^2*z + y*z^2); O = origin(C)
sage: polar_part(C, 1/x^3 - x/y, O)
((x^2*z + z^3)/x^3, x/z)
sage: polar_part(C, 1/x^3 - x/y, O, True)
([0, 1, 0, 1], x/z)
sage: C = P2.subscheme(x^3 - y^2*z + 7*x*z^2 + 3*z^3); P = C([0,1,0]);# elliptic curve
sage: polar_part(C, y, P, True)
([0, 0, 0, 1], x/y)
sage: polar_part(C, y, P)
(y^3/x^3, x/y)
sage: polar_part(C, x^3 - y^2 + 3*x*y, P)
((-42*x^4*y - 7*x^3*y^2 + 3*y^5)/x^5, x/y)
sage: polar_part(C, x^3 - y^2 + 3*x*y, P, True)
([0, -42, -7, 0, 0, 3], x/y)
comb_walks.alggeo.pullback(morphism, lift=True)

Method that takes a rational map between Projective varieties and returns its pullback.

Given a rational map between two projectives varieties \(\varphi: A \rightarrow B\), the pullback is defined as a map on the ring of rational functions over the varieties \(C(A)\) and \(C(B)\) defined as

\[\varphi^* : C(B) \rightarrow C(A),\]

such that for any point \(P \in A\), \(\varphi^*(f)(P) = f(\varphi(P))\), i.e., \(\varphi(f) = f \circ \varphi\).

The argument lift determines if the map is considered in the total ambient space or in the coordinate rings.

EXAMPLES:

sage: from comb_walks.alggeo import *
sage: # Testing a pullback between the ambient spaces
sage: D = ProjectiveSpace(2, QQ, 'xyz'); CD = ProjectiveSpace(2,QQ, 'uvw')
sage: x,y,z = D.gens(); u,v,w = CD.gens()
sage: h = Hom(D, CD)([x^2, y*z-x*z, y^2])
sage: p = pullback(h); p
Ring morphism:
  From: Fraction Field of Multivariate Polynomial Ring in u, v, w over Rational Field
  To:   Fraction Field of Multivariate Polynomial Ring in x, y, z over Rational Field
  Defn: u |--> x^2
        v |--> -x*z + y*z
        w |--> y^2
sage: p(u)
x^2
sage: p(v)
-x*z + y*z
sage: p(w)
y^2
sage: # Testing a pullback between two curves
sage: C1 = D.subscheme(x^2-z*y); # usual parabola
sage: C2 = CD.subscheme(v); # the u axis
sage: h = Hom(C2, C1)([u*w,u^2,w^2]); h # the vertical lifting
Scheme morphism:
  From: Closed subscheme of Projective Space of dimension 2 over Rational Field defined by:
  v
  To:   Closed subscheme of Projective Space of dimension 2 over Rational Field defined by:
  x^2 - y*z
  Defn: Defined on coordinates by sending (u : v : w) to
        (u*w : u^2 : w^2)
sage: p = pullback(h); p
Ring morphism:
  From: Fraction Field of Multivariate Polynomial Ring in x, y, z over Rational Field
  To:   Fraction Field of Multivariate Polynomial Ring in u, v, w over Rational Field
  Defn: x |--> u*w
        y |--> u^2
        z |--> w^2
sage: p(x^2-y*z) == 0 # the pullback of the equation for C2 has to go to the zero element
True
comb_walks.alggeo.simpl_morphism(*args, **kwds)

Static method that simplifies a morphism between projectives varieties.

This method reduces the gcd from the defining polynomials of the morphism provided. Since the map is between projective varieties, the map is defined up to a common factor between its coordinates. In fact, the bigger the number of common factors, the bigger the locus of indeterminacy of the map.

Here we divide all the defining polynomials by the \(gcd\) and also try to recude the size of the content of those polynomials.

EXAMPLES:

sage: from comb_walks.alggeo import *
sage: D = ProjectiveSpace(2, QQ, 'xyz'); CD = ProjectiveSpace(2,QQ, 'uvw')
sage: x,y,z = D.gens(); u,v,w = CD.gens()
sage: C1 = D.subscheme(x^2-z*y); # usual parabola
sage: C2 = CD.subscheme(v); # the u axis
sage: h = Hom(C1, C2)([el*3*(27*x-y+z) for el in [x, 0, z]])
sage: h
Scheme morphism:
  From: Closed subscheme of Projective Space of dimension 2 over Rational Field defined by:
  x^2 - y*z
  To:   Closed subscheme of Projective Space of dimension 2 over Rational Field defined by:
  v
  Defn: Defined on coordinates by sending (x : y : z) to
        (81*x^2 - 3*x*y + 3*x*z : 0 : 81*x*z - 3*y*z + 3*z^2)
sage: simpl_morphism(h)
Scheme morphism:
  From: Closed subscheme of Projective Space of dimension 2 over Rational Field defined by:
  x^2 - y*z
  To:   Closed subscheme of Projective Space of dimension 2 over Rational Field defined by:
  v
  Defn: Defined on coordinates by sending (x : y : z) to
        (x : 0 : z)

The simplification can be done also when the image is a product of projective spaces:

sage: P2 = ProjectiveSpace(1, QQ, 'x').cartesian_product(ProjectiveSpace(1, QQ, 'y'))
sage: x0, x1, y0, y1 = P2.gens()
sage: C3 = P2.subscheme(x0^2*y1 + x1^2*y0); # same as C1 but symmetric in the X axis
sage: h = Hom(C3,C3)([(18*x0*y0+11*x0*y1)*el for el in [x1,x0,y1,y0]])
sage: simpl_morphism(h)
Scheme endomorphism of Closed subscheme of Product of projective spaces P^1 x P^1 over Rational Field defined by:
x1^2*y0 + x0^2*y1
Defn: Defined by sending (x0 : x1 , y0 : y1) to
      (x1 : x0 , y1 : y0).

However, if the original space is not the product of some spaces, the simplification can not be performed. Then we do the usual simplification:

sage: h = Hom(C1,C3)([(23*x-17*y+5*z)*el for el in [x^2*z^2, z^4, y^4, z^4]]); h
Scheme morphism:
  From: Closed subscheme of Projective Space of dimension 2 over Rational Field defined by:
  x^2 - y*z
  To:   Closed subscheme of Product of projective spaces P^1 x P^1 over Rational Field defined by:
  x1^2*y0 + x0^2*y1
  Defn: Defined on coordinates by sending (x : y : z) to
        (23*x^3*z^2 - 17*x^2*y*z^2 + 5*x^2*z^3 : 23*x*z^4 - 17*y*z^4 + 5*z^5 , 23*x*y^4 - 17*y^5 + 5*y^4*z : 23*x*z^4 - 17*y*z^4 + 5*z^5)
sage: simpl_morphism(h)
Scheme morphism:
  From: Closed subscheme of Projective Space of dimension 2 over Rational Field defined by:
  x^2 - y*z
  To:   Closed subscheme of Product of projective spaces P^1 x P^1 over Rational Field defined by:
  x1^2*y0 + x0^2*y1
  Defn: Defined on coordinates by sending (x : y : z) to
            (x^2 : z^2 , y^4 : z^4)
comb_walks.alggeo.simplify_on_affine_curve(*args, **kwargs)

Method that simplifies a rational function over a curve.

This method reduces a rational function defined over a curve reducing the trailing_var to reduce the degree. It computes a canonical form where the rational function has no appearances of trailing_var of degree highter than curve_equation.degree(trailing_var) and it does not show up on the denominator.

Deprecated since version 0.1: This will be removed in 0.2. Use ‘simplify_rational_variety’ instead

comb_walks.alggeo.simplify_rational_variety(*args, **kwds)

Method that simplifies rational functions using the equation of a variety.

This method takes a rational function as an input and build an equivalent rational function using the standard reduction performed by Sage after going to the coordinate ring of a variety.

This simplification usually leads to a cannonical form inside the variety of a rational function and can be use to check some equalities of those rational functions that, sintactically seem different.

EXAMPLES:

sage: from comb_walks.alggeo import *
sage: P2 = ProjectiveSpace(2, QQ, 'xyz'); x,y,z = P2.gens()
sage: simplify_rational_variety(x^2-3*y/z, P2) == x^2 - 3*y/z
True
sage: C = P2.subscheme(x^2-y*z)
sage: simplify_rational_variety(x^2-y*z, C)
0
sage: simplify_rational_variety(x^2/(y*z), C)
1
sage: simplify_rational_variety((x^3-y*x^2+3*x*z^2)/(z^3), C)
(x*y - y^2 + 3*x*z)/z^2
comb_walks.alggeo.zeros_bihom(*args, **kwds)

Method to compute the zeros of a bivariate homogeneous form in the given variables.

Let \(f(x,y) \in F[x,y]\) be homogeneous of degree \(d\). Then:

  • \(f(0,0) = 0\) if and only if \(d > 0\).
  • \(f(ax,ay) = a^d f(x,y)\).

Hence, if \(f(x,y)\) is not a constant polynomials, the zeros of a bivariate homogeneous polynomial are infinitely many. Here we are going to fix the zeros that we may get by saying that \(y = 1\) or \(y=0\) (i.e., looking solutions in the projective line).

If \(f(x,0) = 0\), then \(y\) divides \(f(x,y)\). So, without lost of generality, we may assume that \(f(x,0) \neq 0\) and all zeros of \(f(x,y)\) are of the form \((\alpha, 1)\) and all the multiples we want.

If we evaluate \(y=1\) and factorized \(g(x) = f(x,1)\), then we obtain several factors \(h_1(x),\ldots,h_t(x)\) where \(t \leq d\) (with equality when \(F\) is algebraically closed). Then the numerators of \(h_i(x/y)\) make a factorization of \(f(x,y)\) and we can describe all the zeros for \(f(x,y)\) using those minimal polynomials \(h_1,\ldots,h_t\).

If \(\deg(h_j) = 1\), then we have \(h_j(x,y) = ax + b\), so the point \((-b,a)\) is a zero of the original \(f(x,y)\). If the degree is higer, we may need some algebraic extension on the ground field in orther to express the zeros.

For these algebraic points, we need to extend the field F using the corresponding polynomial \(h_j(x)\). We do so using the method extenstion that can be found in any field when \(\deg(h_j) = 2\). Otherwise we use the (possibly slower) method splitting_field.

INPUT:
  • poly: a homogeneous polynomial in two variables. This is checked by the function.
  • vars: the list of two variables we are going to require on the polynomial. This is also checked by the function.
  • alg_name: name for the algebraic elements that this method generates.
  • diff_names: if set to True, the algebraic elements will be named differently using a number after the preffix given by alg_name. Since we can not guarantee that this method only generates one algebraic extension, it is left for the user to decide if they have to be distinguish or not. It is set to True by default.
  • algebraic: method with interface (R, poly, name) such that takes a ring \(R\) and computes an algebraic extension using poly and name and return the final extension, the list of roots for poly and the number of algebraic extenstions required. By default, this method calls the method extension of the Ring Sage class.
OUTPUT:
A list of tuples \((x_0,y_0)\) such that poly(x=x0,y=y0) returns 0.

EXAMPLES:

sage: from comb_walks.alggeo import *
sage: F.<x,y> = QQ[]
sage: zeros_bihom(F.one(), [x, y])
[]
sage: zeros_bihom(x-1, [x,y])
Traceback (most recent call last):
...
ValueError: Polynomial x - 1 is not homogenous or not in variables [x, y]
sage: G.<a,b> = QQ[]
sage: zeros_bihom(a-b, [x,y])
Traceback (most recent call last):
...
CoercionException: ...
sage: F.<x,y,z> = QQ[]
sage: zeros_bihom(x-z, [x,y])
Traceback (most recent call last):
...
ValueError: Polynomial x - z is not homogenous or not in variables [x, y]
sage: zeros_bihom(x-z, [x,y,z])
Traceback (most recent call last):
...
TypeError: The requested variables ([x, y, z]) are not two --> no bivariate polynomial
sage: zeros_bihom(y*x - y^2, [x,y])
[(1, 0), (1, 1)]
sage: zeros_bihom(y*x - y^2, [y,x]) # here the first coordinate is for `y`
[(1, 1), (0, 1)]
sage: out = zeros_bihom(x^2 + y^2, [x,y]); out
[(a_0, 1), (-a_0, 1)]
sage: out[0][0]^2 == -1 # checking the algebraic property for `a_0`
True
sage: out = zeros_bihom((x^2+y^2)*(x^2+x*y+y^2), [x,y]); out
[(a_0, 1), (-a_0, 1), (a_1, 1), (-a_1 - 1, 1)]
sage: (out[0][0]^2 == -1) and (out[2][0]^2 + out[2][0] + 1 == 0)
True