Crossing the transcendental divide: from Schottky groups to algebraic curves

Samantha Fairchild, Ángel David Ríos Ortiz

This is a Julia 1.8.2 notebook including computations used in the above named paper

All code was run on a MacBook Air with chip Apple M1 2020 and 8 GB memory

import Pkg   ##Import any necessary packages
   Resolving package versions...
  No Changes to `~/.julia/environments/v1.9/Project.toml`
  No Changes to `~/.julia/environments/v1.9/Manifest.toml`
   Resolving package versions...
  No Changes to `~/.julia/environments/v1.9/Project.toml`
  No Changes to `~/.julia/environments/v1.9/Manifest.toml`
   Resolving package versions...
  No Changes to `~/.julia/environments/v1.9/Project.toml`
  No Changes to `~/.julia/environments/v1.9/Manifest.toml`
using LinearAlgebra ##We use these particular Packages
using ImplicitEquations
using Plots

Generating elements of the free group as matrices

It is important to minimize matrix multiplication, so we generate words of bounded length, keeping a tag to keep track of the first and last letter of each word to allow for computing representatives of cosets.

##Function for constructing elements of free group up to bounded length
# list is elements of the form (matrix, last letter, first letter)

##INPUT: len is an integer >1
# g is the genus of the curve we are working with
# mat is a list of g 2x2 matrices generating the free group

function all_words_bounded_length(len,g, mat) #generate all words in the free group of length at most len
    word_list= [(Matrix{ComplexF64}(I,2,2),0,0)]  #initiate identity element in list
    for lev in 1:len
        old_list = copy(word_list) #take all words of length < lev
        word_list = [(Matrix{ComplexF64}(I,2,2),0,0)]
        for oldword in old_list #iterate through all words of length = lev-1 and multiply to generate all words of length lev
            for j in 1:g
                if oldword[3] !=0 #if not multiplying by the identity, first letter is unchaged
                    if oldword[2] != -j
                        push!(word_list, (Matrix{ComplexF64}(oldword[1]*mat[j]), j, oldword[3]))
                    if oldword[2] != j
                        push!(word_list, (Matrix{ComplexF64}(oldword[1]*mat[j]^(-1)), -j, oldword[3]))
                else #if multiplying by identity, first letter is last letter
                    if oldword[2] != -j
                        push!(word_list, (Matrix{ComplexF64}(oldword[1]*mat[j]), j, j))
                    if oldword[2] != j
                        push!(word_list, (Matrix{ComplexF64}(oldword[1]*mat[j]^(-1)), -j, -j))
    return word_list

all_words_bounded_length (generic function with 1 method)

Helper Functions

##Function for easier evaluation of Mobius transformations
function mobius(matrix, num) #given a matrix [a b ; c d] and a number z, return az+b/cz+d
    mobius = ((matrix[1,1]* num) + matrix[1,2])/((matrix[2,1]*num) + matrix[2,2])
    return mobius

##Function for constructing centers and radii of the circles
function point_construct(eta, theta, g)
    z1 = exp(im*((theta+eta)/2)) *sec((theta-eta)/2) #Center of generating circle
    zs = [exp(((j-1)*2*pi*im) / g) * z1 for j in 1:g] #input first tuple of centers z1,....,zg
    ws = [exp(((j-1)*2*pi*im) / g)*conj(z1) for j in 1:g] #input second tuple of center w1,...., wg
    rs = [tan((theta-eta)/2) for _ in 1:g] #input radii of circles r1,...,rg
    return (zs, ws, rs)

##Function for Constructing Matrices of the Schottky Group
function matrix_construct(ws, zs, rs)
    g = length(ws)
    M = zeros(Complex, 2,2)
    mat = [M for idx in 1:g] #create an array of length g where ith entry is matrix of f_i
    for j in 1:g
        argpt =  angle(ws[j] -zs[j]) #Angle between isometric circle centers
        A = Matrix{ComplexF64}([(exp(-im*argpt)* ws[j])/rs[j]  (-(ws[j]*zs[j]* exp(-2im*argpt) + rs[j]^2)/(rs[j]* exp(-im*argpt))); (exp(-im*argpt)/rs[j]) ((-zs[j]* exp(-im*argpt))/rs[j])])
        mat[j] = A
    return mat

