Added class SignatureWriter. Refactorisation. Theta vector parser method.
This commit is contained in:
parent
93911d0dfd
commit
bb11f93196
@ -6,71 +6,38 @@ from collections import Counter
|
|||||||
from sage.arith.functions import LCM_list
|
from sage.arith.functions import LCM_list
|
||||||
import warnings
|
import warnings
|
||||||
import re
|
import re
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import inspect
|
||||||
|
|
||||||
# 9.11 (9.8)
|
# 9.11 (9.8)
|
||||||
# 9.15 (9.9)
|
# 9.15 (9.9)
|
||||||
|
|
||||||
|
|
||||||
class SignatureFunction(object):
|
class SignatureFunction():
|
||||||
|
|
||||||
def __init__(self, values=None, counter=None):
|
def __init__(self, values=None, counter=None):
|
||||||
# builed counter based on values of signature jumps
|
|
||||||
|
# counter of signature jumps
|
||||||
if counter is None:
|
if counter is None:
|
||||||
counter = Counter()
|
counter = Counter()
|
||||||
|
|
||||||
if values is None:
|
if values is None:
|
||||||
values = []
|
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:
|
for k, v in values:
|
||||||
counter[k] += v
|
counter[k] += v
|
||||||
self.cnt_signature_jumps = counter
|
|
||||||
# self.tikz_plot("bum.tex")
|
|
||||||
|
|
||||||
def is_zero_everywhere(self):
|
counter = Counter({k : v for k, v in counter.items() if v != 0})
|
||||||
return not any(self.cnt_signature_jumps.values())
|
if any(k >= 1 for k in counter.keys()):
|
||||||
|
msg = "Signature function is defined on the interval [0, 1)."
|
||||||
|
raise ValueError(msg)
|
||||||
|
|
||||||
def double_cover(self):
|
counter[0] += 0
|
||||||
# to read values for t^2
|
counter[1] += 0
|
||||||
items = self.cnt_signature_jumps.items()
|
self.jumps_counter = counter
|
||||||
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):
|
def __rshift__(self, shift):
|
||||||
# A shift of the signature functions corresponds to the rotation.
|
# A shift of the signature functions corresponds to the rotation.
|
||||||
counter = Counter({mod_one(k + shift) : v \
|
counter = Counter({mod_one(k + shift) : v \
|
||||||
for k, v in self.cnt_signature_jumps.items()})
|
for k, v in self.jumps_counter.items()})
|
||||||
return SignatureFunction(counter=counter)
|
return SignatureFunction(counter=counter)
|
||||||
|
|
||||||
def __lshift__(self, shift):
|
def __lshift__(self, shift):
|
||||||
@ -78,81 +45,171 @@ class SignatureFunction(object):
|
|||||||
|
|
||||||
def __neg__(self):
|
def __neg__(self):
|
||||||
counter = Counter()
|
counter = Counter()
|
||||||
counter.subtract(self.cnt_signature_jumps)
|
counter.subtract(self.jumps_counter)
|
||||||
return SignatureFunction(counter=counter)
|
return SignatureFunction(counter=counter)
|
||||||
|
|
||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
counter = copy(self.cnt_signature_jumps)
|
counter = copy(self.jumps_counter)
|
||||||
counter.update(other.cnt_signature_jumps)
|
counter.update(other.jumps_counter)
|
||||||
return SignatureFunction(counter=counter)
|
return SignatureFunction(counter=counter)
|
||||||
|
|
||||||
def __sub__(self, other):
|
def __sub__(self, other):
|
||||||
counter = copy(self.cnt_signature_jumps)
|
counter = copy(self.jumps_counter)
|
||||||
counter.subtract(other.cnt_signature_jumps)
|
counter.subtract(other.jumps_counter)
|
||||||
return SignatureFunction(counter=counter)
|
return SignatureFunction(counter=counter)
|
||||||
|
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return self.cnt_signature_jumps == other.cnt_signature_jumps
|
return self.jumps_counter == other.jumps_counter
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
result = ''.join([str(jump_arg) + ": " + str(jump) + "\n"
|
result = ''.join([str(jump_arg) + ": " + str(jump) + "\n"
|
||||||
for jump_arg, jump in sorted(self.cnt_signature_jumps.items())
|
for jump_arg, jump in sorted(self.jumps_counter.items())])
|
||||||
if jump != 0])
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
result = ''.join([str(jump_arg) + ": " + str(jump) + ", "
|
result = ''.join([str(jump_arg) + ": " + str(jump) + ", "
|
||||||
for jump_arg, jump in sorted(self.cnt_signature_jumps.items())])
|
for jump_arg, jump in sorted(self.jumps_counter.items())])
|
||||||
return result[:-2] + "."
|
return result[:-2] + "."
|
||||||
|
|
||||||
def __call__(self, arg):
|
def __call__(self, arg):
|
||||||
# return the value of the signature function at the point arg, i.e.
|
# return the value of the signature function at the point arg, i.e.
|
||||||
# sum of all signature jumps that occur before arg
|
# sum of all signature jumps that occur before arg
|
||||||
items = self.cnt_signature_jumps.items()
|
items = self.jumps_counter.items()
|
||||||
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.cnt_signature_jumps[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):
|
||||||
|
# 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 extremum(self, limit=None):
|
||||||
|
max = 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):
|
||||||
|
max = current
|
||||||
|
if limit is not None:
|
||||||
|
if abs(max) > limit:
|
||||||
|
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.cnt_signature_jumps.items())])
|
return sum([j[1] for j in sorted(self.jumps_counter.items())])
|
||||||
|
|
||||||
|
class SignatureWriter():
|
||||||
|
|
||||||
|
def __init__(self, signature_function):
|
||||||
|
self.sf = signature_function
|
||||||
|
|
||||||
|
def plot(self, title=None, subplot=False):
|
||||||
|
|
||||||
|
keys = sorted(self.sf.jumps_counter.keys())
|
||||||
|
y = [self.sf(k) + self.sf.jumps_counter[k] for k in keys]
|
||||||
|
xmax = [k for k in keys if k != 0]
|
||||||
|
xmin = [k for k in keys if k != 1]
|
||||||
|
fig, ax = plt.subplots(1, 1)
|
||||||
|
ax.set(ylabel='signature function')
|
||||||
|
if title is not None:
|
||||||
|
ax.set(title=title)
|
||||||
|
ax.hlines(y, xmin, xmax, color='blue')
|
||||||
|
plt.savefig('sf.png')
|
||||||
|
plt.close()
|
||||||
|
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
image = Image.open('sf.png')
|
||||||
|
image.show()
|
||||||
|
|
||||||
def step_function_data(self):
|
def step_function_data(self):
|
||||||
# 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.
|
||||||
lst = sorted(self.cnt_signature_jumps.items())
|
result = [(k, self.sf(k) + self.sf.jumps_counter[k])
|
||||||
vals = ([(d[0], sum(2 * j[1] for j in lst[:lst.index(d)+1])) for d in lst] +
|
for k in sorted(self.sf.jumps_counter.keys())]
|
||||||
[(0,self.cnt_signature_jumps[0]), (1,self.total_sign_jump())])
|
return result
|
||||||
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):
|
def tikz_plot(self, file_name):
|
||||||
|
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.
|
# Draw the graph of the signature and transform it into TiKz.
|
||||||
# header of the LaTeX file
|
# 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(file_name, "w") as f:
|
with open(file_name, "w") as f:
|
||||||
f.write("\\documentclass[tikz]{standalone}\n")
|
head = \
|
||||||
f.write("\\usetikzlibrary{datavisualization, " +
|
r"""
|
||||||
"datavisualization.formats.functions}\n")
|
\documentclass[tikz]{{standalone}}
|
||||||
f.write("\\begin{document}\n")
|
%\usepackage{{tikz}}
|
||||||
f.write("\\begin{tikzpicture}\n")
|
\usetikzlibrary{{datavisualization}}
|
||||||
data = sorted(self.step_function_data())
|
\usetikzlibrary{{datavisualization.formats.functions}}
|
||||||
print("data")
|
%\usetikzlibrary{{calc}}
|
||||||
print(data)
|
\begin{{document}}
|
||||||
f.write("\\datavisualization[scientific axes, " +
|
\begin{{tikzpicture}}
|
||||||
"visualize as smooth line,\n")
|
\datavisualization[scientific axes, visualize as smooth line,
|
||||||
f.write("x axis={ticks={none,major={at={")
|
x axis={{ticks={{none,major={{at={{, {arg0} " as \\( {val0} \\
|
||||||
f.write(", " + str(N(data[0][0],digits=4)) + " as \\(" + \
|
%]
|
||||||
str(data[0][0]) + "\\)")
|
""".format(arg0=str(N(data[0][0] ,digits=4)), val0=str(data[0][0]))
|
||||||
for jump_arg, jump in data:
|
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)) +
|
f.write(", " + str(N(jump_arg,digits=4)) +
|
||||||
" as \\(" + str(jump_arg) + "\\)")
|
" as \\(" + str(jump_arg) + "\\)")
|
||||||
f.write("}}}}\n")
|
f.write("}}}}\n")
|
||||||
@ -162,12 +219,23 @@ class SignatureFunction(object):
|
|||||||
f.write("func y = \\value x;\n")
|
f.write("func y = \\value x;\n")
|
||||||
f.write("};\n")
|
f.write("};\n")
|
||||||
# close LaTeX enviroments
|
# close LaTeX enviroments
|
||||||
f.write("\\end{tikzpicture}\n")
|
tail = \
|
||||||
f.write("\\end{document}\n")
|
r"""
|
||||||
|
%};
|
||||||
|
\end{tikzpicture}
|
||||||
|
\end{document}
|
||||||
|
"""
|
||||||
|
f.write(tail)
|
||||||
|
|
||||||
|
|
||||||
|
class CableSummand():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class TorusCable(object):
|
|
||||||
|
|
||||||
|
class CableSum():
|
||||||
def __init__(self, knot_formula, k_vector=None, q_vector=None):
|
def __init__(self, knot_formula, k_vector=None, q_vector=None):
|
||||||
|
|
||||||
self._knot_formula = knot_formula
|
self._knot_formula = knot_formula
|
||||||
@ -177,9 +245,8 @@ class TorusCable(object):
|
|||||||
elif q_vector is not None:
|
elif q_vector is not None:
|
||||||
self.q_vector = q_vector
|
self.q_vector = q_vector
|
||||||
else:
|
else:
|
||||||
self.q_vector = self.get_q_vector(self.knot_formula)
|
self.q_vector = self.get_q_vector_alg_slice(self.knot_formula)
|
||||||
|
|
||||||
self._sigma_function = None
|
|
||||||
self._signature_as_function_of_theta = None
|
self._signature_as_function_of_theta = None
|
||||||
|
|
||||||
|
|
||||||
@ -194,11 +261,7 @@ class TorusCable(object):
|
|||||||
@property
|
@property
|
||||||
def knot_formula(self):
|
def knot_formula(self):
|
||||||
return self._knot_formula
|
return self._knot_formula
|
||||||
# @knot_formula.setter
|
|
||||||
# def knot_formula(self, knot_formula):
|
|
||||||
# self._knot_formula = knot_formula
|
|
||||||
|
|
||||||
# knot encoding
|
|
||||||
@property
|
@property
|
||||||
def knot_description(self):
|
def knot_description(self):
|
||||||
return self._knot_description
|
return self._knot_description
|
||||||
@ -211,24 +274,25 @@ class TorusCable(object):
|
|||||||
def knot_sum(self, knot_sum):
|
def knot_sum(self, knot_sum):
|
||||||
self._knot_sum = knot_sum
|
self._knot_sum = knot_sum
|
||||||
self._knot_description = self.get_knot_descrption(knot_sum)
|
self._knot_description = self.get_knot_descrption(knot_sum)
|
||||||
self._last_k_list = [abs(i[-1]) for i in knot_sum]
|
self._patt_k_list = [abs(i[-1]) for i in knot_sum]
|
||||||
self._last_q_list = [2 * i + 1 for i in self._last_k_list]
|
self._patt_q_list = [2 * i + 1 for i in self._patt_k_list]
|
||||||
if any(n not in Primes() for n in self._last_q_list):
|
if any(n not in Primes() for n in self._patt_q_list):
|
||||||
msg = "Incorrect q-vector. This implementation assumes that" + \
|
msg = "Incorrect q-vector. This implementation assumes that" + \
|
||||||
" all last q values are prime numbers.\n" + \
|
" all last q values are prime numbers.\n" + \
|
||||||
str(self._last_q_list)
|
str(self._patt_q_list)
|
||||||
raise ValueError(msg)
|
raise ValueError(msg)
|
||||||
|
|
||||||
self.q_order = LCM_list(self._last_q_list)
|
self.q_order = LCM_list(self._patt_q_list)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def last_k_list(self):
|
def patt_k_list(self):
|
||||||
return self._last_k_list
|
return self._patt_k_list
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def last_q_list(self):
|
def patt_q_list(self):
|
||||||
return self._last_q_list
|
return self._patt_q_list
|
||||||
|
|
||||||
|
# q_order is LCM of all q values for pattern knots
|
||||||
@property
|
@property
|
||||||
def q_order(self):
|
def q_order(self):
|
||||||
return self._q_order
|
return self._q_order
|
||||||
@ -259,35 +323,58 @@ class TorusCable(object):
|
|||||||
|
|
||||||
|
|
||||||
def __add__(self, other):
|
def __add__(self, other):
|
||||||
|
s_formula = self.knot_formula
|
||||||
|
o_formula = other.knot_formula
|
||||||
|
k_vector = self.k_vector
|
||||||
|
|
||||||
if self.k_vector != other.k_vector:
|
if self.k_vector != other.k_vector:
|
||||||
msg = "k_vectors are different. k-vector preserving addition is " +\
|
msg = "k_vectors are different. k-vector preserving addition is " +\
|
||||||
"impossible."
|
"impossible."
|
||||||
warnings.warn(msg)
|
warnings.warn(msg)
|
||||||
shift = len(self.k_vector)
|
shift = len(self.k_vector)
|
||||||
formula = re.sub(r'\d+', lambda x: str(int(x.group()) + shift),
|
o_formula = re.sub(r'\d+', lambda x: str(int(x.group()) + shift),
|
||||||
other.knot_formula)
|
o_formula)
|
||||||
self.k_vector = self.k_vector + other.k_vector
|
k_vector += other.k_vector
|
||||||
other.k_vector = self.k_vector
|
|
||||||
else:
|
knot_formula = s_formula[:-1] + ",\n" + o_formula[1:]
|
||||||
knot_formula = self.knot_formula[:-1] + ",\n" + \
|
cable = CableSum(knot_formula, k_vector=k_vector)
|
||||||
other.knot_formula[1:]
|
s_sig = self.signature_as_function_of_theta
|
||||||
cable = TorusCable(knot_formula, k_vector=self.k_vector)
|
o_sig = other.signature_as_function_of_theta
|
||||||
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):
|
def signature_as_function_of_theta(*thetas, **kwargs):
|
||||||
result = s_signature_as_function_of_theta(*thetas[shift:]) + \
|
thetas = cable.parse_thetas(*thetas)
|
||||||
o_signature_as_function_of_theta(*thetas[0:shift])
|
result = s_sig(*thetas[shift:]) + o_sig(*thetas[0:shift])
|
||||||
return result
|
return result
|
||||||
|
|
||||||
cable._signature_as_function_of_theta = signature_as_function_of_theta
|
cable._signature_as_function_of_theta = signature_as_function_of_theta
|
||||||
return cable
|
return cable
|
||||||
|
|
||||||
|
def parse_thetas(self, *thetas):
|
||||||
|
summands_num = len(self.knot_sum)
|
||||||
|
if not thetas:
|
||||||
|
return summands_num * (0,)
|
||||||
|
if len(thetas) == 1 and summands_num > 1:
|
||||||
|
if isinstance(thetas[0], Iterable):
|
||||||
|
if len(thetas[0]) >= summands_num:
|
||||||
|
return tuple(thetas[0])
|
||||||
|
elif not thetas[0]:
|
||||||
|
return summands_num * (0,)
|
||||||
|
elif thetas[0] == 0:
|
||||||
|
return 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)
|
||||||
|
|
||||||
def get_q_vector(knot_formula, slice=True):
|
@staticmethod
|
||||||
|
def get_q_vector_alg_slice(knot_formula):
|
||||||
lowest_number = 2
|
lowest_number = 2
|
||||||
q_vector = [0] * (TorusCable.extract_max(knot_formula) + 1)
|
q_vector = [0] * (CableSum.extract_max(knot_formula) + 1)
|
||||||
P = Primes()
|
P = Primes()
|
||||||
for layer in TorusCable.get_layers_from_formula(knot_formula)[::-1]:
|
for layer in CableSum.get_layers_from_formula(knot_formula)[::-1]:
|
||||||
for el in layer:
|
for el in layer:
|
||||||
q_vector[el] = P.next(lowest_number)
|
q_vector[el] = P.next(lowest_number)
|
||||||
lowest_number = q_vector[el]
|
lowest_number = q_vector[el]
|
||||||
@ -301,13 +388,18 @@ class TorusCable(object):
|
|||||||
return max(numbers)
|
return max(numbers)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_blanchfield_for_pattern(k_n, theta):
|
def get_blanchfield_for_pattern(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:
|
if theta == 0:
|
||||||
sf = TorusCable.get_untwisted_signature_function(k_n)
|
sf = CableSum.get_untwisted_signature_function(k_n)
|
||||||
return sf.square_root() + sf.minus_square_root()
|
return sf.square_root() + sf.minus_square_root()
|
||||||
|
|
||||||
results = []
|
|
||||||
k = abs(k_n)
|
k = abs(k_n)
|
||||||
|
assert theta <= k, msg
|
||||||
|
results = []
|
||||||
|
|
||||||
ksi = 1/(2 * k + 1)
|
ksi = 1/(2 * k + 1)
|
||||||
|
|
||||||
counter = Counter()
|
counter = Counter()
|
||||||
@ -359,13 +451,15 @@ class TorusCable(object):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_untwisted_signature_function(j):
|
def get_untwisted_signature_function(j):
|
||||||
# return the signature function of the T_{2,2k+1} torus knot
|
# return the signature function of the T_{2, 2k+1} torus knot
|
||||||
k = abs(j)
|
k = abs(j)
|
||||||
q = 2 * k + 1
|
q = 2 * k + 1
|
||||||
values = ([((2 * a + 1)/(2 * q), -1 * sgn(j)) for a in range(k)] +
|
counter = Counter({(2 * a + 1)/(2 * q) : -sgn(j)
|
||||||
[((2 * a + 1)/(2 * q), 1 * sgn(j))
|
for a in range(k)})
|
||||||
for a in range(k + 1, 2 * k + 1)])
|
counter.update(Counter({(2 * a + 1)/(2 * q) : sgn(j)
|
||||||
return SignatureFunction(values=values)
|
for a in range(k + 1, q)}))
|
||||||
|
return SignatureFunction(counter=counter)
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_knot_descrption(knot_sum):
|
def get_knot_descrption(knot_sum):
|
||||||
@ -379,6 +473,7 @@ class TorusCable(object):
|
|||||||
description = description[:-2] + ") # "
|
description = description[:-2] + ") # "
|
||||||
return description[:-3]
|
return description[:-3]
|
||||||
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_layers_from_formula(knot_formula):
|
def get_layers_from_formula(knot_formula):
|
||||||
k_indices = re.sub(r'[k-]', '', knot_formula)
|
k_indices = re.sub(r'[k-]', '', knot_formula)
|
||||||
@ -394,207 +489,154 @@ class TorusCable(object):
|
|||||||
layers.append(layer)
|
layers.append(layer)
|
||||||
return layers
|
return layers
|
||||||
|
|
||||||
|
|
||||||
def get_signature_as_function_of_theta(self, **key_args):
|
def get_signature_as_function_of_theta(self, **key_args):
|
||||||
if 'verbose' in key_args:
|
if 'verbose' in key_args:
|
||||||
verbose_default = key_args['verbose']
|
verbose_default = key_args['verbose']
|
||||||
else:
|
else:
|
||||||
verbose_default = False
|
verbose_default = False
|
||||||
|
knot_desc = self.knot_description
|
||||||
|
|
||||||
def signature_as_function_of_theta(*thetas, **kwargs):
|
def signature_as_function_of_theta(*thetas, **kwargs):
|
||||||
|
# print("\n\nsignature_as_function_of_theta " + knot_desc)
|
||||||
verbose = verbose_default
|
verbose = verbose_default
|
||||||
if 'verbose' in kwargs:
|
if 'verbose' in kwargs:
|
||||||
verbose = kwargs['verbose']
|
verbose = kwargs['verbose']
|
||||||
len_a = len(self.knot_sum)
|
thetas = self.parse_thetas(*thetas)
|
||||||
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()
|
untwisted_part = SignatureFunction()
|
||||||
# for each cable knot in cable sum apply theta
|
twisted_part = SignatureFunction()
|
||||||
|
|
||||||
# print(self.knot_sum)
|
|
||||||
|
|
||||||
|
# for each cable knot (summand) in cable sum apply theta
|
||||||
for i, knot in enumerate(self.knot_sum):
|
for i, knot in enumerate(self.knot_sum):
|
||||||
try:
|
|
||||||
ssf = self.get_summand_signature_as_theta_function(*knot)
|
ssf = self.get_summand_signature_as_theta_function(*knot)
|
||||||
plus, _, up = ssf(thetas[i])
|
tp, up = ssf(thetas[i])
|
||||||
# sf += ssf(thetas[i])
|
twisted_part += tp
|
||||||
sf += plus
|
|
||||||
untwisted_part += up
|
untwisted_part += up
|
||||||
# in case wrong theata value was given
|
sf = twisted_part + untwisted_part
|
||||||
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:
|
if verbose:
|
||||||
print()
|
print()
|
||||||
print(str(thetas))
|
print(str(thetas))
|
||||||
print(sf)
|
print(sf)
|
||||||
msg = "tota signature jump = " + str(sf.total_sign_jump())
|
assert sf.total_sign_jump() == 0
|
||||||
msg += "\nfunction\n" + str(sf)
|
|
||||||
assert sf.total_sign_jump() == 0, msg
|
|
||||||
|
|
||||||
return sf
|
return sf
|
||||||
|
|
||||||
signature_as_function_of_theta.__doc__ =\
|
signature_as_function_of_theta.__doc__ =\
|
||||||
signature_as_function_of_theta_docstring
|
signature_as_function_of_theta_docstring
|
||||||
return signature_as_function_of_theta
|
return signature_as_function_of_theta
|
||||||
|
|
||||||
def get_summand_signature_as_theta_function(self, *knot_as_k_values):
|
def get_untwisted_part(self, *knot_as_k_values, theta=0):
|
||||||
def get_summand_signture_function(theta):
|
patt_k = knot_as_k_values[-1]
|
||||||
# TBD: another formula (for t^2) description
|
ksi = 1/(2 * abs(patt_k) + 1)
|
||||||
# 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 = SignatureFunction()
|
||||||
# untwisted part
|
# For each knot summand consider k values in reversed order,
|
||||||
# for each knot summand consider k values in reversed order
|
# ommit k value for pattern.
|
||||||
# ommit last k = k_n value
|
for layer_num, k in enumerate(knot_as_k_values[:-1][::-1]):
|
||||||
|
sf = CableSum.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()
|
||||||
|
untwisted_part += right_shift + left__shift
|
||||||
|
return untwisted_part
|
||||||
|
|
||||||
ksi = 1/(2 * abs(k_n) + 1)
|
def get_summand_signature_as_theta_function(self, *knot_as_k_values):
|
||||||
for i, k in enumerate(knot_as_k_values[:-1][::-1]):
|
|
||||||
power = 2^i
|
def get_summand_signture_function(theta):
|
||||||
a = TorusCable.get_untwisted_signature_function(k)
|
|
||||||
shift = theta * ksi * power
|
patt_k = knot_as_k_values[-1]
|
||||||
b = a >> shift
|
|
||||||
c = a << shift
|
# theta should not be larger than k for the pattern.
|
||||||
for _ in range(i):
|
theta %= (2 * abs(patt_k) + 1)
|
||||||
b = b.double_cover()
|
theta = min(theta, 2 * abs(patt_k) + 1 - theta)
|
||||||
c = c.double_cover()
|
|
||||||
cable_signature += b + c
|
twisted_part = self.get_blanchfield_for_pattern(patt_k, theta)
|
||||||
untwisted_part += b + c
|
untwisted_part = self.get_untwisted_part(*knot_as_k_values,
|
||||||
return cable_signature, twisted_part, untwisted_part
|
theta=theta)
|
||||||
|
return twisted_part, untwisted_part
|
||||||
get_summand_signture_function.__doc__ = \
|
get_summand_signture_function.__doc__ = \
|
||||||
get_summand_signture_function_docsting
|
get_summand_signture_function_docsting
|
||||||
|
|
||||||
return get_summand_signture_function
|
return get_summand_signture_function
|
||||||
|
|
||||||
def is_metabolizer(self, theta):
|
def is_metabolizer(self, theta):
|
||||||
i = 1
|
# Check if square alternating difference
|
||||||
sum = 0
|
# divided by last q value is integer.
|
||||||
for idx, el in enumerate(theta):
|
result = sum(el^2 / self.patt_q_list[idx] * (-1)^idx
|
||||||
to_add = i * el^2
|
for idx, el in enumerate(theta))
|
||||||
# print("i * el^2 " + str(i * el^2))
|
# for idx, el in enumerate(theta):
|
||||||
to_add /= self.last_q_list[idx]
|
# old_sum += (el^2 / self.patt_q_list[idx] * (-1)^idx)
|
||||||
sum += to_add
|
return result.is_integer()
|
||||||
# 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
|
|
||||||
|
|
||||||
|
|
||||||
def is_signature_big_in_ranges(self, ranges_list):
|
def is_signature_big_in_ranges(self, ranges_list):
|
||||||
for theta in it.product(*ranges_list):
|
|
||||||
if not any(theta):
|
for thetas in it.product(*ranges_list):
|
||||||
|
|
||||||
|
# Check only non-zero metabolizers.
|
||||||
|
if not self.is_metabolizer(thetas) or not any(thetas):
|
||||||
continue
|
continue
|
||||||
we_have_a_problem = True
|
|
||||||
if self.is_metabolizer(theta):
|
signature_is_small = True
|
||||||
|
# Check if any element generated by thetas vector
|
||||||
|
# has a large signature.
|
||||||
for shift in range(1, self.q_order):
|
for shift in range(1, self.q_order):
|
||||||
shifted_theta = [(shift * th) % self.last_q_list[i]
|
shifted_thetas = [shift * th for th in thetas]
|
||||||
for i, th in enumerate(theta)]
|
sf = self.signature_as_function_of_theta(*shifted_thetas)
|
||||||
shifted_theta = [min(th, self.last_q_list[i] - th)
|
limit = 5 + np.count_nonzero(shifted_thetas)
|
||||||
for i, th in enumerate(shifted_theta)]
|
extremum = abs(sf.extremum(limit=limit))
|
||||||
sf = self.signature_as_function_of_theta(*shifted_theta)
|
|
||||||
extremum = abs(sf.extremum())
|
|
||||||
if shift > 1:
|
if shift > 1:
|
||||||
print(shifted_theta, end=" ")
|
print(shifted_thetas, end=" ")
|
||||||
print(extremum)
|
print(extremum)
|
||||||
if extremum > 5 + np.count_nonzero(shifted_theta):
|
if extremum > limit:
|
||||||
# print("ok")
|
signature_is_small = False
|
||||||
we_have_a_problem = False
|
|
||||||
break
|
break
|
||||||
elif shift == 1:
|
elif shift == 1:
|
||||||
print("*" * 10)
|
print("*" * 10)
|
||||||
print(shifted_theta, end=" ")
|
print(shifted_thetas, end=" ")
|
||||||
print(extremum)
|
print(extremum)
|
||||||
if we_have_a_problem:
|
if signature_is_small:
|
||||||
print("\n" * 10 + "!" * 1000)
|
print("\n" * 10 + "!" * 1000)
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def is_signature_big_for_all_metabolizers(self):
|
def is_signature_big_for_all_metabolizers(self):
|
||||||
if len(self.knot_sum) == 8:
|
num_of_summands = len(self.knot_sum)
|
||||||
for shift in range(0, 8, 4):
|
if num_of_summands % 4:
|
||||||
ranges_list = 8 * [range(0, 1)]
|
f_name = self.is_signature_big_for_all_metabolizers.__name__
|
||||||
ranges_list[shift : shift + 3] = [range(0, i + 1) for i in \
|
msg = "Function {}".format(f_name) + " is implemented only for " +\
|
||||||
self.last_k_list[shift: shift + 3]]
|
"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)
|
ranges_list[shift + 3] = range(0, 2)
|
||||||
if not self.is_signature_big_in_ranges(ranges_list):
|
if not self.is_signature_big_in_ranges(ranges_list):
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
print("\n\nok")
|
print("\nOK")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
elif len(self.knot_sum) == 4:
|
|
||||||
upper_bounds = self.last_k_list[:3]
|
|
||||||
ranges_list = [range(0, i + 1) for i in upper_bounds]
|
|
||||||
ranges_list.append(range(0, 2))
|
|
||||||
if not self.is_signature_big_in_ranges(ranges_list):
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
msg = "Function implemented only for knots with 4 or 8 summands"
|
|
||||||
raise ValueError(msg)
|
|
||||||
|
|
||||||
|
|
||||||
def mod_one(n):
|
def mod_one(n):
|
||||||
return n - floor(n)
|
return n - floor(n)
|
||||||
|
|
||||||
|
|
||||||
TorusCable.get_knot_descrption.__doc__ = \
|
# CableSum.get_knot_descrption.__doc__ = \
|
||||||
"""
|
# """
|
||||||
Arguments:
|
# Arguments:
|
||||||
arbitrary number of lists of numbers, each list encodes a single cable.
|
# arbitrary number of lists of numbers, each list encodes a single cable.
|
||||||
Examples:
|
# Examples:
|
||||||
sage: get_knot_descrption([1, 3], [2], [-1, -2], [-3])
|
# 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)'
|
# 'T(2, 3; 2, 7) # T(2, 5) # -T(2, 3; 2, 5) # -T(2, 7)'
|
||||||
"""
|
# """
|
||||||
|
|
||||||
TorusCable.get_signature_as_function_of_theta.__doc__ = \
|
CableSum.get_signature_as_function_of_theta.__doc__ = \
|
||||||
"""
|
"""
|
||||||
Function intended to construct signature function for a connected
|
Function intended to construct signature function for a connected
|
||||||
sum of multiple cables with varying theta parameter values.
|
sum of multiple cables with varying theta parameter values.
|
||||||
@ -653,7 +695,7 @@ SignatureFunction.__doc__ = \
|
|||||||
This simple class encodes twisted and untwisted signature functions
|
This simple class encodes twisted and untwisted signature functions
|
||||||
of knots. Since the signature function is entirely encoded by its signature
|
of knots. Since the signature function is entirely encoded by its signature
|
||||||
jump, the class stores only information about signature jumps
|
jump, the class stores only information about signature jumps
|
||||||
in a dictionary self.cnt_signature_jumps.
|
in a dictionary self.jumps_counter.
|
||||||
The dictionary stores data of the signature jump as a key/values pair,
|
The dictionary stores data of the signature jump as a key/values pair,
|
||||||
where the key is the argument at which the functions jumps
|
where the key is the argument at which the functions jumps
|
||||||
and value encodes the value of the jump. Remember that we treat
|
and value encodes the value of the jump. Remember that we treat
|
||||||
@ -698,7 +740,7 @@ mod_one.__doc__ = \
|
|||||||
1/4
|
1/4
|
||||||
"""
|
"""
|
||||||
|
|
||||||
TorusCable.get_blanchfield_for_pattern.__doc__ = \
|
CableSum.get_blanchfield_for_pattern.__doc__ = \
|
||||||
"""
|
"""
|
||||||
Arguments:
|
Arguments:
|
||||||
k_n: a number s.t. q_n = 2 * k_n + 1, where
|
k_n: a number s.t. q_n = 2 * k_n + 1, where
|
||||||
@ -712,7 +754,7 @@ TorusCable.get_blanchfield_for_pattern.__doc__ = \
|
|||||||
(https://arxiv.org/pdf/1809.08791.pdf)
|
(https://arxiv.org/pdf/1809.08791.pdf)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
TorusCable.get_summand_signature_as_theta_function.__doc__ = \
|
CableSum.get_summand_signature_as_theta_function.__doc__ = \
|
||||||
"""
|
"""
|
||||||
Argument:
|
Argument:
|
||||||
n integers that encode a single cable, i.e.
|
n integers that encode a single cable, i.e.
|
||||||
|
46
main.sage
46
main.sage
@ -14,7 +14,7 @@ import numpy as np
|
|||||||
|
|
||||||
|
|
||||||
attach("cable_signature.sage")
|
attach("cable_signature.sage")
|
||||||
attach("my_signature.sage")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -47,6 +47,8 @@ class Config(object):
|
|||||||
self.verbose = True
|
self.verbose = True
|
||||||
# self.verbose = False
|
# self.verbose = False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def main(arg=None):
|
def main(arg=None):
|
||||||
try:
|
try:
|
||||||
limit = int(arg[1])
|
limit = int(arg[1])
|
||||||
@ -63,8 +65,6 @@ def main(arg=None):
|
|||||||
# q_vector = (3, 5, 7, 13)
|
# q_vector = (3, 5, 7, 13)
|
||||||
# q_vector = (3, 5, 7, 11)
|
# q_vector = (3, 5, 7, 11)
|
||||||
|
|
||||||
# q_vector = (5, 13, 19, 41,\
|
|
||||||
# 5, 17, 23, 43)
|
|
||||||
|
|
||||||
formula_1 = "[[k[0], k[5], k[3]], " + \
|
formula_1 = "[[k[0], k[5], k[3]], " + \
|
||||||
"[-k[1], -k[3]], " + \
|
"[-k[1], -k[3]], " + \
|
||||||
@ -74,11 +74,45 @@ def main(arg=None):
|
|||||||
"[-k[5], -k[7]], " + \
|
"[-k[5], -k[7]], " + \
|
||||||
"[k[6], k[7]], " + \
|
"[k[6], k[7]], " + \
|
||||||
"[-k[4], -k[6], -k[7]]]"
|
"[-k[4], -k[6], -k[7]]]"
|
||||||
q_vector = TorusCable.get_q_vector(formula_1[:-1] + ", " + formula_2[1:])
|
q_vector = (5, 13, 19, 41,\
|
||||||
cab_1 = TorusCable(knot_formula=formula_1, q_vector=q_vector)
|
5, 17, 23, 43)
|
||||||
cab_2 = TorusCable(knot_formula=formula_2, q_vector=q_vector)
|
q_vector = (3, 7, 13, 19,\
|
||||||
|
5, 11, 17, 23)
|
||||||
|
|
||||||
|
cab_1 = CableSum(knot_formula=formula_1, q_vector=q_vector)
|
||||||
|
cab_2 = CableSum(knot_formula=formula_2, q_vector=q_vector)
|
||||||
cable = cab_1 + cab_2
|
cable = cab_1 + cab_2
|
||||||
|
|
||||||
|
|
||||||
|
sf = cab_1.signature_as_function_of_theta(thetas=None)
|
||||||
|
# sf.tikz_plot("hoho.tex")
|
||||||
|
|
||||||
|
# cab_1.is_signature_big_for_all_metabolizers()
|
||||||
|
sf = cab_1.signature_as_function_of_theta()
|
||||||
|
|
||||||
|
sf = cable.signature_as_function_of_theta()
|
||||||
|
|
||||||
|
sf = cable.signature_as_function_of_theta(4,4,4,4,0,0,0,0)
|
||||||
|
writer = SignatureWriter(sf)
|
||||||
|
writer.plot(title="hoho")
|
||||||
|
|
||||||
|
cable.is_signature_big_for_all_metabolizers()
|
||||||
|
|
||||||
|
|
||||||
|
q_vector = CableSum.get_q_vector_alg_slice(formula_1[:-1] + ", " + formula_2[1:])
|
||||||
|
cab_1 = CableSum(knot_formula=formula_1, q_vector=q_vector)
|
||||||
|
cab_2 = CableSum(knot_formula=formula_2, q_vector=q_vector)
|
||||||
|
cable = cab_1 + cab_2
|
||||||
|
cable.is_signature_big_for_all_metabolizers()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
global config
|
global config
|
||||||
config = Config()
|
config = Config()
|
||||||
|
Loading…
Reference in New Issue
Block a user