Compare commits

...

38 Commits

Author SHA1 Message Date
3976496e0b add gitignore 2021-10-07 05:34:22 +02:00
0563fa6464 clean pycache 2021-10-07 05:26:10 +02:00
acff4f75b9 Merge branch 'master' of git.wmi.amu.edu.pl:marchwicka/signature_function 2021-07-22 23:42:17 +02:00
ebce3008aa lit 2021-07-22 23:41:55 +02:00
9fce0ce6ee Update 'README.md' 2021-07-22 23:33:02 +02:00
10c79cb47c saving results to file 2021-07-22 23:24:13 +02:00
e125b72ef2 fix import sage in __init__, logging in utility.py 2021-07-16 18:01:31 +02:00
91e4c24413 refactor, comments 2021-07-16 11:01:28 +02:00
ebfc944e4e fix import_sage usage (part2) 2021-07-15 20:30:45 +02:00
a9784f4948 clean 2021-07-15 13:54:02 +02:00
8f32f6722c Merge branch 'master' of git.wmi.amu.edu.pl:marchwicka/signature_function 2021-07-15 13:51:11 +02:00
8c8654c456 fix problems with import_sage 2021-07-15 13:48:37 +02:00
2cac340cb7 Update 'README.md' 2021-07-15 13:42:34 +02:00
6c8f06405c Update 'README.md' 2021-07-11 20:52:14 +02:00
marchwicka
42f44b507d rename to gaknot 2021-06-29 23:09:45 +02:00
30d1f40670 First commit via PyCharm, refactor. 2021-01-25 02:20:17 +01:00
85478f7e1a sage import 2021-01-13 21:21:05 +01:00
b2feb908e1 test notebook 2021-01-11 09:21:10 +01:00
4d2baf4e46 import sage defined only once in utility 2021-01-11 07:34:03 +01:00
be469ccc2f archit changes 2021-01-11 06:58:30 +01:00
784d677a8d fix th mod q for sigma 2020-12-15 16:50:33 +01:00
68cf72305e plots 2020-11-19 10:32:15 +01:00
8d432c8129 verbose option added 2020-11-19 06:48:27 +01:00
bd39af1099 verbose option added 2020-11-19 06:47:52 +01:00
491ecc2b9c is_function_big_for_all_metabolizers method 2020-11-18 20:05:10 +01:00
683e58aa1a sigma calc 2020-11-10 18:56:58 +01:00
1d4d294458 before adding sigma 2020-11-10 17:55:29 +01:00
b843cb9a1f before adding sigma 2020-11-10 17:20:02 +01:00
412adc9abd add jupyter notebook 2020-11-09 09:34:33 +01:00
ee58512534 SignaturePlote, import instead of attach, jupyter friendly plots 2020-11-09 08:15:43 +01:00
4d549c2dcb Refact. Add SignatureFunction.plot_title var. Plot many sf at once - function plot_many 2020-11-06 04:01:54 +01:00
84a4c2a082 Refact, remove some property from CableSum. Refact. fill_q_vector and get_layers function 2020-11-05 19:18:01 +01:00
96b4860d2e refact 2020-11-05 17:59:36 +01:00
a71888558e plot matrices 2020-11-05 15:10:12 +01:00
bb11f93196 Added class SignatureWriter. Refactorisation. Theta vector parser method. 2020-11-02 09:32:39 +01:00
Maria Marchwicka
93911d0dfd delete some unused fragments from SignatureFunction 2020-10-22 13:33:18 +02:00
Maria Marchwicka
d0505ee366 We have a good knot with a big signature! Function to assigne q-values with wanted proportion between layers for a given knot formula. 2020-10-17 20:15:48 +02:00
Maria Marchwicka
58986ff162 Everything - i think - about sigma function or search for null signature is removed. 2020-10-15 07:16:42 +02:00
15 changed files with 2419 additions and 1532 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
*~
*.pyc
__pycache__/
.ipynb_checkpoints

View File

@ -1,3 +1,5 @@
This script calculates signature functions for knots (cable sums).
The script can be run as a sage script from the terminal or used in interactive mode.
A knot (cable sum) is encoded as a list where each element (also a list) corresponds to a cable knot.
This project allows calculating knot invariants for linear combinations of iterated torus knots.
It was created as part of proof for the main lemma from a paper "On the slice genus of generalized algebraic knots" (Maria Marchwicka and Wojciech Politarczyk).
To recreate calculations that were done for the proof see lemma.ipynb in notebooks.
For a more detailed description see package documentation.

View File

