Compare commits

..

No commits in common. "master" and "looking_for_big_signature" have entirely different histories.

13 changed files with 945 additions and 2192 deletions

4
.gitignore vendored
View File

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

View File

@ -1,5 +1,3 @@
This project allows calculating knot invariants for linear combinations of iterated torus knots. This script calculates signature functions for knots (cable sums).
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). 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.
To recreate calculations that were done for the proof see lemma.ipynb in notebooks.
For a more detailed description see package documentation.

637
cable_signature.sage Normal file
View File

@ -0,0 +1,637 @@
#!/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
import matplotlib.pyplot as plt
import inspect
# 9.11 (9.8)
# 9.15 (9.9)
PLOTS_DIR = "plots"
class CableSummand():
def __init__(self, knot_as_k_values):
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 = None
@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] + ")"
@property
def knot_description(self):
return self._knot_description
@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_summand_signature_as_theta_function()
return self._signature_as_function_of_theta
@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)
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)
@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 = 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(j):
# return the signature function of the T_{2, 2k+1} torus knot
k = abs(j)
q = 2 * k + 1
counter = Counter({(2 * a + 1)/(2 * q) : -sgn(j)
for a in range(k)})
counter.update(Counter({(2 * a + 1)/(2 * q) : sgn(j)
for a in range(k + 1, q)}))
return 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 = 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(*knot_as_k_values,
theta=theta)
return pattern_part, satellite_part
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 = 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)
pp.plot_sum_with_other(sp, title=title, save_path=save_path)
def plot_summand(self):
range_limit = min(self.knot_as_k_values[-1] + 1, 3)
for theta in range(range_limit):
self.plot_summand_for_theta(theta)
class CableSum():
def __init__(self, knot_sum):
self.knot_sum_as_k_valus = knot_sum
self.knot_summands = [CableSummand(k) for k in knot_sum]
self.signature_as_function_of_theta = \
self.get_signature_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 i, knot in enumerate(self.knot_summands):
knot.plot_summand_for_theta(thetas[i], save_path=save_path)
pp, sp = 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)
pp.plot_sum_with_other(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])
for i, knot in enumerate(self.knot_summands)]
# pp, sp = knot.signature_as_function_of_theta(thetas[i])
# (pp + sp) = sp.plot
#
# pp.plot_sum_with_other(sp, title=title, save_path=file_path)
return dir_name
def plot_all_summands(self):
for knot in self.knot_summands:
knot.plot_summand()
@property
def knot_description(self):
return self._knot_description
@property
def patt_k_list(self):
return self._patt_k_list
@property
def patt_q_list(self):
return self._patt_q_list
# q_order is LCM of all q values for pattern knots
@property
def q_order(self):
return self._q_order
@q_order.setter
def q_order(self, val):
self._q_order = val
@property
def knot_sum_as_k_valus(self):
return self._knot_sum_as_k_valus
@knot_sum_as_k_valus.setter
def knot_sum_as_k_valus(self, knot_sum):
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 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)
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):
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
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 = SignatureFunction()
pattern_part = SignatureFunction()
# for each cable knot (summand) in cable sum apply theta
for i, knot in enumerate(self.knot_summands):
sfth = knot.signature_as_function_of_theta
pp, sp = sfth(thetas[i])
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
signature_as_function_of_theta.__doc__ =\
signature_as_function_of_theta_docstring
return signature_as_function_of_theta
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))
# for idx, el in enumerate(theta):
# old_sum += (el^2 / self.patt_q_list[idx] * (-1)^idx)
return result.is_integer()
def is_signature_big_in_ranges(self, 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
signature_is_small = True
# Check if any element generated by thetas vector
# has a large signature.
for shift in range(1, self.q_order):
shifted_thetas = [shift * th for th in thetas]
pp, sp = self.signature_as_function_of_theta(*shifted_thetas)
sf = pp + sp
limit = 5 + np.count_nonzero(shifted_thetas)
extremum = abs(sf.extremum(limit=limit))
if shift > 1:
print(shifted_thetas, end=" ")
print(extremum)
if extremum > limit:
signature_is_small = False
break
elif shift == 1:
print("*" * 10)
print(shifted_thetas, end=" ")
print(extremum)
if signature_is_small:
print("\n" * 10 + "!" * 1000)
return False
return True
def is_signature_big_for_all_metabolizers(self):
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)
for shift in range(0, num_of_summands, 4):
ranges_list = num_of_summands * [range(0, 1)]
ranges_list[shift : shift + 3] = \
[range(0, i + 1) for i in self.patt_k_list[shift: shift + 3]]
ranges_list[shift + 3] = range(0, 2)
if not self.is_signature_big_in_ranges(ranges_list):
return False
else:
print("\nOK")
return True
class CableTemplate():
def __init__(self, knot_formula, q_vector=None, k_vector=None,
generate_q_vector=True, slice_candidate=True):
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(knot_formula, slice_candidate)
@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, slice=True):
if q_vector is None:
q_vector = self.get_q_vector(self.knot_formula)
self.q_vector = q_vector
@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 += "\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)
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)
@classmethod
def get_q_vector(cls, knot_formula, slice=True):
lowest_number = 2
q_vector = [0] * (cls.extract_max(knot_formula) + 1)
P = Primes()
for layer in cls.get_layers_from_formula(knot_formula)[::-1]:
for el in layer:
q_vector[el] = P.next(lowest_number)
lowest_number = q_vector[el]
lowest_number *= 4
return q_vector
@staticmethod
def get_layers_from_formula(knot_formula):
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 = set()
for lst in k_indices:
if len(lst) >= i:
layer.add(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):
s_formula = self.knot_formula
o_formula = other.knot_formula
knot_formula = s_formula[:-1] + ",\n" + o_formula[1:]
cable_template = CableTemplate(knot_formula)
return cable_template
def mod_one(n):
return n - floor(n)
# CableSum.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)'
# """
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 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 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.
"""
#
# 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:
# 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 SignatureFunction for this single cable
# and a theta given as an argument
# """

