Linking numbers of modular knots and scalar products of modular forms¶

The aim of this jupyter notebook is to investigate quantities depending on pairs of conjugacy classes in the modular groups, more precisely identities and statistical properties for linking numbers between modular knots and Weil Peterssen scalar products between modular forms (also corresponding to periods of relative theta series).¶
In [1]:
### ### LIBRARIES ### ###

import numpy as np
from cypari import pari
# import matplotlib.pyplot as plt
from typing import List, Callable, Tuple 
from math import gcd
from functools import reduce

Dictionary of Modular objects, all primitive : Qfb(Z), sl(2;Z), SL(2;Z), RL, Ecf¶

We can translate $Qfb \leftrightarrow sl(2) \leftarrow SL(2) \leftarrow RLw \leftrightarrow Ecf \leftarrow Qfb$.

We first translate $Qfb \leftrightarrow sl(2) \leftarrow SL(2)$.

In [2]:
def sl2_of_Qfb(Q):
    l = pari.component(Q,1)
    m = pari.component(Q,2)
    r = pari.component(Q,3)
    aa = pari.matrix(2,2,[-m,-2*r,2*l,m])
    return aa/pari('2')

def Qfb_of_sl2(a):
    l = a[1,0]
    m = (a[1,1]-a[0,0])
    r = -a[0,1]
    # u = pari.gcd([l,m,r])
    return pari.Qfb(l,m,r)

def sl2_of_SL2(A):
    t = pari.trace(A)
    a = 2*A-t
    u = pari.gcd([a[0,0], a[0,1], a[1,0]])
    if t!=0:
        u *= pari.sign(t)
    return(a/u)
In [3]:
'''Some tests'''

Q1 = pari.Qfb(11, 72, -66)
q1 = sl2_of_Qfb(Q1)
print(Q1, '\t', q1, '\t', Q1 == Qfb_of_sl2(q1))

Q2 = pari.Qfb(12, 72, -66)
q2 = sl2_of_Qfb(Q2)
print(q2, Q2, Q2 == Qfb_of_sl2(q2), '\n')

print(sl2_of_SL2(pari.matrix(2,2,[8,9,7,8])))
print(Qfb_of_sl2(sl2_of_SL2(pari.matrix(2,2,[8,9,7,8]))))
print(sl2_of_Qfb(Qfb_of_sl2(sl2_of_SL2(pari.matrix(2,2,[8,9,7,8])))), '\n')

M = pari("(1/d)*[-k, 0, 0, d^2-k^2 ; 0, 0, d, 0 ; 0, d, 0, 0 ; 1, 0, 0, k]")
Id4 = pari.matid(4)
PMX = pari.matdet(M-'X'*Id4)
print(pari.matdet(M), '\t', pari.trace(M))
print(PMX)
print(pari.factor(PMX))
print(pari.sign(1)==1, pari.gcd([1/2,2]))
Qfb(11, 72, -66) 	 [-36, 66; 11, 36] 	 True
[-36, 66; 12, 36] Qfb(12, 72, -66) True 

[0, 9; 7, 0]
Qfb(7, 0, -9)
[0, 9; 7, 0] 

1 	 0
(X^4 - 2*X^2 + 1)
[X - 1, 2; X + 1, 2]
True 1

We now translate $SL2 \leftarrow RLw \leftrightarrow Ecf$.

In [4]:
''' SL2 Matrices, SL2q Matrices, RL words, Euclidean continued fractions '''
''' (Beware that by convention all euclidean periods start with the R exponent) '''

def SL2_of_RLw(word):

    L1 = pari("[1,0;1,1]")
    R1 = pari("[1,1;0,1]")

    mat = pari("[1,0;0,1]")
    for C in word:
        if C=='R':
            mat=mat*R1
        elif C=='L':
            mat=mat*L1
        else:
            raise Exception("Not an RLw as it contains {} of type {}".format(C,type(C)))
    return(mat)

def SL2q_of_RLw(word):

    L1 = pari("[q,0;1,1/q]")
    R1 = pari("[q,1;0,1/q]")

    mat = pari("[1,0;0,1]")
    for C in word:
        if C=='R':
            mat=mat*R1
        elif C=='L':
            mat=mat*L1
        else:
            raise Exception("Not an RLw as it contains {} of type {}".format(C,type(C)))
    return(mat)

def SL2_of_Ecf(ecf):

    L1 = pari("[1,0;1,1]")
    R1 = pari("[1,1;0,1]")

    mat = pari("[1,0;0,1]")
    for k,n in enumerate(ecf):
        if k%2==0 :
            mat=mat*(R1**n)
        if k%2==1 :
            mat=mat*(L1**n)
    return(mat)

def SL2q_of_Ecf(ecf):

    L1 = pari("[q,0;1,1/q]")
    R1 = pari("[q,1;0,1/q]")

    mat = pari("[1,0;0,1]")
    for k,n in enumerate(ecf):
        if k%2==0 :
            mat=mat*(R1**n)
        if k%2==1 :
            mat=mat*(L1**n)
    return(mat)


def RLw_of_Ecf(ecf):
    word = ''
    for k,n in enumerate(ecf) :
        if k%2==0:
            word+='R'*n
        elif k%2==1:
            word+='L'*n
    return(word)

def Ecf_of_RLw(word):
    
    if not word:
        return([])
    
    first_letter = word[0]
    current_letter = word[0]
    exponents = [0]

    if word[0]=='L':
        exponents+=[0]
    
    for c in word:
        if c == current_letter:
            exponents[-1] += 1
        else:
            current_letter = c
            exponents.append(1) 
    return exponents
In [5]:
'''Some Tests'''
print(RLw_of_Ecf([1,2,1,1,2,2,2,1,1,2,1,1,2,2,2,1,1,2,1]))
print(Ecf_of_RLw(''), Ecf_of_RLw('R'), Ecf_of_RLw('L'))
print(Ecf_of_RLw('RL'), Ecf_of_RLw('LR'), Ecf_of_RLw('LRLLRL'))
print(SL2_of_RLw('RLLRLRRLLRRLRLLRLRRLLRRLRLLR'))
print(RLw_of_Ecf([1,3,2,1,3]))
RLLRLRRLLRRLRLLRLRRLLRRLRLLR
[] [1] [0, 1]
[1, 1] [0, 1, 1] [0, 1, 1, 2, 1, 1]
[144075, 199796; 103894, 144075]
RLLLRRLRRR

Finally we translate $Ecf \leftarrow Qfb$, first extracting the first root of a Qfb to compute its continued fraction and find the corresponding Ecf by inspection, and then using the Conway river function relying on the systematic reduction of quadratic forms by Lagrange.

In [6]:
def exact_firoot_Qfb(Q):
    
    l = pari.component(Q,1)
    m = pari.component(Q,2)
    r = pari.component(Q,3)
    f = pari("{}*x^2+{}*x+{}".format(l,m,r))
    
    Delta = pari.poldisc(f)
    # w = (beta+sqrt(Delta))/2 with beta = 0,1 = Delta mod 2
    w = pari.quadgen(Delta)
    
    if pari.Mod(Delta,4) == 0 :
        root = 2*w
    elif pari.Mod(Delta,4) == 1 :
        root = 2*w-1
    
    phi = (-m+root)/(2*l)
    return phi

# Remark : the dicriminant of the field Q(sqrt(Delta))
# fundisc = pari.coredisc(Delta)
# but we don't care in so far as we compute with Delta in O_\delta

def approx_firoot_Qfb(Q):
    
    l = pari.component(Q,1)
    m = pari.component(Q,2)
    r = pari.component(Q,3)
    f = pari("{}*x^2+{}*x+{}".format(l,m,r))
    
    phi = pari.polroots(f)
    phi0, phi1 = phi[0].real(), phi[1].real()
    
    if l>0 :
        return pari.max(phi0, phi1) 
    elif l<0 :
        return pari.min(phi0, phi1)
In [7]:
'''Some tests'''
Q1 = pari.Qfb(-11, 71, -66)
print(exact_firoot_Qfb(Q1))
print(exact_firoot_Qfb(Q1)*1.)
print(approx_firoot_Qfb(Q1))
print(pari.contfrac(approx_firoot_Qfb(Q1)))
36/11 - 1/11*w
1.12601375794069
1.12601375794069
[1, 7, 1, 14, 1, 1, 6, 11, 2, 2, 11, 6, 1, 1, 14, 1, 7]

Recuperating class groups of a given discriminant¶

In [8]:
def indiQuad(disc):
    
    ClDisc = pari.quadclassunit(disc) #print('Structure = \t', ClDisc, '\n')
    
    if ClDisc[0]==1:
        print("Principal ideal domain")
        return([(),()])
    
    factorders = ClDisc[1]
    generators = ClDisc[2]
    principal = pari.qfbpow(generators[0], 0)
    
    #indices = [()] #neutral
    #quadClasses = [principal]
    indiQuad = [((),principal)]
    
    for factor in range(len(factorders)):
        
        order = factorders[factor]
        generator = generators[factor]
        
        #indices = [index+(exponent,) for index in indices for exponent in range(order)]
        
        #quadClasses = [pari.qfbcomp(quadclass, pari.qfbpow(generator, exponent))\
        #               for quadclass in quadClasses for exponent in range(order)]
        
        indiQuad = [(indi+(exponent,), pari.qfbred(pari.qfbcomp(quad, pari.qfbpow(generator, exponent)))) \
                    for (indi, quad) in indiQuad for exponent in range(order)]
        
    return(indiQuad)
In [9]:
indiQuad(1596)
Out[9]:
[((0, 0), Qfb(1, 38, -38)),
 ((0, 1), Qfb(7, 28, -29)),
 ((1, 0), Qfb(17, 24, -15)),
 ((1, 1), Qfb(-10, 26, 23)),
 ((2, 0), Qfb(-3, 36, 25)),
 ((2, 1), Qfb(-2, 38, 19)),
 ((3, 0), Qfb(-15, 24, 17)),
 ((3, 1), Qfb(11, 32, -13))]

We collected the following list of discriminants with interesting class groups by inspection, and encoded their class groups as tables of Ecf (also computed by inspection, ie without the River, but checked).

In [10]:
perioDisc = dict()

############ CYCLIC GROUPS [2] ############

###### CYCLIC GROUPS [2] ######
perioDisc[60] = [[6, 1], [3,2]]
perioDisc[40] = [[6, 6], [2, 1, 1, 2, 1, 1]]

