diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 374b070..bae5e71 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -20,8 +20,8 @@ jobs: - name: Run pycodestyle shell: bash -l {0} # We currently only check for some warnings. We should enable & fix more of them. - run: pycodestyle --select=E111,E221,E222,E225,E227,E228,E302,E306,E401,E701,E702,E703,E704,W391,W605,E711,E713,E721 spherogram_src/ - run: pycodestyle --select=E111,E221,E222,E225,E227,E228,E306,E401,E701,E702,E703,E704,W391,W605,E711,E713,E721 dev/ + run: pycodestyle --select=E111,E221,E222,E225,E227,E228,E302,E305,E306,E401,E701,E702,E703,E704,W391,W605,E711,E713,E721 spherogram_src/ + run: pycodestyle --select=E111,E221,E222,E225,E227,E228,E305,E306,E401,E701,E702,E703,E704,W391,W605,E711,E713,E721 dev/ - name: Run cython-lint shell: bash -l {0} run: cython-lint . diff --git a/dev/DTcodes.py b/dev/DTcodes.py index 21072fb..f6af208 100644 --- a/dev/DTcodes.py +++ b/dev/DTcodes.py @@ -65,14 +65,18 @@ def partition_list(L, parts): # equivalent to interchanging the other pair. So in practice we # only interchange the North-South pair. + South, East, North, West = 0, 1, 2, 3 + class FlippingError(Exception): pass + class EmbeddingError(Exception): pass + class DTvertex(tuple): """ A vertex of the 4-valent graph which is described by a DT code. diff --git a/dev/dev_malik/fast_jones_patch.py b/dev/dev_malik/fast_jones_patch.py index 1a1f78b..3279904 100644 --- a/dev/dev_malik/fast_jones_patch.py +++ b/dev/dev_malik/fast_jones_patch.py @@ -118,7 +118,7 @@ def all_labels(strand_vars): return strand_labels -def combine_strands(v1,v2, common_label): +def combine_strands(v1, v2, common_label): labels1 = var_to_strand_labels(v1) labels2 = var_to_strand_labels(v2) l1 = labels1[1 - labels1.index(common_label)] @@ -126,6 +126,7 @@ def combine_strands(v1,v2, common_label): l1, l2 = sorted([l1,l2]) return sympy.Symbol('P'+str(l1)+'c'+str(l2)) + def remove_squares(monomial): A, B = sympy.symbols('A,B') strand_vars = monomial.free_symbols - set([A,B]) @@ -133,6 +134,7 @@ def remove_squares(monomial): monomial = monomial.subs(v*v,-A**2-(1/A)**2) return monomial + def remove_loops(monomial): A, B = sympy.symbols('A,B') strand_vars = monomial.free_symbols - set([A,B]) @@ -144,11 +146,13 @@ def remove_loops(monomial): def var_to_strand_labels(v): - return map(int,str(v)[1:].split('c')) + return map(int, str(v)[1:].split('c')) + def join_strands(v1, v2): pass + """ def jones_ring(n): names_dict = {(i,j):'P'+str(i)+'c'+str(j) for i in range(2*n) for j in range(i,2*n)} diff --git a/dev/dev_malik/mutation/tangle_patch.py b/dev/dev_malik/mutation/tangle_patch.py index e7a4b39..3828fcc 100644 --- a/dev/dev_malik/mutation/tangle_patch.py +++ b/dev/dev_malik/mutation/tangle_patch.py @@ -6,40 +6,44 @@ def rotate_list(L, s): n = len(L) - return [ L[(i + s) % n] for i in range(n) ] + return [L[(i + s) % n] for i in range(n)] + def flip(L): - half = len(L)/2 + half = len(L) // 2 for i in range(half): L[i],L[i+half] = L[i+half],L[i] + def clear_orientations(crossings): for c in crossings: c.directions.clear() c.sign = 0 -""" -Rotate a tangle in a circular fashion. -""" -def circular_rotate(self,n): + +def circular_rotate(self, n): + """ + Rotate a tangle in a circular fashion. + """ tangle_copy = self.copy() adj = tangle_copy.adjacent #reverse second half - adj[len(adj)/2:] = reversed(adj[len(adj)/2:]) - rotated_adj = rotate_list(adj,n) + adj[len(adj)//2:] = reversed(adj[len(adj)//2:]) + rotated_adj = rotate_list(adj, n) #undo reversal of second half - rotated_adj[len(rotated_adj)/2:] = reversed(rotated_adj[len(rotated_adj)/2:]) + rotated_adj[len(rotated_adj)//2:] = reversed(rotated_adj[len(rotated_adj)//2:]) tangle_copy.adjacent = rotated_adj return tangle_copy -""" -Randomly chooses position on boundary of the tangle and splits into a new -crossing. -""" + def add_random_crossing(self,label): + """ + Randomly chooses position on boundary of the tangle and splits into a new + crossing. + """ tangle_copy = self.copy() adj = tangle_copy.adjacent - adj[len(adj)/2:] = reversed(adj[len(adj)/2:]) + adj[len(adj)//2:] = reversed(adj[len(adj)//2:]) new_crossing = spherogram.Crossing(label) old_position = randint(0,len(adj)-1) old_crossing, old_strand = adj.pop(old_position) @@ -47,11 +51,12 @@ def add_random_crossing(self,label): old_crossing[old_strand] = new_crossing[new_strand] for i in range(1,4): adj.insert(old_position,(new_crossing,(new_strand-i) % 4)) - adj[len(adj)/2:] = reversed(adj[len(adj)/2:]) + adj[len(adj)//2:] = reversed(adj[len(adj)//2:]) tangle_copy.crossings.append(new_crossing) tangle_copy.n = self.n+1 return tangle_copy + """ Repeatedly splits crossings, starting with a single crossing, resulting in a random 4-valent tree of crossings. @@ -63,6 +68,7 @@ def random_tree(size): T = T.add_random_crossing(i) return T + """ Generate two random tree tangles and glues together. Gives a link of size 2*size @@ -88,6 +94,7 @@ def random_tree_knot(size, simplify=None, prime_decomp=False): cant_deconnect = (len(ds) > 1) return knot + """ Glue two tangles together. There are many ways to do this. Choice is given by the choice if integer n @@ -97,6 +104,7 @@ def circular_sum(self,other,n): raise Exception("Tangles do not have the same number of strands") return (self*(other.circular_rotate(n))).denominator_closure() + """ All possible tangle sums as above """ @@ -294,6 +302,8 @@ def _is_injection(pairs): if (pair1[1] == pair2[1]) and (pair1[0] != pair2[0]): return False return True + + """ Give a list of the crossing strands encountered starting at a strand on the boundary of a tangle and moving to the other @@ -309,6 +319,7 @@ def cross_strand(self, i): strand.append(cs) return strand + """ Get the closed loop starting at crossing strand cs """ @@ -321,6 +332,7 @@ def loop_strand(cs): cs = cs[0].adjacent[(cs[1] + 2) % 4] return strand + """ Returns all the strands but without duplicate in the opposite direction, starting at position 0 and going clockwise, and then components that @@ -405,6 +417,8 @@ def all_cross_strands(self): Tangle.min_isosig = min_isosig Tangle.isosig_with_gluings = isosig_with_gluings Tangle.min_isosig_with_gluings = min_isosig_with_gluings + + """ Uses networkx's cycle basis function on dual graph and converts to form with spherogram objects """ @@ -441,6 +455,7 @@ def all_four_cycles_at_vertex(G, start_vertex): return four_cycles + def unknot_search(num_attempts, backtrack_height, num_mutations): c = spherogram.Crossing(0) c[0] = c[1] @@ -458,7 +473,7 @@ def unknot_search(num_attempts, backtrack_height, num_mutations): return None -#Returns first nontrivial 4-cycle found +# Returns first nontrivial 4-cycle found def get_four_cycle(G, start_vertex): adjacent = G.children(start_vertex) for v in adjacent: @@ -479,6 +494,7 @@ def get_four_cycle(G, start_vertex): return four_cycle return [] + def all_four_cycles(G): four_cycles = [x for v in G.vertices for x in all_four_cycles_at_vertex(G,v)] four_cycles_no_duplicates = [] @@ -492,11 +508,12 @@ def all_four_cycles(G): four_cycles_no_duplicates.append(fc) return four_cycles_no_duplicates + """ Converts from list of vertices of dual graph to list of edges. If multiple edges, just chooses one. """ -def edge_cycle(vert_list,G): +def edge_cycle(vert_list, G): edges = list(G.edges) cycle = [] for i in range(len(vert_list)-1): @@ -512,6 +529,7 @@ def edge_cycle(vert_list,G): break return cycle + """ Returns the crossings within distance r of the crossing, in the form of a dictionary, where the values are the distances to the center crossing, @@ -536,8 +554,8 @@ def crossing_ball(crossing,radius): return distances, map(lambda x: x.opposite(), opposite_positions) -def boundary_components(link,crossing,radius): - crossings, adjacent = crossing_ball(crossing,radius) +def boundary_components(link, crossing, radius): + crossings, adjacent = crossing_ball(crossing, radius) crossings = list(crossings) G = underlying_graph(link) for c in crossings: @@ -549,17 +567,19 @@ def boundary_comp_dist(samples,size,radius,edge_conn=2): dist = [] for i in range(samples): print(i) - K = map_to_link(random_map(size,edge_conn)) + K = map_to_link(random_map(size, edge_conn)) c = choice(K.crossings) - dist.append(num_boundary_components(K,c,radius)) + dist.append(num_boundary_components(K, c, radius)) return Counter(dist) + def underlying_graph(link): G = nx.Graph() edges = [(c,adj) for c in link.crossings for adj in map(lambda x: x[0],c.adjacent)] G.add_edges_from(edges) return G + def trace_boundary_component(start_cs,full_boundary): boundary_comp = [start_cs] cs = start_cs.next_corner() @@ -645,13 +665,15 @@ def tangle_neighborhood(link,crossing,radius,return_gluings=True,hull=False): else: return Tangle(n,crossings,adjacent),Tangle(n,outside_crossings,opposites) + def mutate(link,four_cycle): link_copy = link.copy() four_cycle_copy = [corresponding_edge(link_copy,edge) for edge in four_cycle] T1,T2 = tangle_cut(link_copy,four_cycle_copy) - #print(len(T1.crossings),len(T2.crossings)) + # print(len(T1.crossings),len(T2.crossings)) return T1.circular_sum(T2,2) + def mutate_reflect(link,four_cycle,other_reflection=False): link_copy = link.copy() four_cycle_copy = [corresponding_edge(link_copy,edge) for edge in four_cycle] @@ -667,6 +689,7 @@ def mutate_reflect(link,four_cycle,other_reflection=False): new_link._rebuild() return new_link + def tangle_reflect(T): for c in T.crossings: c1adj = c.adjacent[1][:] @@ -679,10 +702,12 @@ def tangle_reflect(T): # print(T.adjacent) return T + def all_rotation_mutants(link): G = link.dual_graph() return [mutate(link,four_cycle) for four_cycle in all_four_cycles(G)] + def all_mutants(link): G = link.dual_graph() fcs = all_four_cycles(G) @@ -712,6 +737,7 @@ def corresponding_edge(new_link,edge): if str(new_edge) == str(edge): return new_edge + """ Creates two Tangle objects from a given cycle (with no self intersections) in the dual graph of a link (inside and outside). Cycle is given @@ -865,16 +891,17 @@ def fill_in_crossings(link,sides): Returns a set of all the crossings encountered along the way, including ones on the boundary, and which side (0 or 1) is hit """ -def meander(cs,sides): +def meander(cs, sides): crossings_encountered = [cs.crossing] end_side = 0 while True: - cs = cs.opposite().rotate(randint(1,3)) - if cslabel(cs) in sides: # hit the side + cs = cs.opposite().rotate(randint(1, 3)) + if cslabel(cs) in sides: # hit the side end_side = sides[cslabel(cs)] break crossings_encountered.append(cs.crossing) - return set(crossings_encountered),end_side + return set(crossings_encountered), end_side + """ Label of crossing strand, without frills @@ -882,6 +909,7 @@ def meander(cs,sides): def cslabel(cs): return (cs[0].label,cs[1]) + """ Find crossing strand object from it's name in the format of cslabel above """ @@ -977,8 +1005,9 @@ def all_neighborhoods(link,radius): nhds.append(T1) return nhds -def neighborhood_distribution(link,radius): - nhds = all_neighborhoods(link,radius) + +def neighborhood_distribution(link, radius): + nhds = all_neighborhoods(link, radius) nhd_classes = [] for nhd in nhds: already_found = False @@ -991,8 +1020,11 @@ def neighborhood_distribution(link,radius): nhd_classes.append([nhd,1]) return nhd_classes + from collections import Counter -def isosig_dist(num_samples,size,radius,edge_conn=2): + + +def isosig_dist(num_samples, size, radius, edge_conn=2): nhds = [] for i in range(num_samples): print(i) @@ -1058,7 +1090,7 @@ def neighborhood_distribution_different_links(num_samples,size,radius): already_found = True break if not already_found: - nhd_classes.append([nhd,1]) + nhd_classes.append([nhd, 1]) return nhd_classes @@ -1073,6 +1105,7 @@ def all_neighborhood_volumes(link,radius): vols.append((double,v)) return vols + def all_nhd_vol_dists(link,radius,tolerance): nhds = [] for c in link.crossings: @@ -1106,6 +1139,7 @@ def close_float_sets(L1, L2, tolerance): return False return True + K = map_to_link(random_map(100,2)) Kc = K.copy() c = Kc.crossings[0] diff --git a/dev/dev_malik/thompson/homomorphisms.py b/dev/dev_malik/thompson/homomorphisms.py index 6dce689..fb39e18 100644 --- a/dev/dev_malik/thompson/homomorphisms.py +++ b/dev/dev_malik/thompson/homomorphisms.py @@ -50,11 +50,15 @@ def word_image(word, image_a, image_b): images.append(image) return ''.join(images) + def inverse(word): return word[::-1].swapcase() + def link(word): - return link_diagram(ABWord(word,a,b).evaluate().planar_graph()) + return link_diagram(ABWord(word, a, b).evaluate().planar_graph()) + + """ f = open('knotted_words.txt','r') split_lines = [line.split() for line in f] diff --git a/dev/dev_malik/thompson/thompson.py b/dev/dev_malik/thompson/thompson.py index 6ff6e76..80e2bae 100644 --- a/dev/dev_malik/thompson/thompson.py +++ b/dev/dev_malik/thompson/thompson.py @@ -416,13 +416,16 @@ def random_word(complexity): word = ''.join([word, new_lets[randint(0, 2)]]) return word + a = TreeMap( TreeSequence([DyadicRational(3,2)]) , TreeSequence([DyadicRational(1,2)]) ) b = TreeMap( TreeSequence([DyadicRational(7,3)]) , TreeSequence([DyadicRational(5,3)]) ) A = a.inverse() B = b.inverse() + def random_sequence(length, bound): - return [randint(0,bound) for i in range(length)] + return [randint(0, bound) for i in range(length)] + num_gens = 100 @@ -433,6 +436,7 @@ def random_sequence(length, bound): x = A*x*a x_invs = [i.inverse() for i in xs] + def exponents_to_tree_map(exps_domain, exps_range): result = a*A inv = a*A diff --git a/dev/hard_links.py b/dev/hard_links.py index 25f2403..0ee2a8e 100644 --- a/dev/hard_links.py +++ b/dev/hard_links.py @@ -14,6 +14,7 @@ def test(link): expected = len(link.link_components) + L.unlinked_unknot_components print(L, L.unlinked_unknot_components, '\n') + C = spherogram.Link([(9, 17, 10, 16), (5, 18, 6, 19), (17, 4, 18, 5), (2, 15, 3, 16), (14, 8, 15, 7), (6, 14, 7, 13), (1, 13, 2, 12), (28, 22, 29, 21), (19, 11, 20, 10), (8, 4, 9, 3), (24, 27, 25, 0), (26, 23, 27, 24), (22, 25, 23, 26), (11, 21, 12, 20), (29, 0, 28, 1)]) test(C) diff --git a/dev/tangle_patch.py b/dev/tangle_patch.py index a5b99b9..64741b4 100644 --- a/dev/tangle_patch.py +++ b/dev/tangle_patch.py @@ -1,7 +1,7 @@ import spherogram from spherogram.links.tangles import Tangle, OneTangle, MinusOneTangle import networkx as nx -from random import randint,choice,sample +from random import randint, choice, sample from spherogram.links.random_links import map_to_link, random_map """ @@ -11,12 +11,14 @@ def rotate_list(L, s): n = len(L) - return [ L[(i + s) % n] for i in range(n) ] + return [L[(i + s) % n] for i in range(n)] + def flip(L): - half = len(L)/2 + half = len(L) // 2 for i in range(half): - L[i],L[i+half] = L[i+half],L[i] + L[i], L[i + half] = L[i + half], L[i] + def clear_orientations(crossings): for c in crossings: @@ -111,21 +113,23 @@ def min_isosig_with_gluings(self, gluings, root=None): rotated_root = crossing_strand_from_name(rotated_tangle,cs_name) else: rotated_root = None - #permuting the indices in the gluings + # permuting the indices in the gluings perm = range(len(self.adjacent)) - perm[len(perm)/2:] = reversed(perm[len(perm)/2:]) - perm = rotate_list(perm,i) - perm[len(perm)/2:] = reversed(perm[len(perm)/2:]) + perm[len(perm)//2:] = reversed(perm[len(perm)//2:]) + perm = rotate_list(perm, i) + perm[len(perm)//2:] = reversed(perm[len(perm)//2:]) rotated_gluings = [] for g in gluings: - new_g = [perm[g[0]],perm[g[1]]] + new_g = [perm[g[0]], perm[g[1]]] new_g.sort() rotated_gluings.append(tuple(new_g)) rotated_gluings.sort() - isosigs.append(rotated_tangle.isosig_with_gluings(rotated_gluings,root=rotated_root)) + isosigs.append(rotated_tangle.isosig_with_gluings(rotated_gluings, + root=rotated_root)) return min(isosigs) + Tangle.all_circular_sums = all_circular_sums Tangle.add_random_crossing = add_random_crossing @@ -318,7 +322,7 @@ def tangle_neighborhood(link,crossing,radius,return_gluings=True,hull=False): outside_crossings = [c for c in link.crossings if c not in crossings] if len(outside_crossings) == 0: raise Exception("Neighborhood is entire link") - n = len(adjacent)/2 + n = len(adjacent) // 2 if hull: comps = list(boundary_components(link,crossing,radius)) @@ -500,10 +504,12 @@ def tangle_cut(link, cycle): crossing_sides = fill_in_crossings(link,sides) n = len(cycle) - side0[n/2:] = reversed(side0[n/2:]) #flip to use as adjacent in tangle - side1[n/2:] = reversed(side1[n/2:]) - crossings0 = [crossing_from_name(link,c) for c in crossing_sides if crossing_sides[c] == 0] - crossings1 = [crossing_from_name(link,c) for c in crossing_sides if crossing_sides[c] == 1] + side0[n//2:] = reversed(side0[n//2:]) # flip to use as adjacent in tangle + side1[n//2:] = reversed(side1[n//2:]) + crossings0 = [crossing_from_name(link, c) for c in crossing_sides + if crossing_sides[c] == 0] + crossings1 = [crossing_from_name(link, c) for c in crossing_sides + if crossing_sides[c] == 1] # clear crossing info clear_orientations(crossings0) @@ -532,7 +538,8 @@ def tangle_cut(link, cycle): else: # print('flipped side 1') flip(side1) - return Tangle(n/2,crossings0,side0),Tangle(n/2,crossings1,side1) + return Tangle(n//2, crossings0, side0), Tangle(n//2, crossings1, side1) + def fill_in_crossings(link, sides): """ diff --git a/setup.py b/setup.py index afc23ff..7cc0989 100644 --- a/setup.py +++ b/setup.py @@ -161,6 +161,7 @@ def run(self): '--upgrade-strategy', 'only-if-needed', new_wheel]) + # The planarmap extension pmap_dir = 'planarmap_src/'