##Function for evaluating truncated series
function ptheta(samplepts, mat,g, L) #samples Poincare Theta series for all words of length < L, given as a funciton of z
    sampleproj = [zeros(Complex,1,g) for idx in samplepts];
    list_of_words = all_words_bounded_length(L,g, mat)

    for j in 1:g #create the jth coset G/ [mat[j]]
        jCoset = []
        for pts in list_of_words
            if pts[2] != j
                if pts[2] != -j
                    push!(jCoset, pts)
        jCoset_mat = [pts[1] for pts in jCoset]
        ##Aj and Bj are the fixed points
        jDisc = sqrt((mat[j][1,1] - mat[j][2,2])^2 + 4*mat[j][1,2]* mat[j][2,1]) #discriminant is sqrt((a-d)^2 + 4bc)
        Aj = ((mat[j][1,1] - mat[j][2,2])+ jDisc)/(2*mat[j][2,1])
        Bj = ((mat[j][1,1] - mat[j][2,2])- jDisc)/(2*mat[j][2,1])

        #Now for each point in sapmleproj, evaluate truncated series and place into sampleproj[k][j]
        for k in 1:length(samplepts)
            samplevalue = 0
            for sig in jCoset_mat
                samplevalue = samplevalue + 1/(samplepts[k] - mobius(sig, Bj)) - 1/(samplepts[k] - mobius(sig, Aj))
            sampleproj[k][j] = samplevalue
    return sampleproj

ptheta (generic function with 1 method)

Finding Weierstrass points in the case of Genus 2

Input - Genus \(g\) - Two numbers \(0<\eta < \theta < \frac{\pi}{g}.\) - The length of words that we are generating in the free group \(L\)

Output The approximate algebraic curve associated to the constructed curve, where we compute the 6 weierstrass points.

function Genus2_WP(eta, theta, L)
    g=2 ##We are in the case of genus 2
    #L is the length of words that we are generating in the free group
    (zs, ws, rs) = point_construct(eta, theta, g)
    println("Initialized circles")

    ##6 Weierstrass points
    ##Solve for the two intersection points of C1 with circle centered at sec((theta + eta) / 2)
    samplepts = [1, -1, exp(im*theta), exp(im*eta), exp(im* (pi-theta)), exp(im*(pi - eta))]

    println("Sampled 6 points on the curve")

    mat = matrix_construct(ws, zs, rs)
    println("Computed Matrices")

    ##Evaluate the points in P^1 which are approximate images of the algebraic curve
    PointsinPg = ptheta(samplepts, mat, g, L)

    ##Find approximate algebraic curve by finding 6 Weierstrass points
    Wpts = [PointsinPg[j][1]/PointsinPg[j][2] for j in 1:6]
    return Wpts

Genus2_WP (generic function with 1 method)
𝜂 = pi /12  #Pick a positive number less than 𝜃
𝜃 =pi/4 # Pick a positive number less than 𝜋/3
L = 8 # L is the length of words that we generate in the free group

p = Genus2_WP(𝜂,𝜃, L)
Initialized circles
Sampled 6 points on the curve
Computed Matrices
6-element Vector{ComplexF64}:
  -11.474487171228489 + 1.030089577387785e-12im
 -0.08714986431005209 + 1.1554187097143076e-14im
    4.084600904724572 - 8.763125896606477e-13im
  -11.787692697355707 + 3.887475415761668e-12im
  0.24482196016832608 - 1.3653789500384812e-14im
 -0.08483424412857071 + 1.9297359926505143e-15im
##Verify conjugate pairs
p[1]*p[2] , p[3]*p[5], p[4]*p[6]
(0.9999999999999962 - 2.2235053851668632e-13im, 0.9999999999999878 - 2.7031084686709243e-13im, 1.0000000000000442 - 3.525381733329332e-13im)
####Plot graph using Plots and Implicit Equations
f(x,y) = y^2 - ((x-real(p[1]))* (x-real(p[2]))*(x-real(p[3]))* (x-real(p[4]))*(x-real(p[5]))* (x-real(p[6])))

plot(f  0, xlims = (-20,10), ylims=(-1000,1000), M=10, N=10, linewidth=2)