Skip to content

Instantly share code, notes, and snippets.

@GM3D
Last active October 2, 2017 07:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save GM3D/dc157342b8313c4fbba4b2bcce3a8a7c to your computer and use it in GitHub Desktop.
Save GM3D/dc157342b8313c4fbba4b2bcce3a8a7c to your computer and use it in GitHub Desktop.
Coursera Modular forms 2017 exercise I-2-2
#!/usr/bin/python3
from math import sqrt, factorial
from operator import mul
def binomial(n, k):
return factorial(n)/(factorial(n-k) * factorial(k))
def dot_prod(a, b):
return sum(map(mul, a, b))
def square(x):
return x*x
def norm2(v):
return sum(map(square, v))
def possible_vs(m, n):
vv = 2 * n
max_x = int(sqrt(vv))
d = 2 * max_x + 1
for i in range(d**m):
v = decode_v(m, d, i)
v2 = norm2(v)
if vv == norm2(v):
yield v
def decode_v(m, d, i):
"""
Returns the decoded vector v corresponding to the integer i.
i: an integer encoding the vector v in D_m
d: odd number which satisfies d >= 3. given by 2*int(sqrt(2*n)) + 1.
i is converted into d-nary representation, and j-th digit represents
the j-th component of the vector v, with the mapping
0, 1, ... d-1 <-> -x, -x+1, ..., 0, 1, ... x where x is the maximum
value that one component can take, given by int(sqrt(2*n)) (v.v = 2*n).
m: dimension of the lattice D_m, i.e. the length of the vector v.
Caution: the algorithm is pretty ineffective, it searches d**m vectors
exhaustively.
"""
if d < 3: # exceptional case. happens only when n = 0.
return [0]*m
digits = []
# maximum value that a component can take.
# following lines just converts i into d-ary number representation.
x = (d-1)//2
while i >= d:
i0 = i
d0 = d
i, r = divmod(i, d)
digits.append(r)
digits.append(i)
for j in range(len(digits)):
digits[j] -= x
# fills empty digits with minimum values.
digits.extend([-x] * (m - len(digits)))
return list(digits)
def jts_coeffs(m, n_max):
"""calculates Jacobi theta series of D_m, with u fixed to
u = (1, -1, 0, ..., 0) up to the given order n."""
u = [0]*m
u[0] = 1
u[1] = -1
coeffs = {}
for n in range(n_max + 1):
for v in possible_vs(m, n):
l = dot_prod(u, v)
if (n, l) in coeffs:
coeffs[(n, l)] += 1
else:
coeffs[(n, l)] = 1
return coeffs
def convert_to_string(coeffs):
output = ""
for exponent in sorted(coeffs):
n, l = exponent
a = coeffs[exponent]
term = '%d*q^%d*r^%d' % (a, n, l)
if output:
output = output + ' + ' + term
else:
output = term
return output
#my pre-calculated solutions
a = {(0, 0):(lambda m:1),
(1, 0):(lambda m:4*binomial(m-2, 2) + 2),
(1, 1):(lambda m:4*(m-2)),
(2, 0):(lambda m:2**4 * binomial(m-2, 4) + 2**3 * binomial(m-2, 2)
+ 2*(m-2)),
(2, 1):(lambda m:2**4 * binomial(m-2, 3)),
(2, 2):(lambda m:2*2 * binomial(m-2, 2) + 2),
(3, 0):(lambda m:2**6 * binomial(m-2, 6) + 2**5 * binomial(m-2, 4)
+ 2**2 * factorial(3) * binomial(m-2, 3) + 2**2 * (m-2))
}
if __name__ == '__main__':
for n in range(4):
m_min = 2*n + 2
for m in range(m_min, m_min + 3):
coeffs = jts_coeffs(m, n)
# the follwing lines are to compare the output with
# precaluculated values.
# for key in sorted(coeffs):
# j, l = key
# a0 = coeffs[key]
# print('a0(%d, %d) = %d' % (j, l, a0))
# if key in a:
# a1 = a[key](m)
# print('a1(%d, %d) = %d' % (j, l, a1))
# if a0 == a1:
# print('match')
# else:
# print('not match')
print('jacobi_theta(%d, %d) = %s'
% (m, n, convert_to_string(coeffs)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment