SignaturePlote, import instead of attach, jupyter friendly plots

This commit is contained in:
Maria Marchwicka 2020-11-09 08:15:43 +01:00
parent 4d549c2dcb
commit ee58512534
2 changed files with 118 additions and 106 deletions

View File

@ -1,16 +1,31 @@
#!/usr/bin/env sage -python #!/usr/bin/env sage -python
import numpy as np import numpy as np
import itertools as it import itertools as it
import matplotlib.pyplot as plt
import warnings import warnings
import re import re
import inspect
from typing import Iterable from typing import Iterable
from collections import Counter from collections import Counter
from sage.arith.functions import LCM_list from sage.arith.functions import LCM_list
import importlib
def import_sage(module_name):
importlib.invalidate_caches()
sage_name = module_name + ".sage"
python_name = module_name + ".sage.py"
if os.path.isfile(sage_name):
os.system('sage --preparse {}'.format(sage_name));
os.system('mv {} {}.py'.format(python_name, module_name))
if module_name in sys.modules:
return importlib.reload(sys.modules[module_name])
return importlib.import_module(module_name, package=None)
sig = import_sage('signature')
# #############################################################################
# 9.11 (9.8) # 9.11 (9.8)
# 9.15 (9.9) # 9.15 (9.9)
PLOTS_DIR = "plots" PLOTS_DIR = "plots"
@ -18,8 +33,8 @@ PLOTS_DIR = "plots"
class CableSummand(): class CableSummand():
def __init__(self, knot_as_k_values): def __init__(self, knot_as_k_values):
self.knot_as_k_values = 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.knot_description = self.get_summand_descrption(knot_as_k_values)
self.signature_as_function_of_theta = \ self.signature_as_function_of_theta = \
self.get_summand_signature_as_theta_function() self.get_summand_signature_as_theta_function()
@ -90,14 +105,14 @@ class CableSummand():
results.append((e * ksi, -1 * sgn(k_n))) results.append((e * ksi, -1 * sgn(k_n)))
results.append((1 - e * ksi, 1 * sgn(k_n))) results.append((1 - e * ksi, 1 * sgn(k_n)))
return SignatureFunction(values=results) return sig.SignatureFunction(values=results)
@classmethod @classmethod
def get_satellite_part(cls, *knot_as_k_values, theta=0): def get_satellite_part(cls, *knot_as_k_values, theta=0):
patt_k = knot_as_k_values[-1] patt_k = knot_as_k_values[-1]
ksi = 1/(2 * abs(patt_k) + 1) ksi = 1/(2 * abs(patt_k) + 1)
satellite_part = SignatureFunction() satellite_part = sig.SignatureFunction()
# 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 k value for pattern.
for layer_num, k in enumerate(knot_as_k_values[:-1][::-1]): for layer_num, k in enumerate(knot_as_k_values[:-1][::-1]):
@ -120,7 +135,7 @@ class CableSummand():
for a in range(k)}) for a in range(k)})
counter.update(Counter({(2 * a + 1)/(2 * q) : sgn(j) counter.update(Counter({(2 * a + 1)/(2 * q) : sgn(j)
for a in range(k + 1, q)})) for a in range(k + 1, q)}))
return SignatureFunction(counter=counter) return sig.SignatureFunction(counter=counter)
def get_summand_signature_as_theta_function(self): def get_summand_signature_as_theta_function(self):
knot_as_k_values = self.knot_as_k_values knot_as_k_values = self.knot_as_k_values
@ -168,7 +183,8 @@ class CableSummand():
if save_path is not None: if save_path is not None:
file_name = self.get_file_name_for_summand_plot(theta) file_name = self.get_file_name_for_summand_plot(theta)
save_path = os.path.join(save_path, file_name) save_path = os.path.join(save_path, file_name)
pp.plot_sum_with_other(sp, title=title, save_path=save_path) sig.SignaturePloter.plot_sum_of_two(pp, sp, title=title,
save_path=save_path)
def plot_summand(self): def plot_summand(self):
range_limit = min(self.knot_as_k_values[-1] + 1, 3) range_limit = min(self.knot_as_k_values[-1] + 1, 3)
@ -178,10 +194,12 @@ class CableSummand():
class CableSum(): class CableSum():
def __init__(self, knot_sum): def __init__(self, knot_sum):
self.knot_sum_as_k_valus = knot_sum self.knot_sum_as_k_valus = knot_sum
self.knot_description = self.get_knot_descrption(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_k_list = [abs(i[-1]) for i in knot_sum]
self.patt_q_list = [2 * i + 1 for i in self.patt_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.patt_q_list): if any(n not in Primes() for n in self.patt_q_list):
msg = "Incorrect k- or q-vector. This implementation assumes that"\ msg = "Incorrect k- or q-vector. This implementation assumes that"\
+ " all last q values are prime numbers.\n" + \ + " all last q values are prime numbers.\n" + \
@ -234,7 +252,8 @@ class CableSum():
file_name = re.sub(r', ', '_', str(thetas)) file_name = re.sub(r', ', '_', str(thetas))
file_name = re.sub(r'[\[\]]', '', str(file_name)) file_name = re.sub(r'[\[\]]', '', str(file_name))
file_path = os.path.join(save_path, file_name) file_path = os.path.join(save_path, file_name)
pp.plot_sum_with_other(sp, title=title, save_path=file_path) sig.SignaturePloter.plot_sum_of_two(pp, sp, title=title,
save_path=file_path)
if save_path is not None: if save_path is not None:
@ -249,11 +268,12 @@ class CableSum():
sf_list.append(sf_list[-1]) sf_list.append(sf_list[-1])
# sf_list.append(sf_list[-1]) # sf_list.append(sf_list[-1])
SignatureFunction.plot_many(*sf_list) sig.SignaturePloter.plot_many(*sf_list)
# pp, sp, sf = knot.signature_as_function_of_theta(thetas[i]) # pp, sp, sf = knot.signature_as_function_of_theta(thetas[i])
# (pp + sp) = sp.plot # (pp + sp) = sp.plot
# #
# pp.plot_sum_with_other(sp, title=title, save_path=file_path) # sig.SignatureFunction.plot_sum_of_two(pp, sp, title=title,
# save_path=file_path)
@ -311,8 +331,8 @@ class CableSum():
verbose = kwargs['verbose'] verbose = kwargs['verbose']
thetas = self.parse_thetas(*thetas) thetas = self.parse_thetas(*thetas)
satellite_part = SignatureFunction() satellite_part = sig.SignatureFunction()
pattern_part = SignatureFunction() pattern_part = sig.SignatureFunction()
# for each cable knot (summand) in cable sum apply theta # for each cable knot (summand) in cable sum apply theta
for i, knot in enumerate(self.knot_summands): for i, knot in enumerate(self.knot_summands):
@ -357,7 +377,7 @@ class CableSum():
shifted_thetas = [shift * th for th in thetas] shifted_thetas = [shift * th for th in thetas]
pp, sp, sf= self.signature_as_function_of_theta(*shifted_thetas) pp, sp, sf= self.signature_as_function_of_theta(*shifted_thetas)
limit = 5 + np.count_nonzero(shifted_thetas) limit = 5 + np.count_nonzero(shifted_thetas)
extremum = abs(sf.extremum(limit=limit)) extremum = abs(sf.extremum(limit=limit)[1])
if shift > 1: if shift > 1:
print(shifted_thetas, end=" ") print(shifted_thetas, end=" ")
print(extremum) print(extremum)
@ -509,7 +529,7 @@ CableSum.get_signature_as_function_of_theta.__doc__ = \
k - parameters for a cable: parameters k_i (i=1,.., n-1) for satelit knots k - parameters for a cable: parameters k_i (i=1,.., n-1) for satelit knots
T(2, 2k_i + 1) and - the last one - k_n for a pattern knot T(2, 2k_n + 1). T(2, 2k_i + 1) and - the last one - k_n for a pattern knot T(2, 2k_n + 1).
Returns a function that will take theta vector as an argument and return Returns a function that will take theta vector as an argument and return
an object SignatureFunction. an object sig.SignatureFunction.
To calculate signature function for a cable sum and a theta values vector, To calculate signature function for a cable sum and a theta values vector,
use as below. use as below.
@ -555,7 +575,7 @@ CableSum.get_signature_as_function_of_theta.__doc__ = \
get_summand_signture_function_docsting = \ get_summand_signture_function_docsting = \
""" """
This function returns SignatureFunction for previously defined single This function returns sig.SignatureFunction for previously defined single
cable T_(2, q) and a theta given as an argument. cable T_(2, q) and a theta given as an argument.
The cable was defined by calling function The cable was defined by calling function
get_summand_signature_as_theta_function(*arg) get_summand_signature_as_theta_function(*arg)
@ -570,7 +590,7 @@ signature_as_function_of_theta_docstring = \
""" """
Arguments: Arguments:
Returns object of SignatureFunction class for a previously defined Returns object of sig.SignatureFunction class for a previously defined
connected sum of len(arg) cables. connected sum of len(arg) cables.
Accept len(arg) arguments: for each cable one theta parameter. Accept len(arg) arguments: for each cable one theta parameter.
If call with no arguments, all theta parameters are set to be 0. If call with no arguments, all theta parameters are set to be 0.
@ -583,7 +603,7 @@ signature_as_function_of_theta_docstring = \
# T(2, q_n) is a pattern knot for a single cable from a cable sum # 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) # theta: twist/character for the cable (value form v vector)
# Return: # Return:
# SignatureFunction created for pattern signature function # sig.SignatureFunction created for pattern signature function
# for a given cable and theta/character # for a given cable and theta/character
# Based on: # Based on:
# Proposition 9.8. in Twisted Blanchfield Pairing # Proposition 9.8. in Twisted Blanchfield Pairing
@ -596,6 +616,6 @@ signature_as_function_of_theta_docstring = \
# n integers that encode a single cable, i.e. # n integers that encode a single cable, i.e.
# values of q_i for T(2,q_0; 2,q_1; ... 2, q_n) # values of q_i for T(2,q_0; 2,q_1; ... 2, q_n)
# Return: # Return:
# a function that returns SignatureFunction for this single cable # a function that returns sig.SignatureFunction for this single cable
# and a theta given as an argument # and a theta given as an argument
# """ # """

View File

@ -1,20 +1,31 @@
#!/usr/bin/env sage -python #!/usr/bin/env sage -python
import numpy as np
import itertools as it
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
# 9.11 (9.8) # 9.11 (9.8)
# 9.15 (9.9) # 9.15 (9.9)
class SignatureFunction(): 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()
class SignatureFunction:
def __init__(self, values=None, counter=None, plot_title=''): def __init__(self, values=None, counter=None, plot_title=''):
@ -104,35 +115,43 @@ 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 is_zero_everywhere(self):
return not any(self.jumps_counter.values()) return not any(self.jumps_counter.values())
def extremum(self, limit=math.inf):
def extremum(self, limit=None): max_point = (0, 0)
max = 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): if abs(current) > abs(max_point[1]):
max = current max_point = (arg, current)
if limit is not None: if abs(current) > limit:
if abs(max) > limit:
break break
return max return max_point
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())])
@staticmethod def plot(self, *args, **kargs):
def plot_many(*sf_list, save_path=None, title='',): SignaturePloter.plot(self, *args, **kargs)
class SignaturePloter:
@classmethod
def plot_many(cls, *sf_list, save_path=None, title='',):
axes_num = len(sf_list) axes_num = len(sf_list)
if axes_num > 36: if axes_num > 36:
sf_list = sf_list[36] sf_list = sf_list[36]
axes_num = 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 # print war, set val in conf
rows = ceil(sqrt(axes_num)) rows = ceil(sqrt(axes_num))
cols = ceil(axes_num/rows) cols = ceil(axes_num/rows)
@ -146,55 +165,21 @@ class SignatureFunction():
ax=axes_matrix[row][col], ax=axes_matrix[row][col],
title=sf.plot_title) title=sf.plot_title)
fig.suptitle(title)
plt.tight_layout() plt.tight_layout()
save_path = save_path or os.path.join(os.getcwd(),"tmp.png")
save_path = Path(save_path).with_suffix('.png')
plt.savefig(save_path) cls.show_and_save(save_path)
plt.close()
image = Image.open(save_path)
image.show()
return @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[1][0],
color='red',
linestyle='dotted')
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()
save_path = save_path or os.path.join(os.getcwd(),"tmp.png")
save_path = Path(save_path).with_suffix('.png')
plt.savefig(save_path)
plt.close()
image = Image.open(save_path)
image.show()
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]) ax=axes_matrix[0][1])
up.plot(subplot=True, sf2.plot(subplot=True,
ax=axes_matrix[1][0], ax=axes_matrix[1][0],
color='red', color='red',
linestyle='dotted') linestyle='dotted')
@ -203,11 +188,11 @@ class SignatureFunction():
ax=axes_matrix[0][0], ax=axes_matrix[0][0],
color='black') color='black')
tp.plot(subplot=True, sf1.plot(subplot=True,
ax=axes_matrix[1][1], ax=axes_matrix[1][1],
alpha=0.3) alpha=0.3)
up.plot(subplot=True, sf2.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')
@ -218,22 +203,12 @@ class SignatureFunction():
alpha=0.7,) alpha=0.7,)
fig.suptitle(title) fig.suptitle(title)
plt.tight_layout() plt.tight_layout()
save_path = save_path or os.path.join(os.getcwd(),"tmp.png") cls.show_and_save(save_path)
save_path = Path(save_path)
save_path = save_path.with_suffix('.png')
@classmethod
# print(save_as) def plot(cls, sf, subplot=False, ax=None, save_path=None,
plt.savefig(save_path)
plt.close()
image = Image.open(save_path)
image.show()
def plot(self, subplot=False, ax=None, save_as='sf',
title="", title="",
alpha=1, alpha=1,
color='blue', color='blue',
@ -243,8 +218,8 @@ class SignatureFunction():
if ax is None: if ax is None:
fig, ax = plt.subplots(1, 1) fig, ax = plt.subplots(1, 1)
keys = sorted(self.jumps_counter.keys()) keys = sorted(sf.jumps_counter.keys())
y = [self(k) + self.jumps_counter[k] for k in keys] y = [sf(k) + sf.jumps_counter[k] for k in keys]
xmax = keys[1:] xmax = keys[1:]
xmin = keys[:-1] xmin = keys[:-1]
@ -255,20 +230,37 @@ class SignatureFunction():
if subplot: if subplot:
return ax return ax
save_as += ".png" cls.show_and_save(save_path)
plt.savefig(save_as)
plt.close()
image = Image.open(save_as)
image.show()
def step_function_data(self): @staticmethod
def show_and_save(save_path):
if save_path is not None:
save_path = Path(save_path)
save_path = save_path.with_suffix('.png')
plt.savefig(save_path)
if ipython_info == JUPYTER:
plt.show()
elif True: # save_path is None:
plt.savefig('tmp.png')
plt.close()
image = Image.open('tmp.png')
image.show()
# msg = "For interactive shell set save_path."
# warnings.warn(msg)
@staticmethod
def step_function_data(sf):
# Transform the signature jump data to a format understandable # Transform the signature jump data to a format understandable
# by the plot function. # by the plot function.
result = [(k, self.sf(k) + self.jumps_counter[k]) result = [(k, sf.sf(k) + sf.jumps_counter[k])
for k in sorted(self.jumps_counter.keys())] for k in sorted(sf.jumps_counter.keys())]
return result return result
def tikz_plot(self, save_as): @staticmethod
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")