This commit is contained in:
Maria Marchwicka 2019-05-12 16:58:40 +02:00
parent 88866dac58
commit 8092b64e61

View File

@ -1,72 +1,102 @@
#!/usr/bin/env python #!/usr/bin/env python
import collections # TBD: remove part of the description to readme/example
import sys """
This script calculates signature functions for knots (cable sums).
The script can be run as a sage script from the terminal or used in inetactive
mode.
To calculate the number of characters for which signature function vanish use
the function eval_cable_for_thetas as shown below:
sage: eval_cable_for_thetas([[1, 3], [2], [-1, -2], [-3]])
T(2, 3; 2, 7) # T(2, 5) # -T(2, 3; 2, 5) # -T(2, 7)
Zero cases: 69
All cases: 1225
Zero theta combinations:
(0, 0, 0, 0)
(1, 1, 1, 1)
(1, 2, 2, 1)
(2, 1, 1, 2)
(2, 2, 2, 2)
(3, 0, 0, 3)
('T(2, 3; 2, 7) # T(2, 5) # -T(2, 3; 2, 5) # -T(2, 7)', 69, 1225)
sage:
The numbers given to the function eval_cable_for_thetas are k-values for each
component/cable in a direct sum.
To calculate signature function for a knot and a theta value, use function
get_function_of_theta_for_sum as follow:
sage: signature_function_generator = get_function_of_theta_for_sum([1, 3], [2], [-1, -2], [-3])
sage: sf = signature_function_generator(2, 1, 2, 2)
sage: print sf
0: 0
1/7: 0
1/6: 0
1/5: -1
2/5: 1
3/7: 0
1/2: 0
4/7: 0
3/5: -1
4/5: 1
5/6: 0
6/7: 0
sage:
or like below:
sage: print get_function_of_theta_for_sum([1, 3], [2], [-1, -2], [-3])(2, 1, 2, 2)
0: 0
1/7: 0
1/6: 0
1/5: -1
2/5: 1
3/7: 0
1/2: 0
4/7: 0
3/5: -1
4/5: 1
5/6: 0
6/7: 0
sage:
"""
import os import os
import sys
import collections
import inspect import inspect
import pandas as pd
import itertools as it import itertools as it
import pandas as pd
import re import re
class MySettings(object): class MySettings(object):
def __init__(self): def __init__(self):
self.f_results = os.path.join(os.getcwd(), "results.out") self.f_results = os.path.join(os.getcwd(), "results.out")
# is the ratio restriction for values in k_vector taken into account
# False flag is usefull to make quick script tests
self.only_slice_candidates = True self.only_slice_candidates = True
self.only_slice_candidates = False # self.only_slice_candidates = False
# knot_sum_formula is a schema for knots which signature function
# will be calculated
self.knot_sum_formula = "[[k[0], k[1], k[2]], [k[3], k[4]], \
[-k[0], -k[3], -k[4]], [-k[1], -k[2]]]"
# self.knot_sum_formula = "[[k[0], k[1], k[2]], [k[3]],\
# [-k[0], -k[1], -k[3]], [-k[2]]]"
self.default_limit = 3 self.default_limit = 3
def main(arg):
try:
limit = int(arg[1])
except:
limit = None
second_knot_sum_formula = "[[k[0], k[1], k[2]], [k[3], k[4]], \
[-k[0], -k[3], -k[4]], [-k[1], -k[2]]]"
first_knot_sum_formula = "[[k[0], k[1], k[2]], [k[3]],\
[-k[0], -k[1], -k[3]], [-k[2]]]"
tests(second_knot_sum_formula, limit)
tests(first_knot_sum_formula, limit)
def is_trivial_combination(knot_sum):
if len(knot_sum) == 4:
oposit_to_first = [-k for k in knot_sum[0]]
if oposit_to_first in knot_sum:
return True
return False
def tests(knot_sum_formula, limit=None):
settings = MySettings()
if limit is None:
limit = settings.default_limit
k_vector_size = extract_max(knot_sum_formula) + 1
with open(settings.f_results, 'w') as f_results:
combinations = it.combinations_with_replacement(range(1, limit + 1),
k_vector_size)
for comb in combinations:
if settings.only_slice_candidates and k_vector_size == 5:
comb = [comb[0], 4 * comb[0] + comb[1],
4 * (4 * comb[0] + comb[1]) + comb[2],
4 * comb[0] + comb[3],
4 * (4 * comb[0] + comb[3]) + comb[4]]
k = comb
knot_sum = eval(knot_sum_formula)
if is_trivial_combination(knot_sum):
continue
result = eval_cable_for_thetas(knot_sum)
if result is not None:
knot_description, null_comb, all_comb = result
line = (str(k) + ", " + str(null_comb) + ", " +
str(all_comb) + "\n")
f_results.write(line)
class SignatureFunction(object): class SignatureFunction(object):
""" """
This simple class encodes twisted and untwisted signature functions This simple class encodes twisted and untwisted signature functions
@ -87,23 +117,12 @@ class SignatureFunction(object):
"Signature function is defined on the interval [0, 1)." "Signature function is defined on the interval [0, 1)."
self.data[jump_arg] = jump self.data[jump_arg] = jump
def value(self, arg):
# Compute the value of the signature function at the point arg.
# This requires summing all signature jumps that occur before arg.
assert 0 <= arg < 1, \
"Signature function is defined on the interval [0, 1)."
val = 0
for jump_arg, jump in self.data.items():
if jump_arg < arg:
val += 2 * jump
elif jump_arg == arg:
val += jump
return val
def sum_of_absolute_values(self): def sum_of_absolute_values(self):
return sum([abs(i) for i in self.data.values()]) return sum([abs(i) for i in self.data.values()])
def double_cover(self): def double_cover(self):
# to read values for t^2
new_data = [] new_data = []
for jump_arg, jump in self.data.items(): for jump_arg, jump in self.data.items():
new_data.append((mod_one(jump_arg/2), jump)) new_data.append((mod_one(jump_arg/2), jump))
@ -111,7 +130,7 @@ class SignatureFunction(object):
return SignatureFunction(new_data) return SignatureFunction(new_data)
def __lshift__(self, shift): def __lshift__(self, shift):
# Shift of the signature functions correspond to the rotations. # A shift of the signature functions corresponds to the rotation.
return self.__rshift__(-shift) return self.__rshift__(-shift)
def __rshift__(self, shift): def __rshift__(self, shift):
@ -120,11 +139,8 @@ class SignatureFunction(object):
new_data.append((mod_one(jump_arg + shift), jump)) new_data.append((mod_one(jump_arg + shift), jump))
return SignatureFunction(new_data) return SignatureFunction(new_data)
def __sub__(self, other):
# we can perform arithmetic operations on signature functions.
return self + other.__neg__()
def __neg__(self): def __neg__(self):
# we can perform arithmetic operations on signature functions.
new_data = [] new_data = []
for jump_arg, jump in self.data.items(): for jump_arg, jump in self.data.items():
new_data.append(jump_arg, -jump) new_data.append(jump_arg, -jump)
@ -141,16 +157,105 @@ class SignatureFunction(object):
new_signature_function.data = new_data new_signature_function.data = new_data
return new_signature_function return new_signature_function
def __sub__(self, other):
return self + other.__neg__()
def __str__(self): def __str__(self):
return '\n'.join([str(jump_arg) + ": " + str(jump) return '\n'.join([str(jump_arg) + ": " + str(jump)
for jump_arg, jump in sorted(self.data.items())]) for jump_arg, jump in sorted(self.data.items())])
# def __repr__(self):
# return self.__str__() def main(arg):
"""
This function is run if the script was called from the terminal.
It calls another function to calculate signature functions for a schema
of a cable sum defined in the class MySettings.
Optionaly a parameter (a limit for k_0 value) can be given.
Thought to be run for time consuming calculations.
"""
try:
new_limit = int(arg[1])
except:
new_limit = None
perform_calculations(limit=new_limit)
def perform_calculations(knot_sum_formula=None, limit=None):
"""
This function calculates signature functions for knots constracted
accordinga a schema for a cable sum. The schema is given as an argument
or defined in the class MySettings.
Results of calculations will be writen to a file and to the stdout.
limit is the upper bound for the first value in k_vector, i.e first k value
in a cable sum (the number of knots that will be constracted depends
on limit value).
For each knot/cable sum the function eval_cable_for_thetas is called.
eval_cable_for_thetas calculetes the number of all possible thetas
(characters) and the number of combinations for which signature function
equeles zero. In case the first number is larger than squere of the second,
eval_cable_for_thetas returns None (i.e. the knot can not be slice).
Data for knots that are candidates for slice knots are saved to a file.
"""
settings = MySettings()
if limit is None:
limit = settings.default_limit
if knot_sum_formula is None:
knot_sum_formula = settings.knot_sum_formula
k_vector_size = extract_max(knot_sum_formula) + 1
combinations = it.combinations_with_replacement(range(1, limit + 1),
k_vector_size)
with open(settings.f_results, 'w') as f_results:
for k in combinations:
# print
print k
# TBD: maybe the following condition or the function
# get_shifted_combination should be redefined to a dynamic version
if settings.only_slice_candidates and k_vector_size == 5:
k = get_shifted_combination(k)
print k
knot_sum = eval(knot_sum_formula)
if is_trivial_combination(knot_sum):
continue
result = eval_cable_for_thetas(knot_sum, print_results=False)
if result is not None:
knot_description, null_comb, all_comb = result
line = (str(k) + ", " + str(null_comb) + ", " +
str(all_comb) + "\n")
f_results.write(line)
def is_trivial_combination(knot_sum):
# for now is applicable only for schema that are sums of 4 cables
if len(knot_sum) == 4:
oposit_to_first = [-k for k in knot_sum[0]]
if oposit_to_first in knot_sum:
return True
return False
def get_shifted_combination(combination):
# for now applicable only for schama
# "[[k[0], k[1], k[2]], [k[3], k[4]],
# [-k[0], -k[3], -k[4]], [-k[1], -k[2]]]"
# shift the combination so that the knot can be a candidate for slice
combination = [combination[0], 4 * combination[0] + combination[1],
4 * (4 * combination[0] + combination[1]) + combination[2],
4 * combination[0] + combination[3],
4 * (4 * combination[0] + combination[3]) + combination[4]]
return combination
# Proposition 9.8.
def get_blanchfield_for_pattern(k_n, theta): def get_blanchfield_for_pattern(k_n, theta):
"""
This function calculates a twisted signature function for a given cable
and theta/character. It returns object of class SignatureFunction.
It is based on Proposition 9.8. in Twisted Blanchfield Pairing.
"""
if theta == 0: if theta == 0:
return get_untwisted_signature_function(k_n) return get_untwisted_signature_function(k_n)
results = [] results = []
@ -176,16 +281,27 @@ def get_blanchfield_for_pattern(k_n, theta):
return SignatureFunction(results) return SignatureFunction(results)
# Bl_theta(K'_(2, d) =
# Bl_theta(T_2, d) + Bl(K')(ksi_l^(-theta) * t)
# + Bl(K')(ksi_l^theta * t)
def get_cable_signature_as_theta_function(*arg): def get_cable_signature_as_theta_function(*arg):
def signture_function(theta): """
This function takes as an argument a single cable and returns another
function that alow to calculate signature function for previously defined
cable and a theta given as an argument.
"""
def get_signture_function(theta):
"""
This function returns SignatureFunction for previously defined cable
and a theta given as an argument.
It is an implementaion of the formula:
Bl_theta(K'_(2, d)) =
Bl_theta(T_2, d) + Bl(K')(ksi_l^(-theta) * t)
+ Bl(K')(ksi_l^theta * t)
"""
if theta > abs(arg[-1]): if theta > abs(arg[-1]):
print "k for pattern is " + str(arg[-1]) print "k for the pattern in the cable is " + str(arg[-1])
print "theta shouldn't be larger than this" print "theta shouldn't be larger than this"
return None return None
cable_signature = get_blanchfield_for_pattern(arg[-1], theta) cable_signature = get_blanchfield_for_pattern(arg[-1], theta)
for i, k in enumerate(arg[:-1][::-1]): for i, k in enumerate(arg[:-1][::-1]):
ksi = 1/(2 * abs(k) + 1) ksi = 1/(2 * abs(k) + 1)
power = 2^i power = 2^i
@ -198,11 +314,12 @@ def get_cable_signature_as_theta_function(*arg):
c = c.double_cover() c = c.double_cover()
cable_signature += b + c cable_signature += b + c
return cable_signature return cable_signature
return signture_function return get_signture_function
def get_untwisted_signature_function(j): def get_untwisted_signature_function(j):
# Return the signature function of the T_{2,2k+1} torus knot. """This function returns the signature function of the T_{2,2k+1}
torus knot."""
k = abs(j) k = abs(j)
w = ([((2 * a + 1)/(4 * k + 2), -1 * sgn(j)) for a in range(k)] + w = ([((2 * a + 1)/(4 * k + 2), -1 * sgn(j)) for a in range(k)] +
[((2 * a + 1)/(4 * k + 2), 1 * sgn(j)) [((2 * a + 1)/(4 * k + 2), 1 * sgn(j))
@ -214,17 +331,23 @@ def get_function_of_theta_for_sum(*arg):
""" """
Function intended to calculate signature function for a connected Function intended to calculate signature function for a connected
sum of multiple cables with varying theta parameter values. sum of multiple cables with varying theta parameter values.
Accept arbitrary number of arguments (number of cables in connected sum). Accept arbitrary number of arguments (depending on number of cables in
connected sum).
Each argument should be given as list of integer representing Each argument should be given as list of integer representing
k - parameters for a cable: parameters k_i (i=1,.., n-1) for satelit knots k - parameters for a cable: parameters k_i (i=1,.., n-1) for satelit knots
T(2, 2k_i + 1) and - the last one - k_n for a pattern knot T(2, 2k_n + 1). T(2, 2k_i + 1) and - the last one - k_n for a pattern knot T(2, 2k_n + 1).
Returns a function described below. Returns a function that will take theta vector as an argument and return
an object SignatureFunction.
""" """
def signature_function_for_sum(*thetas): def signature_function_for_sum(*thetas):
# Returns object of SignatureFunction class for a previously defined """
# connercted sum of len(arg) cables. Returns object of SignatureFunction class for a previously defined
# Accept len(arg) arguments: for each cable one theta parameter. connercted sum of len(arg) cables.
# If call with no arguments, all theta parameters are set to be 0. Accept len(arg) arguments: for each cable one theta parameter.
If call with no arguments, all theta parameters are set to be 0.
"""
la = len(arg) la = len(arg)
lt = len(thetas) lt = len(thetas)
if lt == 0: if lt == 0:
@ -240,13 +363,17 @@ def get_function_of_theta_for_sum(*arg):
return signature_function_for_sum return signature_function_for_sum
def mod_one(n): def eval_cable_for_thetas(knot_sum, print_results=True):
"""This function returns the fractional part of some number.""" """
return n - floor(n) This function calculates all possible twisted signature functions for
a knot that is given as an argument. The knot should be encoded as a list
of its direct component. Each component schould be presented as a list
def eval_cable_for_thetas(knot_sum): of integers. This integers correspond to the k - values in each component/
cable. If a component is a mirror image of a cable the minus sign should
be written before each number for this component. For example:
eval_cable_for_thetas([[1, 8], [2], [-2, -8], [-2]])
eval_cable_for_thetas([[1, 2], [-1, -2]])
"""
f = get_function_of_theta_for_sum(*knot_sum) f = get_function_of_theta_for_sum(*knot_sum)
knot_description = get_knot_descrption(*knot_sum) knot_description = get_knot_descrption(*knot_sum)
all_combinations = get_number_of_combinations(*knot_sum) all_combinations = get_number_of_combinations(*knot_sum)
@ -261,17 +388,19 @@ def eval_cable_for_thetas(knot_sum):
zero_theta_combinations.append(v_theta) zero_theta_combinations.append(v_theta)
m = len([theta for theta in v_theta if theta != 0]) m = len([theta for theta in v_theta if theta != 0])
null_combinations += 2^m null_combinations += 2^m
else: # else:
assert sum(v_theta) != 0 # assert sum(v_theta) != 0
if null_combinations^2 >= all_combinations: if print_results:
print print
print knot_description print knot_description
print "Zero cases: " + str(null_combinations) print "Zero cases: " + str(null_combinations)
print "All cases: " + str(all_combinations) print "All cases: " + str(all_combinations)
print "Zero theta combinations: " if zero_theta_combinations:
for el in zero_theta_combinations: print "Zero theta combinations: "
print el for el in zero_theta_combinations:
print el
if null_combinations^2 >= all_combinations:
return knot_description, null_combinations, all_combinations return knot_description, null_combinations, all_combinations
return None return None
@ -294,27 +423,18 @@ def get_number_of_combinations(*arg):
number_of_combinations *= (2 * abs(knot[-1]) + 1) number_of_combinations *= (2 * abs(knot[-1]) + 1)
return number_of_combinations return number_of_combinations
def extract_max(string): def extract_max(string):
numbers = re.findall('\d+',string) """This function returns maximal number from given string."""
numbers = map(int,numbers) numbers = re.findall('\d+', string)
numbers = map(int, numbers)
return max(numbers) return max(numbers)
def mod_one(n):
"""This function returns the fractional part of some number."""
return n - floor(n)
if __name__ == '__main__' and '__file__' in globals(): if __name__ == '__main__' and '__file__' in globals():
main(sys.argv) main(sys.argv)
#
# def get_sigma(t, k):
# p = 2
# q = 2 * k + 1
# sigma_set = get_sigma_set(p, q)
# sigma = len(sigma_set) - 2 * len([z for z in sigma_set if t < z < 1 + t])
# return sigma
#
#
# def get_sigma_set(p, q):
# sigma_set = set()
# for i in range(1, p):
# for j in range(1, q):
# sigma_set.add(j/q + i/p)
# return sigma_set