###### CYCLIC GROUPS [3] ######
perioDisc[316] = [[7, 1, 16, 1], [1, 1, 5, 3, 2, 1], [3, 5, 1, 1, 1, 2]]
perioDisc[229] = [[15,15], [2, 4, 1, 2, 4, 1], [2, 1, 4, 2, 1, 4]]

###### CYCLIC GROUPS [4] ######
perioDisc[876] = [[3, 1, 28, 1], [1, 3, 1, 2, 5, 1], [1, 13, 1, 8], [5, 2, 1, 3, 1, 1]]
perioDisc[145] = [[1, 11, 1, 1, 11, 1], [3, 5, 1, 3, 5, 1], 
                  [1, 1, 1, 2, 2, 1, 1, 1, 2, 2], [3, 1, 5, 3, 1, 5]]

perioDisc[8088] = [[88, 1, 28, 1],[1, 4, 2, 2, 3, 1, 2, 7],
                   [2, 44, 2, 14],[2, 4, 1, 7, 2, 1, 3, 2]]


###### CYCLIC GROUPS [5] ######
perioDisc[1756] = [[19, 1, 40, 1], [1, 1, 6, 3, 13, 1], [1, 2, 3, 1, 3, 1, 7, 1], 
                   [1, 3, 1, 3, 2, 1, 1, 7], [3, 6, 1, 1, 1, 13]]
perioDisc[401] = [[1, 19, 1, 1, 19, 1], [1, 2, 4, 1, 1, 1, 2, 4, 1, 1], [1, 3, 9, 1, 3, 9],
                  [1, 9, 3, 1, 9, 3], [1, 1, 4, 2, 1, 1, 1, 4, 2, 1]]

###### CYCLIC GROUPS [6] ######
perioDisc[1708] = [[1, 1, 40, 1], [1, 1, 1, 1, 3, 6],
                   [4, 13, 1, 1], [4, 1, 19, 1], 
                   [1, 13, 4, 1], [3, 1, 1, 1, 1, 6]]
perioDisc[1384] = [[1, 1, 1, 36, 1, 1, 1, 1, 36, 1], [6, 1, 11, 1, 1, 6, 1, 11, 1, 1],
                   [1, 5, 2, 3, 3, 1, 5, 2, 3, 3], [3, 18, 3, 3, 18, 3], 
                   [5, 1, 3, 3, 2, 5, 1, 3, 3, 2], [1, 6, 1, 1, 11, 1, 6, 1, 1, 11]]

###### CYCLIC GROUP [7] ######
perioDisc[4348] = [[31, 1, 64, 1], 
                   [1, 1, 10, 3, 21,1], 
                   [3, 9, 7, 4, 1, 1],
                   [5, 3, 1, 2, 2, 1, 1, 1, 2, 1], 
                   [1, 1, 1, 2, 2, 1, 3, 5, 1, 2],
                   [9, 3, 1, 1, 4, 7],
                   [1, 1, 1, 21, 3, 10]]

perioDisc[577] = [[1, 23, 1, 1, 23, 1], 
                  [11, 3, 1, 11, 3, 1], [1, 5, 7, 1, 5, 7], 
                  [1, 3, 2, 2, 1, 1, 3, 2, 2, 1], [1, 2, 2, 3, 1, 1, 2, 2, 3, 1], 
                  [7, 5, 1, 7, 5, 1], [1, 3, 11, 1, 3, 11]]

############ NON CYCLIC ############


###### NON CYCLIC GROUP [2,2] ######
perioDisc[780] = [[26, 1], [1, 4, 1, 3], 
                  [1, 8, 1, 1], [13, 2]]
perioDisc[520] = [[2, 22, 2, 2, 22, 2], [7, 7, 2, 7, 7, 2], 
                  [1, 3, 4, 3, 1, 1, 3, 4, 3, 1], [2, 2, 1, 10, 1, 2, 2, 1, 10, 1]]

###### NON CYCLIC GROUP [2,4] ######
perioDisc[1596] = [[38, 1], [19, 2], 
                  [3, 1, 2, 3], [7, 2, 1, 1], 
                  [1, 12, 1, 1], [1, 4, 1, 5], 
                  [3, 3, 2, 1], [7, 1, 1, 2]]
perioDisc[1768] = [[42, 42], [1, 1, 20, 1, 1, 20], 
                  [2, 13, 1, 2, 13,1], [1, 5, 6, 1, 5, 6], 
                  [2, 4, 4, 2, 4, 4], [2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1], 
                  [2, 1, 13, 2, 1, 13], [6, 5, 1, 6, 5, 1]]


print(perioDisc.keys())
dict_keys([60, 40, 316, 229, 876, 145, 8088, 1756, 401, 1708, 1384, 4348, 577, 780, 520, 1596, 1768])

Combinatorial functions on RLw and Ecf¶

List of circular shifts and Lyndon representative (maximal representative, to have uniqueness)¶

In [11]:
'''List of circular shifts and Lyndon representative (maximal representative, to have uniqueness)'''

def list_of_circular_shifts(word):
    n = len(word)
    return ["".join([word[i - j] for i in range(n)]) for j in range(n,0,-1)]


def lyndon_of_word(cycle, comp = max):
    if cycle == '':
        return ''
    return comp(list_of_circular_shifts(cycle))
In [12]:
wA = 'RLRLRLRLRLRLRRRRL'
lyndon_of_word(wA), list_of_circular_shifts(wA),
Out[12]:
('RRRRLRLRLRLRLRLRL',
 ['RLRLRLRLRLRLRRRRL',
  'LRLRLRLRLRLRRRRLR',
  'RLRLRLRLRLRRRRLRL',
  'LRLRLRLRLRRRRLRLR',
  'RLRLRLRLRRRRLRLRL',
  'LRLRLRLRRRRLRLRLR',
  'RLRLRLRRRRLRLRLRL',
  'LRLRLRRRRLRLRLRLR',
  'RLRLRRRRLRLRLRLRL',
  'LRLRRRRLRLRLRLRLR',
  'RLRRRRLRLRLRLRLRL',
  'LRRRRLRLRLRLRLRLR',
  'RRRRLRLRLRLRLRLRL',
  'RRRLRLRLRLRLRLRLR',
  'RRLRLRLRLRLRLRLRR',
  'RLRLRLRLRLRLRLRRR',
  'LRLRLRLRLRLRLRRRR'])
In [13]:
def Rad_RLw(word):
    return word.count('R')-word.count('L')

def Rad_Ecf(ecf):
    numR = sum([r for r in ecf[0::2]])
    numL = sum([r for r in ecf[1::2]])
    return numR-numL
In [14]:
print(Rad_RLw('LRRLLLRLRLRLRRRRLRRL')) 
print(Rad_Ecf([0,1,2,3]))
2
-2

Involutions (inverse, transpose, conJ) on RL words¶

In [15]:
def transpose_RLw(word):
    """
    transpose_RLw : str('RLL') --> str('RRL')
    """
    wordPR = word.replace('L','P')
    wordPL = wordPR.replace('R','L')
    wordLR = wordPL.replace('P','R')
    return wordLR[::-1]


def transpose_Ecf(ecf):
    if len(ecf)%2==1:
        raise Exception("Ecf of odd lenght")
    return ecf[::-1]


def conJ_RLw(word):
    """
    The J conjugation on words exchangies R and L
    conJ_RLw : str('RLL') --> str('LRR') 
    """
    wordPR = word.replace('L','P')
    wordPL = wordPR.replace('R','L')
    wordLR = wordPL.replace('P','R')
    return wordLR

def conJ_Ecf(ecf):
    if len(ecf)%2==1:
        raise Exception("Ecf of odd length")
    return (ecf[1:]+ecf[0:1])

"""
def conJ_RLw(word):
    to_return = ""
    for c in word:
        if c == 'L':
            to_return += 'R'
        else:
            to_return += 'L'

    return to_return
"""

def is_symmetric(word):
    return lyndon_of_word(word) == lyndon_of_word(transpose_RLw(word))

def is_inert(word):
    return lyndon_of_word(word) == lyndon_of_word(conJ_RLw(word))

def is_reciprocal(word):
    return lyndon_of_word(word) == lyndon_of_word(transpose_RLw(conJ_RLw(word)))
In [16]:
is_symmetric('LRRLRLLRLRR'), is_inert('LRRLRLLRLRR'), is_reciprocal('LRRLRLLRLRR')
Out[16]:
(False, False, True)

Linking intersection cosign cocycle on RLw and Ecf¶

Lexicorder and symplectic intersection (for RLw with distinct last letters, otherwize gives 0)¶

The following functions apply to words on any alphabet whose elements are linearly ordered.

In [17]:
def orderlex(wa, wb, ka=0, kb=0):
    """ lexicographic order on infinite words, returns : 
    -1 if the ka-th shift of wA^infty is smaller than the kB-th shift of wB^infty, 
    =0 if the ka-th shift of wA^infty is equal to the kB-th shift of wB^infty, 
    +1 if the ka-th shift of wA^infty is larger than the kB-th shift of wB^infty
    (this can be determined by looking only length(l1)+length(l2) letters 
    since wa^infty = wb^infty iff wa.wb = wb.wb).
    """
    k = 0
    while( (wa[(k+ka)%len(wa)] == wb[(k+kb)%len(wb)]) & (k <= (len(wa)+len(wb))) ):
        k+=1
    if wa[(k+ka)%len(wa)] < wb[(k+kb)%len(wb)]:
        return -1
    if wa[(k+ka)%len(wa)] > wb[(k+kb)%len(wb)]:
        return 1
    if wa[(k+ka)%len(wa)] == wb[(k+kb)%len(wb)]:
        return 0

def cross_RLw(wa, wb, ka=0, kb=0):
    """symplectic intersection of words RLw provided last letters are distinct"""
    if (wa[(ka-1)%len(wa)] < wb[(kb-1)%len(wb)]) & (orderlex(wa,wb,ka,kb) == +1):
        return -1
    if (wa[(ka-1)%len(wa)] > wb[(kb-1)%len(wb)]) & (orderlex(wa,wb,ka,kb) == -1):
        return +1
    return 0
In [48]:
def link_RLw(wa, wb):
    '''Computes the sum of abs(cross_RLw(wA,wB))) over all pairs of cyclic representatives'''
    c = 0
    for ka in range(len(wa)):
        for kb in range(len(wb)):
            if (wa[(ka-1)%len(wa)] < wb[(kb-1)%len(wb)]) & (orderlex(wa,wb,ka,kb) == +1):
                c+=1
            if (wa[(ka-1)%len(wa)] > wb[(kb-1)%len(wb)]) & (orderlex(wa,wb,ka,kb) == -1):
                c+=1
    return c//2

def half_inter_RLw(wA,wB):
    """ This returns half the geometric intersection number.
    Geometric intersection is even for loops in a spherical orbifold. """
    wBT = transpose_RLw(wB)
    return link_RLw(wA,wB)+link_RLw(wA,wBT)

def coboundary_link_RLw(wA,wB,wC):
    return link_RLw(wA,wB)+link_RLw(wA,wC)-link_RLw(wA, wB+wC)

def cosign_sum_RLw(wA,wB):
    wBT = transpose_RLw(wB)
    return link_RLw(wA,wB)-link_RLw(wA,wBT)

def cosign_cocycle_RLw(wA,wB,wC):
    return cosign_sum_RLw(wA,wB)+cosign_sum_RLw(wA,wC)-cosign_sum_RLw(wA, wB+wC)

def link_homology_Rad_RLw(wA,wB):
    return link_RLw(wA,wB)-Rad_RLw(wA)*Rad_RLw(wB)+6*Rad_RLw(wA)*Rad_RLw(wB)
In [49]:
print(link_RLw('RLL','RLL'))
print(link_RLw('RLL','RRL'))

print(link_RLw('RLLL','RLLL'))
print(link_RLw('RLLL','RRRL'))
2
1
3
1
In [50]:
a='RLLRL'
x='RLLLR'
y='RLLRL'

print(link_RLw(a,x), half_inter_RLw(a,x), cosign_sum_RLw(a,x), '\n')
print(link_RLw(a,y), half_inter_RLw(a,y), cosign_sum_RLw(a,y), '\n')
print(link_RLw(x,y), half_inter_RLw(x,y), cosign_sum_RLw(x,y), '\n')

print(cosign_sum_RLw(x,y))
print(cosign_cocycle_RLw(a,x,y))
print(coboundary_link_RLw(a,x,y))
5 9 1 

6 10 2 

5 9 1 

1
0
0
In [51]:
a = 'LLRLRRLRRLRLLRL' 
b = 'RLLRLLLLLRRRRLLLRLRLR'
c = 'LLLLLRLLLRLLLLLRLRLRL'

RbL = 'R'+b+'L'
LbR = 'L'+b+'R'
LbL = 'L'+b+'L'
RbR = 'R'+b+'R'

RcL = 'R'+c+'L'
LcR = 'L'+c+'R'
LcL = 'L'+c+'L'
RcR = 'R'+c+'R'


print(coboundary_link_RLw(a,RbL,RcR), coboundary_link_RLw(a,RbR,LcR), \
      coboundary_link_RLw(a,LbL,LcL), coboundary_link_RLw(a,RbR,RcR), )

print('\n By appropriate choices of a,b,c, we can find non vanishing of any of the following quantities, \n \
            are there is always at least one that must vanish ? which one depending on the cross and cosign ?' )

print(coboundary_link_RLw(a,RbL,RcL), coboundary_link_RLw(a,RbL,LcR), \
      coboundary_link_RLw(a,RbL,LcL), coboundary_link_RLw(a,RbL,RcR), )

print(coboundary_link_RLw(a,LbR,RcL), coboundary_link_RLw(a,LbR,LcR), \
      coboundary_link_RLw(a,LbR,LcL), coboundary_link_RLw(a,LbR,RcR), )

print(coboundary_link_RLw(a,LbL,RcL), coboundary_link_RLw(a,LbL,LcR), \
      coboundary_link_RLw(a,LbL,LcL), coboundary_link_RLw(a,LbL,RcR), )

print(coboundary_link_RLw(a,RbR,RcL), coboundary_link_RLw(a,RbR,LcR), \
      coboundary_link_RLw(a,RbR,LcL), coboundary_link_RLw(a,RbR,RcR), )
2 -3 -1 -1

 By appropriate choices of a,b,c, we can find non vanishing of any of the following quantities, 
             are there is always at least one that must vanish ? which one depending on the cross and cosign ?
-1 5 -2 2
2 -1 -4 1
0 2 -1 -1
-4 -3 -10 -1

Observing linking, half_intersection, cosign_sum and link_cocycle in a given discriminant¶

In [52]:
def link_Ecf(ecfA,ecfB):
    RLwA = RLw_of_Ecf(ecfA)
    RLwB = RLw_of_Ecf(ecfB)
    return link_RLw(RLwA, RLwB)

def half_inter_Ecf(ecfA,ecfB):
    RLwA = RLw_of_Ecf(ecfA)
    RLwB = RLw_of_Ecf(ecfB)
    RLwBT = transpose_RLw(RLwB)
    return link_RLw(RLwA,RLwB)+link_RLw(RLwA,RLwBT)

def coboundary_link_Ecf(ecfA,ecfB,ecfC):
    RLwA = RLw_of_Ecf(ecfA)
    RLwB = RLw_of_Ecf(ecfB)
    RLwC = RLw_of_Ecf(ecfC)    
    return coboundary_link_RLw(RLwA,RLwB,RLwC)

def cosign_sum_Ecf(ecfA,ecfB):
    RLwA = RLw_of_Ecf(ecfA)
    RLwB = RLw_of_Ecf(ecfB)
    RLwBT = transpose_RLw(RLwB)
    return link_RLw(RLwA,RLwB)-link_RLw(RLwA,RLwBT)

def cosign_cocycle_Ecf(ecfA,ecfB,ecfC):
    RLwA = RLw_of_Ecf(ecfA)
    RLwB = RLw_of_Ecf(ecfB)
    RLwC = RLw_of_Ecf(ecfC)
    return cosign_cocycle_RLw(RLwA,RLwB,RLwC)

def link_homology_Rad_Ecf(ecfA,ecfB):
    RLwA = RLw_of_Ecf(ecfA)
    RLwB = RLw_of_Ecf(ecfB)
    return link_RLw(RLwA,RLwB)-Rad_RLw(RLwA)*Rad_RLw(RLwB)
In [53]:
p1 = [2,1,1,2,1,9]
p2 = [2,1,1,5]
print(link_Ecf(p1,p2), half_inter_Ecf(p1,p2), cosign_sum_Ecf(p1,p2), '\n')

p3 = [2,4,5,3]
print(link_Ecf(p3,p3), half_inter_Ecf(p3,p3), cosign_sum_Ecf(p3,p3), '\n')

print(cosign_sum_Ecf(p1,p2))
print(cosign_cocycle_Ecf(p1,p2,p3), cosign_cocycle_Ecf(p1,p2,p3), '\n')

aR=[1,2,42,1]
aL=[0]+aR
xR=[1,2,1,4,2,1]
xL=[0]+xR
yR=[1,1,1,2,3,4,5,8]
yL=[0]+yR


print(coboundary_link_Ecf(aL,xL,yL), coboundary_link_Ecf(aR,yL,yL))
print(coboundary_link_Ecf(aL,xL,yR), coboundary_link_Ecf(aR,xL,yR))
print(coboundary_link_Ecf(aL,xR,yL), coboundary_link_Ecf(aR,xR,yL))
print(coboundary_link_Ecf(aL,xR,yR), coboundary_link_Ecf(aR,xR,yR))
14 24 4 

21 41 1 

4
0 0 

0 0
0 2
2 0
0 0
In [54]:
# A = [1,2,3,1,1,2,3,1,2,1]
# B = [1,2,5,2,1,1,2,1,2,3]

# A = [1,2,3,1,1,2,3,13]
# B = [1,2,5,2,1,1,2,12]

A = [1,2,3,1,8,2,3,1]
B = [1,2,5,2,9,1,2,9]

AJ = conJ_Ecf(A)
BJ = conJ_Ecf(B)
AT = transpose_Ecf(A)
BT = transpose_Ecf(B)
print(A,B, '\n', AT,BT, '\n', AJ,BJ)

print(link_Ecf(A,B), cosign_sum_Ecf(A,B), half_inter_Ecf(A,B))
# print(link_Ecf(A,BT), cosign_sum(A,BT), halfinter_Ecf(A,BT))
print(link_Ecf(A,BJ), cosign_sum_Ecf(A,BJ), half_inter_Ecf(A,BJ))

print(link_Ecf(A,B)-Rad_Ecf(A)*Rad_Ecf(B))
# print(Rad_Ecf(A), Rad_Ecf(B), Rad_Ecf(A)*Rad_Ecf(B))
[1, 2, 3, 1, 8, 2, 3, 1] [1, 2, 5, 2, 9, 1, 2, 9] 
 [1, 3, 2, 8, 1, 3, 2, 1] [9, 2, 1, 9, 2, 5, 2, 1] 
 [2, 3, 1, 8, 2, 3, 1, 1] [2, 5, 2, 9, 1, 2, 9, 1]
50 6 94
44 -5 93
23
In [ ]:
 

Bilinear matrices (of Link Inter Cosign) on class groups¶

In [79]:
help(pari.matrix)
Help on built-in function matrix:

matrix(...) method of cypari._pari.Pari instance
    matrix(long m, long n, entries=None): Create and return the m x n
    PARI matrix with given list of entries.
    
    Examples:
    
    >>> from cypari import pari
    >>> pari.matrix(3, 3, range(9))
    [0, 1, 2; 3, 4, 5; 6, 7, 8]

In [104]:
def bilinear_matrix(function_of_periods, sequence_of_periods):
    size = len(sequence_of_periods)
    bilist = []
    bil_mat = [[0 for i in sequence_of_periods] for j in sequence_of_periods]
    for i, p1 in enumerate(sequence_of_periods):
        for j, p2 in enumerate(sequence_of_periods):
            bil_mat[i][j]=function_of_periods(p1,p2)
            bilist.append(function_of_periods(p1,p2))
        print(bil_mat[i])
    bil_mat_pari = pari.matrix(size, size, bilist)
    print(bil_mat_pari.matdet())
    # return bil_mat, 
In [106]:
disc = 1596
modulus = 5
print('\n', pari.factor(disc), '\n')
bilinear_matrix(cosign_sum_Ecf, perioDisc[disc])
print('\n', modulus, (pari.factor(disc)[:1])%modulus, '\n')
bilinear_matrix(lambda x,y : cosign_sum_Ecf(x,y)%modulus, perioDisc[disc])
 [2, 2; 3, 1; 7, 1; 19, 1] 