View File

@ -1,64 +0,0 @@
#!/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.
#

View File

@ -1,863 +0,0 @@
#!/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
# """

View File

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

View File

@ -1,325 +0,0 @@
#!/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()

View File

@ -1,90 +0,0 @@
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))

173
main.sage Normal file
View File

@ -0,0 +1,173 @@
#!/usr/bin/python
# TBD: read about Factory Method, variable in docstring, sage documentation,
# print calc. to output file
# delete separation for twisted_part and untwisted_part
# decide about printing option
import os
import sys
import itertools as it
import re
import numpy as np
attach("signature.sage")
attach("cable_signature.sage")
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
self.verbose = True
# self.verbose = False
def main(arg=None):
try:
limit = int(arg[1])
except (IndexError, TypeError):
limit = None
# global cable_template , cable_template_2, cable_template_1
knot_formula = "[[k[0], k[1], k[3]], " + \
"[-k[1], -k[3]], " + \
"[k[2], k[3]], " + \
"[-k[0], -k[2], -k[3]]]"
template = CableTemplate(knot_formula, q_vector=[3, 5, 7, 11])
cab = template.cable
# cab.plot_all_summands()
cab.plot_sum_for_theta_vector([0,4,0,4], save_to_dir=True)
# knot_formula = config.knot_formula
# q_vector = (3, 5, 7, 13)
# q_vector = (3, 5, 7, 11)
return
formula_1 = "[[k[0], k[5], k[3]], " + \
"[-k[1], -k[3]], " + \
"[k[2], k[3]], " + \
"[-k[0], -k[2], -k[3]]]"
formula_2 = "[[k[4], k[1], k[7]], " + \
"[-k[5], -k[7]], " + \
"[k[6], k[7]], " + \
"[-k[4], -k[6], -k[7]]]"
q_vector = (5, 13, 19, 41,\
7, 17, 23, 43)
q_vector_small = (3, 7, 13, 19,\
5, 11, 17, 23)
cable_template_1 = CableTemplate(knot_formula=formula_1)
cable_template_2 = CableTemplate(knot_formula=formula_2)
cable_template = cable_template_1 + cable_template_2
cable_with_shift = cable_template_1.add_with_shift(cable_template_2)
print(cable_with_shift.knot_formula)
cable_template.fill_q_vector()
cable = cable_template.cable
sf = cable(4,4,4,4,0,0,0,0)
writer = SignatureWriter(sf)
writer.plot(title="hoho")
sf = cable_template.cable.signature_as_function_of_theta(4,1,1,4,0,0,0,0)
writer = SignatureWriter(sf)
writer.plot(title="hoho", color='red')
cable_template.cable.is_signature_big_for_all_metabolizers()
cable_template_1 = CableTemplate(knot_formula=formula_1)
cable_template_2 = CableTemplate(knot_formula=formula_2)
cable_template = cable_template_1 + cable_template_2
cable_template.cable.is_signature_big_for_all_metabolizers()
sf = cable_template.cable.signature_as_function_of_theta(4,4,4,4,0,0,0,0)
writer = SignatureWriter(sf)
writer.plot(title="hoho")
if __name__ == '__main__':
global config
config = Config()
if '__file__' in globals():
# skiped in interactive mode as __file__ is not defined
main(sys.argv)
else:
pass
# main()
"""
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.
"""