@ -1,947 +0,0 @@
#!/usr/bin/python
import numpy as np
import itertools as it
from typing import Iterable
from collections import Counter
from sage.arith.functions import LCM_list
import warnings
import re
SIGNATURE = 0
SIGMA = 1
# 9.11 (9.8)
# 9.15 (9.9)
class SignatureFunction(object):
def __init__(self, values=None, counter=None):
# builed counter based on values of signature jumps
if counter is None:
counter = Counter()
if values is None:
values = []
else:
msg = "Signature function is defined on the interval [0, 1)."
assert all(k < 1 for k, v in values), msg
for k, v in values:
counter[k] += v
self.cnt_signature_jumps = counter
# self.tikz_plot("bum.tex")
def is_zero_everywhere(self):
return not any(self.cnt_signature_jumps.values())
def double_cover(self):
# to read values for t^2
items = self.cnt_signature_jumps.items()
counter = Counter({(1 + k) / 2 : v for k, v in items})
counter.update(Counter({k / 2 : v for k, v in items}))
return SignatureFunction(counter=counter)
def square_root(self):
# to read values for t^(1/2)
counter = Counter()
for jump_arg, jump in self.cnt_signature_jumps.items():
if jump_arg < 1/2:
counter[2 * jump_arg] = jump
return SignatureFunction(counter=counter)
def minus_square_root(self):
# to read values for t^(1/2)
items = self.cnt_signature_jumps.items()
counter = Counter({mod_one(2 * k) : v for k, v in items if k >= 1/2})
return SignatureFunction(counter=counter)
def extremum(self):
max = 0
current = 0
items = sorted(self.cnt_signature_jumps.items())
for arg, jump in items:
current += 2 * jump
assert current == self(arg) + jump
if abs(current) > abs(max):
max = current
# if abs(max) > 9:
# return max
return max
def __rshift__(self, shift):
# A shift of the signature functions corresponds to the rotation.
counter = Counter({mod_one(k + shift) : v \
for k, v in self.cnt_signature_jumps.items()})
return SignatureFunction(counter=counter)
def __lshift__(self, shift):
return self.__rshift__(-shift)
def __neg__(self):
counter = Counter()
counter.subtract(self.cnt_signature_jumps)
return SignatureFunction(counter=counter)
def __add__(self, other):
counter = copy(self.cnt_signature_jumps)
counter.update(other.cnt_signature_jumps)
return SignatureFunction(counter=counter)
def __sub__(self, other):
counter = copy(self.cnt_signature_jumps)
counter.subtract(other.cnt_signature_jumps)
return SignatureFunction(counter=counter)
def __eq__(self, other):
self_cnt = Counter({k : v for k, v in self.cnt_signature_jumps.items()
if v != 0})
other_cnt = Counter({k : v for k, v in other.cnt_signature_jumps.items()
if v != 0})
return self.cnt_signature_jumps == other.cnt_signature_jumps
def __str__(self):
result = ''.join([str(jump_arg) + ": " + str(jump) + "\n"
for jump_arg, jump in sorted(self.cnt_signature_jumps.items())
if jump != 0])
return result
def __repr__(self):
result = ''.join([str(jump_arg) + ": " + str(jump) + ", "
for jump_arg, jump in sorted(self.cnt_signature_jumps.items())])
return result[:-2] + "."
def __call__(self, arg):
# return the value of the signature function at the point arg, i.e.
# sum of all signature jumps that occur before arg
items = self.cnt_signature_jumps.items()
result = [jump for jump_arg, jump in items if jump_arg < mod_one(arg)]
return 2 * sum(result) + self.cnt_signature_jumps[arg]
def total_sign_jump(self):
# Total signature jump is the sum of all jumps.
return sum([j[1] for j in self.to_list()])
def to_list(self):
# Return signature jumps formated as a list
return sorted(self.cnt_signature_jumps.items())
def step_function_data(self):
# Transform the signature jump data to a format understandable
# by the plot function.
l = self.to_list()
assert l == sorted(self.cnt_signature_jumps.items())
vals = ([(d[0], sum(2 * j[1] for j in l[:l.index(d)+1])) for d in l] +
[(0,self.cnt_signature_jumps[0]), (1,self.total_sign_jump())])
print("step_function_data")
print(vals)
counter = copy(self.cnt_signature_jumps)
counter[0] = self.cnt_signature_jumps[0]
counter[1] = self.total_sign_jump()
print(sorted(counter.items()))
return vals
def plot(self):
# plot the signture function
plot_step_function(self.step_function_data())
def tikz_plot(self, file_name):
# Draw the graph of the signature and transform it into TiKz.
# header of the LaTeX file
with open(file_name, "w") as f:
f.write("\\documentclass[tikz]{standalone}\n")
f.write("\\usetikzlibrary{datavisualization, " +
"datavisualization.formats.functions}\n")
f.write("\\begin{document}\n")
f.write("\\begin{tikzpicture}\n")
data = sorted(self.step_function_data())
print("data")
print(data)
f.write("\\datavisualization[scientific axes, " +
"visualize as smooth line,\n")
f.write("x axis={ticks={none,major={at={")
f.write(", " + str(N(data[0][0],digits=4)) + " as \\(" + \
str(data[0][0]) + "\\)")
for jump_arg, jump in data:
f.write(", " + str(N(jump_arg,digits=4)) +
" as \\(" + str(jump_arg) + "\\)")
f.write("}}}}\n")
f.write(" ]\n")
f.write("data [format=function]{\n")
f.write("var x : interval [0:1];\n")
f.write("func y = \\value x;\n")
f.write("};\n")
# close LaTeX enviroments
f.write("\\end{tikzpicture}\n")
f.write("\\end{document}\n")
class TorusCable(object):
def __init__(self, knot_formula, k_vector=None, q_vector=None):
self._knot_formula = knot_formula
# q_i = 2 * k_i + 1
if k_vector is not None:
self.k_vector = k_vector
elif q_vector is not None:
self.q_vector = q_vector
else:
msg = "Please give a list of k (k_vector) or q values (q_vector)."
raise ValueError(msg)
self._sigma_function = None
self._signature_as_function_of_theta = None
# SIGMA & SIGNATURE
@property
def sigma_function(self):
if self._sigma_function is None:
self._sigma_function = self.get_sigma_function()
return self._sigma_function
@property
def signature_as_function_of_theta(self):
if self._signature_as_function_of_theta is None:
self._signature_as_function_of_theta = \
self.get_signature_as_function_of_theta()
return self._signature_as_function_of_theta
# knot encoding
@property
def knot_formula(self):
return self._knot_formula
# @knot_formula.setter
# def knot_formula(self, knot_formula):
# self._knot_formula = knot_formula
# knot encoding
@property
def knot_description(self):
return self._knot_description
# knot encoding
@property
def knot_sum(self):
return self._knot_sum
@knot_sum.setter
def knot_sum(self, knot_sum):
self._knot_sum = knot_sum
self._knot_description = self.get_knot_descrption(knot_sum)
self._last_k_list = [abs(i[-1]) for i in knot_sum]
self._last_q_list = [2 * i + 1 for i in self._last_k_list]
if any(n not in Primes() for n in self._last_q_list):
msg = "Incorrect q-vector. This implementation assumes that" + \
" all last q values are prime numbers.\n" + \
str(self._last_q_list)
raise ValueError(msg)
self.q_order = LCM_list(self._last_q_list)
@property
def last_k_list(self):
return self._last_k_list
@property
def last_q_list(self):
return self._last_q_list
@property
def q_order(self):
return self._q_order
@q_order.setter
def q_order(self, val):
self._q_order = val
@property
def k_vector(self):
return self._k_vector
@k_vector.setter
def k_vector(self, k):
self._k_vector = k
if self.extract_max(self.knot_formula) > len(k) - 1:
msg = "The vector for knot_formula evaluation is to short!"
msg += "\nk_vector " + str(k) + " \nknot_formula " \
+ str(self.knot_formula)
raise IndexError(msg)
self.knot_sum = eval(self.knot_formula)
self._q_vector = [2 * k_val + 1 for k_val in k]
@property
def q_vector(self):
return self._q_vector
@q_vector.setter
def q_vector(self, new_q_vector):
self.k_vector = [(q - 1)/2 for q in new_q_vector]
def add_with_shift(self, other):
# print("*" * 100)
# print("BEFORE")
# print(self.knot_description)
# print(self.knot_sum)
# print("*" * 100)
# print("BEFORE k_vectors self, other")
# print(self.k_vector)
# print(other.k_vector)
shift = len(self.k_vector)
formula = re.sub(r'\d+', lambda x: str(int(x.group()) + shift),
other.knot_formula)
knot_formula = self.knot_formula[:-1] + ",\n" + formula[1:]
k_vector = self.k_vector + other.k_vector
cable = TorusCable(knot_formula, k_vector=k_vector)
s_signature_as_function_of_theta = self.signature_as_function_of_theta
o_signature_as_function_of_theta = other.signature_as_function_of_theta
shift = len(self.knot_sum)
shift = len(self.knot_sum)
def signature_as_function_of_theta(*thetas, **kwargs):
result = s_signature_as_function_of_theta(*thetas[shift:]) + \
o_signature_as_function_of_theta(*thetas[0:shift])
return result
cable._signature_as_function_of_theta = signature_as_function_of_theta
# print("*" * 100)
# print("AFTER")
# print(self.knot_description)
# print(self.knot_formula)
# print(self.knot_sum)
# print("*" * 100)
# print("AFTER k_vector, q_vector")
# print(self.k_vector)
# print(self.q_vector)
return cable
def __add__(self, other):
if self.k_vector != other.k_vector:
msg = "k_vectors are different. k-vector preserving addition is " +\
"impossible. The function add_with_shift was called instead"
warnings.warn(msg)
# print("*" * 100)
# print("BEFORE")
# print(self.knot_description)
# print(self.knot_sum)
# print("*" * 100)
# print("BEFORE k_vectors self, other")
knot_formula = self.knot_formula[:-1] + ",\n" + other.knot_formula[1:]
cable = TorusCable(knot_formula, k_vector=self.k_vector)
s_signature_as_function_of_theta = self.signature_as_function_of_theta
o_signature_as_function_of_theta = other.signature_as_function_of_theta
# print("FUNCTIONS ")
# print(s_signature_as_function_of_theta([1,1,1,2]))
# print(o_signature_as_function_of_theta([1,1,1,2]))
# print("FUNCTIONS 1111")
# print(s_signature_as_function_of_theta([1,1,1,1]))
# print(o_signature_as_function_of_theta([1,1,1,1]))
shift = len(self.knot_sum)
def signature_as_function_of_theta(*thetas, **kwargs):
result = s_signature_as_function_of_theta(*thetas[shift:]) + \
o_signature_as_function_of_theta(*thetas[0:shift])
return result
cable._signature_as_function_of_theta = signature_as_function_of_theta
# print("*" * 100)
# print("AFTER")
# print(self.knot_description)
# print(self.knot_formula)
# print(self.knot_sum)
# print("*" * 100)
# print("AFTER k_vector, q_vector")
# print(self.k_vector)
# print(self.q_vector)
return cable
@staticmethod
def extract_max(string):
numbers = re.findall(r'\d+', string)
numbers = map(int, numbers)
return max(numbers)
@staticmethod
def get_blanchfield_for_pattern(k_n, theta):
if theta == 0:
sf = TorusCable.get_untwisted_signature_function(k_n)
return sf.square_root() + sf.minus_square_root()
results = []
k = abs(k_n)
ksi = 1/(2 * k + 1)
counter = Counter()
# print("lambda_odd, i.e. (theta + e) % 2 != 0")
for e in range(1, k + 1):
if (theta + e) % 2 != 0:
counter[e * ksi] = 1 * sgn(k_n)
counter[1 - e * ksi] = -1 * sgn(k_n)
results.append((e * ksi, 1 * sgn(k_n)))
results.append((1 - e * ksi, -1 * sgn(k_n)))
# for example for k = 9 (q = 19) from this part we get
# for even theta
# 2/19: 1
# 4/19: 1
# 6/19: 1
# 8/19: 1
# 11/19: -1
# 13/19: -1
# 15/19: -1
# 17/19: -1
#
# for odd theta
# 1/19: 1
# 3/19: 1
# 5/19: 1
# 7/19: 1
# 9/19: 1
# 10/19: -1
# 12/19: -1
# 14/19: -1
# 16/19: -1
# 18/19: -1
# print("lambda_even")
# print("normal")
for e in range(1, theta):
if (theta + e) % 2 == 0:
results.append((e * ksi, 1 * sgn(k_n)))
results.append((1 - e * ksi, -1 * sgn(k_n)))
# print("reversed")
for e in range(theta + 1, k + 1):
if (theta + e) % 2 == 0:
results.append((e * ksi, -1 * sgn(k_n)))
results.append((1 - e * ksi, 1 * sgn(k_n)))
return SignatureFunction(values=results)
@staticmethod
def get_untwisted_signature_function(j):
# return the signature function of the T_{2,2k+1} torus knot
k = abs(j)
q = 2 * k + 1
values = ([((2 * a + 1)/(2 * q), -1 * sgn(j)) for a in range(k)] +
[((2 * a + 1)/(2 * q), 1 * sgn(j))
for a in range(k + 1, 2 * k + 1)])
return SignatureFunction(values=values)
@staticmethod
def get_knot_descrption(knot_sum):
description = ""
for knot in knot_sum:
if knot[0] < 0:
description += "-"
description += "T("
for k in knot:
description += "2, " + str(2 * abs(k) + 1) + "; "
description = description[:-2] + ") # "
return description[:-3]
def get_signature_as_function_of_theta(self, **key_args):
if 'verbose' in key_args:
verbose_default = key_args['verbose']
else:
verbose_default = False
def signature_as_function_of_theta(*thetas, **kwargs):
verbose = verbose_default
if 'verbose' in kwargs:
verbose = kwargs['verbose']
len_a = len(self.knot_sum)
len_t = len(thetas)
# call with no arguments
if len_t == 0:
return signature_as_function_of_theta(*(len_a * [0]))
if len_t != len_a:
if isinstance(thetas, Iterable):
if len(thetas[0]) == len_a:
thetas = thetas[0]
else:
msg = "This function takes exactly " + str(len_a) + \
" arguments or no argument at all (" + str(len_t) + \
" given)."
raise TypeError(msg)
sf = SignatureFunction()
untwisted_part = SignatureFunction()
# for each cable knot in cable sum apply theta
# print(self.knot_sum)
for i, knot in enumerate(self.knot_sum):
try:
ssf = self.get_summand_signature_as_theta_function(*knot)
plus, _, up = ssf(thetas[i])
# sf += ssf(thetas[i])
sf += plus
untwisted_part += up
# in case wrong theata value was given
except ValueError as e:
print("ValueError: " + str(e.args[0]) +\
" Please change " + str(i + 1) + ". parameter.")
return None
# a = thetas[0]
# # last_q = abs (2 * self.knot_sum[-1][-1]) + 1
# if all(i == thetas[0] for i in thetas):
# print()
# print("\n" + "*" * 100)
# print(self.knot_description)
# print("one vector " + str(thetas))
# print("max sf " + str(sf.extremum()))
# print()
# # assert untwisted_part.is_zero_everywhere()
if verbose:
print()
print(str(thetas))
print(sf)
msg = "tota signature jump = " + str(sf.total_sign_jump())
msg += "\nfunction\n" + str(sf)
assert sf.total_sign_jump() == 0, msg
return sf
signature_as_function_of_theta.__doc__ =\
signature_as_function_of_theta_docstring
return signature_as_function_of_theta
def get_summand_signature_as_theta_function(self, *knot_as_k_values):
def get_summand_signture_function(theta):
# TBD: another formula (for t^2) description
# TBD if theata condition
k_n = knot_as_k_values[-1]
if theta > 2 * abs(k_n):
msg = "k for the pattern in the cable is " + str(k_n) + \
". Parameter theta should not be larger than abs(k)."
raise ValueError(msg)
# twisted part
cable_signature = self.get_blanchfield_for_pattern(k_n, theta)
twisted_part = self.get_blanchfield_for_pattern(k_n, theta)
untwisted_part = SignatureFunction()
# untwisted part
# for each knot summand consider k values in reversed order
# ommit last k = k_n value
ksi = 1/(2 * abs(k_n) + 1)
for i, k in enumerate(knot_as_k_values[:-1][::-1]):
power = 2^i
a = TorusCable.get_untwisted_signature_function(k)
shift = theta * ksi * power
b = a >> shift
c = a << shift
for _ in range(i):
b = b.double_cover()
c = c.double_cover()
cable_signature += b + c
untwisted_part += b + c
return cable_signature, twisted_part, untwisted_part
get_summand_signture_function.__doc__ = \
get_summand_signture_function_docsting
return get_summand_signture_function
def is_metaboliser(self, theta):
i = 1
sum = 0
for idx, el in enumerate(theta):
to_add = i * el^2
# print("i * el^2 " + str(i * el^2))
to_add /= self.last_q_list[idx]
sum += to_add
# print("i * el^2 % q_4: " + str(to_add))
# print("sum ", sum)
i *= -1
# if sum is integer
# continue
# if all(a in [1, last_q - 1] for a in vector):
# pass
# else:
# continue
# print(theta, end=" ")
# print(sum)
if sum.is_integer():
# print("#" * 100)
# print(theta)
return True
return False
# if self.is_value_for_vector_class_big(vector, sigma_or_sign):
# good_vectors.append(vector)
# else:
# # print(vector)
# bad_vectors.append(vector)
# return good_vectors, bad_vectors
# searching for signature == 0
def get_number_of_combinations_of_theta(self):
number_of_combinations = 1
for knot in self.knot_sum:
number_of_combinations *= (2 * abs(knot[-1]) + 1)
return number_of_combinations
# searching for signature == 0
def check_for_null_theta_combinations(self, verbose=False):
list_of_good_vectors= []
number_of_null_comb = 0
f = self.signature_as_function_of_theta
range_list = [range(abs(knot[-1]) + 1) for knot in self.knot_sum]
for theta_vector in it.product(*range_list):
if f(*theta_vector).is_zero_everywhere():
list_of_good_vectors.append(theta_vector)
m = len([theta for theta in theta_vector if theta != 0])
number_of_null_comb += 2^m
return number_of_null_comb, list_of_good_vectors
# searching for signature == 0
def eval_cable_for_null_signature(self, print_results=False, verbose=False):
# search for zero combinations
number_of_all_comb = self.get_number_of_combinations_of_theta()
result = self.check_for_null_theta_combinations(verbose=verbose)
number_of_null_comb, list_of_good_vectors = result
if print_results:
print()
print(self.knot_description)
print("Zero cases: " + str(number_of_null_comb))
print("All cases: " + str(number_of_all_comb))
if list_of_good_vectors:
print("Zero theta combinations: ")
for el in list_of_good_vectors:
print(el)
if number_of_null_comb^2 >= number_of_all_comb:
return number_of_null_comb, number_of_all_comb
return None
##############################################################################
# sigma function
def get_sigma_function(self):
if len(self.k_vector) != 4:
msg = "This function is not implemented for k_vectors " +\
"with len other than 4."
raise IndexError(msg)
k_1, k_2, k_3, k_4 = [abs(k) for k in self.k_vector]
last_q = 2 * k_4 + 1
ksi = 1/last_q
sigma_q_1 = self.get_untwisted_signature_function(k_1)
sigma_q_2 = self.get_untwisted_signature_function(k_2)
sigma_q_3 = self.get_untwisted_signature_function(k_3)
def sigma_function(theta_vector, print_results=False):
# "untwisted" part (Levine-Tristram signatures)
a_1, a_2, a_3, a_4 = theta_vector
untwisted_part = 2 * (sigma_q_2(ksi * a_1) -
sigma_q_2(ksi * a_2) +
sigma_q_3(ksi * a_3) -
sigma_q_3(ksi * a_4) +
sigma_q_1(ksi * a_1 * 2) -
sigma_q_1(ksi * a_4 * 2))
# "twisted" part
tp = [0, 0, 0, 0]
for i, a in enumerate(theta_vector):
if a:
tp[i] = -last_q + 2 * a - 2 * (a^2/last_q)
twisted_part = tp[0] - tp[1] + tp[2] - tp[3]
# if print_results:
# self.print_results_LT(theta_vector, untwisted_part)
# self.print_results_LT(theta_vector, twisted_part)
sigma_v = untwisted_part + twisted_part
return sigma_v
return sigma_function
def print_results_LT(self, theta_vector, untwisted_part):
knot_description = self.knot_description
k_1, k_2, k_3, k_4 = [abs(k) for k in self.k_vector]
a_1, a_2, a_3, a_4 = theta_vector
last_q = 2 * k_4 + 1
ksi = 1/last_q
sigma_q_1 = self.get_untwisted_signature_function(k_1)
sigma_q_2 = self.get_untwisted_signature_function(k_2)
sigma_q_3 = self.get_untwisted_signature_function(k_3)
print("\n\nLevine-Tristram signatures for the cable sum: ")
print(knot_description)
print("and characters:\n" + str(theta_vector) + ",")
print("ksi = " + str(ksi))
print("\n\n2 * (sigma_q_2(ksi * a_1) + " + \
"sigma_q_1(ksi * a_1 * 2) - " +\
"sigma_q_2(ksi * a_2) + " +\
"sigma_q_3(ksi * a_3) - " +\
"sigma_q_3(ksi * a_4) - " +\
"sigma_q_1(ksi * a_4 * 2))" +\
\
" = \n\n2 * (sigma_q_2(" + \
str(ksi) + " * " + str(a_1) + \
") + sigma_q_1(" + \
str(ksi) + " * " + str(a_1) + " * 2" + \
") - sigma_q_2(" + \
str(ksi) + " * " + str(a_2) + \
") + sigma_q_3(" + \
str(ksi) + " * " + str(a_3) + \
") - sigma_q_3(" + \
str(ksi) + " * " + str(a_4) + \
") - sigma_q_1(" + \
str(ksi) + " * " + str(a_4) + " * 2)) " + \
\
" = \n\n2 * (sigma_q_2(" + \
str(mod_one(ksi * a_1)) + \
") + sigma_q_1(" + \
str(mod_one(ksi * a_1 * 2)) + \
") - sigma_q_2(" + \
str(mod_one(ksi * a_2)) + \
") + sigma_q_3(" + \
str(mod_one(ksi * a_3)) + \
") - sigma_q_3(" + \
str(mod_one(ksi * a_4)) + \
") - sigma_q_1(" + \
str(mod_one(ksi * a_4 * 2)) + \
\
") = \n\n2 * ((" + \
str(sigma_q_2(ksi * a_1)) + \
") + (" + \
str(sigma_q_1(ksi * a_1 * 2)) + \
") - (" + \
str(sigma_q_2(ksi * a_2)) + \
") + (" + \
str(sigma_q_3(ksi * a_3)) + \
") - (" + \
str(sigma_q_3(ksi * a_4)) + \
") - (" + \
str(sigma_q_1(ksi * a_4 * 2)) + ")) = " + \
"\n\n2 * (" + \
str(sigma_q_2(ksi * a_1) +
sigma_q_1(ksi * a_1 * 2) -
sigma_q_2(ksi * a_2) +
sigma_q_3(ksi * a_3) -
sigma_q_3(ksi * a_4) -
sigma_q_1(ksi * a_4 * 2)) + \
") = " + str(untwisted_part))
print("\nSignatures:")
print("\nq_1 = " + str(2 * k_1 + 1) + ": " + repr(sigma_q_1))
print("\nq_2 = " + str(2 * k_2 + 1) + ": " + repr(sigma_q_2))
print("\nq_3 = " + str(2 * k_3 + 1) + ": " + repr(sigma_q_3))
def print_results_sigma(self, theta_vector, twisted_part):
a_1, a_2, a_3, a_4 = theta_vector
knot_description = self.knot_description
last_q = self.q_vector[-1]
print("\n\nSigma values for the cable sum: ")
print(knot_description)
print("and characters: " + str(v_theta))
print("\nsigma(T_{2, q_4}, ksi_a) = " + \
"-q + (2 * a * (q_4 - a)/q_4) " +\
"= -q + 2 * a - 2 * a^2/q_4 if a != 0,\n\t\t\t" +\
" = 0 if a == 0.")
print("\nsigma(T_{2, q_4}, chi_a_1) = ", end="")
if a_1:
print("- (" + str(last_q) + ") + 2 * " + str(a_1) + " + " +\
"- 2 * " + str(a_1^2) + "/" + str(last_q) + \
" = " + str(tp[0]))
else:
print("0")
print("\nsigma(T_{2, q_4}, chi_a_2) = ", end ="")
if a_2:
print("- (" + str(last_q) + ") + 2 * " + str(a_2) + " + " +\
"- 2 * " + str(a_2^2) + "/" + str(last_q) + \
" = " + str(tp[1]))
else:
print("0", end="")
print("\nsigma(T_{2, q_4}, chi_a_3) = ", end="")
if a_3:
print("- (" + str(last_q) + ") + 2 * " + str(a_3) + " + " +\
"- 2 * " + str(a_3^2) + "/" + str(last_q) + \
" = " + str(tp[2]))
else:
print("0", end="")
print("\nsigma(T_{2, q_4}, chi_a_4) = ", end="")
if a_4:
print("- (" + str(last_q) + ") + 2 * " + str(a_4) + " + " +\
"- 2 * " + str(a_4^2) + "/" + str(last_q) + \
" = " + str(tp[3]))
else:
print("0")
print("\n\nsigma(T_{2, q_4}, chi_a_1) " + \
"- sigma(T_{2, q_4}, chi_a_2) " + \
"+ sigma(T_{2, q_4}, chi_a_3) " + \
"- sigma(T_{2, q_4}, chi_a_4) =\n" + \
"sigma(T_{2, q_4}, " + str(a_1) + \
") - sigma(T_{2, q_4}, " + str(a_2) + \
") + sigma(T_{2, q_4}, " + str(a_3) + \
") - sigma(T_{2, q_4}, " + str(a_4) + ") = " + \
str(tp[0] - tp[1] + tp[2] - tp[3]))
def mod_one(n):
return n - floor(n)
TorusCable.get_number_of_combinations_of_theta.__doc__ = \
"""
Arguments:
arbitrary number of lists of numbers, each list encodes a single cable
Return:
number of possible theta values combinations that could be applied
for a given cable sum,
i.e. the product of q_j for j = {1,.. n},
where n is a number of direct components in the cable sum,
and q_j is the last q parameter for the component (a single cable)
"""
TorusCable.get_knot_descrption.__doc__ = \
"""
Arguments:
arbitrary number of lists of numbers, each list encodes a single cable.
Examples:
sage: get_knot_descrption([1, 3], [2], [-1, -2], [-3])
'T(2, 3; 2, 7) # T(2, 5) # -T(2, 3; 2, 5) # -T(2, 7)'
"""
TorusCable.eval_cable_for_null_signature.__doc__ = \
"""
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_null_signature([[1, 8], [2], [-2, -8], [-2]])
eval_cable_for_null_signature([[1, 2], [-1, -2]])
sage: eval_cable_for_null_signature([[1, 3], [2], [-1, -2], [-3]])
T(2, 3; 2, 7) # T(2, 5) # -T(2, 3; 2, 5) # -T(2, 7)
Zero cases: 1
All cases: 1225
Zero theta combinations:
(0, 0, 0, 0)
sage:
The numbers given to the function eval_cable_for_null_signature
are k-values for each component/cable in a direct sum.
"""
TorusCable.get_signature_as_function_of_theta.__doc__ = \
"""
Function intended to construct signature function for a connected
sum of multiple cables with varying theta parameter values.
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 that will take theta vector as an argument and return
an object SignatureFunction.
To calculate signature function for a cable sum and a theta values vector,
use as below.
sage: signature_function_generator = get_signature_as_function_of_theta(
[1, 3], [2], [-1, -2], [-3])
sage: sf = signature_function_generator(2, 1, 2, 2)
sage: print(sf)
0: 0
5/42: 1
1/7: 0
1/5: -1
7/30: -1
2/5: 1
3/7: 0
13/30: -1
19/42: -1
23/42: 1
17/30: 1
4/7: 0
3/5: -1
23/30: 1
4/5: 1
6/7: 0
37/42: -1
Or like below.
sage: print(get_signature_as_function_of_theta([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
"""
SignatureFunction.__doc__ = \
"""
This simple class encodes twisted and untwisted signature functions
of knots. Since the signature function is entirely encoded by its signature
jump, the class stores only information about signature jumps
in a dictionary self.cnt_signature_jumps.
The dictionary stores data of the signature jump as a key/values pair,
where the key is the argument at which the functions jumps
and value encodes the value of the jump. Remember that we treat
signature functions as defined on the interval [0,1).
"""
get_summand_signture_function_docsting = \
"""
This function returns SignatureFunction for previously defined single
cable T_(2, q) and a theta given as an argument.
The cable was defined by calling function
get_summand_signature_as_theta_function(*arg)
with the cable description 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)
"""
signature_as_function_of_theta_docstring = \
"""
Arguments:
Returns object of SignatureFunction class for a previously defined
connected 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.
"""
mod_one.__doc__ = \
"""
Argument:
a number
Return:
the fractional part of the argument
Examples:
sage: mod_one(9 + 3/4)
3/4
sage: mod_one(-9 + 3/4)
3/4
sage: mod_one(-3/4)
1/4
"""
TorusCable.get_blanchfield_for_pattern.__doc__ = \
"""
Arguments:
k_n: a number s.t. q_n = 2 * k_n + 1, where
T(2, q_n) is a pattern knot for a single cable from a cable sum
theta: twist/character for the cable (value form v vector)
Return:
SignatureFunction created for twisted signature function
for a given cable and theta/character
Based on:
Proposition 9.8. in Twisted Blanchfield Pairing
(https://arxiv.org/pdf/1809.08791.pdf)
"""
TorusCable.get_summand_signature_as_theta_function.__doc__ = \
"""
Argument:
n integers that encode a single cable, i.e.
values of q_i for T(2,q_0; 2,q_1; ... 2, q_n)
Return:
a function that returns SignatureFunction for this single cable
and a theta given as an argument
"""

64
gaknot/__init__.py Normal file
View File

@ -0,0 +1,64 @@
#!/usr/bin/env python3
r"""calculations of signature function and sigma invariant of generalized algebraic knots (GA-knots)
The package was used to prove Lemma 3.2 from a paper
'On the slice genus of generalized algebraic knots' Maria Marchwicka and Wojciech Politarczyk).
It contains the following submodules.
1) main.sage - with function prove_lemma
2) signature.sage - contains SignatureFunction class;
it encodes twisted and untwisted signature functions
of knots and allows to perform algebraic operations on them.
3) cable_signature.sage - contains the following classes:
a) CableSummand - it represents a single cable knot,
b) CableSum - it represents a cable sum, i. e. linear combination of single cable knots;
since the signature function and sigma invariant are additive under connected sum,
the class use calculations from CableSummand objects,
c) CableTemplate - it represents a scheme for a cable sums.
"""
from .utility import import_sage
import os
package = __name__.split('.')[0]
dirname = os.path.dirname
path = dirname(dirname(__file__))
import_sage('signature', package=package, path=path)
import_sage('cable_signature', package=package, path=path)
import_sage('main', package=package, path=path)
from .main import prove_lemma
# EXAMPLES::
#
# sage: eval_cable_for_null_signature([[1, 3], [2], [-1, -2], [-3]])
#
# T(2, 3; 2, 7) # T(2, 5) # -T(2, 3; 2, 5) # -T(2, 7)
# Zero cases: 1
# All cases: 1225
# Zero theta combinations:
# (0, 0, 0, 0)
#
# sage:
#
# The numbers given to the function eval_cable_for_null_signature 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_signature_as_function_of_theta (see help/docstring for details).
#
# About notation:
# Cables that we work with follow a schema:
# T(2, q_1; 2, q_2; 2, q_4) # -T(2, q_2; 2, q_4) #
# # T(2, q_3; 2, q_4) # -T(2, q_1; 2, q_3; 2, q_4)
# In knot_formula each k[i] is related with some q_i value, where
# q_i = 2*k[i] + 1.
# So we can work in the following steps:
# 1) choose a schema/formula by changing the value of knot_formula
# 2) set each q_i all or choose range in which q_i should varry
# 3) choose vector v / theta vector.
#

863
gaknot/cable_signature.sage Normal file
View File