[37, 17, 1, 5, -11, -7, 1, 5]
[17, 17, 1, 5, -11, -7, 1, 5]
[1, 1, 1, 1, -2, -2, 1, 1]
[5, 5, 1, 5, -5, -7, 1, 5]
[-11, -11, -2, -5, 12, 9, -2, -5]
[-7, -7, -2, -7, 9, 14, -2, -7]
[1, 1, 1, 1, -2, -2, 1, 1]
[5, 5, 1, 5, -5, -7, 1, 5]
0

 5 [[2, 3, 2, 4]~] 

[2, 2, 1, 0, 4, 3, 1, 0]
[2, 2, 1, 0, 4, 3, 1, 0]
[1, 1, 1, 1, 3, 3, 1, 1]
[0, 0, 1, 0, 0, 3, 1, 0]
[4, 4, 3, 0, 2, 4, 3, 0]
[3, 3, 3, 3, 4, 4, 3, 3]
[1, 1, 1, 1, 3, 3, 1, 1]
[0, 0, 1, 0, 0, 3, 1, 0]
0
In [109]:
print(perioDisc.keys())
disc = 876
modulus = 3
print('\n', pari.factor(disc), '\n')
bilinear_matrix(link_homology_Rad_Ecf, perioDisc[disc])
print('\n', modulus, (pari.factor(disc)[:1])%modulus, '\n')
bilinear_matrix(lambda x,y : link_homology_Rad_Ecf(x,y)%modulus, perioDisc[disc])
dict_keys([60, 40, 316, 229, 876, 145, 8088, 1756, 401, 1708, 1384, 4348, 577, 780, 520, 1596, 1768])

 [2, 2; 3, 1; 73, 1] 

[-803, -16, 555, -16]
[-16, 19, 31, 20]
[555, 31, -323, 31]
[-16, 20, 31, 19]
1289962

 3 [[2, 0, 1]~] 

[1, 2, 0, 2]
[2, 1, 1, 2]
[0, 1, 1, 1]
[2, 2, 1, 1]
7
In [28]:
for d in perioDisc.keys():
    print('\n discriminant =', d, ' with factorisation = ', pari.factor(d))
    print('cosigns')
    bilinear_matrix(cosign_sum_Ecf, perioDisc[d])
    print('half-intersections')
    bilinear_matrix(half_inter_Ecf, perioDisc[d])
 discriminant = 60  with factorisation =  [2, 2; 3, 1; 5, 1]
cosigns
[5, 1]
[1, 1]
half-intersections
[7, 5]
[5, 7]

 discriminant = 40  with factorisation =  [2, 3; 5, 1]
cosigns
[0, 0]
[0, 0]
half-intersections
[22, 16]
[16, 26]

 discriminant = 316  with factorisation =  [2, 2; 79, 1]
cosigns
[34, 6, -6]
[6, 4, -4]
[-6, -4, 4]
half-intersections
[42, 26, 26]
[26, 40, 40]
[26, 40, 40]

 discriminant = 229  with factorisation =  Mat([229, 1])
cosigns
[0, 0, 0]
[0, -1, 1]
[0, 1, -1]
half-intersections
[58, 28, 28]
[28, 47, 47]
[28, 47, 47]

 discriminant = 876  with factorisation =  [2, 2; 3, 1; 73, 1]
cosigns
[34, 1, -25, 1]
[1, 2, -2, 2]
[-25, -2, 34, -2]
[1, 2, -2, 2]
half-intersections
[42, 25, 33, 25]
[25, 38, 26, 40]
[33, 26, 42, 26]
[25, 40, 26, 38]

 discriminant = 145  with factorisation =  [5, 1; 29, 1]
cosigns
[0, 0, 0, 0]
[0, -1, 0, 1]
[0, 0, 0, 0]
[0, 1, 0, -1]
half-intersections
[62, 48, 48, 48]
[48, 63, 56, 63]
[48, 56, 82, 56]
[48, 63, 56, 63]

 discriminant = 8088  with factorisation =  [2, 3; 3, 1; 337, 1]
cosigns
[169, -12, -93, -12]
[-12, 8, 12, 9]
[-93, 12, 79, 12]
[-12, 9, 12, 8]
half-intersections
[177, 44, 109, 44]
[44, 90, 60, 93]
[109, 60, 103, 60]
[44, 93, 60, 90]

 discriminant = 1756  with factorisation =  [2, 2; 439, 1]
cosigns
[94, 30, 18, -18, -30]
[30, 23, 16, -16, -23]
[18, 16, 19, -19, -16]
[-18, -16, -19, 19, 16]
[-30, -23, -16, 16, 23]
half-intersections
[102, 50, 38, 38, 50]
[50, 61, 60, 60, 61]
[38, 60, 65, 65, 60]
[38, 60, 65, 65, 60]
[50, 61, 60, 60, 61]

 discriminant = 401  with factorisation =  Mat([401, 1])
cosigns
[0, 0, 0, 0, 0]
[0, -1, 0, 0, 1]
[0, 0, -1, 1, 0]
[0, 0, 1, -1, 0]
[0, 1, 0, 0, -1]
half-intersections
[94, 56, 64, 64, 56]
[56, 91, 68, 68, 91]
[64, 68, 79, 79, 68]
[64, 68, 79, 79, 68]
[56, 91, 68, 68, 91]

 discriminant = 1708  with factorisation =  [2, 2; 7, 1; 61, 1]
cosigns
[40, -3, -9, 23, -9, -3]
[-3, 3, 3, -5, 2, 3]
[-9, 3, 9, -10, 9, 2]
[23, -5, -10, 28, -10, -5]
[-9, 2, 9, -10, 9, 3]
[-3, 3, 2, -5, 3, 3]
half-intersections
[48, 21, 25, 31, 25, 21]
[21, 33, 29, 25, 30, 37]
[25, 29, 31, 30, 33, 30]
[31, 25, 30, 36, 30, 25]
[25, 30, 33, 30, 31, 29]
[21, 37, 30, 25, 29, 33]

 discriminant = 1384  with factorisation =  [2, 3; 173, 1]
cosigns
[0, 0, 0, 0, 0, 0]
[0, -1, 0, 0, 0, 1]
[0, 0, -1, 0, 1, 0]
[0, 0, 0, 0, 0, 0]
[0, 0, 1, 0, -1, 0]
[0, 1, 0, 0, 0, -1]
half-intersections
[198, 120, 108, 132, 108, 120]
[120, 159, 132, 136, 132, 159]
[108, 132, 167, 132, 167, 132]
[132, 136, 132, 154, 132, 136]
[108, 132, 167, 132, 167, 132]
[120, 159, 132, 136, 132, 159]

 discriminant = 4348  with factorisation =  [2, 2; 1087, 1]
cosigns
[154, 54, -6, 6, -6, 6, -54]
[54, 43, -6, 6, -6, 6, -43]
[-6, -6, 3, 0, 0, -3, 6]
[6, 6, 0, 4, -4, 0, -6]
[-6, -6, 0, -4, 4, 0, 6]
[6, 6, -3, 0, 0, 3, -6]
[-54, -43, 6, -6, 6, -6, 43]
half-intersections
[162, 74, 50, 38, 38, 50, 74]
[74, 81, 70, 68, 68, 70, 81]
[50, 70, 81, 80, 80, 81, 70]
[38, 68, 80, 92, 92, 80, 68]
[38, 68, 80, 92, 92, 80, 68]
[50, 70, 81, 80, 80, 81, 70]
[74, 81, 70, 68, 68, 70, 81]

 discriminant = 577  with factorisation =  Mat([577, 1])
cosigns
[0, 0, 0, 0, 0, 0, 0]
[0, -1, 0, 0, 0, 0, 1]
[0, 0, -1, 0, 0, 1, 0]
[0, 0, 0, -1, 1, 0, 0]
[0, 0, 0, 1, -1, 0, 0]
[0, 0, 1, 0, 0, -1, 0]
[0, 1, 0, 0, 0, 0, -1]
half-intersections
[110, 72, 64, 56, 56, 64, 72]
[72, 87, 80, 72, 72, 80, 87]
[64, 80, 95, 72, 72, 95, 80]
[56, 72, 72, 107, 107, 72, 72]
[56, 72, 72, 107, 107, 72, 72]
[64, 80, 95, 72, 72, 95, 80]
[72, 87, 80, 72, 72, 80, 87]

 discriminant = 780  with factorisation =  [2, 2; 3, 1; 5, 1; 13, 1]
cosigns
[25, -5, -7, 11]
[-5, 10, 7, -5]
[-7, 7, 8, -7]
[11, -5, -7, 11]
half-intersections
[27, 9, 11, 15]
[9, 18, 15, 13]
[11, 15, 16, 15]
[15, 13, 15, 17]

 discriminant = 520  with factorisation =  [2, 3; 5, 1; 13, 1]
cosigns
[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 0, 0]
half-intersections
[138, 100, 96, 108]
[100, 138, 116, 116]
[96, 116, 150, 120]
[108, 116, 120, 126]

 discriminant = 1596  with factorisation =  [2, 2; 3, 1; 7, 1; 19, 1]
cosigns
[37, 17, 1, 5, -11, -7, 1, 5]
[17, 17, 1, 5, -11, -7, 1, 5]
[1, 1, 1, 1, -2, -2, 1, 1]
[5, 5, 1, 5, -5, -7, 1, 5]
[-11, -11, -2, -5, 12, 9, -2, -5]
[-7, -7, -2, -7, 9, 14, -2, -7]
[1, 1, 1, 1, -2, -2, 1, 1]
[5, 5, 1, 5, -5, -7, 1, 5]
half-intersections
[39, 21, 9, 11, 15, 11, 9, 11]
[21, 23, 13, 15, 19, 15, 13, 15]
[9, 13, 23, 19, 16, 18, 25, 19]
[11, 15, 19, 19, 17, 19, 19, 21]
[15, 19, 16, 17, 20, 17, 16, 17]
[11, 15, 18, 19, 17, 22, 18, 19]
[9, 13, 25, 19, 16, 18, 23, 19]
[11, 15, 19, 21, 17, 19, 19, 19]

 discriminant = 1768  with factorisation =  [2, 3; 13, 1; 17, 1]
