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
import numpy as np
import itertools as it
import matplotlib.pyplot as plt
import warnings
import re
import inspect
from typing import Iterable
from collections import Counter
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.15 (9.9)
PLOTS_DIR = "plots"
@ -18,8 +33,8 @@ PLOTS_DIR = "plots"
class CableSummand():
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.signature_as_function_of_theta = \
self.get_summand_signature_as_theta_function()
@ -90,14 +105,14 @@ class CableSummand():
results.append((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
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()
satellite_part = sig.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]):
@ -120,7 +135,7 @@ class CableSummand():
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)
return sig.SignatureFunction(counter=counter)
def get_summand_signature_as_theta_function(self):
knot_as_k_values = self.knot_as_k_values
@ -168,7 +183,8 @@ class CableSummand():
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)
sig.SignaturePloter.plot_sum_of_two(pp, sp, title=title,
save_path=save_path)
def plot_summand(self):
range_limit = min(self.knot_as_k_values[-1] + 1, 3)
@ -178,10 +194,12 @@ class CableSummand():
class CableSum():
def __init__(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 k- or q-vector. This implementation assumes that"\
+ " 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(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:
@ -249,11 +268,12 @@ class CableSum():
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) = 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']
thetas = self.parse_thetas(*thetas)
satellite_part = SignatureFunction()
pattern_part = SignatureFunction()
satellite_part = sig.SignatureFunction()
pattern_part = sig.SignatureFunction()
# for each cable knot (summand) in cable sum apply theta
for i, knot in enumerate(self.knot_summands):
@ -357,7 +377,7 @@ class CableSum():
shifted_thetas = [shift * th for th in thetas]
pp, sp, sf= self.signature_as_function_of_theta(*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:
print(shifted_thetas, end=" ")
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
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.
an object sig.SignatureFunction.
To calculate signature function for a cable sum and a theta values vector,
use as below.
@ -555,7 +575,7 @@ CableSum.get_signature_as_function_of_theta.__doc__ = \
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.
The cable was defined by calling function
get_summand_signature_as_theta_function(*arg)
@ -570,7 +590,7 @@ signature_as_function_of_theta_docstring = \
"""
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.
Accept len(arg) arguments: for each cable one theta parameter.
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
# theta: twist/character for the cable (value form v vector)
# Return:
# SignatureFunction created for pattern signature function
# sig.SignatureFunction created for pattern signature function
# for a given cable and theta/character
# Based on:
# 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.
# 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
# a function that returns sig.SignatureFunction for this single cable
# and a theta given as an argument
# """

View File

@ -1,20 +1,31 @@
#!/usr/bin/env sage -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
from PIL import Image
from pathlib import Path
import warnings
# 9.11 (9.8)
# 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=''):
@ -104,35 +115,43 @@ class SignatureFunction():
counter = Counter({mod_one(2 * k) : v for k, v in items if k >= 1/2})
return SignatureFunction(counter=counter)
def is_zero_everywhere(self):
return not any(self.jumps_counter.values())
def extremum(self, limit=None):
max = 0
def extremum(self, limit=math.inf):
max_point = (0, 0)
current = 0
items = sorted(self.jumps_counter.items())
for arg, jump in items:
current += 2 * jump
assert current == self(arg) + jump
if abs(current) > abs(max):
max = current
if limit is not None:
if abs(max) > limit:
if abs(current) > abs(max_point[1]):
max_point = (arg, current)
if abs(current) > limit:
break
return max
return max_point
def total_sign_jump(self):
# Total signature jump is the sum of all jumps.
return sum([j[1] for j in sorted(self.jumps_counter.items())])
@staticmethod
def plot_many(*sf_list, save_path=None, title='',):
def plot(self, *args, **kargs):
SignaturePloter.plot(self, *args, **kargs)
class SignaturePloter:
@classmethod
def plot_many(cls, *sf_list, save_path=None, title='',):
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
rows = ceil(sqrt(axes_num))
cols = ceil(axes_num/rows)
@ -146,55 +165,21 @@ class SignatureFunction():
ax=axes_matrix[row][col],
title=sf.plot_title)
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()
cls.show_and_save(save_path)
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,
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])
up.plot(subplot=True,
sf2.plot(subplot=True,
ax=axes_matrix[1][0],
color='red',
linestyle='dotted')
@ -203,11 +188,11 @@ class SignatureFunction():
ax=axes_matrix[0][0],
color='black')
tp.plot(subplot=True,
sf1.plot(subplot=True,
ax=axes_matrix[1][1],
alpha=0.3)
up.plot(subplot=True,
sf2.plot(subplot=True,
ax=axes_matrix[1][1],
color='red', alpha=0.3,
linestyle='dotted')
@ -218,22 +203,12 @@ class SignatureFunction():
alpha=0.7,)
fig.suptitle(title)
plt.tight_layout()
save_path = save_path or 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)
# print(save_as)
plt.savefig(save_path)
plt.close()
image = Image.open(save_path)
image.show()
def plot(self, subplot=False, ax=None, save_as='sf',
@classmethod
def plot(cls, sf, subplot=False, ax=None, save_path=None,
title="",
alpha=1,
color='blue',
@ -243,8 +218,8 @@ class SignatureFunction():
if ax is None:
fig, ax = plt.subplots(1, 1)
keys = sorted(self.jumps_counter.keys())
y = [self(k) + self.jumps_counter[k] for k in keys]
keys = sorted(sf.jumps_counter.keys())
y = [sf(k) + sf.jumps_counter[k] for k in keys]
xmax = keys[1:]
xmin = keys[:-1]
@ -255,20 +230,37 @@ class SignatureFunction():
if subplot:
return ax
save_as += ".png"
plt.savefig(save_as)
plt.close()
image = Image.open(save_as)
image.show()
cls.show_and_save(save_path)
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
# by the plot function.
result = [(k, self.sf(k) + self.jumps_counter[k])
for k in sorted(self.jumps_counter.keys())]
result = [(k, sf.sf(k) + sf.jumps_counter[k])
for k in sorted(sf.jumps_counter.keys())]
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.show()
plt_sin.save("MyPic.pdf")