@ -0,0 +1,863 @@
#!/usr/bin/env sage -python
import numpy as np
import itertools as it
import warnings
import logging
import re
import ast
from typing import Iterable
from collections import Counter
from sage.arith.functions import LCM_list
# import importlib
# preparsing sage script and import as module
if __name__ == '__main__': # TBD - check is this is optimal
from utility import import_sage
package = None
path = ''
else: # called from package
from .utility import import_sage
package = os.path.join( __name__.split('.')[0])
path = '../'
sg = import_sage('signature', package=package, path=path)
# constants - which invariants should be calculate
SIGMA = 0
SIGNATURE = 1
# #############################################################################
# 9.11 (9.8)
# 9.15 (9.9)
PLOTS_DIR = "../plots"
class CableSummand:
def __init__(self, knot_as_k_values, verbose=False):
self.verbose = verbose
self.knot_as_k_values = knot_as_k_values
self.knot_description = self.get_summand_descrption(knot_as_k_values)
self.signature_as_function_of_theta = \
self.get_summand_signature_as_theta_function()
self.sigma_as_function_of_theta = self.get_sigma_as_function_of_theta()
@staticmethod
def get_summand_descrption(knot_as_k_values):
description = ""
if knot_as_k_values[0] < 0:
description += "-"
description += "T("
for k in knot_as_k_values:
description += "2, " + str(2 * abs(k) + 1) + "; "
return description[:-2] + ")"
@classmethod
def get_blanchfield_for_pattern(cls, k_n, theta=0):
msg = "Theorem on which this function is based, assumes " +\
"theta < k, where q = 2*k + 1 for pattern knot T(p, q)."
if theta == 0:
sf = cls.get_untwisted_signature_function(k_n)
return sf.square_root() + sf.minus_square_root()
k = abs(k_n)
assert theta <= k, msg
results = []
ksi = 1/(2 * k + 1)
# print("lambda_odd, i.e. (theta + e) % 2 != 0")
for e in range(1, k + 1):
if (theta + e) % 2 != 0:
results.append((e * ksi, 1 * sgn(k_n)))
results.append((1 - e * ksi, -1 * sgn(k_n)))
# for example for k = 9 (q = 19) from this part we get
# for even theta
# 2/19: 1
# 4/19: 1
# 6/19: 1
# 8/19: 1
# 11/19: -1
# 13/19: -1
# 15/19: -1
# 17/19: -1
#
# for odd theta
# 1/19: 1
# 3/19: 1
# 5/19: 1
# 7/19: 1
# 9/19: 1
# 10/19: -1
# 12/19: -1
# 14/19: -1
# 16/19: -1
# 18/19: -1
# print("lambda_even")
# print("normal")
for e in range(1, theta):
if (theta + e) % 2 == 0:
results.append((e * ksi, 1 * sgn(k_n)))
results.append((1 - e * ksi, -1 * sgn(k_n)))
# print("reversed")
for e in range(theta + 1, k + 1):
if (theta + e) % 2 == 0:
results.append((e * ksi, -1 * sgn(k_n)))
results.append((1 - e * ksi, 1 * sgn(k_n)))
return sg.SignatureFunction(values=results)
@classmethod
def get_satellite_part(cls, *knot_as_k_values, theta=0):
patt_k = knot_as_k_values[-1]
ksi = 1/(2 * abs(patt_k) + 1)
satellite_part = sg.SignatureFunction()
# For each knot summand consider k values in reversed order,
# ommit k value for pattern.
for layer_num, k in enumerate(knot_as_k_values[:-1][::-1]):
sf = cls.get_untwisted_signature_function(k)
shift = theta * ksi * 2^layer_num
right_shift = sf >> shift
left__shift = sf << shift
for _ in range(layer_num):
right_shift = right_shift.double_cover()
left__shift = left__shift.double_cover()
satellite_part += right_shift + left__shift
return satellite_part
@staticmethod
def get_untwisted_signature_function(k=None, q=None):
# return the signature function of the T_{2, 2k+1} torus knot
if q is not None:
signum = sign(q)
q = abs(q)
k = (q - 1)/2
elif k is not None:
signum = sign(k)
k = abs(k)
q = 2 * k + 1
else:
raise ValueError('k or q value must be given')
counter = Counter({(2 * a + 1)/(2 * q): -signum
for a in range(k)})
counter.update(Counter({(2 * a + 1)/(2 * q): signum
for a in range(k + 1, q)}))
return sg.SignatureFunction(counter=counter)
def get_summand_signature_as_theta_function(self):
# knot_as_k_values = self.knot_as_k_values
def get_summand_signture_function(theta):
patt_k = self.knot_as_k_values[-1]
# theta should not be larger than k for the pattern.
theta %= (2 * abs(patt_k) + 1)
theta = min(theta, 2 * abs(patt_k) + 1 - theta)
pattern_part = self.get_blanchfield_for_pattern(patt_k, theta)
satellite_part = self.get_satellite_part(*self.knot_as_k_values,
theta=theta)
sf = satellite_part + pattern_part
satellite_part.plot_title = self.knot_description + \
", theta = " + str(theta) + \
", satellite part."
pattern_part.plot_title = self.knot_description + \
", theta = " + str(theta) + \
", pattern part."
sf.plot_title = self.knot_description +\
", theta = " + str(theta)
return pattern_part, satellite_part, sf
get_summand_signture_function.__doc__ = \
get_summand_signture_function_docsting
return get_summand_signture_function
def get_file_name_for_summand_plot(self, theta=0):
if self.knot_as_k_values[0] < 0:
name = "inv_T_"
else:
name = "T_"
for k in self.knot_as_k_values:
name += str(abs(k)) + "_"
name += "_theta_" + str(theta)
return name
def plot_summand_for_theta(self, theta, save_path=None):
pp, sp, sf = self.signature_as_function_of_theta(theta)
title = self.knot_description + ", theta = " + str(theta)
if save_path is not None:
file_name = self.get_file_name_for_summand_plot(theta)
save_path = os.path.join(save_path, file_name)
sg.SignaturePloter.plot_sum_of_two(pp, sp, title=title,
save_path=save_path)
def plot_summand_sigma(self):
sigma = self.sigma_as_function_of_theta
# pattern part
th_values = list(range(abs(self.knot_as_k_values[-1]) + 1))
y = [sigma(th)[0] for th in th_values]
print("plot_summand_sigma")
print(th_values)
print(y)
# satellite_part
patt_k = self.knot_as_k_values[-1]
patt_q = 2 * abs(patt_k) + 1
ksi = 1/patt_q
x = []
s = self.get_untwisted_signature_function
list_of_signatue_functions = [s(k) for k in self.knot_as_k_values[:-1]]
for i, k in enumerate(self.knot_as_k_values[:-1][::-1]):
layer_num = i + 1
x.append(ksi * layer_num)
print("\nx")
print(x)
print(th_values)
print("\nx product")
x = list(set(it.product(x, th_values)))
x = [(a * b) for (a, b) in x]
print(x)
def print_sigma_as_function_of_theta(self, theta):
if not theta:
return
# theta should not be larger than q for the pattern.
patt_k = self.knot_as_k_values[-1]
patt_q = 2 * abs(patt_k) + 1
theta %= patt_q
ksi = 1/patt_q
# satellite part (Levine-Tristram signatures)
print(3 * "\n" + 10 * "#" + " " + self.knot_description +
" " + 10 * "#" + "\n")
satellite_part = 0
for layer_num, k in enumerate(self.knot_as_k_values[::-1]):
sigma_q = self.get_untwisted_signature_function(k)
arg = ksi * theta * layer_num
sp = sigma_q(arg)
satellite_part += 2 * sp
if details and arg:
label = "ksi * theta * layer_num = " + str(arg)
title = self.knot_description + ", layer " + str(layer_num)
title += ", theta = " + str(theta)
sigma_q.plot(special_point=(mod_one(arg), sp),
special_label=label,
title=title,)
pp = (-patt_q + 2 * theta - 2 * (theta^2/patt_q)) * sign(patt_k)
sigma = pp + satellite_part
print(self.knot_description + ", theta = " + str(theta))
print("pp = " + str(pp), end=', ')
print("satellite_part = " + str(satellite_part) + "\n")
def get_sigma_as_function_of_theta(self):
patt_k = self.knot_as_k_values[-1]
patt_q = 2 * abs(patt_k) + 1
ksi = 1/patt_q
def sigma_as_function_of_theta(theta):
if theta == 0:
return 0, 0, 0
# theta should not be larger than q for the pattern.
patt_k = self.knot_as_k_values[-1]
theta %= (2 * abs(patt_k) + 1)
satellite_part = 0
for i, k in enumerate(self.knot_as_k_values[:-1][::-1]):
layer_num = i + 1
sigma_q = self.get_untwisted_signature_function(k)
sp = 2 * sigma_q(ksi * theta * layer_num)
satellite_part += sp
if theta:
pp = (-patt_q + 2 * theta - 2 * (theta^2/patt_q)) * sign(patt_k)
else:
pp = 0
return pp, satellite_part, pp + satellite_part
return sigma_as_function_of_theta
class CableSum:
def __init__(self, knot_sum, verbose=False):
self.verbose = verbose
self.knot_sum_as_k_valus = knot_sum
self.knot_description = self.get_knot_descrption(knot_sum)
self.patt_k_list = [abs(i[-1]) for i in knot_sum]
self.patt_q_list = [2 * i + 1 for i in self.patt_k_list]
if any(n not in Primes() for n in self.patt_q_list):
msg = "Incorrect k- or q-vector. This implementation assumes that"\
+ " all last q values are prime numbers.\n" + \
str(self.patt_q_list)
raise ValueError(msg)
self.q_order = LCM_list(self.patt_q_list)
self.knot_summands = [CableSummand(k, verbose) for k in knot_sum]
self.signature_as_function_of_theta = \
self.get_signature_as_function_of_theta()
self.sigma_as_function_of_theta = \
self.get_sigma_as_function_of_theta()
def __call__(self, *thetas):
return self.signature_as_function_of_theta(*thetas)
def get_dir_name_for_plots(self, dir=None):
dir_name = ''
for knot in self.knot_summands:
if knot.knot_as_k_values[0] < 0:
dir_name += "inv_"
dir_name += "T_"
for k in knot.knot_as_k_values:
k = 2 * abs (k) + 1
dir_name += str(k) + "_"
dir_name = dir_name[:-1]
print(dir_name)
dir_path = os.getcwd()
if dir is not None:
dir_path = os.path.join(dir_path, dir)
dir_path = os.path.join(dir_path, dir_name)
if not os.path.isdir(dir_path):
os.mkdir(dir_path)
return dir_name
def plot_sum_for_theta_vector(self, thetas, save_to_dir=False):
if save_to_dir:
if not os.path.isdir(PLOTS_DIR):
os.mkdir(PLOTS_DIR)
dir_name = self.get_dir_name_for_plots(dir=PLOTS_DIR)
save_path = os.path.join(os.getcwd(), PLOTS_DIR)
save_path = os.path.join(save_path, dir_name)
else:
save_path = None
# for theta, knot in zip(thetas, self.knot_summands):
# knot.plot_summand_for_theta(thetas, save_path=save_path)
# pp, sp, sf = self.signature_as_function_of_theta(*thetas)
# title = self.knot_description + ", thetas = " + str(thetas)
# if save_path is not None:
# file_name = re.sub(r', ', '_', str(thetas))
# file_name = re.sub(r'[\[\]]', '', str(file_name))
# file_path = os.path.join(save_path, file_name)
# sg.SignaturePloter.plot_sum_of_two(pp, sp, title=title,
# save_path=file_path)
#
# if save_path is not None:
# file_path = os.path.join(save_path, "all_" + file_name)
# sf_list = [knot.signature_as_function_of_theta(thetas[i])[2]
# for i, knot in enumerate(self.knot_summands)]
# sg.SignaturePloter.plot_many(*sf_list, cols=2)
# pp, sp, sf = knot.signature_as_function_of_theta(thetas[i])
# (pp + sp) = sp.plot
#
# sg.SignatureFunction.plot_sum_of_two(pp, sp, title=title,
# save_path=file_path)
return dir_name
def plot_sigma_for_summands(self):
for knot in self.knot_summands:
knot.plot_summand_sigma()
def parse_thetas(self, *thetas):
summands_num = len(self.knot_sum_as_k_valus)
if not thetas:
thetas = summands_num * (0,)
elif len(thetas) == 1 and summands_num > 1:
if isinstance(thetas[0], Iterable):
if len(thetas[0]) >= summands_num:
thetas = thetas[0]
elif not thetas[0]:
thetas = summands_num * (0,)
elif thetas[0] == 0:
thetas = summands_num * (0,)
else:
msg = "This function takes at least " + str(summands_num) + \
" arguments or no argument at all (" + str(len(thetas)) \
+ " given)."
raise TypeError(msg)
return tuple(thetas)
@staticmethod
def get_knot_descrption(knot_sum):
"""
Arguments:
arbitrary number of lists of numbers,
each list encodes a single cable.
Examples:
sage: get_knot_descrption([1, 3], [2], [-1, -2], [-3])
'T(2, 3; 2, 7) # T(2, 5) # -T(2, 3; 2, 5) # -T(2, 7)'
"""
description = ""
for knot in knot_sum:
if knot[0] < 0:
description += "-"
description += "T("
for k in knot:
description += "2, " + str(2 * abs(k) + 1) + "; "
description = description[:-2] + ") # "
return description[:-3]
def get_sigma_as_function_of_theta(self, verbose=None):
default_verbose = verbose or self.verbose
def sigma_as_function_of_theta(*thetas, verbose=None, **kwargs):
verbose = verbose or default_verbose
thetas = self.parse_thetas(*thetas)
sigma = 0
for th, knot in zip(thetas, self.knot_summands):
_, _, s = knot.sigma_as_function_of_theta(th)
sigma += s
return sigma
return sigma_as_function_of_theta
def get_signature_as_function_of_theta(self, **key_args):
if 'verbose' in key_args:
verbose_default = key_args['verbose']
else:
verbose_default = False
knot_desc = self.knot_description
def signature_as_function_of_theta(*thetas, **kwargs):
# print("\n\nsignature_as_function_of_theta " + knot_desc)
verbose = verbose_default
if 'verbose' in kwargs:
verbose = kwargs['verbose']
thetas = self.parse_thetas(*thetas)
satellite_part = sg.SignatureFunction()
pattern_part = sg.SignatureFunction()
# for each cable knot (summand) in cable sum apply theta
for th, knot in zip(thetas, self.knot_summands):
pp, sp, _ = knot.signature_as_function_of_theta(th)
pattern_part += pp
satellite_part += sp
sf = pattern_part + satellite_part
if verbose:
print()
print(str(thetas))
print(sf)
assert sf.total_sign_jump() == 0
return pattern_part, satellite_part, sf
signature_as_function_of_theta.__doc__ =\
signature_as_function_of_theta_docstring
return signature_as_function_of_theta
def get_sign_ext_for_theta(self, thetas, limit):
_, _, sf = self.signature_as_function_of_theta(*thetas)
return sf.extremum(limit=limit)[1]
def is_metabolizer(self, theta):
# Check if square alternating difference
# divided by last q value is integer.
result = sum(el^2 / self.patt_q_list[idx] * (-1)^idx
for idx, el in enumerate(theta))
return result.is_integer()
def is_function_big_in_ranges(self, ranges_list, invariant=SIGMA,
verbose=None, details=False,
p_part_order=None):
verbose = verbose or self.verbose
p_part_order = p_part_order or self.q_order
if invariant == SIGNATURE:
get_invariant = self.get_sign_ext_for_theta
name = "signature (extremum)"
else:
get_invariant = self.sigma_as_function_of_theta
name = "sigma value"
if verbose:
msg = "\nCalculating {}-primary part.".format(p_part_order)
print(msg)
logging.info(str(ranges_list))
for thetas in it.product(*ranges_list):
# Check only non-zero metabolizers.
if not self.is_metabolizer(thetas) or not any(thetas):
continue
function_is_small = True
# Check if any element generated by thetas vector
# has a large signature or sigma.
for shift in range(1, p_part_order):
shifted_thetas = \
[(shift * th) % p_part_order for th in thetas]
limit = 5 + np.count_nonzero(shifted_thetas)
inv_value = get_invariant(shifted_thetas, limit=limit)
abs_value = abs(inv_value)
if verbose:
if shift == 1:
print("\n" + "*" * 10)
print("[ character ] " + name)
print(shifted_thetas, end=" ")
print(inv_value)
if abs_value > limit:
function_is_small = False
if details:
self.print_calculations_for_sigma(*shifted_thetas)
break
if function_is_small:
if verbose and invariant == SIGMA:
msg = "For an element x = " + str(thetas)
msg += " there does not exist any shift such that "
msg += "|sigma(K, shift * x)| > {}.\n".format(limit)
msg += "We can not give any lower bound for 4-genus."
print(msg)
return False
return True
def print_calculations_for_sigma(self, *thetas):
print("Calculation details for a cable sum:\n" +
self.knot_description + "\nand theta vector: " +
str(thetas) + "\n")
for i, (th, knot) in enumerate(zip(thetas, self.knot_summands)):
print("{}. {}, theta = {}".format(i + 1, knot.knot_description, th))
if not th:
continue
patt_k = knot.knot_as_k_values[-1]
q = 2 * abs(patt_k) + 1
th %= q
if patt_k > 0:
print("Pattern part = pp")
else:
print("Pattern part = -pp")
print("pp = -q + 2 * theta * (q - theta)/q =")
print(" = -{} + 2 * {} * ({} - {} )/{} =".format(
q, th, q, th, q))
print(" = -{} + {} * ({} )/{} =".format(
q, 2 * th, q - th, q))
print(" = -{} + {} * {} = ".format(
q, 2 * th, (q - th)/ q))
print(" = -{} + {} = ".format(
q, 2 * th * (q - th)/ q))
print(" = {} ".format(
-q + (2 * th * (q - th)/ q)))
pp = (-q + 2 * th - 2 * (th^2/q)) * sign(patt_k)
sigma = knot.sigma_as_function_of_theta(th)
print("Pattern part = {} ~ {}".format(sigma[0],int(sigma[0])))
print("Satellite part = {}".format(sigma[1]))
print("Sigma = {} ~ {}\n".format(sigma[2], int(sigma[2])))
# function that proves the main lemma
def is_function_big_for_all_metabolizers(self, invariant=SIGMA,
verbose=False, details=False):
logging.info("start of is_function_big_for_all_metabolizers")
if invariant == SIGNATURE:
name = "signature (extremum)"
else:
name = "sigma value"
num_of_summands = len(self.knot_sum_as_k_valus)
if num_of_summands % 4:
f_name = self.is_signature_big_for_all_metabolizers.__name__
msg = "Function {}".format(f_name) + " is implemented only for " +\
"knots that are direct sums of 4n direct summands."
raise ValueError(msg)
# check each p-primary part separately
logging.info("number of summands is {}".format(num_of_summands))
for shift in range(0, num_of_summands, 4):
logging.info("shift is {}".format(shift))
p_part_order = 2 * self.patt_k_list[shift] + 1
logging.info("set character (twist) to be zero for all summands")
ranges_list = num_of_summands * [range(1)]
logging.info(str(ranges_list))
# change ranges for all but one character for current p-primary part
ranges_list[shift + 1 : shift + 4] = 3 * [range(p_part_order)]
# change range for first character in current p-primary part
# this one is consider to be 0 or 1
# For any characters combinations (vectors) [a_1, a_2, a_3, a_4]
# where a_4 != 0 exists such a k < p that k * a_1 % p == 1.
ranges_list[shift] = range(0, 2)
logging.info("\n\ncall is_function_big_in_ranges with values")
logging.info(str(ranges_list))
if not self.is_function_big_in_ranges(ranges_list, invariant,
p_part_order=p_part_order,
verbose=verbose,
details=details):
logging.info("Not proven.")
return False
if verbose and invariant == SIGMA:
msg = "For each p-primery part for each vector x there exist "
msg += "a number 0 < shift < p such that "
msg += "sigma(K, shift * x) > 5 + eta(K, shift * x)."
print(msg)
print("q.e.d.")
return True
class CableTemplate:
def __init__(self, knot_formula, q_vector=None, k_vector=None,
generate_q_vector=True, algebraic=True, verbose=False):
self.verbose = verbose
self._knot_formula = knot_formula
# q_i = 2 * k_i + 1
if k_vector is not None:
self.k_vector = k_vector
elif q_vector is not None:
self.q_vector = q_vector
elif generate_q_vector:
self.q_vector = self.get_q_vector(algebraic=slice)
self.templete_description = self.get_template_descrption()
def __str__(self):
return self.templete_description
def get_template_descrption(self):
k_indices = re.sub(r'[k ]', '', self.knot_formula)
k_indices = re.sub(r'\[\d+\]', lambda x: x.group()[1:-1], k_indices)
k_indices = ast.literal_eval(k_indices)
description = ""
for summand in k_indices :
part = ""
if summand[0] < 0:
part += "-"
part += "T("
for k in summand:
part += "2, q_" + str(abs(k)) + "; "
description += part[:-2] + ") # "
return description[:-3]
@property
def cable(self):
if self._cable is None:
msg = "q_vector for cable instance has not been set explicit. " + \
"The variable is assigned a default value."
warnings.warn(msg)
self.fill_q_vector()
return self._cable
def fill_q_vector(self, q_vector=None, algebraic=True, lowest_number=2):
self.q_vector = q_vector or self.get_q_vector(slice, lowest_number)
@property
def knot_formula(self):
return self._knot_formula
@property
def k_vector(self):
return self._k_vector
@k_vector.setter
def k_vector(self, k):
self._k_vector = k
if self.extract_max(self.knot_formula) > len(k) - 1:
msg = "The vector for knot_formula evaluation is to short!"
msg += "Note that we count from 0 (not 1)."
msg += "\nk_vector " + str(k) + " \nknot_formula " \
+ str(self.knot_formula)
raise IndexError(msg)
self.knot_sum_as_k_valus = eval(self.knot_formula)
self._cable = CableSum(self.knot_sum_as_k_valus, verbose=self.verbose)
self._q_vector = [2 * k_val + 1 for k_val in k]
@property
def q_vector(self):
return self._q_vector
@q_vector.setter
def q_vector(self, new_q_vector):
self.k_vector = [(q - 1)/2 for q in new_q_vector]
@staticmethod
def extract_max(string):
numbers = re.findall(r'\d+', string)
numbers = map(int, numbers)
return max(numbers)
def get_q_vector(self, algebraic=True, lowest_number=2):
r"""
Returns q-vector with certain properties.
If slice a specific relation for each cabling-level is preserved.
Consider a cable T(2, q_0; 2, q_1; ...; 2, q_n).
Then for every q_i, q_(i + 1): q_(i + 1) > q_i * 4
"""
knot_formula = self.knot_formula
q_vector = [0] * (self.extract_max(knot_formula) + 1)
P = Primes()
for layer in self._get_layers_from_formula(knot_formula)[::-1]:
for el in layer:
q_vector[el] = P.next(lowest_number)
lowest_number = q_vector[el]
if slice:
lowest_number *= 4
return q_vector
@staticmethod
def _get_layers_from_formula(knot_formula):
r"""helper method for get_q_vector"""
k_indices = re.sub(r'[k-]', '', knot_formula)
k_indices = re.sub(r'\[\d+\]', lambda x: x.group()[1:-1], k_indices)
k_indices = eval(k_indices)
number_of_layers = max(len(lst) for lst in k_indices)
layers = []
for i in range(1, number_of_layers + 1):
layer = [lst[-i] for lst in k_indices if len(lst)>= i]
layers.append(layer)
return layers
def add_with_shift(self, other):
shift = self.extract_max(self.knot_formula) + 1
o_formula = re.sub(r'\d+', lambda x: str(int(x.group()) + shift),
other.knot_formula)
return self + CableTemplate(o_formula)
def __add__(self, other):
knot_formula = self.knot_formula[:-1] + ",\n" + other.knot_formula[1:]
return CableTemplate(knot_formula)
CableSum.get_signature_as_function_of_theta.__doc__ = \
"""
Function intended to construct signature function for a connected
sum of multiple cables with varying theta parameter values.
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 that will take theta vector as an argument and return
an object sg.SignatureFunction.
To calculate signature function for a cable sum and a theta values vector,
use as below.
sage: signature_function_generator = get_signature_as_function_of_theta(
[1, 3], [2], [-1, -2], [-3])
sage: sf = signature_function_generator(2, 1, 2, 2)
sage: print(sf)
0: 0
5/42: 1
1/7: 0
1/5: -1
7/30: -1
2/5: 1
3/7: 0
13/30: -1
19/42: -1
23/42: 1
17/30: 1
4/7: 0
3/5: -1
23/30: 1
4/5: 1
6/7: 0
37/42: -1
Or like below.
sage: print(get_signature_as_function_of_theta([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
"""
get_summand_signture_function_docsting = \
"""
This function returns sg.SignatureFunction for previously defined single
cable T_(2, q) and a theta given as an argument.
The cable was defined by calling function
get_summand_signature_as_theta_function(*arg)
with the cable description 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)
"""
signature_as_function_of_theta_docstring = \
"""
Arguments:
Returns object of sg.SignatureFunction class for a previously defined
connected 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.
"""
#
# CableSummand.get_blanchfield_for_pattern.__doc__ = \
# """
# Arguments:
# k_n: a number s.t. q_n = 2 * k_n + 1, where
# T(2, q_n) is a pattern knot for a single cable from a cable sum
# theta: twist/character for the cable (value form v vector)
# Return:
# sg.SignatureFunction created for pattern signature function
# for a given cable and theta/character
# Based on:
# Proposition 9.8. in Twisted Blanchfield Pairing
# (https://arxiv.org/pdf/1809.08791.pdf)
# """
# CableSummand.get_summand_signature_as_theta_function.__doc__ = \
# """
# Argument:
# n integers that encode a single cable, i.e.
# values of q_i for T(2,q_0; 2,q_1; ... 2, q_n)
# Return:
# a function that returns sg.SignatureFunction for this single cable
# and a theta given as an argument
# """

