# GSoC midterm summary (June 20-27)

This week is the midterm evaluation period for GSoC. In this post I will provide a description of what’s been accomplished so far and give some examples of the new functionality that’s been implemented.

The tickets that have been opened for this project as of now are #20676, #20697, #20698, #20774, #20811, #20790, #20839, and #20848. Of these, #20811, #20839, and #20848 are still open, and the goal is to finish them this week.

In #20697 and #20698, the class structure of general curves was revised. Among the revisions made, the curve class names and inheritance structure was changed to facilitate implementation of functionality that can work for arbitrary curves, not just plane curves. The main classes for curves are broken into projective curve classes, and affine curve classes. The projective curve classes are now ProjectiveCurve, ProjectivePlaneCurve, ProjectivePlaneCurve_finite_field, ProjectivePlaneCurve_prime_finte_field, listed by order of inheritance (ProjectiveCurve is the most general class). The same naming scheme is used for the affine curve classes. Another important part of these revisions was to change how curve objects are initialized.

Before the changes, the only way to initialize curves was to specify some elements of a polynomial ring:

sage: x,y,z,w = QQ['x,y,z,w'].gens()
sage: C = Curve([x^3 + y^3 - z^3 - w^3, x^5 - y*z^4]); C
Projective Curve over Rational Field defined by x^3 + y^3 - z^3 - w^3,
x^5 - y*z^4

The constructor for the curve classes decides based on whether the given polynomials are homogeneous or not to create an affine or projective curve. This initialization is somewhat limiting, and also has the downside that the user cannot specify the space in which the curve is created; a new ambient space object is always created:

sage: A.<x,y,z> = AffineSpace(QQ, 3)
sage: C = Curve([y - x^2, z - x^3])
sage: C.ambient_space() == A
False

To revise this initialization, we added an optional parameter to the constructor that allows users to specify a space to initialize a curve in,

sage: A.<x,y,z> = AffineSpace(QQ, 3)
sage: C = Curve([y - x^2, z - x^3], A)
sage: C.ambient_space() == A
True

and also added some helper functions in the ambient space classes so users can define curves in this way as well:

sage: A.<x,y> = AffineSpace(QQ, 2)
sage: C = A.curve([y - x^2]); C
Affine Plane Curve over Rational Field defined by -x^2 + y

After these revisions, we were able to start working on implementing the next functionality goals, the first of which was to implement some interface between affine and projective curves in the form of projective closures and affine patches (done in #20676). Because of the class inheritance structure revisions for curves, and because of the increased control over curve initialization, we could make use of the existing affine/projective subscheme functionality for affine patches/projective closures.

sage: P.<x,y,z> = ProjectiveSpace(QQ, 2)
sage: C = Curve(x^3 - x^2*y + y^3 - x^2*z, P)
sage: C.affine_patch(1)
Affine Plane Curve over Rational Field defined by x0^3 - x0^2*x1 - x0^2 + 1
sage: A.<x,y,z> = AffineSpace(QQ, 3)
sage: C = Curve([y - x^2, z - x^3], A)
sage: C.projective_closure()
Projective Curve over Rational Field defined by
x1^2 - x0*x2, x1*x2 - x0*x3, x2^2 - x1*x3

The affine patch functionality is especially important for investigating local properties of projective curves, and was essential to implementing some of the basic singularity analysis functionality in ticket #20774. Given a curve, we can now compute the subscheme defining its singular locus, its set of singular points (if finite), and the multiplicities of its singular points. For plane curves, computation of tangents and testing whether a singularity is ordinary or not was also implemented:

sage: P.<x,y,z,w> = ProjectiveSpace(QQ, 3)
sage: C = Curve([y^8 - x^2*z*w^5, w^2 - 2*y^2 - x*z], P)
sage: C.singular_subscheme()
Closed subscheme of Projective Space of dimension 3 over Rational Field
defined by:
y^8 - x^2*z*w^5,
-2*y^2 - x*z + w^2,
-x^3*y*z^4 + 3*x^2*y*z^3*w^2 - 3*x*y*z^2*w^4 + 8*x*y*z*w^5 + y*z*w^6,
x^2*z*w^5,
-5*x^2*z^2*w^4 - 4*x*z*w^6,
x^4*y*z^3 - 3*x^3*y*z^2*w^2 + 3*x^2*y*z*w^4 - 4*x^2*y*w^5 - x*y*w^6,
-2*x^3*y*z^3*w + 6*x^2*y*z^2*w^3 - 20*x^2*y*z*w^4 - 6*x*y*z*w^5 +
2*y*w^7,
-5*x^3*z*w^4 - 2*x^2*w^6
sage: C.singular_points()
[(0 : 0 : 1 : 0), (1 : 0 : 0 : 0)]
sage: Q = P([0,0,1,0])
sage: C.multiplicity(Q)
8
sage: R.<a> = QQ[]
sage: K.<b> = NumberField(a^2 - 3)
sage: A.<x,y> = AffineSpace(K, 2)
sage: C = Curve([(x^2 + y^2 - 2*x)^2 - x^2 - y^2], A)
sage: C.singular_points()
[(0, 0)]
sage: Q = A([0,0])
sage: C.multiplicity(Q)
2
sage: C.tangents(Q)
[x + (-1/3*b)*y, x + (1/3*b)*y]
sage: C.is_ordinary_singularity(Q)
True

Functionality for finding plane curve models for curves in higher dimension ambient spaces was implemented in ticket #20790. Such models are constructed by projecting curves into planes. The main portion of the projection functionality is implemented in the projection affine/projective curve class member functions. These functions return a tuple consisting of two elements: a scheme morphism from the starting curve to an ambient space of dimension one less than the ambient space of the original curve, and the curve that is the image of that morphism (if the image isn’t a curve, a subscheme object is returned instead). For affine curves, the user can specify the coordinates on which to project by passing in a list/tuple of coordinate indices or variables:

sage: A.<x,y,z> = AffineSpace(QQ, 3)
sage: C = Curve([y^7 - x^2 + x^3 - 2*z, z^2 - x^7 - y^2], A)
sage: C.projection([0,1])
(Scheme morphism:
From: Affine Curve over Rational Field defined by y^7 + x^3 - x^2 -
2*z, -x^7 - y^2 + z^2
To: Affine Space of dimension 2 over Rational Field
Defn: Defined on coordinates by sending (x, y, z) to
(x, y),
Affine Plane Curve over Rational Field defined by x1^14 + 2*x0^3*x1^7 -
2*x0^2*x1^7 - 4*x0^7 + x0^6 - 2*x0^5 + x0^4 - 4*x1^2)

If the user wishes, they may also specify to keep the same variable names when creating the ambient space for the projected curve:

sage: A.<x,y,z,w> = AffineSpace(QQ, 4)
sage: C = A.curve([x*y - z^3, x*z - w^3, w^2 - x^3])
sage: C.projection([y,z], False)
(Scheme morphism:
From: Affine Curve over Rational Field defined by -z^3 + x*y, -w^3 +
x*z, -x^3 + w^2
To: Affine Space of dimension 2 over Rational Field
Defn: Defined on coordinates by sending (x, y, z, w) to
(y, z),
Affine Plane Curve over Rational Field defined by z^23 - y^7*z^4)

For projective curves, the implementation and user options of projection are slightly different. Creating a well-defined projection map defined on a projective curve relies on the ability to find a point that’s not on the curve. The user may specify such a point, or by default, the method will attempt to construct one. This is always possible when working over a characteristic zero field, but when working over finite field, it is possible to have a curve that contains all of the points of its ambient space (infinite fields of positive characteristic have not yet been implemented well enough in Sage to be able to consider much functionality for curves defined over them). Once a point not on the given curve is found, a projection map can easily be created.

sage: P.<x,y,z,w,a,b,c> = ProjectiveSpace(QQ, 6)
sage: C = Curve([y - x, z - a - b, w^2 - c^2, z - x - a, x^2 - w*z], P)
sage: C.projection()
(Scheme morphism:
From: Projective Curve over Rational Field defined by -x + y, z - a -
b, w^2 - c^2, -x + z - a, x^2 - z*w
To: Projective Space of dimension 5 over Rational Field
Defn: Defined on coordinates by sending (x : y : z : w : a : b : c)
to
(x : y : -z + w : a : b : c),
Projective Curve over Rational Field defined by x1 - x4, x0 - x4, x2*x3
+ x3^2 + x2*x4 + 2*x3*x4, x2^2 - x3^2 - 2*x3*x4 + x4^2 - x5^2, x2*x4^2 +
x3*x4^2 + x4^3 - x3*x5^2 - x4*x5^2, x4^4 - x3^2*x5^2 - 2*x3*x4*x5^2 -
x4^2*x5^2)

In addition to the projection function, projective and affine curves also now have plane_projection functions which just repeatedly call the corresponding projection functions until a projection map into a plane is constructed. Some care is taken to ensure that the image of the starting curve by the resulting projection map will still be a curve.

sage: A.<x,y,z,w> = AffineSpace(QQ, 4)
sage: C = Curve([x^2 - y*z*w, z^3 - w, w + x*y - 3*z^3], A)
sage: C.plane_projection()
(Scheme morphism:
From: Affine Curve over Rational Field defined by -y*z*w + x^2, z^3 -
w, -3*z^3 + x*y + w
To: Affine Space of dimension 2 over Rational Field
Defn: Defined on coordinates by sending (x, y, z, w) to
(x, y), Affine Plane Curve over Rational Field defined by
x0^2*x1^7 - 16*x0^4)

Tickets #20839 and #20848 are currently close to being finished. The goal of #20839 is to implement some basic intersection theory for curves. This includes computing intersection multiplicity and computing the set of intersection points between two curves. For plane curves, there is also a check now to determine whether two curves intersect transversely at a given point (this happens when the point is a nonsingular point of both curves, and the tangents of the curves there are distinct):

sage: A.<x,y> = AffineSpace(QQ, 2)
sage: C = Curve([y - x^3], A)
sage: D = Curve([y + x], A)
sage: C.intersection_points(D)
[(0,0)]
sage: Q = A([0,0])
sage: C.intersects_at(D, Q)
True
sage: C.intersection_multiplicity(D, Q)
1
sage: print C.tangents(Q)
[y]
sage: print D.tangents(Q)
[x + y]
sage: C.is_transverse(D, Q)
True

We are currently experimenting with implementing intersection multiplicity in general for projective/affine subschemes by using Serre’s Tor formula. To apply the formula, some restrictions on the dimensions of the subschemes and their intersection are needed, but plane curves that do not share common components already satisfy these conditions.

sage: P.<x,y,z,w> = ProjectiveSpace(QQ, 3)
sage: X = P.subscheme([x^2 - z^2 + x*y, y^2 - w*x])
sage: Y = P.subscheme([w^2 + x*y + z^2])
sage: Q = P([1,-1,0,1])
sage: X.intersection_multiplicity(Y, Q)
2

In ticket #20848 we have implemented degree computation for projective subschemes. The degree of a subscheme is computed from the Hilbert polynomial of that subscheme. In fact, it is just the leading coefficient of the Hilbert polynomial scaled by the factorial of the dimension of the subscheme:

sage: P.<x,y,z,w> = ProjectiveSpace(GF(13), 3)
sage: X = P.subscheme([y^2 - w^2])
sage: X.defining_ideal().hilbert_polynomial()
t^2 + 2*t + 1
sage: X.degree()
2

We have also implemented arithmetic genus computation for arbitrary (not necessarily plane) irreducible projective curves. This again can easily be computed from the Hilbert polynomial of the curve; if $P$ is the Hilbert polynomial of a projective curve, then the curve’s arithmetic genus is $1 - P(0)$:

sage: P.<x,y,z,w> = ProjectiveSpace(QQ, 3)
sage: C = P.curve([w*z - x^2, w^2 + y^2 + z^2])
sage: C.defining_ideal().hilbert_polynomial()
4*t
sage: C.arithmetic_genus()
1

Finally, in #20811 I will add classes to Sage for points on curves and give objects of those classes access to the singularity analysis functionality.

In the next half of GSoC 2016, my main goal is to implement resolution of singularities for curves. First up, in week 6 (June 27 – July 3) I will implement a function to compute ordinary plane curve models of curves. It can be proven that a plane curve can be transformed, via a finite sequence of change of coordinate maps and repeated application of the standard quadratic transformation map of $\mathbb{P}^{2}$, into a plane curve with only ordinary singularities. Combined with the plane curve model functionality implemented already, this provides a means of finding an ordinary plane curve birational to a given, arbitrary curve. The motivation for doing this is that ordinary singularities are nice singularities to work with (an ordinary singularity is a singular point at which the tangents of the plane curve there are distinct).

For example, once this is implemented, a possible application is an alternate (though probably not the most efficient) means of computing the geometric genus of a curve. Geometric genus is a birational invariant, and for a projective plane curve $C$ of degree $d$ with only ordinary singularities, we can compute its geometric genus as $\mathrm{genus}(C) = \frac{1}{2}((d-1)(d-2) - \sum_{P\in\mathrm{sing(C)}}\mathrm{mult}_{P}(C)(\mathrm{mult}_{P}(C) - 1))$, where $\mathrm{mult}$ denotes the multiplicity of a singularity, and $\mathrm{sing}$ denotes the set of singular points.

So far, participating in GSoC 2016 has been a lot of fun. I really appreciate this chance to contribute to Sage and the guidance of my project mentors Dr. Benjamin Hutz and Dr. Miguel Marco, who have already helped me so much to understand the needed concepts and improve my coding technique. I’ve been able to keep close to the progress timeline set for this project so far, and will do my best to stay on pace and implement the remaining project functionality goals in the second half of GSoC!