From 8092b64e61dcc8618b5988991e2efe477fc08ac1 Mon Sep 17 00:00:00 2001 From: Maria Marchwicka Date: Sun, 12 May 2019 16:58:40 +0200 Subject: [PATCH] comments --- my_signature.sage | 356 +++++++++++++++++++++++++++++++--------------- 1 file changed, 238 insertions(+), 118 deletions(-) diff --git a/my_signature.sage b/my_signature.sage index fdd9d46..ad4f803 100644 --- a/my_signature.sage +++ b/my_signature.sage @@ -1,72 +1,102 @@ #!/usr/bin/env python -import collections -import sys +# TBD: remove part of the description to readme/example +""" +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 sys + +import collections import inspect -import pandas as pd import itertools as it +import pandas as pd import re class MySettings(object): def __init__(self): 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 = 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 -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): """ 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)." 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): return sum([abs(i) for i in self.data.values()]) def double_cover(self): + # to read values for t^2 new_data = [] for jump_arg, jump in self.data.items(): new_data.append((mod_one(jump_arg/2), jump)) @@ -111,7 +130,7 @@ class SignatureFunction(object): return SignatureFunction(new_data) 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) def __rshift__(self, shift): @@ -120,11 +139,8 @@ class SignatureFunction(object): new_data.append((mod_one(jump_arg + shift), jump)) return SignatureFunction(new_data) - def __sub__(self, other): - # we can perform arithmetic operations on signature functions. - return self + other.__neg__() - def __neg__(self): + # we can perform arithmetic operations on signature functions. new_data = [] for jump_arg, jump in self.data.items(): new_data.append(jump_arg, -jump) @@ -141,16 +157,105 @@ class SignatureFunction(object): new_signature_function.data = new_data return new_signature_function + def __sub__(self, other): + return self + other.__neg__() + def __str__(self): return '\n'.join([str(jump_arg) + ": " + str(jump) 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): + """ + 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: return get_untwisted_signature_function(k_n) results = [] @@ -176,16 +281,27 @@ def get_blanchfield_for_pattern(k_n, theta): 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 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]): - 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" return None cable_signature = get_blanchfield_for_pattern(arg[-1], theta) + for i, k in enumerate(arg[:-1][::-1]): ksi = 1/(2 * abs(k) + 1) power = 2^i @@ -198,11 +314,12 @@ def get_cable_signature_as_theta_function(*arg): c = c.double_cover() cable_signature += b + c return cable_signature - return signture_function + return get_signture_function 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) w = ([((2 * a + 1)/(4 * k + 2), -1 * sgn(j)) for a in range(k)] + [((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 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 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). - 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): - # Returns object of SignatureFunction class for a previously defined - # connercted sum of len(arg) cables. - # Accept len(arg) arguments: for each cable one theta parameter. - # If call with no arguments, all theta parameters are set to be 0. + """ + Returns object of SignatureFunction class for a previously defined + connercted sum of len(arg) cables. + 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) lt = len(thetas) if lt == 0: @@ -240,13 +363,17 @@ def get_function_of_theta_for_sum(*arg): return signature_function_for_sum -def mod_one(n): - """This function returns the fractional part of some number.""" - return n - floor(n) - - -def eval_cable_for_thetas(knot_sum): - +def eval_cable_for_thetas(knot_sum, print_results=True): + """ + 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 + 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) knot_description = get_knot_descrption(*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) m = len([theta for theta in v_theta if theta != 0]) null_combinations += 2^m - else: - assert sum(v_theta) != 0 + # else: + # assert sum(v_theta) != 0 - if null_combinations^2 >= all_combinations: + if print_results: print print knot_description print "Zero cases: " + str(null_combinations) print "All cases: " + str(all_combinations) - print "Zero theta combinations: " - for el in zero_theta_combinations: - print el + if zero_theta_combinations: + print "Zero theta combinations: " + for el in zero_theta_combinations: + print el + if null_combinations^2 >= all_combinations: return knot_description, null_combinations, all_combinations return None @@ -294,27 +423,18 @@ def get_number_of_combinations(*arg): number_of_combinations *= (2 * abs(knot[-1]) + 1) return number_of_combinations + def extract_max(string): - numbers = re.findall('\d+',string) - numbers = map(int,numbers) + """This function returns maximal number from given string.""" + numbers = re.findall('\d+', string) + numbers = map(int, 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(): 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