5
gaknot/clean.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/bash
rm cable_signature.py
rm signature.py
rm main.py

325
gaknot/main.sage Normal file
View File

@ -0,0 +1,325 @@
#!/usr/bin/env sage -python
# TBD
# print scheme and knot nicely
# read about Factory Method, variable in docstring, sage documentation,
# print calc. to output file
# decide about printing option
# make __main__?
import os
import sys
import itertools as it
import re
import numpy as np
import importlib
# constants - which invariants should be calculate
SIGMA = 0
SIGNATURE = 1
if __name__ == '__main__':
from utility import import_sage
package = None
path = ''
else:
from .utility import import_sage
package = os.path.join( __name__.split('.')[0])
path = '../'
sg = import_sage('signature', package=package, path=path)
cs = import_sage('cable_signature', package=package, path=path)
# class Config:
# def __init__(self):
# self.f_results = os.path.join(os.getcwd(), "results.out")
class Schema:
r"""This class stores interesting schema of cable sums.
Cable knots sum can be given as a scheme, e.g. a scheme from the paper:
K(p_1 , p_2 , q_1 , q_2 , q_3 ) =
T(2, q_1; 2, p_1) # -T(2, q_2; 2, p_1) # T(2, p_1) # -T(2, q_3; 2, p_1) +
T(2, q_2; 2, p_2) # -T(2, p_2) # T(2, q_3; 2, p_2) # -T(2, q_1; 2, p_2).
We can represent it as nested list:
lemma_scheme = "[[ k[5], k[3]], " + \
"[ -k[1], -k[3]], " + \
"[ k[3]], " + \
"[ -k[6], -k[3]], " + \
"[ k[1], k[7]], " + \
"[ -k[7]], " + \
"[ k[6], k[7]], " + \
"[ -k[5], -k[7]]]",
where each k[i] corresponds to some q_i or p_i.
This expression will be later evaluated with k_vector.
See k_vector setter in class CableTemplate in cable_signature.sage module.
Remark 1
In the paper, we used p_i and q_i to describe torus knots and cables.
It was convenient for writing, but in all the code and documentation
only 'q' letter is used to encode torus knots or cables.
Remark 2
There are two ways to set k[i] values for a scheme:
via q_vector or k_vector.
Both should be lists and the relation is q[i] = 2 * k[i] + 1,
i.e. q should be an odd prime and k should be an even number such that
2 * k + 1 is prime.
To fill the scheme listed above we should use a list of length 8,
and k[0] will be omitted as it is not used in the scheme.
Remark 3
Except for development purposes, q_vector was computed with
a method CableTemplate.get_q_vector and flag slice=True.
The reason for that is that we were interested only in cases
where a specific relation for each cabling level is preserved.
Consider a cable T(2, q_0; 2, q_1; ...; 2, q_n).
Then for every q_i, q_(i + 1): q_(i + 1) > q_i * 4.
"""
# scheme that is used in the paper
lemma_scheme_a = "[ [k[5], k[3]], " + \
"[ -k[1], -k[3]], " + \
"[ k[3]], " + \
"[ -k[6], -k[3]]]"
lemma_scheme_b = "[[ k[1], k[7]], " + \
"[ -k[7]], " + \
"[ k[6], k[7]], " + \
"[ -k[5], -k[7]]]"
lemma_scheme = "[[ k[5], k[3]], " + \
"[ -k[1], -k[3]], " + \
"[ k[3]], " + \
"[ -k[6], -k[3]], " + \
"[ k[1], k[7]], " + \
"[ -k[7]], " + \
"[ k[6], k[7]], " + \
"[ -k[5], -k[7]]]"
#
# formula_long = "[[k[0], k[5], k[3]], " + \
# "[ -k[5], -k[3]], " + \
# "[ k[2], k[3]], " + \
# "[-k[4], -k[2], -k[3]]" + \
# "[ k[4], k[1], k[7]], " + \
# "[ -k[1], -k[7]], " + \
# "[ k[6], k[7]], " + \
# "[-k[0], -k[6], -k[7]]]"
#
# formula_a = "[[k[0], k[5], k[3]], " + \
# "[ -k[1], -k[3]], " + \
# "[ k[3]], " + \
# "[-k[4], -k[6], -k[3]]]"
#
# formula_b = "[[k[4], k[1], k[7]], " + \
# "[ -k[7]], " + \
# "[ k[6], k[7]], " + \
# "[-k[0], -k[5], -k[7]]]"
#
# formula_a = "[[ k[0], k[5], k[3]], " + \
# "[ -k[1], -k[3]], " + \
# "[ k[2], k[3]], " + \
# "[ -k[0], -k[2], -k[3]]]"
#
# formula_b = "[[ k[4], k[1], k[7]], " + \
# "[ -k[5], -k[7]], " + \
# "[ k[6], k[7]], " + \
# "[ -k[4], -k[6], -k[7]]]"
#
# formula_a = "[[ k[0], k[5], k[3]], " + \
# "[ -k[5], -k[3]], " + \
# "[ k[2], k[3]], " + \
# "[-k[4], -k[2], -k[3]]]"
#
# formula_b = "[[ k[4], k[1], k[7]], " + \
# "[ -k[1], -k[7]], " + \
# "[ k[6], k[7]], " + \
# "[-k[0], -k[6], -k[7]]]"
#
#
# three_layers_formula_a = "[[k[0], k[1], k[2]],\
# [ k[3], k[4]],\
# [-k[0], -k[3], -k[4]],\
# [ -k[1], -k[2]]]"
#
# three_layers_formula_b = "[[k[0], k[1], k[2]],\
# [ k[3]],\
# [-k[0], -k[1], -k[3]],\
# [ -k[2]]]"
#
# short_3_layers_b = "[[ k[5], k[3]], " + \
# "[ -k[1], -k[3]], " + \
# "[ k[3]], " + \
# "[ -k[4], -k[6], -k[3]]]"
#
# short_3_layers_b = "[[k[4], k[1], k[7]], " + \
# "[ -k[7]], " + \
# "[ k[6], k[7]], " + \
# "[ -k[5], -k[7]]]"
#
# four_summands_scheme = "[[ k[0], k[1], k[3]]," + \
# "[ -k[1], -k[3]]," + \
# "[ k[2], k[3]]," + \
# "[ -k[0], -k[2], -k[3]]]"
#
# two_summands_scheme = "[ [k[0], k[1], k[4]], [-k[1], -k[3]],\
# [k[2], k[3]], [-k[0], -k[2], -k[4]] ]"
# two_small_summands_scheme = "[[k[3]], [-k[3]],\
# [k[3]], [-k[3]] ]"
def main(arg=None):
try:
limit = int(arg[1])
except (IndexError, TypeError):
limit = None
conf = Config()
cable_loop_with_details(conf)
def prove_lemma(verbose=True, details=False):
if verbose:
msg = "CALCULATIONS OF THE SIGMA INVARIANT\n"
msg += "Proof of main lemma from "
msg += "ON THE SLICE GENUS OF GENERALIZED ALGEBRAIC KNOTS\n\n"
print(msg)
lemma_scheme = Schema.lemma_scheme
cable_template = cs.CableTemplate(knot_formula=lemma_scheme,
verbose=verbose)
cable_template.fill_q_vector()
q_v = cable_template.q_vector
cable = cable_template.cable
if verbose:
msg = "Let us consider a cable knot: \nK = "
msg += cable.knot_description + ".\n"
msg += "It is an example of cable knots of a scheme:\n"
msg += str(cable_template) + "."
print(msg)
cable.is_function_big_for_all_metabolizers(invariant=SIGMA,
verbose=verbose,
details=details)
def print_sigma_for_cable(verbose=True):
lemma_scheme_a = Schema.lemma_scheme_a
lemma_scheme_b = Schema.lemma_scheme_b
lemma_scheme = Schema.lemma_scheme
scheme_four = Schema.four_summands_scheme
cable_template = cs.CableTemplate(knot_formula=lemma_scheme)
cable_template.fill_q_vector()
q_v = cable_template.q_vector
print(q_v)
print(cable_template.cable.knot_description)
cable_a = cs.CableTemplate(knot_formula=lemma_scheme_a,
verbose=verbose,
q_vector=q_v
).cable
cable_b = cs.CableTemplate(knot_formula=lemma_scheme_b,
verbose=verbose,
q_vector=q_v
).cable
cable = cs.CableTemplate(knot_formula=lemma_scheme_a,
verbose=verbose,
q_vector=q_v
).cable
cable.plot_sigma_for_summands()
# cable_a.plot_sigma_for_summands()
# cable_b.plot_sigma_for_summands()
def cable_loop_with_details(verbose=True):
# verbose = False
lemma_scheme_a = Schema.lemma_scheme_a
lemma_scheme_b = Schema.lemma_scheme_b
lemma_scheme = Schema.lemma_scheme
cable_template = cs.CableTemplate(knot_formula=lemma_scheme)
list_of_q_vectors = []
# for el in [2, 3, 5, 7, 11, 13]:
for el in [2]:
cable_template.fill_q_vector(lowest_number=el)
q_v = cable_template.q_vector
print(q_v)
print(cable_template.cable.knot_description)
cable_a = cs.CableTemplate(knot_formula=lemma_scheme_a,
verbose=verbose,
q_vector=q_v
).cable
cable_b = cs.CableTemplate(knot_formula=lemma_scheme_b,
verbose=verbose,
q_vector=q_v
).cable
# print("\n")
# print(cable_a.knot_description)
is_a = cable_a.is_function_big_for_all_metabolizers(invariant=cs.SIGMA,
verbose=True,
details=True)
is_b = cable_b.is_function_big_for_all_metabolizers(invariant=cs.SIGMA,
verbose=True,
details=True)
if is_a and is_b:
print("sigma is big for all metabolizers")
else:
print("sigma is not big for all metabolizers")
print("\n" * 3)
def few_cable_without_calc(verbose=False):
lemma_scheme_a = Schema.lemma_scheme_a
lemma_scheme_b = Schema.lemma_scheme_b
lemma_scheme = Schema.lemma_scheme
cable_template = cs.CableTemplate(knot_formula=lemma_scheme)
list_of_q_vectors = []
for el in [2, 3, 5, 7, 11, 13]:
cable_template.fill_q_vector(lowest_number=el)
q_v = cable_template.q_vector
print(q_v)
print(cable_template.cable.knot_description)
cable_a = cs.CableTemplate(knot_formula=lemma_scheme_a,
verbose=verbose,
q_vector=q_v
).cable
cable_b = cs.CableTemplate(knot_formula=lemma_scheme_b,
verbose=verbose,
q_vector=q_v
).cable
is_a = cable_a.is_function_big_for_all_metabolizers(invariant=sigma)
is_b = cable_b.is_function_big_for_all_metabolizers(invariant=sigma)
if is_a and is_b:
print("sigma is big for all metabolizers")
else:
print("sigma is not big for all metabolizers")
print("\n" * 3)
def plot_many_untwisted_signature_functions(range_tuple=(1, 10)):
P = Primes()
for i in range(*range_tuple):
q = P.unrank(i)
a = cs.CableSummand.get_untwisted_signature_function(q=q)
a.plot()
if __name__ == '__main__':
if '__file__' in globals():
# skiped in interactive mode as __file__ is not defined
main(sys.argv)
else:
pass
# main()