View File

@ -1,99 +0,0 @@
{
"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
}

View File

@ -1,8 +0,0 @@
#!/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)

View File

@ -1,580 +0,0 @@
{
"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,51 +1,28 @@
#!/usr/bin/env sage -python #!/usr/bin/python
import numpy as np
import sys import itertools as it
# import os from typing import Iterable
from collections import Counter from collections import Counter
from sage.arith.functions import LCM_list
import warnings
import re
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import inspect import inspect
from PIL import Image from PIL import Image
from pathlib import Path 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.11 (9.8)
# 9.15 (9.9) # 9.15 (9.9)
class SignatureFunction: class SignatureFunction():
r"""signature function is entirely encoded by its signature
jump, the class stores only information about signature jumps def __init__(self, values=None, counter=None):
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 # counter of signature jumps
if counter is None: if counter is None:
counter = Counter() counter = Counter()
values = values or [] if values is None:
values = []
for k, v in values: for k, v in values:
counter[k] += v counter[k] += v
@ -57,7 +34,6 @@ class SignatureFunction:
counter[0] += 0 counter[0] += 0
counter[1] += 0 counter[1] += 0
self.jumps_counter = counter self.jumps_counter = counter
self.plot_title = plot_title
def __rshift__(self, shift): def __rshift__(self, shift):
# A shift of the signature functions corresponds to the rotation. # A shift of the signature functions corresponds to the rotation.
@ -76,26 +52,13 @@ class SignatureFunction:
def __add__(self, other): def __add__(self, other):
counter = copy(self.jumps_counter) counter = copy(self.jumps_counter)
counter.update(other.jumps_counter) counter.update(other.jumps_counter)
if self.plot_title and other.plot_title: return SignatureFunction(counter=counter)
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): def __sub__(self, other):
counter = copy(self.jumps_counter) counter = copy(self.jumps_counter)
counter.subtract(other.jumps_counter) counter.subtract(other.jumps_counter)
return SignatureFunction(counter=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): def __eq__(self, other):
return self.jumps_counter == other.jumps_counter return self.jumps_counter == other.jumps_counter
@ -116,6 +79,9 @@ class SignatureFunction:
result = [jump for jump_arg, jump in items if jump_arg < mod_one(arg)] result = [jump for jump_arg, jump in items if jump_arg < mod_one(arg)]
return 2 * sum(result) + self.jumps_counter[arg] return 2 * sum(result) + self.jumps_counter[arg]
def is_zero_everywhere(self):
return not any(self.jumps_counter.values())
def double_cover(self): def double_cover(self):
# to read values for t^2 # to read values for t^2
items = self.jumps_counter.items() items = self.jumps_counter.items()
@ -137,74 +103,75 @@ class SignatureFunction:
counter = Counter({mod_one(2 * k) : v for k, v in items if k >= 1/2}) counter = Counter({mod_one(2 * k) : v for k, v in items if k >= 1/2})
return SignatureFunction(counter=counter) return SignatureFunction(counter=counter)
def is_zero_everywhere(self): def extremum(self, limit=None):
return not any(self.jumps_counter.values()) max = 0
def extremum(self, limit=math.inf):
max_point = (0, 0)
current = 0 current = 0
items = sorted(self.jumps_counter.items()) items = sorted(self.jumps_counter.items())
for arg, jump in items: for arg, jump in items:
current += 2 * jump current += 2 * jump
assert current == self(arg) + jump assert current == self(arg) + jump
if abs(current) > abs(max_point[1]): if abs(current) > abs(max):
max_point = (arg, current) max = current
if abs(current) > limit: if limit is not None:
break if abs(max) > limit:
return max_point break
return max
def total_sign_jump(self): def total_sign_jump(self):
# Total signature jump is the sum of all jumps. # Total signature jump is the sum of all jumps.
return sum([j[1] for j in sorted(self.jumps_counter.items())]) return sum([j[1] for j in sorted(self.jumps_counter.items())])
def plot(self, *args, **kargs): def plot_four(self, sf1, sf2, sf3, save_path=None, title=''):
SignaturePloter.plot(self, *args, **kargs)
fig, axes_matrix = plt.subplots(2, 2, sharey=True,
class SignaturePloter: figsize=(10,5))
sf0 = self
@classmethod sf.plot(subplot=True,
def plot_many(cls, *sf_list, save_path=None, title='', cols=None): ax=axes_matrix[0][1])
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, sf1.plot(subplot=True,
ax=axes_matrix[0][1]) ax=axes_matrix[1][0],
color='red',
linestyle='dotted')
sf2.plot(subplot=True, sf2.plot(subplot=True,
ax=axes_matrix[0][0],
color='black')
sf3.plot(subplot=True,
ax=axes_matrix[1][1],
alpha=0.3)
fig.suptitle(title)
plt.tight_layout()
if save_path is None:
save_path = os.path.join(os.getcwd(),"tmp.png")
save_path = Path(save_path)
save_path = save_path.with_suffix('.png')
plt.savefig(save_path)
plt.close()
image = Image.open(save_path)
image.show()
pass
def plot_sum_with_other(self, other,
save_path=None, title=''):
tp = self
up = other
sf = tp + up
fig, axes_matrix = plt.subplots(2, 2, sharey=True,
figsize=(10,5))
tp.plot(subplot=True,
ax=axes_matrix[0][1])
up.plot(subplot=True,
ax=axes_matrix[1][0], ax=axes_matrix[1][0],
color='red', color='red',
linestyle='dotted') linestyle='dotted')
@ -213,11 +180,11 @@ class SignaturePloter:
ax=axes_matrix[0][0], ax=axes_matrix[0][0],
color='black') color='black')
sf1.plot(subplot=True, tp.plot(subplot=True,
ax=axes_matrix[1][1], ax=axes_matrix[1][1],
alpha=0.3) alpha=0.3)
sf2.plot(subplot=True, up.plot(subplot=True,
ax=axes_matrix[1][1], ax=axes_matrix[1][1],
color='red', alpha=0.3, color='red', alpha=0.3,
linestyle='dotted') linestyle='dotted')
@ -228,78 +195,63 @@ class SignaturePloter:
alpha=0.7,) alpha=0.7,)
fig.suptitle(title) fig.suptitle(title)
plt.tight_layout() plt.tight_layout()
if save_path is None:
save_path = os.path.join(os.getcwd(),"tmp.png")
save_path = Path(save_path)
save_path = save_path.with_suffix('.png')
cls.show_and_save(save_path)
@classmethod # print(save_as)
def plot(cls, sf, subplot=False, ax=None,
save_path=None, plt.savefig(save_path)
title='', plt.close()
image = Image.open(save_path)
image.show()
pass
def plot(self, subplot=False, ax=None, save_as='sf',
title="",
alpha=1, alpha=1,
color='blue', color='blue',
linestyle='solid', linestyle='solid',
special_point=None,
special_label='',
extraticks=None,
ylabel=''): ylabel=''):
if ax is None: if ax is None:
fig, ax = plt.subplots(1, 1) fig, ax = plt.subplots(1, 1)
keys = sorted(sf.jumps_counter.keys()) keys = sorted(self.jumps_counter.keys())
y = [(sf(k) + sf.jumps_counter[k]) for k in keys[:-1]] y = [self(k) + self.jumps_counter[k] for k in keys]
xmax = keys[1:] xmax = keys[1:]
xmin = keys[:-1] xmin = keys[:-1]
ax.set(ylabel=ylabel) ax.set(ylabel=ylabel)
ax.set(title=title) ax.set(title=title)
ax.hlines(y, xmin, xmax, color=color, linestyle=linestyle, alpha=alpha) 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: if subplot:
return ax return ax
cls.show_and_save(save_path) save_as += ".png"
plt.savefig(save_as)
plt.close()
image = Image.open(save_as)
image.show()
@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: def step_function_data(self):
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 # Transform the signature jump data to a format understandable
# by the plot function. # by the plot function.
result = [(k, sf.sf(k) + sf.jumps_counter[k]) result = [(k, self.sf(k) + self.jumps_counter[k])
for k in sorted(sf.jumps_counter.keys())] for k in sorted(self.jumps_counter.keys())]
return result return result
@staticmethod def tikz_plot(self, save_as):
def tikz_plot(sf, save_as):
plt_sin = plot(sin(x), (x, 0, 2*pi)) plt_sin = plot(sin(x), (x, 0, 2*pi))
# plt_sin.show() # plt_sin.show()
plt_sin.save("MyPic.pdf") plt_sin.save("MyPic.pdf")
@ -374,3 +326,34 @@ class SignaturePloter:
\end{document} \end{document}
""" """
f.write(tail) f.write(tail)
def mod_one(n):
return n - floor(n)
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.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).
"""
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
"""