I am currently implementing an algorithm for computing the valuation divisor of a differential on a Riemann surface. The valuation divisor
is a formal sum of places and on the surface such that is zero at each with multiplicity and has a pole at each of order . Valuation divisors of differentials play a key role in computing the Riemann constant vector, a major goal of my code.
The way in which I find these places depends whether the place is discriminant or not. That is, if the x-projection of the place onto the curve is a discriminant point of the curve.
If is discriminant, meaning that is a place that might share an x- or y-projection with some other place onto the curve, then is given in terms of a Puiseux series expansion, . The strategy is to then “localize” at this place by writing
using the technique discussed in my previous post.
So in the discriminant case, if above is less than zero then is a pole of order and if it’s greater than zero then it’s a zero of order .
In the case when is regular we can also use Puiseux series expansions. However, it may be more computationally efficient to use Hasse derivatives.
Hasse Derivatives
Let and be a polynomial in the . Consider another vector of indeterminants and the polynomial
where each of the ’s are polynomials in . These polynomials are called the k’th Hasse derivatives of the polynomial . The notion of a Hasse derivative extends to all analytic functions and even functions defined over finite fields.
The multiplicity of at is the smallest integer such that for all but for some .
The code for efficiently doing this in Sympy is very clean:
From my tests it’s 20-200 times faster than a “naive approach” using partial derivative evaluation at .
Note that the “strategy” used to determine the membership and
multiplicity of in the valuation divisor is strongly dependent of
what kind of place we’re looking at. Therefore, the Place
class and
subclasses RegularPlace
and DiscriminantPlace
should contain the
logic for this membership. I realized this after attempting to implement
the algorithm within the Differential
class as
Differential.valuation_divisor()
and ran into a bunch of conditional
statements that looked like this:
This is a clear violation of the Open/Closed Principle (pdf): suppose I were
to add another Place
type, say, InfinitePlace
. I would then have to
add another set of conditionals wherever they may appear in this
algorithm. Suppose later someone who isn’t as familiar with my code
wants to add yet another place type. Although I (currently) remember
where these lines sit this poor contributor would have to hunt and peck
or receive numerous runtime errors until they add conditionals wherever
one should exist for their new type.
Hence, the code should look more like this:
(Let’s set aside the fact that I’ve already implemented artihmetic on
divisors so that the statement D += multiplicity * P
makes sense and
works.) This way, a new Place
can immediately be used in computing
valuation divisors by simply defining the valuation()
method.
At this stage in my career this is practically obvious to me and probably to any programmer reading this. However, I would not have thought of this two years ago. Plus, I wanted to share because it’s exciting seeing abstract mathematical objects become completely explicit and solidified in well-organized code.