376
gaknot/signature.sage Normal file
View File

@ -0,0 +1,376 @@
#!/usr/bin/env sage -python
import sys
# import os
from collections import Counter
import matplotlib.pyplot as plt
import inspect
from PIL import Image
from pathlib import Path
import warnings
from .utility import mod_one
# check if used in Jupyter Notebook to show plots in proper way
JUPYTER = 'ipykernel'
IPy_TERMINAL = 'IPython'
def get_ipython_info():
if JUPYTER in sys.modules:
return JUPYTER
elif IPy_TERMINAL in sys.modules:
return IPy_TERMINAL
return False
global ipython_info
ipython_info = get_ipython_info()
#
#
# 9.11 (9.8)
# 9.15 (9.9)
class SignatureFunction:
r"""signature function is entirely encoded by its signature
jump, the class stores only information about signature jumps
in a dictionary self.jumps_counter
The dictionary stores data of the signature jump as a key/values pair,
where the key is the argument at which the functions jumps
and value encodes the value of the jump. Remember that we treat
signature functions as defined on the interval \[0,1).
"""
def __init__(self, values=None, counter=None, plot_title=''):
# counter of signature jumps
if counter is None:
counter = Counter()
values = values or []
for k, v in values:
counter[k] += v
counter = Counter({k : v for k, v in counter.items() if v != 0})
if any(k >= 1 for k in counter.keys()):
msg = "Signature function is defined on the interval [0, 1)."
raise ValueError(msg)
counter[0] += 0
counter[1] += 0
self.jumps_counter = counter
self.plot_title = plot_title
def __rshift__(self, shift):
# A shift of the signature functions corresponds to the rotation.
counter = Counter({mod_one(k + shift) : v \
for k, v in self.jumps_counter.items()})
return SignatureFunction(counter=counter)
def __lshift__(self, shift):
return self.__rshift__(-shift)
def __neg__(self):
counter = Counter()
counter.subtract(self.jumps_counter)
return SignatureFunction(counter=counter)
def __add__(self, other):
counter = copy(self.jumps_counter)
counter.update(other.jumps_counter)
if self.plot_title and other.plot_title:
title = self.plot_title + " + " + other.plot_title
else:
title = self.plot_title or other.plot_title
return SignatureFunction(counter=counter, plot_title=title)
def __sub__(self, other):
counter = copy(self.jumps_counter)
counter.subtract(other.jumps_counter)
return SignatureFunction(counter=counter)
def __mul__(self, number):
# scalar multiplication
counter = Counter({k : number * v \
for k, v in self.jumps_counter.items()})
return SignatureFunction(counter=counter)
def __rmul__(self, number):
return(self.__mul__(number))
def __eq__(self, other):
return self.jumps_counter == other.jumps_counter
def __str__(self):
result = ''.join([str(jump_arg) + ": " + str(jump) + "\n"
for jump_arg, jump in sorted(self.jumps_counter.items())])
return result
def __repr__(self):
result = ''.join([str(jump_arg) + ": " + str(jump) + ", "
for jump_arg, jump in sorted(self.jumps_counter.items())])
return result[:-2] + "."
def __call__(self, arg):
# return the value of the signature function at the point arg, i.e.
# sum of all signature jumps that occur before arg
items = self.jumps_counter.items()
result = [jump for jump_arg, jump in items if jump_arg < mod_one(arg)]
return 2 * sum(result) + self.jumps_counter[arg]
def double_cover(self):
# to read values for t^2
items = self.jumps_counter.items()
counter = Counter({(1 + k) / 2 : v for k, v in items})
counter.update(Counter({k / 2 : v for k, v in items}))
return SignatureFunction(counter=counter)
def square_root(self):
# to read values for t^(1/2)
counter = Counter()
for jump_arg, jump in self.jumps_counter.items():
if jump_arg < 1/2:
counter[2 * jump_arg] = jump
return SignatureFunction(counter=counter)
def minus_square_root(self):
# to read values for t^(1/2)
items = self.jumps_counter.items()
counter = Counter({mod_one(2 * k) : v for k, v in items if k >= 1/2})
return SignatureFunction(counter=counter)
def is_zero_everywhere(self):
return not any(self.jumps_counter.values())
def extremum(self, limit=math.inf):
max_point = (0, 0)
current = 0
items = sorted(self.jumps_counter.items())
for arg, jump in items:
current += 2 * jump
assert current == self(arg) + jump
if abs(current) > abs(max_point[1]):
max_point = (arg, current)
if abs(current) > limit:
break
return max_point
def total_sign_jump(self):
# Total signature jump is the sum of all jumps.
return sum([j[1] for j in sorted(self.jumps_counter.items())])
def plot(self, *args, **kargs):
SignaturePloter.plot(self, *args, **kargs)
class SignaturePloter:
@classmethod
def plot_many(cls, *sf_list, save_path=None, title='', cols=None):
axes_num = len(sf_list)
if axes_num > 36:
sf_list = sf_list[36]
axes_num = 36
msg = "To many functions for the plot were given. "
msg += "Only 36 can be plotted "
warnings.warn(msg)
# print war, set val in conf
cols = cols or ceil(sqrt(axes_num))
rows = ceil(axes_num/cols)
fig, axes_matrix = plt.subplots(rows, cols,
sharex='col', sharey='row',
gridspec_kw={'hspace': 0, 'wspace': 0},
# sharey=True,
# sharex=True,
)
for i, sf in enumerate(sf_list):
col = i % cols
row = (i - col)/cols
sf.plot(subplot=True,
ax=axes_matrix[row][col],
title=sf.plot_title)
fig.suptitle(title)
plt.tight_layout()
cls.show_and_save(save_path)
@classmethod
def plot_sum_of_two(cls, sf1, sf2, save_path=None, title=''):
sf = sf1 + sf2
fig, axes_matrix = plt.subplots(2, 2, sharey=True, figsize=(10,5))
sf1.plot(subplot=True,
ax=axes_matrix[0][1])
sf2.plot(subplot=True,
ax=axes_matrix[1][0],
color='red',
linestyle='dotted')
sf.plot(subplot=True,
ax=axes_matrix[0][0],
color='black')
sf1.plot(subplot=True,
ax=axes_matrix[1][1],
alpha=0.3)
sf2.plot(subplot=True,
ax=axes_matrix[1][1],
color='red', alpha=0.3,
linestyle='dotted')
sf.plot(subplot=True,
ax=axes_matrix[1][1],
color='black',
alpha=0.7,)
fig.suptitle(title)
plt.tight_layout()
cls.show_and_save(save_path)
@classmethod
def plot(cls, sf, subplot=False, ax=None,
save_path=None,
title='',
alpha=1,
color='blue',
linestyle='solid',
special_point=None,
special_label='',
extraticks=None,
ylabel=''):
if ax is None:
fig, ax = plt.subplots(1, 1)
keys = sorted(sf.jumps_counter.keys())
y = [(sf(k) + sf.jumps_counter[k]) for k in keys[:-1]]
xmax = keys[1:]
xmin = keys[:-1]
ax.set(ylabel=ylabel)
ax.set(title=title)
ax.hlines(y, xmin, xmax, color=color, linestyle=linestyle, alpha=alpha)
if special_point is not None:
arg, val = special_point
extraticks = extraticks or []
plt.xticks(list(plt.xticks()[0]) + extraticks)
ext = sf.extremum()[1]
ytext = ext/2 + 1/2
xtext = arg + 1/5
ax.annotate(special_label, xy=(arg, val), xytext=(xtext, ytext),
arrowprops=dict(facecolor='black', shrink=0.05,
alpha=0.7, width=2),)
if subplot:
return ax
cls.show_and_save(save_path)
@staticmethod
def show_and_save(save_path):
if save_path is not None:
save_path = Path(save_path)
save_path = save_path.with_suffix('.png')
plt.savefig(save_path)
if ipython_info == JUPYTER:
plt.show()
elif True: # save_path is None:
plt.savefig('tmp.png')
plt.close()
image = Image.open('tmp.png')
image.show()
# msg = "For interactive shell set save_path."
# warnings.warn(msg)
@staticmethod
def step_function_data(sf):
# Transform the signature jump data to a format understandable
# by the plot function.
result = [(k, sf.sf(k) + sf.jumps_counter[k])
for k in sorted(sf.jumps_counter.keys())]
return result
@staticmethod
def tikz_plot(sf, save_as):
plt_sin = plot(sin(x), (x, 0, 2*pi))
# plt_sin.show()
plt_sin.save("MyPic.pdf")
return
# Draw the graph of the signature and transform it into TiKz.
# header of the LaTeX file
head = inspect.cleandoc(
r"""
\documentclass{standalone}
\usepackage{tikz}
\usetikzlibrary{calc}
\begin{document}
\begin{tikzpicture}
""")
body = \
r"""
%A piecewise linear function is drawn over the interval.
\draw (5,0) -- (6,-4);
%The axes are drawn.
\draw[latex-latex] ($(0,{-4*(2/5)}) +(0pt,-12.5pt)$) --
($(0,{4*(2/5)}) +(0pt,12.5pt)$) node[above right]{$y$};
\draw[latex-latex] ($({-4*(2/5)},0) +(-12.5pt,0pt)$) --
($({12*(2/5)},0) +(12.5pt,0pt)$) node[below right]{$x$};
"""
tail = \
r"""
\end{tikzpicture}
\end{document}
"""
tikzpicture = re.sub(r' +', ' ', ''.join([head, body, tail]))
tikzpicture = re.sub(r'\n ', '\n', tikzpicture)
with open("tmp.tex", "w") as f:
f.write(tikzpicture)
data = self.step_function_data()
with open(save_as, "w") as f:
head = \
r"""
\documentclass[tikz]{{standalone}}
%\usepackage{{tikz}}
\usetikzlibrary{{datavisualization}}
\usetikzlibrary{{datavisualization.formats.functions}}
%\usetikzlibrary{{calc}}
\begin{{document}}
\begin{{tikzpicture}}
\datavisualization[scientific axes, visualize as smooth line,
x axis={{ticks={{none,major={{at={{, {arg0} " as \\( {val0} \\
%]
""".format(arg0=str(N(data[0][0] ,digits=4)), val0=str(data[0][0]))
f.write(head)
# f.write(", " + str(N(data[0][0],digits=4)) + " as \\(" + \
# str(data[0][0]) + "\\)")
for jump_arg, jump in data[1:3]:
f.write(", " + str(N(jump_arg,digits=4)) +
" as \\(" + str(jump_arg) + "\\)")
f.write("}}}}\n")
f.write(" ]\n")
f.write("data [format=function]{\n")
f.write("var x : interval [0:1];\n")
f.write("func y = \\value x;\n")
f.write("};\n")
# close LaTeX enviroments
tail = \
r"""
%};
\end{tikzpicture}
\end{document}
"""
f.write(tail)

90
gaknot/utility.py Normal file
View File

@ -0,0 +1,90 @@
import importlib
import os
import sys
import re
import math
import logging
def mod_one(n):
r"""calculates the fractional part of the argument
Argument:
a number
Return:
the fractional part of the argument
Examples:
sage: mod_one(9 + 3/4)
3/4
sage: mod_one(-9 + 3/4)
3/4
sage: mod_one(-3/4)
1/4
"""
return n - math.floor(n)
def import_sage(module_name, package=None, path=''):
r"""Import or reload SageMath modules with preparse if the sage file exist.
Arguments:
module_name - name of the module (without file extension!)
package - use only if module is used as a part of a package
Return:
module
Examples:
from utility import import_sage
# equivalent to import module_name as my_prefered_shortcut}
my_prefered_shortcut = import_sage('module_name')
"""
sage_name = module_name + ".sage"
python_name = module_name + ".sage.py"
logging.info("\n\nimport_sage called with arguments:" +
"\n\tmodule_name: " + module_name +
"\n\tpackage: " + str(package) +
"\n\tpath: " + path)
if package is not None:
path_from_package_name = re.sub(r'\.', r'\\', package)
path = os.path.join(path, path_from_package_name)
logging.info("path with package name: " + str(path))
sage_path = os.path.join(path, sage_name)
python_path = os.path.join(path, python_name)
module_path = os.path.join(path, module_name)
if os.path.isfile(sage_path):
logging.info("\nPreparsing sage file " + sage_name + ".")
os.system('sage --preparse {}'.format(sage_path));
os.system('mv {} {}.py'.format(python_path, module_path))
else:
logging.info("sage file not found: " + str(sage_path))
if package is not None:
module_name = package + "." + module_name
if module_name in sys.modules:
logging.info("\nmodule " + module_name + " was found.")
return importlib.reload(sys.modules[module_name])
return importlib.import_module(module_name, package=package)
def parse_sage(module_name):
dir = os.path.dirname(__file__)
sage_name = os.path.join(dir, module_name + ".sage")
python_name = os.path.join(dir, module_name + ".sage.py")
module_name = os.path.join(dir, module_name + ".py")
os.system('sage --preparse {}'.format(sage_name))
os.system('mv {} {}'.format(python_name, module_name))

112
main.sage
View File

@ -1,112 +0,0 @@
#!/usr/bin/python
attach("cable_signature.sage")
# attach("my_signature.sage")
import numpy as np
def main():
global cable, cab_2, cab_1, joined_formula
# self.knot_formula = "[[k[0], k[1], k[3]], " + \
# "[-k[1], -k[3]], " + \
# "[k[2], k[3]], " + \
# "[-k[0], -k[2], -k[3]]]"
# knot_formula = config.knot_formula
# q_vector = (3, 5, 7, 13)
# cab_to_update = TorusCable(knot_formula=knot_formula, q_vector=q_vector)
# q_vector = (3, 5, 7, 11)
# cab_to_add = TorusCable(knot_formula=knot_formula, q_vector=q_vector)
# cab_shifted = cab_to_update.add_with_shift(cab_to_add)
q_vector = (5, 13, 19, 41,\
5, 17, 23, 43)
knot_formula = "[[k[0], k[5], k[3]], " + \
"[-k[1], -k[3]], " + \
"[k[2], k[3]], " + \
"[-k[0], -k[2], -k[3]]]"
cab_1 = TorusCable(knot_formula=knot_formula, q_vector=q_vector)
knot_formula = "[[k[4], k[1], k[7]], " + \
"[-k[5], -k[7]], " + \
"[k[6], k[7]], " + \
"[-k[4], -k[6], -k[7]]]"
cab_2 = TorusCable(knot_formula=knot_formula, q_vector=q_vector)
cable = cab_1 + cab_2
joined_formula = cable.knot_formula
def is_big_in_ranges(cable, ranges_list):
we_have_no_problem = True
for theta in it.product(*ranges_list):
if all(i == 0 for i in theta):
continue
we_have_a_problem = True
if cable.is_metaboliser(theta):
# print("\n" * 10)
for shift in range(1, cable.q_order):
shifted_theta = [(shift * th) % cable.last_q_list[i]
for i, th in enumerate(theta)]
shifted_theta = [min(th, cable.last_q_list[i] - th)
for i, th in enumerate(shifted_theta)]
sf = cable.signature_as_function_of_theta(*shifted_theta)
extremum = abs(sf.extremum())
if shift > 1:
print(shifted_theta, end=" ")
print(extremum)
if extremum > 5 + np.count_nonzero(shifted_theta):
# print("ok")
we_have_a_problem = False
break
elif shift == 1:
print("*" * 10)
print(shifted_theta, end=" ")
print(extremum)
if we_have_a_problem:
we_have_a_big_problem = True
break
if not we_have_no_problem:
print("we have a big problem")
return we_have_no_problem
def check_all_thetas(cable):
upper_bounds = cable.last_k_list[:3]
ranges_list = [range(0, i + 1) for i in upper_bounds]
ranges_list.append(range(0, 2))
ranges_list += [range(0, 1) for _ in range(4)]
if not is_big_in_ranges(cable, ranges_list):
return False
upper_bounds = cable.last_k_list[5:8]
ranges_list = [range(0, 1) for _ in range(4)]
ranges_list += [range(0, i + 1) for i in upper_bounds]
ranges_list.append(range(0, 2))
if not is_big_in_ranges(cable, ranges_list):
return False
return True
def get_q_vector(q_vector_size, lowest_number=1):
q = [lowest_number] * q_vector_size
P = Primes()
next_number = P.next(lowest_number)
for i in range(q_vector_size):
q[i] = next_number
next_number = P.next(4 * next_number)
return q
next_number = P.next(lowest_number)
for i, q in enumerate(q_vector):
q[i] = next_number
next_number = P.next(lowest_number)
q = [P.unrank(i) for i in c]
ratio = q[3] > 4 * q[2] and q[2] > 4 * q[1] and q[1] > 4 * q[0]
if not ratio:
# print("Ratio-condition does not hold")
continue
print("q = ", q)

View File

@ -1,345 +0,0 @@
#!/usr/bin/python
# TBD: read about Factory Method, variable in docstring, sage documentation
import os
import sys
import itertools as it
import re
# if not os.path.isfile('cable_signature.py'):
# os.system('sage --preparse cable_signature.sage')
# os.system('mv cable_signature.sage.py cable_signature.py')
# from cable_signature import SignatureFunction, TorusCable, SIGNATURE, SIGMA
class Config(object):
def __init__(self):
self.f_results = os.path.join(os.getcwd(), "results.out")
# knot_formula is a schema for knots which signature function
# will be calculated
self.knot_formula = "[[k[0], k[1], k[3]], " + \
"[-k[1], -k[3]], " + \
"[k[2], k[3]], " + \
"[-k[0], -k[2], -k[3]]]"
# self.knot_formula = "[[k[0], k[1], k[4]], [-k[1], -k[3]], \
# [k[2], k[3]], [-k[0], -k[2], -k[4]]]"
#
#
# self.knot_formula = "[[k[3]], [-k[3]], \
# [k[3]], [-k[3]] ]"
# self.knot_formula = "[[k[3], k[2], k[0]], [-k[2], -k[0]], \
# [k[1], k[0]], [-k[3], -k[1], -k[0]]]"
# self.knot_formula = "[[k[0], k[1], k[2]], [k[3], k[4]], \
# [-k[0], -k[3], -k[4]], [-k[1], -k[2]]]"
# self.knot_formula = "[[k[0], k[1], k[2]], [k[3]],\
# [-k[0], -k[1], -k[3]], [-k[2]]]"
self.limit = 3
# in rch for large sigma, for 1. checked knot q_1 = 3 + start_shift
self.start_shift = 0
self.verbose = True
# self.verbose = False
self.print_results = True
# self.print_results = False
self.print_calculations_for_large_sigma = True
self.print_calculations_for_large_sigma = False
# is the ratio restriction for values in q_vector taken into account
self.only_slice_candidates = True
self.only_slice_candidates = False
# range for a_i, v = [a_1, a_2, a_3, a_4], for sigma calculations
# upper bound supposed to be ub = k + 1
def get_list_of_ranges(self, ub):
list_of_ranges = [
# all characters a_1, a_2, a_3, a_4 != 0
it.product(range(1, ub), range(1, ub), range(1, ub), range(1, 2)),
# a_1 == 0, a_2, a_3, a_4 != 0
it.product(range(1), range(1, ub), range(1, ub), range(1, 2)),
# a_2 == 0, a_1, a_3, a_4 != 0
it.product(range(1, ub), range(1), range(1, ub), range(1, 2)),
# a_3 == 0, a_1, a_2, a_4 != 0
it.product(range(1, ub), range(1, ub), range(1), range(1, 2)),
# a_4 == 0, a_1, a_2, a_3 != 0
it.product(range(1, ub), range(1, ub), range(1, 2), range(1)),
# a_1 == 0, a_2 == 0, a_3, a_4 != 0
it.product(range(1), range(1), range(1, ub), range(1, 2)),
# a_1 == 0, a_3 == 0, a_2, a_4 != 0
it.product(range(1), range(1, ub), range(1), range(1, 2)),
# a_1 == 0, a_4 == 0, a_3, a_2 != 0
it.product(range(1), range(1, ub), range(1, 2), range(1)),
# a_2 == 0, a_3 == 0, a_1, a_4 != 0
it.product(range(1, ub), range(1), range(1), range(1, 2)),
# a_2 == 0, a_4 == 0, a_1, a_3 != 0
it.product(range(1, ub), range(1), range(1, 2), range(1)),
# a_3 == 0, a_4 == 0, a_1, a_2 != 0
it.product(range(1, ub), range(1, 2), range(1), range(1)),
]
return list_of_ranges
def main(arg):
try:
limit = int(arg[1])
except IndexError:
limit = None
search_for_large_signature_value(limit=limit)
knots_with_large_sigma = search_for_large_sigma_value(limit=limit)
# search_for_null_signature_value(limit=limit)
def set_parameters(knot_formula, limit, verbose, print_results):
if limit is None:
limit = config.limit
if knot_formula is None:
knot_formula = config.knot_formula
if verbose is None:
verbose = config.verbose
if print_results is None:
print_results = config.print_results
return knot_formula, limit, verbose, print_results
# searching for sigma > 5 + #(v_i != 0) over given knot schema
def search_for_large_sigma_value(knot_formula=None, limit=None,
verbose=None, print_results=None):
knot_formula, limit, verbose, print_results = \
set_parameters(knot_formula, limit, verbose, print_results)
k_vector_size = extract_max(knot_formula) + 1
limit = max(limit, k_vector_size)
# number of k_i (q_i) variables to substitute
combinations = it.combinations(range(1, limit + 1), k_vector_size)
P = Primes()
good_knots = []
# iterate over q-vector
for c in combinations:
q = [P.unrank(i + config.start_shift) for i in c]
if config.only_slice_candidates:
ratio = q[3] > 4 * q[2] and q[2] > 4 * q[1] and q[1] > 4 * q[0]
if not ratio:
if verbose:
print("Ratio-condition does not hold")
continue
cable = TorusCable(knot_formula=knot_formula, q_vector=q)
list_of_ranges = config.get_list_of_ranges(cable.q_order)
if cable.eval_cable_for_large_values(list_of_ranges, SIGMA,
verbose=verbose,
print_results=print_results):
good_knots.append(cable.knot_description)
return good_knots
# searching for signature == 0
def search_for_null_signature_value(knot_formula=None, limit=None,
verbose=None, print_results=None):
knot_formula, limit, verbose, print_results = \
set_parameters(knot_formula, limit, verbose, print_results)
k_vector_size = extract_max(knot_formula) + 1
limit = max(limit, k_vector_size)
combinations = it.combinations_with_replacement(range(1, limit + 1),
k_vector_size)
with open(config.f_results, 'w') as f_results:
for k in combinations:
if config.only_slice_candidates and k_vector_size == 5:
k = get_shifted_combination(k)
cable = TorusCable(knot_formula, k_vector=k)
if is_trivial_combination(cable.knot_sum):
print(cable.knot_sum)
continue
result = cable.eval_cable_for_null_signature(verbose=verbose,
print_results=print_results)
if result is not None:
null_comb, all_comb = result
line = (str(k) + ", " + str(null_comb) + ", " +
str(all_comb) + "\n")
f_results.write(line)
def check_one_cable(cable, sigma_or_sign=None,
verbose=None, print_results=None):
if sigma_or_sign is None:
sigma_or_sign = SIGNATURE
if verbose is None:
verbos = config.verbose
if print_results is None:
print_results = config.print_results
list_of_ranges = config.get_list_of_ranges(cable.q_vector[-1])
return cable.eval_cable_for_large_values(list_of_ranges, sigma_or_sign,
verbose=verbose,
print_results=print_results)
# searching for signature > 5 + #(v_i != 0) over given knot schema
def search_for_large_signature_value(knot_formula=None, limit=None,
verbose=None, print_results=None):
knot_formula, limit, verbose, print_results = \
set_parameters(knot_formula, limit, verbose, print_results)
k_vector_size = extract_max(knot_formula) + 1
combinations = it.combinations(range(1, limit + 1), k_vector_size)
P = Primes()
good_knots = []
# iterate over q-vector
for c in combinations:
q = [P.unrank(i + config.start_shift) for i in c]
if config.only_slice_candidates:
ratio = q[3] > 4 * q[2] and q[2] > 4 * q[1] and q[1] > 4 * q[0]
if not ratio:
if verbose:
print("Ratio-condition does not hold")
continue
cable = TorusCable(knot_formula=knot_formula, q_vector=q)
list_of_ranges = config.get_list_of_ranges(cable.q_vector[-1])
if cable.eval_cable_for_large_values(list_of_ranges, SIGNATURE,
verbose=verbose,
print_results=print_results):
good_knots.append(cable.knot_description)
return good_knots
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
def extract_max(string):
numbers = re.findall(r'\d+', string)
numbers = map(int, numbers)
return max(numbers)
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
search_for_null_signature_value.__doc__ = \
"""
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 Config.
Results of calculations will be writen to a file and the stdout.
limit is the upper bound for the first value in k_vector,
i.e k[0] value in a cable sum, where q_0 = 2 * k[0] + 1.
(the number of knots that will be constracted depends on limit value).
For each knot/cable sum the function eval_cable_for_null_signature
is called.
eval_cable_for_null_signature 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_null_signature returns None (i.e. the knot can not be slice).
Data for knots that are candidates for slice knots are saved to a file.
"""
main.__doc__ = \
"""
This function is run if the script was called from the terminal.
It calls another function, search_for_null_signature_value,
to calculate signature functions for a schema
of a cable sum defined in the class Config.
Optionaly a parameter (a limit for k_0 value) can be given.
Thought to be run for time consuming calculations.
"""
extract_max.__doc__ = \
"""
Return:
maximum of absolute values of numbers from given string
Examples:
sage: extract_max("([1, 3], [2], [-1, -2], [-10])")
10
sage: extract_max("3, 55, ewewe, -42, 3300, 50")
3300
"""
if __name__ == '__main__':
global config
config = Config()
if '__file__' in globals():
# skiped in interactive mode as __file__ is not defined
main(sys.argv)
"""
This script calculates signature functions for knots (cable sums).
The script can be run as a sage script from the terminal
or used in interactive mode.
A knot (cable sum) is encoded as a list where each element (also a list)
corresponds to a cable knot, e.g. a list
[[1, 3], [2], [-1, -2], [-3]] encodes
T(2, 3; 2, 7) # T(2, 5) # -T(2, 3; 2, 5) # -T(2, 7).
To calculate the number of characters for which signature function vanish use
the function eval_cable_for_null_signature as shown below.
sage: eval_cable_for_null_signature([[1, 3], [2], [-1, -2], [-3]])
T(2, 3; 2, 7) # T(2, 5) # -T(2, 3; 2, 5) # -T(2, 7)
Zero cases: 1
All cases: 1225
Zero theta combinations:
(0, 0, 0, 0)
sage:
The numbers given to the function eval_cable_for_null_signature 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_signature_as_function_of_theta (see help/docstring for details).
About notation:
Cables that we work with follow a schema:
T(2, q_1; 2, q_2; 2, q_4) # -T(2, q_2; 2, q_4) #
# T(2, q_3; 2, q_4) # -T(2, q_1; 2, q_3; 2, q_4)
In knot_formula each k[i] is related with some q_i value, where
q_i = 2*k[i] + 1.
So we can work in the following steps:
1) choose a schema/formula by changing the value of knot_formula
2) set each q_i all or choose range in which q_i should varry
3) choose vector v / theata vector.
"""

99
notebooks/lemma.ipynb Normal file
View File

@ -0,0 +1,99 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import path\n",
"import logging\n",
"from contextlib import redirect_stdout\n",
"# logging.basicConfig(level=logging.INFO)\n",
"import gaknot"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Lemma "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": false
},
"outputs": [],
"source": [
"gaknot.prove_lemma()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Details to file"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"with open('very_detailed_calculations.txt', 'w') as f:\n",
" with redirect_stdout(f):\n",
" gaknot.prove_lemma(details=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Calculations to file"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"with open('calculations.txt', 'w') as f:\n",
" with redirect_stdout(f):\n",
" gaknot.prove_lemma(details=False)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "SageMath 9.0",
"language": "sage",
"name": "sagemath"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.10"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

8
notebooks/path.py Normal file
View File

@ -0,0 +1,8 @@
#!/usr/bin/env python3
import sys
import os
module_path = os.path.abspath(os.pardir)
if module_path not in sys.path:
sys.path.append(module_path)

580
notebooks/tests.ipynb Normal file
View File

@ -0,0 +1,580 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%matplotlib inline\n",
"%load_ext pycodestyle_magic\n",
"\n",
"# display full output, not only last result, except ended with semicolon\n",
"from IPython.core.interactiveshell import InteractiveShell\n",
"InteractiveShell.ast_node_interactivity = 'all';\n",
"from IPython.display import Image, SVG\n",
"import logging\n",
"logging.basicConfig(level=logging.INFO)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import path\n",
"from gaknot import import_sage\n",
"cs = import_sage('cable_signature', package='gaknot', path=path.module_path)\n",
"sg = import_sage('signature', package='gaknot', path=path.module_path)\n",
"m = import_sage('main', package='gaknot', path=path.module_path)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": false
},
"outputs": [],
"source": [
"m.prove_lemma(verbose=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Other cables"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": false
},
"outputs": [],
"source": [
"# knot_formula = \"[[k[0], k[1], k[3]],\" + \\\n",
"# \" [-k[1], -k[3]],\" + \\\n",
"# \" [k[2], k[3]],\" + \\\n",
"# \" [-k[0], -k[2], -k[3]]]\"\n",
"# # q_vector = (3, 5, 7, 13)\n",
"# q_vector = (3, 5, 7, 11)\n",
"\n",
"# template = cs.CableTemplate(knot_formula, q_vector=q_vector, verbose=True)\n",
"# cable = template.cable\n",
"# # cable.plot_all_summands()\n",
"# cable.is_function_big_for_all_metabolizers(invariant=cs.SIGMA)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Cables with 8 direct summands "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"formula_1 = \"[[k[0], k[5], k[3]], \" + \\\n",
" \"[-k[1], -k[3]], \" + \\\n",
" \"[k[2], k[3]], \" + \\\n",
" \"[-k[0], -k[2], -k[3]]]\"\n",
"formula_2 = \"[[k[4], k[1], k[7]], \" + \\\n",
" \"[-k[5], -k[7]], \" + \\\n",
" \"[k[6], k[7]], \" + \\\n",
" \"[-k[4], -k[6], -k[7]]]\"\n",
"\n",
"\n",
"\n",
"# cable_template_1 = cs.CableTemplate(knot_formula=formula_1)\n",
"# cable_template_2 = cs.CableTemplate(knot_formula=formula_2)\n",
"# cable_template = cable_template_1 + cable_template_2"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# cable_template.knot_formula"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Relatively small cables "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# q_vector = (5, 13, 19, 41,\\\n",
"# 7, 17, 23, 43)\n",
"# cable_template.fill_q_vector(q_vector=q_vector)\n",
"# cable = cable_template.cable\n",
"# print(cable.knot_description)\n",
"\n",
"# q_vector_small = (3, 7, 13, 19,\\\n",
"# 5, 11, 17, 23)\n",
"# cable_template.fill_q_vector(q_vector=q_vector)\n",
"# cable = cable_template.cable\n",
"# print(cable.knot_description)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"# cable.is_signature_big_for_all_metabolizers()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Slice candidate"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\n",
"# cable_template.fill_q_vector()\n",
"# # print(cable_template.q_vector)\n",
"# # print(cable_template.knot_formula)\n",
"\n",
"# slice_canidate = cable_template.cable\n",
"# print(slice_canidate.knot_description)\n",
"# sf = slice_canidate(4,4,4,4,0,0,0,0)\n",
"# sf = slice_canidate(4,1,1,4,0,0,0,0)\n",
"\n",
"\n",
"# cable.is_signature_big_for_all_metabolizers()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\n",
"\n",
"# knot_formula = \"[[k[0], k[1], k[4]], [-k[1], -k[3]],\\\n",
"# [k[2], k[3]], [-k[0], -k[2], -k[4]]]\"\n",
"\n",
"# knot_formula = \"[[k[3]], [-k[3]],\\\n",
"# [k[3]], [-k[3]] ]\"\n",
"\n",
"# knot_formula = \"[[k[3], k[2], k[0]], [-k[2], -k[0]],\\\n",
"# [k[1], k[0]], [-k[3], -k[1], -k[0]]]\"\n",
"\n",
"# knot_formula = \"[[k[0], k[1], k[2]], [k[3], k[4]],\\\n",
"# [-k[0], -k[3], -k[4]], [-k[1], -k[2]]]\"\n",
"\n",
"# knot_formula = \"[[k[0], k[1], k[2]], [k[3]],\\\n",
"# [-k[0], -k[1], -k[3]], [-k[2]]]\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"formula_1 = \"[[k[0], k[5], k[3]], \" + \\\n",
" \"[-k[1], -k[3]], \" + \\\n",
" \"[k[2], k[3]], \" + \\\n",
" \"[-k[0], -k[2], -k[3]]]\"\n",
"formula_2 = \"[[k[4], k[1], k[7]], \" + \\\n",
" \"[-k[5], -k[7]], \" + \\\n",
" \"[k[6], k[7]], \" + \\\n",
" \"[-k[4], -k[6], -k[7]]]\"\n",
"\n",
"\n",
"\n",
"# cable_template_1 = cs.CableTemplate(knot_formula=formula_1)\n",
"# cable_template_2 = cs.CableTemplate(knot_formula=formula_2)\n",
"# cable_template = cable_template_1 + cable_template_2\n",
"\n",
"# cable_template.fill_q_vector()\n",
"# # print(cable_template.q_vector)\n",
"# # print(cable_template.knot_formula)\n",
"\n",
"# slice_canidate = cable_template.cable\n",
"# print(slice_canidate.knot_description)\n",
"# sf = slice_canidate(4,4,4,4,0,0,0,0)\n",
"# sf = slice_canidate(4,1,1,4,0,0,0,0)\n",
"\n",
"\n",
"# slice_canidate.q_order\n",
"# # slice_canidate.is_signature_big_for_all_metabolizers()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"formula_1 = \"[[k[0], k[5], k[3]], \" + \\\n",
" \"[-k[5], -k[3]], \" + \\\n",
" \"[k[2], k[3]], \" + \\\n",
" \"[-k[4], -k[2], -k[3]]]\"\n",
"formula_2 = \"[[k[4], k[1], k[7]], \" + \\\n",
" \"[-k[1], -k[7]], \" + \\\n",
" \"[k[6], k[7]], \" + \\\n",
" \"[-k[0], -k[6], -k[7]]]\"\n",
"\n",
"\n",
"\n",
"\n",
"# cable_template_1 = cs.CableTemplate(knot_formula=formula_1)\n",
"# cable_template_2 = cs.CableTemplate(knot_formula=formula_2)\n",
"# cable_template = cable_template_1 + cable_template_2\n",
"# cable_template.fill_q_vector()\n",
"\n",
"# slice_canidate = cable_template.cable\n",
"# print(slice_canidate.knot_description)\n",
"\n",
"# slice_canidate.q_order\n",
"# # slice_canidate.is_signature_big_for_all_metabolizers()\n",
"# sigma = slice_canidate.get_sigma_as_function_of_theta()\n",
"# # sigma((0, 6, 6, 0, 0,0,0,0))\n",
"# # 13450/83\n",
"# sigma((9, 9, 9, 9, 0,0,0,0))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\n",
"\n",
"\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": false
},
"outputs": [],
"source": [
"formula_1 = \"[[k[0], k[5], k[3]], \" + \\\n",
" \"[-k[1], -k[3]], \" + \\\n",
" \"[ k[3]], \" + \\\n",
" \"[-k[4], -k[6], -k[3]]]\"\n",
"\n",
"formula_2 = \"[[k[4], k[1], k[7]], \" + \\\n",
" \"[ -k[7]], \" + \\\n",
" \"[k[6], k[7]], \" + \\\n",
" \"[-k[0], -k[5], -k[7]]]\"\n",
"\n",
"\n",
"\n",
"\n",
"# cable_template_1 = cs.CableTemplate(knot_formula=formula_1)\n",
"# cable_template_2 = cs.CableTemplate(knot_formula=formula_2)\n",
"# cable_template = cable_template_1 + cable_template_2\n",
"\n",
"# cable_template.fill_q_vector()\n",
"# # print(cable_template.q_vector)\n",
"# # print(cable_template.knot_formula)\n",
"\n",
"# slice_canidate = cable_template.cable\n",
"# print(slice_canidate.knot_description)\n",
"# sf = slice_canidate(4,4,4,4,0,0,0,0)\n",
"# sf = slice_canidate(4,1,1,4,0,0,0,0)\n",
"# slice_canidate.is_signature_big_for_all_metabolizers()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": false
},
"outputs": [],
"source": [
"formula_1 = \" [ [k[5], k[3]], \" + \\\n",
" \" [ -k[1], -k[3]], \" + \\\n",
" \" [ k[3]], \" + \\\n",
" \"[-k[4], -k[6], -k[3]]]\"\n",
"\n",
"formula_2 = \"[[k[4], k[1], k[7]], \" + \\\n",
" \"[ -k[7]], \" + \\\n",
" \"[k[6], k[7]], \" + \\\n",
" \"[-k[5], -k[7]]]\"\n",
"\n",
"\n",
"\n",
"\n",
"# cable_template_1 = cs.CableTemplate(knot_formula=formula_1)\n",
"# cable_template_2 = cs.CableTemplate(knot_formula=formula_2)\n",
"# cable_template = cable_template_1 + cable_template_2\n",
"\n",
"# cable_template.fill_q_vector()\n",
"# # print(cable_template.q_vector)\n",
"# # print(cable_template.knot_formula)\n",
"\n",
"# slice_canidate = cable_template.cable\n",
"# print(slice_canidate.knot_description)\n",
"# sf = slice_canidate(4,4,4,4,0,0,0,0)\n",
"# sf = slice_canidate(4,1,1,4,0,0,0,0)\n",
"# slice_canidate.q_order\n",
"# # slice_canidate.is_signature_big_for_all_metabolizers()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# sf = slice_canidate()\n",
"# sf = sf[2]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# sf.plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"formula_1 = \"[ [k[5], k[3]], \" + \\\n",
" \"[ -k[1], -k[3]], \" + \\\n",
" \"[ k[3]], \" + \\\n",
" \"[ -k[6], -k[3]]]\"\n",
"\n",
"formula_2 = \"[[ k[1], k[7]], \" + \\\n",
" \"[ -k[7]], \" + \\\n",
" \"[ k[6], k[7]], \" + \\\n",
" \"[ -k[5], -k[7]]]\""
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# cable_template_1 = cs.CableTemplate(knot_formula=formula_1)\n",
"# cable_template_2 = cs.CableTemplate(knot_formula=formula_2)\n",
"# cable_template = cable_template_1 + cable_template_2"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# cable_template.knot_formula"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# cable_template.fill_q_vector()\n",
"# print(cable_template.q_vector)\n",
"# print(cable_template.knot_formula)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# cable_template.fill_q_vector()\n",
"# print(cable_template.q_vector)\n",
"# print(cable_template.knot_formula)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# slice_canidate = cable_template.cable\n",
"# print(slice_canidate.knot_description)\n",
"# sf = slice_canidate(4,4,4,4,0,0,0,0)\n",
"# sf = slice_canidate(4,1,1,4,0,0,0,0)\n",
"# slice_canidate.q_order"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# slice_canidate.is_sigma_big_for_all_metabolizers()\n",
"# slice_canidate.is_signature_big_for_all_metabolizers()\n",
"# slice_canidate.is_function_big_for_all_metabolizers()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"# slice_canidate.is_function_big_for_all_metabolizers(invariant=cs.SIGMA)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"# slice_canidate.is_function_big_for_all_metabolizers(invariant=cs.SIGNATURE)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# sigma = slice_canidate.get_sigma_as_function_of_theta()\n",
"# sigma((0, 6, 6, 0, 0,0,0,0))\n",
"# # 13450/83\n",
"# sigma((9, 0, 0, 9, 0,0,0,0))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# _, _, sf = slice_canidate((1, 1, 0, 0, 0,0,0,0))\n",
"# sf"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# sg.SignaturePloter.plot(sf)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"\n",
"# slice_canidate.plot_sum_for_theta_vector([0r,4r,0r,4r,0r,0r,0r,0r], save_to_dir=True)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# sf.plot()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# help(cs.CableTemplate.get_q_vector)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "SageMath 9.0",
"language": "sage",
"name": "sagemath"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.10"
}
},
"nbformat": 4,
"nbformat_minor": 4
}

View File

@ -1,125 +0,0 @@
#!/usr/bin/env python
import collections
def mod_one(n):
"""This function returns the fractional part of some number."""
if n >= 1:
return mod_one(n - 1)
if n < 0:
return mod_one(n + 1)
else:
return n
class av_signature_function(object):
'''
This simple class encodes twisted and untwisted signature functions
of knots. Since the signature function is entirely encoded by its signature
jump, the class stores only information about signature jumps
in a dictionary self.data.
The dictionary stores data of the signature jump as a key/values pair,
where the key is the argument at which the functions jumps
and value encodes the value of the jump.
Remember that we treat signature functions as defined on the interval [0,1).
'''
def __init__(self,values=[]):
# We will store data of signature jumps here.
self.data = collections.defaultdict(int)
# values contain initial data of singature jumps
for jump_arg, jump in values:
assert 0 <= jump_arg and jump_arg < 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 and arg < 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 total_sign_jump(self):
# Total signature jump is the sum of all jumps.
return sum([j[1] for j in self.to_list()])
def to_list(self):
# Return signature jumps formated as a list
return sorted(self.data.items(), key = lambda x: x[0])
def step_function_data(self):
# Transform the signature jump data to a format understandable
# by the plot function.
l = self.to_list()
vals = ([(d[0], sum(2 * j[1] for j in l[:l.index(d)+1])) for d in l] +
[(0,self.data[0]), (1,self.total_sign_jump())])
return vals
def plot(self):
# plot the signture function
plot_step_function(self.step_function_data())
def tikz_plot(self, file_name):
# Draw the graph of the signature and transform it into TiKz.
# header of the LaTeX file
output_file = open(file_name, "w")
output_file.write("\\documentclass[tikz]{standalone}\n")
output_file.write("\\usetikzlibrary{datavisualization,datavisualization.formats.functions}\n")
output_file.write("\\begin{document}\n")
output_file.write("\\begin{tikzpicture}\n")
data = sorted(self.step_function_data())
output_file.write(" \\datavisualization[scientific axes,visualize as smooth line,\n")
output_file.write(" x axis={ticks={none,major={at={")
output_file.write(", " + str(N(data[0][0],digits=4)) + " as \\(" + str(data[0][0]) + "\\)")
for jump_arg,jump in data[1:]:
output_file.write(", " + str(N(jump_arg,digits=4)) + " as \\(" + str(jump_arg) + "\\)")
output_file.write("}}}}\n")
output_file.write(" ]\n")
output_file.write("data [format=function]{\n")
output_file.write("var x : interval [0:1];\n")
output_file.write("func y = \\value x;\n")
output_file.write("};\n")
# close LaTeX enviroments
output_file.write("\\end{tikzpicture}\n")
output_file.write("\\end{document}\n")
output_file.close()
def __lshift__(self, shift):
# Shift of the signture functions correspond to the rotations.
return self.__rshift__(-shift)
def __rshift__(self, shift):
new_data = list()
for jump_arg, jump in self.data.items():
new_data.append((mod_one(jump_arg + shift),jump))
return av_signature_function(new_data)
def __sub__(self, other):
# we cn perform arithmetic operations on signature functions.
return self + other.__neg__()
def __neg__(self):
for jump_arg in self.data.keys():
self.data[jump_arg] *= (-1)
return self
def __add__(self, other):
for jump_arg, jump in other.data.items():
self.data[jump_arg] += jump
return self
def __str__(self):
return '\n'.join([str(jump_arg) + ": " + str(jump)
for jump_arg, jump in self.data.items()])
def __repr__(self):
print self.__str__()
def untw_signature(k):
# Return the signture function of the T_{2,2k+1} torus knot.
l = ([((2 * a + 1)/(4 * k + 2), -1) for a in range(k)] +
[((2 * a + 1)/(4 * k + 2), 1) for a in range(k + 1, 2 * k + 1)])
return av_signature_function(l)