cosigns
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, -1, 0, 0, 0, 1, 0]
[0, 0, 0, -1, 0, 0, 0, 1]
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 1, 0, 0, 0, -1, 0]
[0, 0, 0, 1, 0, 0, 0, -1]
half-intersections
[166, 88, 64, 48, 40, 32, 64, 48]
[88, 98, 76, 60, 52, 60, 76, 60]
[64, 76, 83, 68, 64, 64, 83, 68]
[48, 60, 68, 91, 80, 64, 68, 91]
[40, 52, 64, 80, 90, 92, 64, 80]
[32, 60, 64, 64, 92, 114, 64, 64]
[64, 76, 83, 68, 64, 64, 83, 68]
[48, 60, 68, 91, 80, 64, 68, 91]

Checking validity of linking formula with combinatorial intersection kernel for interlaced patterns¶

Be carfull not to try enl(w1,w2) on words of lenth > 12, it is very slow and serves just as a verification.

In [29]:
def linking_patterns(max_word_length, current_pair=('LR', 'RL')):
    """ Returns the word base for the computation of enl.
    
    # The set of pairs form a binary tree which we fill in à la Pascal
    
    # The root pair is ('LR','RL')
    # The pairs to the extreme left are ('Ln R', 'R Ln')
    # The pairs on the extreme right are ('L Rn', 'Rn L')

    # The children of (G, D) are
    # to the left (GP, GQ) with GP=G[:-1]+'LR' (on enlève 'R' on rajoute 'LR') and GQ=D+'L'
    # to the right (DP, DQ) with DP=G+'R' and DQ=D[:-1]+'RL' (on enlève 'L' on rajoute 'RL')
    
    # Note the properties : 'G' ends with 'R' and 'D' ends with 'L' are preserved
    # which is why G[:-1] = G - 'R' and D[:-1] = D - 'L'
    """
 
    if len(current_pair[0]) > max_word_length:
        return []
 
    pair_left = current_pair[0][:-1]+'LR', current_pair[1]+'L'
    pair_right = current_pair[0]+'R', current_pair[1][:-1]+'RL'
 
    return [current_pair] + linking_patterns(max_word_length, pair_left) +\
                            linking_patterns(max_word_length, pair_right)
 
def occ(P,A):
    # Returns the number of times P appears at the begining of circular shifts of A.
    shifts = list_of_circular_shifts(A)
    counter = 0
    n,l = len(P), len(A)
 
    for shift in shifts:
        power = shift + shift * (n//l)
        if power[:n] == P:
            counter += 1
 
    return counter
 
def scal_PQ(P,Q,A,B):
    return (occ(P,A)*occ(Q,B)+ occ(P,B)*occ(Q,A))
 
def enl(A,B):
    # Returns the enl metric on the words A and B in the L/T alphabet.
    patterns = linking_patterns(len(A)+len(B)+1)
    return sum([scal_PQ(P,Q,A,B) for P,Q in patterns])//2
In [30]:
linking_patterns(5)
Out[30]:
[('LR', 'RL'),
 ('LLR', 'RLL'),
 ('LLLR', 'RLLL'),
 ('LLLLR', 'RLLLL'),
 ('LLLRR', 'RLLRL'),
 ('LLRR', 'RLRL'),
 ('LLRLR', 'RLRLL'),
 ('LLRRR', 'RLRRL'),
 ('LRR', 'RRL'),
 ('LRLR', 'RRLL'),
 ('LRLLR', 'RRLLL'),
 ('LRLRR', 'RRLRL'),
 ('LRRR', 'RRRL'),
 ('LRRLR', 'RRRLL'),
 ('LRRRR', 'RRRRL')]
In [31]:
w1 = 'RLRLL' #'RLRLRLLRR'
w2 = 'RLRLL' #'RLLLRLRR'
print(link_RLw(w1,w2), enl(w1,w2))
6 6
In [32]:
A,B = 'L', 'R' 
#A,B = 'LR', 'RL'
U,V = A+B, B+A
W1 = 2*U+V
W2 = 2*V+U

print(W1, W2)

print(link_RLw(W1,2*A+B),link_RLw(W1,2*A+B+B),link_RLw(W1,2*A+B+A))
print(link_RLw(W2,2*A+B),link_RLw(W2,2*A+B+B),link_RLw(W2,2*A+B+A))

print(link_RLw(W1,'RLLRRL'), link_RLw(W2, 'RLLRRL'))


for pattern in linking_patterns(max(len(W1), len(W2))):
    if occ(pattern[0], W1) != occ(pattern[0], W2):
        print(pattern[0])
        print(occ(pattern[0], W1), occ(pattern[0], W2))
        print(link_RLw(pattern[0], W1), link_RLw(pattern[0], W2))
    if occ(pattern[1], W1) != occ(pattern[1], W2):
        print(pattern[1])
        print(occ(pattern[1], W1), occ(pattern[1], W2))
        print(link_RLw(pattern[1], W1), link_RLw(pattern[1], W2))
LRLRRL RLRLLR
3 5 3
3 5 3
8 7
RLLRL
1 0
6 6
RLLRRL
0 1
8 7
LLRR
0 1
5 5
RLRL
0 1
6 6
LLRLR
1 0
6 6
RLRLL
0 1
6 6
LLRLRR
1 0
7 8
RLRRL
1 0
6 6
LLRRLR
0 1
8 7
RLRRLL
1 0
7 8
LRLR
1 0
6 6
RRLL
1 0
5 5
LRLLR
0 1
6 6
LRLLRR
0 1
8 7
RRLLRL
1 0
7 8
LRLRR
1 0
6 6
RRLRL
0 1
6 6
RRLRLL
0 1
8 7
LRRLR
0 1
6 6
LRRLLR
1 0
7 8

Geometric functions on Qfb and sl or SL matrices¶

In [34]:
pari.trace(A)
P = pari.numerator(F)
trace = P.eval(1)
pari.poldegree(P)
pari.polroots(P)
pari.poldisc(P)
qId = pari("[t,0;0,1/t]")
qDisc = pari.matdet(mat.subst('q','p^2')-qId)
alext = alex.subst('q^2','-t')

poly = pari('q^2+3')
poly.subst('q',1)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In [34], line 2
      1 pari.trace(A)
----> 2 P = pari.numerator(F)
      3 trace = P.eval(1)
      4 pari.poldegree(P)

NameError: name 'F' is not defined
In [35]:
'''The disc_mat works for matrices in GL(2) and scal_sl2 for all matrices in sl(2)
in each case over any commutative the ring, such as Z or Z[q]
for the cosine we need to be have a preferred sqrt over the ring (implemented in PARI)'''

def disc_mat(M):
    return pari.trace(M)**2-4*pari.matdet(M)

def scal_sl2(a,b):
    return -pari.trace(a*b)/2

def cosine_sl2(a,b):
    '''The minus is because we are dealing with a,b both of negative det'''
    return -scal_sl2(a,b)/pari.sqrt(scal_sl2(a,a)*scal_sl2(b,b))    

def bir_sl2(a,b):
    cab=cosine_sl2(a,b)
    if cab == -1:
        return(pari('oo'))
    return 2/(1+cab)

''' The following functions work for matrices in SL(Z) or SL(Z[q])
for the cosine we have a preferred sqrt ? (implemented in PARI) '''

def cotrace_SL2(A,B):
    s = pari.sign((pari.trace(A)*pari.trace(B)).subst('q',1))
    return s*(pari.trace(A*B)-pari.trace(A/B))

def cosine_SL2(A,B):
    nume = cotrace_SL2(A,B)
    deno = pari.sqrt(disc_mat(A)*disc_mat(B))
    return nume/deno

def cotrace_RLw(wA,wB):
    Aq = SL2_of_RLw(wA)
    Bq = SL2_of_RLw(wB)
    return cotrace_SL2(Aq,Bq)

def cotrace_q_RLw(wA,wB):
    Aq = SL2q_of_RLw(wA)
    Bq = SL2q_of_RLw(wB)
    return cotrace_SL2(Aq,Bq)

def cosine_q_RLw(wA,wB):
    Aq = SL2q_of_RLw(wA)
    Bq = SL2q_of_RLw(wB)
    nume = cotrace_SL2(A,B)
    deno = pari.sqrt(disc_mat(Aq)*disc_mat(Bq))
    return nume/deno

'''The following functions apply to pari.Qfb'''

def bir_approx_Qfb(QA,QB): 
    '''Computes cross-ratio(alpha,alpha',beta,beta') 
       for alpha beta the first roots of QA QB'''
    phiA = exact_firoot_Qfb(QA)
    phiB = exact_firoot_Qfb(QB)
    phiAconj = pari.norm(phiA)/phiA
    phiBconj = pari.norm(phiB)/phiB
    nume = 1.*(phiA-phiAconj)*(phiB-phiBconj)
    deno = (phiA*1.-phiBconj*1.)*(phiB*1.-phiAconj*1.)
    # we may use the function pari.simplify() to simplify the type
    # here sending a t_QUAD which is rational to a t_FRAC (or t_INT)
    ratio = (nume/deno) #.simplify()
    return ratio 


def bir_samefield_Qfb(QA,QB): 
    '''Computes cross-ratio(alpha,alpha',beta,beta') 
       for alpha beta the first roots of QA QB'''
    phiA = exact_firoot_Qfb(QA)
    phiB = exact_firoot_Qfb(QB)
    phiAconj = pari.norm(phiA)/phiA
    phiBconj = pari.norm(phiB)/phiB
    nume = (phiA-phiAconj)*(phiB-phiBconj)
    deno = (phiA-phiBconj)*(phiB-phiAconj)
    # we may use the function pari.simplify() to simplify the type
    # here sending a t_QUAD which is rational to a t_FRAC (or t_INT)
    ratio = (nume/deno) #.simplify()
    return ratio

def birinv_samedisc_Qfb(QA,QB):
    '''Computes inverse of the cross-ratio(alpha,alpha',beta,beta')
       using the scalar product of the QA QB assuming they have same disc
       this is a more restrictive case than same field and may be ignored'''
    lA, mA, rA = QA[0], QA[1], QA[2]
    lB, mB, rB = QB[0], QB[1], QB[2]
    discA = mA**2-4*lA*rA
    discB = mB**2-4*lB*rB
    if discA == discB:
        # invBir = (-(lA*mB-lB*mA)**2 + discA*(lA+lB)**2)/(4*lA*lB*discA)
        birinv = (discA+mA*mB-2*(lA*rB+lB*rA))/(2*discA)
        return(birinv)
    else :
        raise Exception('Not same disc')

def cosine_RLw(wA, wB):
    A = SL2_of_RLw(wA)
    sA = sl2_of_SL2(A)
    
    B = SL2_of_RLw(wB)
    sB = sl2_of_SL2(B)
    
    return cosine_sl2(sA,sB)

def bir_approx_RLw(wA, wB):
    A = SL2_of_RLw(wA)
    sA = sl2_of_SL2(A)
    
    B = SL2_of_RLw(wB)
    sB = sl2_of_SL2(B)
    
    return bir_sl2(sA,sB)

def bir_samefield_RLw(wA, wB):
    A = SL2_of_RLw(wA)
    sA = sl2_of_SL2(A)
    QA = Qfb_of_sl2(sA)
    
    B = SL2_of_RLw(wB)
    sB = sl2_of_SL2(B)
    QB = Qfb_of_sl2(sB)
    
    return bir_samefield_Qfb(QA,QB)
In [36]:
''' Testing that all computations of bir form same disc or same field are consistent : they are !'''

for ecfA in perioDisc[8088]:
    for ecfB in perioDisc[8088]:
#        if ecfA == ecfB:
#            continue
        
        A = SL2_of_Ecf(ecfA)
        sA = sl2_of_SL2(A)
        QA = Qfb_of_sl2(sA)
        
        B = SL2_of_Ecf(ecfB)
        sB = sl2_of_SL2(B)
        QB = Qfb_of_sl2(sB)
        
        birSL2 = 2/(1+cosine_SL2(A,B))
        birsl2 = bir_sl2(sA,sB)
        birqfbsd = 1/birinv_samedisc_Qfb(QA,QB)
        birqfbsf = bir_samefield_Qfb(QA,QB)
        print('\n', sA, '\t', sB, '\t \n', '\n', birSL2, birsl2, birqfbsf*1., '\t', birqfbsf, birqfbsd)
        
        Aq = SL2q_of_Ecf(ecfA)
        Bq = SL2q_of_Ecf(ecfB)
        birSL2q = 2/(1+cosine_SL2(Aq,Bq))
        print(birSL2q)
 [44, 86; 1, -44] 	 [44, 86; 1, -44] 	 
 
 1.00000000000000 1.00000000000000 1.00000000000000 	 1 1
1 + O(q^16)

 [44, 86; 1, -44] 	 [36, 11; 66, -36] 	 
 
 0.627025350802388 0.627025350802388 0.627025350802388 	 8088/12899 8088/12899
1 - q^6 + q^12 + q^14 + O(q^16)

 [44, 86; 1, -44] 	 [42, 6; 43, -42] 	 
 
 0.706745893044390 0.706745893044390 0.706745893044390 	 2022/2861 2022/2861
1 - q^8 + q^12 + O(q^16)

 [44, 86; 1, -44] 	 [30, 33; 34, -30] 	 
 
 0.838917124779587 0.838917124779587 0.838917124779587 	 8088/9641 8088/9641
1 - q^8 + q^10 + q^12 - 2*q^14 + O(q^16)

 [36, 11; 66, -36] 	 [44, 86; 1, -44] 	 
 
 0.627025350802388 0.627025350802388 0.627025350802388 	 8088/12899 8088/12899
1 - q^6 + q^12 + q^14 + O(q^16)

 [36, 11; 66, -36] 	 [36, 11; 66, -36] 	 
 
 1.00000000000000 1.00000000000000 1.00000000000000 	 1 1
1 + O(q^16)

 [36, 11; 66, -36] 	 [42, 6; 43, -42] 	 
 
 1.01902482046113 1.01902482046113 1.01902482046113 	 8088/7937 8088/7937
1 + O(q^16)

 [36, 11; 66, -36] 	 [30, 33; 34, -30] 	 
 
 0.923709456372773 0.923709456372773 0.923709456372773 	 2022/2189 2022/2189
1 - q^8 + q^10 + q^12 - q^14 + O(q^16)

 [42, 6; 43, -42] 	 [44, 86; 1, -44] 	 
 
 0.706745893044390 0.706745893044390 0.706745893044390 	 2022/2861 2022/2861
1 - q^8 + q^12 + O(q^16)

 [42, 6; 43, -42] 	 [36, 11; 66, -36] 	 
 
 1.01902482046113 1.01902482046113 1.01902482046113 	 8088/7937 8088/7937
1 + O(q^16)

 [42, 6; 43, -42] 	 [42, 6; 43, -42] 	 
 
 1.00000000000000 1.00000000000000 1.00000000000000 	 1 1
1 + O(q^16)

 [42, 6; 43, -42] 	 [30, 33; 34, -30] 	 
 
 0.987907658482961 0.987907658482961 0.987907658482961 	 2696/2729 2696/2729
1 + O(q^16)

 [30, 33; 34, -30] 	 [44, 86; 1, -44] 	 
 
 0.838917124779587 0.838917124779587 0.838917124779587 	 8088/9641 8088/9641
1 - q^8 + q^10 + q^12 - 2*q^14 + O(q^16)

 [30, 33; 34, -30] 	 [36, 11; 66, -36] 	 
 
 0.923709456372773 0.923709456372773 0.923709456372773 	 2022/2189 2022/2189
1 - q^8 + q^10 + q^12 - q^14 + O(q^16)

 [30, 33; 34, -30] 	 [42, 6; 43, -42] 	 
 
 0.987907658482961 0.987907658482961 0.987907658482961 	 2696/2729 2696/2729
1 + O(q^16)

 [30, 33; 34, -30] 	 [30, 33; 34, -30] 	 
 
 1.00000000000000 1.00000000000000 1.00000000000000 	 1 1
1 + O(q^16)

Summing functions over linked pairs and intersection points¶

Iterables of linked pairs and intersecting points¶

The intersection points of two modular geodesics corresponding to modular conjugacy classes $[A]$ and $[B]$ correspond to the set of all pairs of representatives for the conjugacy classes whose axes have interlaced endpoints modulo simultaneous conjugacy of the pair.

Up to simultaneous conjugacy, we may assume that the intersection happens at the base edge of the trivalent tree and that the representative $A$ is in the Euclidean monoid $PSL(2;N)$ of $R\& L$-words, and that the representative $B$ is either in $PSL(2;N)$ or its transpose inverse $SBS^{-1} = {}^t\!B^{-1}$ is in $PSL(2;N)$. Moreover the intersection is uniquely parametrised when $cross(A,B)\ne 0$ or $cross(A, SBS^{-1})\ne 0$ respectively, which means that their last letters are distinct and ordered oppositely to their futures.

In summary the intersection points are parametrized by the the pairs of RL words (wA, wB) and (wA, wBT), where we are remembering in the latter case that the actual representative is the inverse of wBT, in particular the $\cos(A,SBS^{-1}) = -\cos(A,{}^t\!B)$ and $cross(A,SBS^{-1}) = -cross(A,{}^t\!B)$.

In [37]:
def iter_funcond_cycles_RLw(wa, wb, func = lambda x, y : (x,y), \
                            cond = lambda wa, wb : cross_RLw(wa, wb)!=0):
    '''Generates iterator of func(A,B) over pairs of cyclic representatives with cond(A,B)'''
    for ka in range(len(wa)):
        for kb in range(len(wb)):
            waka = wa[ka:]+wa[:ka]
            wbkb = wb[kb:]+wb[:kb]
            if cond(waka,wbkb):
                yield func(waka,wbkb)

def iter_funcond_inter_RLw(wa, wb, func = lambda x,y, iota : (x,y), \
                           cond = lambda wa, wb, iota : cross_RLw(wa, wb)!=0):
    '''
    Generates iterator iterable of func(A,B, iota) with cond(A,B, iota)
    over all independant pairs of conj classes that interesect modulo sumplutaneous conjugacy. 
    We may restrict to those passing through the base edge of the trivalent tree, and choose 
    the first to be in PSL(2;N) ie an RLw and the other in PSL(2;-N) ie an S(LRw)S^{-1}
    '''
    
    wbT = transpose_RLw(wb)
    for ka in range(len(wa)):
        for kb in range(len(wb)):
            waka = wa[ka:]+wa[:ka]
            wbkb = wb[kb:]+wb[:kb]
            wbTkb = wbT[kb:]+wbT[:kb]
            if cond(waka, wbkb, +1):
                yield func(waka, wbkb, +1)
            if cond(waka, wbTkb, -1):
                yield func(waka, wbTkb, -1)

The following examples recalls that $(cross \ne 0) \Rightarrow (cross ratio \ge 1) $ but $(cross \ne 0) \not\Leftarrow (cross ratio \ge 1) $ because :

  1. $bir$ or equivalently $\cos$ measures the geometric angle (for the inclusion representation of $PSL(2;Z) \subset PSL(2;R)$ ie at $q=1$)
  2. $cross$ measures the combinatorial angles only at confluent edges (for the degenerate representation $PSL(2;Z) \to Aut(Tree)$ ie at $q=\infty$).
In [38]:
print(perioDisc[1596], '\n')
wA = RLw_of_Ecf([3,1,2,3]) 
wB = RLw_of_Ecf([7,2,1,1]) # 19,2

print('\n We see that cross non zero implies bir >= 1 : \n')
func = lambda wa, wb : (bir_samefield_RLw(wa,wb), pari.floor(bir_samefield_RLw(wa,wb)*1.))
print([p for p in iter_funcond_cycles_RLw(wA,wB,func)])

print('\n We compute the cosines at intersection points : \n')
funci = lambda wa, wb, iota : (2/bir_samefield_RLw(wa,wb)-1)*iota
print([p for p in iter_funcond_inter_RLw(wA, wB, funci)])
[[38, 1], [19, 2], [3, 1, 2, 3], [7, 2, 1, 1], [1, 12, 1, 1], [1, 4, 1, 5], [3, 3, 2, 1], [7, 1, 1, 2]] 


 We see that cross non zero implies bir >= 1 : 

[(1596/1531, 1), (532/417, 1), (1596/871, 1), (1596/391, 4), (532/345, 1), (532/477, 1), (1596/1111, 1), (28/27, 1), (1596/691, 2), (1596/1027, 1), (532/417, 1), (399/187, 2), (1596/1375, 1), (399/283, 1), (532/477, 1), (228/85, 2), (1596/1375, 1), (399/199, 2), (1596/1543, 1), (399/283, 1)]

 We compute the cosines at intersection points : 

[-121/399, -17/42, 733/798, 397/798, 151/266, 73/798, -407/798, 79/266, 211/266, 313/798, -683/798, 13/14, -107/798, -1/7, -29/114, 229/798, -11/14, -89/114, -25/399, 151/266, -25/399, 577/798, 167/399, -89/114, -599/798, 211/266, -29/114, 79/266, -1/399, 577/798, -1/399, -611/798, -121/399, -515/798, 745/798, -743/798, -1/7, 167/399]

We compute the sums $S(P_n,\chi;A,B)=\sum_{x} \chi(\epsilon_x(A,B)) \times P_n(\cos_x(A,B))$¶

where $P_n$ often belongs to a familly of orthogonal polynomials like: powers, Legendre, Chebychev,...

and compare with the values of $lk(A,B), cosign(A,B), inter(A,B),...$.

In [39]:
def sum_funchi_inter_RLw(wa, wb, func, chi):
    '''
    Computes sum of func(A,B, iota)*chi(cross(A,B)) over intersections, 
    parametrized by pairs of of conj classes that interesect modulo simultaneous conjugacy,
    namely (wA,wB) with cross(wA,wB) != 0 and (wA, wBT inverse) with -cross(wA, wBT) != 0
    '''
    somme = 0
    wbT = transpose_RLw(wb)
    for ka in range(len(wa)):
        for kb in range(len(wb)):
            waka = wa[ka:]+wa[:ka]
            wbkb = wb[kb:]+wb[:kb]
            wbTkb = wbT[kb:]+wbT[:kb]
            somme += func(waka, wbkb, +1)*chi(cross_RLw(waka, wbkb))
            somme += func(waka, wbTkb, -1)*chi(-cross_RLw(waka, wbTkb))
    return somme


def prod_funchi_inter_RLw(wa, wb, func, chi):
    '''
    Computes prod of func(A,B, iota)**chi(cross(A,B)) over intersections, 
    parametrized by pairs of of conj classes that interesect modulo simultaneous conjugacy,
    namely (wA,wB) with cross(wA,wB) != 0 and (wA, wBT inverse) with -cross(wA, wBT) != 0
    '''
    produit = 1
    wbT = transpose_RLw(wb)
    for ka in range(len(wa)):
        for kb in range(len(wb)):
            waka = wa[ka:]+wa[:ka]
            wbkb = wb[kb:]+wb[:kb]
            wbTkb = wbT[kb:]+wbT[:kb]
            produit *= func(waka, wbkb, +1)**chi(cross_RLw(waka, wbkb))
            produit *= func(waka, wbTkb, -1)**chi(-cross_RLw(waka, wbTkb))
    return produit
In [40]:
def sum_invBir_chi_RLw(wA,wB,n,chi):
    '''Sum of chi(cross(A,B))*(1/bir(A,B))^n'''
    discAdiscB = disc_mat(SL2_of_RLw(wA))*disc_mat(SL2_of_RLw(wB))
    # delta = pari.sqrt(discAdiscB)
    if discAdiscB.issquare():
        funci = lambda wa, wb, iota : ((1+iota*(2/bir_samefield_RLw(wa,wb)-1))/2)**n
    else : 
        funci = lambda wa, wb, iota : ((1+iota*(2/bir_approx_RLw(wa,wb)-1))/2)**n
    return sum_funchi_inter_RLw(wA, wB, funci, chi)

def prod_invBir_chi_RLw(wA,wB,n,chi):
    '''Prod of (1/bir(A,B))^(chi(cross(A,B)).n)'''
    discAdiscB = disc_mat(SL2_of_RLw(wA))*disc_mat(SL2_of_RLw(wB))
    if discAdiscB.issquare():
        funci = lambda wa, wb, iota : ((1+iota*(2/bir_samefield_RLw(wa,wb)-1))/2)**n
    else : 
        funci = lambda wa, wb, iota : ((1+iota*(2/bir_approx_RLw(wa,wb)-1))/2)**n
    return prod_funchi_inter_RLw(wA, wB, funci, chi)
In [41]:
ecfA = [3,1,2,3] # [6,1] # 
ecfB = [19,2] # [7,2,1,1] # [3,2] # 

wA = RLw_of_Ecf(ecfA)
wB = RLw_of_Ecf(ecfB)
wBJ = conJ_RLw(wB)

discAdiscB = disc_mat(SL2_of_RLw(wA))*disc_mat(SL2_of_RLw(wB))
print(discAdiscB, '\n')

print(link_RLw(wA,wB), cosign_sum_RLw(wA,wB), 2*half_inter_RLw(wA,wB), '\t', \
      link_RLw(wA,wBJ), cosign_sum_RLw(wA,wBJ), 2*half_inter_RLw(wA,wBJ),  '\n')


print(sum_invBir_chi_RLw(wA,wB, 0, lambda x : (x)), \
      sum_invBir_chi_RLw(wA,wB, 0, lambda x : abs(x)), '\n',\
      sum_invBir_chi_RLw(wA,wB, 1, lambda x : (x)), \
      sum_invBir_chi_RLw(wA,wB, 1, lambda x : abs(x)), '\n',\
      sum_invBir_chi_RLw(wA,wB, 2, lambda x : (x)), \
      sum_invBir_chi_RLw(wA,wB, 2, lambda x : abs(x)), '\n',\
      sum_invBir_chi_RLw(wA,wB, 3, lambda x : (x)), \
      sum_invBir_chi_RLw(wA,wB, 3, lambda x : abs(x)), '\n', \
      sum_invBir_chi_RLw(wA,wB, 4, lambda x : (x)), \
      sum_invBir_chi_RLw(wA,wB, 4, lambda x : abs(x)), '\n')
print('\n')

if discAdiscB.issquare():
    print(prod_invBir_chi_RLw(wA,wB, 1, lambda x : (x)), '\n', \
          pari.factor(pari.simplify(prod_invBir_chi_RLw(wA,wB, 1, lambda x : (x))))[:1], '\n', \
          pari.factor(pari.simplify(prod_invBir_chi_RLw(wA,wB, 1, lambda x : (x))))[1:], '\n', \
          prod_invBir_chi_RLw(wA,wB, 1, lambda x : abs(x)), '\n', \
          pari.factor(pari.simplify(prod_invBir_chi_RLw(wA,wB, 1, lambda x : abs(x))))[:1], '\n', \
          pari.factor(pari.simplify(prod_invBir_chi_RLw(wA,wB, 1, lambda x : abs(x))))[1:])
2547216 

7 1 26 	 6 -1 26 

0 26 
 0 5246/399 
 0 1343507/159201 
 0 1543225409/254084796 
 0 1889095198745/405519334416 



11202345070866585093794787/14383891192135961400019363 
 [[3, 11, 17, 23, 29, 53, 59, 79, 151, 167, 191, 331, 379, 751, 991, 1171, 1471]~] 
 [[5, 2, 2, -2, 1, -2, -2, -3, -1, -1, -1, 2, 1, 1, 1, -1, 1]~] 
 211870827499856154617528286311080757069945692069988796875/1790101910978244237672749753761738423691647995748881939263744114688 
 [[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 53, 59, 79, 151, 167, 191, 331, 379, 751, 991, 1171, 1471]~] 
 [[-28, -9, 6, -24, 4, 2, 2, -26, 2, 1, 2, 2, 3, 1, 1, 1, 2, 1, 1, 1, 1, 1]~]
In [42]:
def sum_cotrace_chi_RLw(wA,wB,n,chi):
    '''Sum of chi(cross(A,B))*( (sign(Tr(A)Tr(B))*(Tr(AB)-Tr(A/B)) )^n'''

    funci = lambda wa, wb, iota : (iota*cotrace_RLw(wA,wB))**n
    return sum_funchi_inter_RLw(wA, wB, funci, chi)

def sum_cotrace_q_chi_RLw(wA,wB,n,chi):
    '''Sum of chi(cross(A,B))*( (sign(Tr(A)Tr(B))*(Tr(AqBq)-Tr(Aq/Bq)) )^n'''

    funci = lambda wa, wb, iota : (iota*cotrace_q_RLw(wA,wB))**n
    return sum_funchi_inter_RLw(wA, wB, funci, chi)
In [43]:
ecfA, ecfB = [1, 1, 6, 3, 13, 1], [19, 1, 40, 1] 
ecfA, ecfB = [1, 1, 6, 3, 13, 1], [1, 2, 3, 1, 3, 1, 7, 1] 
ecfA, ecfB = [1, 1, 6, 3, 13, 1], [1, 3, 1, 3, 2, 1, 1, 7] 
ecfA, ecfB = [3, 6, 1, 1, 1, 13], [1, 3, 1, 3, 2, 1, 1, 7]

#ecfA = [3,1,2,3] # [6,1] # 
#ecfB = [19,2] # [7,2,1,1] # [3,2] # 

wA = RLw_of_Ecf(ecfA)
wB = RLw_of_Ecf(ecfB)
wBJ = conJ_RLw(wB)

discAdiscB = disc_mat(SL2_of_RLw(wA))*disc_mat(SL2_of_RLw(wB))
print(discAdiscB, discAdiscB.factor()[:1], '\n')

print(link_RLw(wA,wB), cosign_sum_RLw(wA,wB), 2*half_inter_RLw(wA,wB), '\t', \
      link_RLw(wA,wBJ), cosign_sum_RLw(wA,wBJ), 2*half_inter_RLw(wA,wBJ),  '\n')

print(sum_cotrace_chi_RLw(wA,wB, 0, lambda x : (x)).factor()[:1], \
      sum_cotrace_chi_RLw(wA,wB, 0, lambda x : abs(x)).factor()[:1], '\n',\
      sum_cotrace_chi_RLw(wA,wB, 1, lambda x : (x)).factor()[:1], \
      sum_cotrace_chi_RLw(wA,wB, 1, lambda x : abs(x)).factor()[:1], '\n',\
      sum_cotrace_chi_RLw(wA,wB, 2, lambda x : (x)).factor()[:1], \
      sum_cotrace_chi_RLw(wA,wB, 2, lambda x : abs(x)).factor()[:1], '\n',\
      sum_cotrace_chi_RLw(wA,wB, 3, lambda x : (x)).factor()[:1], \
      sum_cotrace_chi_RLw(wA,wB, 3, lambda x : abs(x)).factor()[:1], '\n', \
      sum_cotrace_chi_RLw(wA,wB, 7, lambda x : (x)).factor()[:1], \
      sum_cotrace_chi_RLw(wA,wB, 7, lambda x : abs(x)).factor()[:1], '\n')

print(sum_cotrace_q_chi_RLw(wA,wB, 0, lambda x : (x)), \
      #sum_cotrace_q_chi_RLw(wA,wB, 0, lambda x : abs(x)), '\n',\
      sum_cotrace_q_chi_RLw(wA,wB, 1, lambda x : (x)), \
      #sum_cotrace_q_chi_RLw(wA,wB, 1, lambda x : abs(x)), '\n',\
      sum_cotrace_q_chi_RLw(wA,wB, 2, lambda x : (x))
      #sum_cotrace_q_chi_RLw(wA,wB, 2, lambda x : abs(x)), '\n' 
     )
599689164816 [[2, 3, 7, 439]~] 

38 16 120 	 22 -17 122 

[[0]~] [[2, 3, 5]~] 
 [[0]~] [[2, 3, 5, 7, 167]~] 
 [[0]~] [[2, 3, 5, 7, 167]~] 
 [[0]~] [[2, 3, 5, 7, 167]~] 
 [[0]~] [[2, 3, 5, 7, 167]~] 

0 0 0

For prod_invBir_chi_RLw(wA,wB, 1, chi) we observe a lot of simplifications from chi(x)= abs(x) to chi(x)=x, some completely dissapear and the big exponents decrease drastically. For example when $ecfA = [3,1,2,3]$ and $ecfB = [19,2]$ the primes $2,5,13,17$ dissappear.

We also observe that these cross ratios have many small primes, and indeed we expect them to be smaller than the discriminant.

In [44]:
for n in range(6):
    print(pari.pollegendre(n),'\t\t\t', pari.polchebyshev(n))
1 			 1
x 			 x
3/2*x^2 - 1/2 			 2*x^2 - 1
5/2*x^3 - 3/2*x 			 4*x^3 - 3*x
35/8*x^4 - 15/4*x^2 + 3/8 			 8*x^4 - 8*x^2 + 1
63/8*x^5 - 35/4*x^3 + 15/8*x 			 16*x^5 - 20*x^3 + 5*x
In [45]:
def sum_Power_cos_chi_RLw(wA,wB,n,chi):
    '''Sum of chi(cross(A,B)) times (cos(A,B))^n'''
    discAdiscB = disc_mat(SL2_of_RLw(wA))*disc_mat(SL2_of_RLw(wB))
    if discAdiscB.issquare():
        funci = lambda wa, wb, iota : (iota*(2/bir_samefield_RLw(wa,wb)-1))**n
    else : 
        funci = lambda wa, wb, iota : (iota*(2/bir_approx_RLw(wa,wb)-1))**n
    #coefnAB = pari.sqrt(discAdiscB)**n*pari(2)**(2*n+1)/pari.binomial(2*n,n)
    return sum_funchi_inter_RLw(wA, wB, funci, chi)

def sum_Legendre_cos_chi_RLw(wA,wB,n,chi):
    '''Sum of chi(cross(A,B)) times (cos(A,B))^n'''
    discAdiscB = disc_mat(SL2_of_RLw(wA))*disc_mat(SL2_of_RLw(wB))
    if discAdiscB.issquare() :
        funci = lambda wa, wb, iota : pari.pollegendre(n).eval(iota*(2/bir_samefield_RLw(wa,wb)-1))
    else : 
        funci = lambda wa, wb, iota : pari.pollegendre(n).eval(iota*(2/bir_approx_RLw(wa,wb)-1))
    #coefnAB = pari.sqrt(discAdiscB)**n*pari(2)**(2*n+1)/pari.binomial(2*n,n)
    return sum_funchi_inter_RLw(wA, wB, funci, chi)

def sum_Chebyshev_cos_chi_RLw(wA,wB,n,chi):
    '''Sum of chi(cross(A,B)) times (cos(A,B))^n'''
    discAdiscB = disc_mat(SL2_of_RLw(wA))*disc_mat(SL2_of_RLw(wB))
    if discAdiscB.issquare() :
        funci = lambda wa, wb, iota : pari.polchebyshev(n).eval(iota*(2/bir_samefield_RLw(wa,wb)-1))
    else : 
        funci = lambda wa, wb, iota : pari.polchebyshev(n).eval(iota*(2/bir_approx_RLw(wa,wb)-1))
    #coefnAB = pari.sqrt(discAdiscB)**n*pari(2)**(2*n+1)/pari.binomial(2*n,n)
    return sum_funchi_inter_RLw(wA, wB, funci, chi)
In [46]:
ecfA = [3,1,2,3] #[6,1] # 
ecfB = [7,2,1,1] #[3,2] #

wA = RLw_of_Ecf(ecfA)
wB = RLw_of_Ecf(ecfB)
wBJ = conJ_RLw(wB)

print(pari.sqrt(disc_mat(SL2_of_RLw(wA))*disc_mat(SL2_of_RLw(wB))), '\n')

print(link_RLw(wA,wB), cosign_sum_RLw(wA,wB), 2*half_inter_RLw(wA,wB), '\t', \
      link_RLw(wA,wBJ), cosign_sum_RLw(wA,wBJ), 2*half_inter_RLw(wA,wBJ),  '\n')


print(sum_Power_cos_chi_RLw(wA,wB, 0, lambda x : (x)), \
      sum_Power_cos_chi_RLw(wA,wB, 0, lambda x : abs(x)), \
      sum_Power_cos_chi_RLw(wA,wB, 1, lambda x : (x)), \
      sum_Power_cos_chi_RLw(wA,wB, 1, lambda x : abs(x)), \
      sum_Power_cos_chi_RLw(wA,wB, 3, lambda x : (x)), \
      sum_Power_cos_chi_RLw(wA,wB, 3, lambda x : abs(x)))

print(sum_Power_cos_chi_RLw(wA,wB, 2, lambda x : (x)), \
      sum_Power_cos_chi_RLw(wA,wB, 2, lambda x : abs(x)), \
      sum_Power_cos_chi_RLw(wA,wB, 3, lambda x : (x)), \
      sum_Power_cos_chi_RLw(wA,wB, 3, lambda x : abs(x)) )

print(sum_Legendre_cos_chi_RLw(wA,wB, 2, lambda x : (x)), \
      sum_Legendre_cos_chi_RLw(wA,wB, 2, lambda x : abs(x)), \
      sum_Legendre_cos_chi_RLw(wA,wB, 3, lambda x : (x)), \
      sum_Legendre_cos_chi_RLw(wA,wB, 3, lambda x : abs(x)) )

print(sum_Chebyshev_cos_chi_RLw(wA,wB, 2, lambda x : (x)), \
      sum_Chebyshev_cos_chi_RLw(wA,wB, 2, lambda x : abs(x)), \
      sum_Chebyshev_cos_chi_RLw(wA,wB, 3, lambda x : (x)), \
      sum_Chebyshev_cos_chi_RLw(wA,wB, 3, lambda x : abs(x)) )
1596.00000000000 

10 1 38 	 9 -1 38 

0 38 0 44/57 0 4068889/6686442
0 1966273/159201 0 4068889/6686442
0 -50273/106134 0 4860053/13372884
0 -2117092/159201 0 395582/3343221
In [ ]:
 

Experimentations and observations¶

In [47]:
print(perioDisc.keys())
print(perioDisc[60])
dict_keys([60, 40, 316, 229, 876, 145, 8088, 1756, 401, 1708, 1384, 4348, 577, 780, 520, 1596, 1768])
[[6, 1], [3, 2]]

We know that loops in a spherical orbifold have trivial algebraic intersection number so : \begin{equation*} \sum_{x} \epsilon_x(A,B) \times \cos_x(A,B)^{0} = 0 \end{equation*}

Since the Poisson structure is trivial (as the character variety has dimension one), we also know that: \begin{equation*} \sum_{x} \epsilon_x(A,B) \times \cos_x(A,B)^{1} = 0 \end{equation*}

Our experiments suggest that we also have: \begin{equation*} \sum_{x} \epsilon_x(A,B) \times \cos_x(A,B)^{3} = 0 \end{equation*}

In [48]:
for ecfA in perioDisc[1756]:
    for ecfB in perioDisc[1756]:
        wA = RLw_of_Ecf(ecfA)
        wB = RLw_of_Ecf(ecfB)
        for n in range(8):
            s= sum_Power_cos_chi_RLw(wA,wB, n, lambda x : x)
            if s*10**10>1:
                print(ecfA,ecfB, n, s)
                break
print("Done")
[1, 1, 6, 3, 13, 1] [19, 1, 40, 1] 5 867063859200/16305067506199
[1, 1, 6, 3, 13, 1] [1, 2, 3, 1, 3, 1, 7, 1] 5 1939919385600/16305067506199
[1, 1, 6, 3, 13, 1] [1, 3, 1, 3, 2, 1, 1, 7] 5 1939919385600/16305067506199
[1, 2, 3, 1, 3, 1, 7, 1] [19, 1, 40, 1] 5 4149519897600/16305067506199
[1, 2, 3, 1, 3, 1, 7, 1] [1, 1, 6, 3, 13, 1] 7 145398942368265600/3142328914862177479
[1, 2, 3, 1, 3, 1, 7, 1] [3, 6, 1, 1, 1, 13] 7 145398942368265600/3142328914862177479
[1, 3, 1, 3, 2, 1, 1, 7] [19, 1, 40, 1] 5 4149519897600/16305067506199
[1, 3, 1, 3, 2, 1, 1, 7] [1, 1, 6, 3, 13, 1] 7 145398942368265600/3142328914862177479
[1, 3, 1, 3, 2, 1, 1, 7] [3, 6, 1, 1, 1, 13] 7 145398942368265600/3142328914862177479
[3, 6, 1, 1, 1, 13] [19, 1, 40, 1] 5 867063859200/16305067506199
[3, 6, 1, 1, 1, 13] [1, 2, 3, 1, 3, 1, 7, 1] 5 1939919385600/16305067506199
[3, 6, 1, 1, 1, 13] [1, 3, 1, 3, 2, 1, 1, 7] 5 1939919385600/16305067506199
Done