From bb402e849cff585fb7f565cb2927f3e8bf11b1e6 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Fri, 8 May 2026 15:35:53 -0500 Subject: [PATCH 01/53] Done with _orient_crossings, to implement _build_components Co-authored-by: Copilot --- spherogram_src/links/links_base.py | 15 +- spherogram_src/links/tangles.py | 214 ++++++++++++++++++++++++++++- spherogram_src/version.py | 2 +- 3 files changed, 222 insertions(+), 9 deletions(-) diff --git a/spherogram_src/links/links_base.py b/spherogram_src/links/links_base.py index 61c5473..b3b3a9f 100644 --- a/spherogram_src/links/links_base.py +++ b/spherogram_src/links/links_base.py @@ -124,6 +124,16 @@ def make_tail(self, a): raise ValueError("Can only orient a strand once.") self.directions.add(b) + def make_head(self, a): + """ + Orients the strand joining input "a" to input" a+2" to start at "a" and end at + "a+2". + """ + b = ((a + 2) % 4, a) + if (b[1], b[0]) in self.directions: + raise ValueError("Can only orient a strand once.") + self.directions.add(b) + def rotate(self, s): """ Rotate the incoming connections by 90*s degrees anticlockwise. @@ -515,7 +525,7 @@ def __init__(self, crossings=None, braid_closure=None, check_planarity=True, bui if not all(isinstance(c, Crossing) for c, _ in s.adjacent): raise ValueError("Strands with a component index must be in the same" " component as a crossing") - # Go through the component strands to construct component_starts + # Go through the component strands to construct component_spec if component_strands: component_spec = [] for s in component_strands: @@ -884,8 +894,7 @@ def _build_components(self, component_starts=None): # and turn them into CrossingEntryPoints component_starts = [cs.crossing.entry_points()[cs.strand_index % 2] for cs in component_starts] - remaining, components = OrderedSet( - self.crossing_entries()), LinkComponents() + remaining, components = OrderedSet(self.crossing_entries()), LinkComponents() other_crossing_entries = [] self.labels = labels = Labels() for c in self.crossings: diff --git a/spherogram_src/links/tangles.py b/spherogram_src/links/tangles.py index 5dc201d..750e166 100644 --- a/spherogram_src/links/tangles.py +++ b/spherogram_src/links/tangles.py @@ -21,7 +21,9 @@ """ import pickle -from .links import Crossing, Strand, Link +from collections import OrderedDict +from .ordered_set import OrderedSet +from .links import Crossing, Strand, Link, Labels from . import planar_isotopy @@ -76,9 +78,12 @@ def decode_boundary(boundary): raise ValueError("Number of top boundary strands cannot be negative") return (m, n) +class TangleComponents: + #TODO + pass class Tangle: - def __init__(self, boundary=2, crossings=None, entry_points=None, label=None): + def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, label=None): """ A tangle is a fragment of a Link with some number of boundary strands. Tangles can be composed in various ways along their boundary strands, @@ -106,12 +111,21 @@ def __init__(self, boundary=2, crossings=None, entry_points=None, label=None): """ m, n = decode_boundary(boundary) + component_starts = None + start_orientations = None if crossings is None: crossings = [] - for c in crossings: - if not isinstance(c, (Crossing, Strand)): - raise ValueError("Every element of crossings must be a Crossing or a Strand") + else: + if isinstance(crossings, str): + raise NotImplementedError("Not Implemented. If you are trying to create a tangle from a PD code, input the PD code as a list instead.") + + if len(crossings) > 0 and not isinstance(crossings[0], (Strand, Crossing)): + crossings, component_starts, entry_points = self._crossings_from_PD_code(crossings, entry_points) + start_orientations = component_starts[:] + + if not all(isinstance(c, (Crossing, Strand)) for c in crossings): + raise ValueError("Every element of crossings must be a Crossing or a Strand") self.crossings = crossings # the pair for the number of lower strands and the number of upper strands @@ -126,11 +140,201 @@ def __init__(self, boundary=2, crossings=None, entry_points=None, label=None): if len(entry_points) != m + n: raise ValueError("The number of boundary strands is not equal to the length" " of entry_points") + + if build: + if start_orientations is None: + # By default, orient the components so that the Tangle is upward pointing + start_orientations = [(c, (i + 2) % 4) for (c,i) in entry_points[:m]] + + self._build(start_orientations, component_starts) + for i, e in enumerate(entry_points): + # TODO: make it so that the entry points are attached to Strands join_strands((self, i), e) self.label = label + def _build(self, start_orientations=None, component_starts=None): + self._orient_crossings(start_orientations=start_orientations) + self._build_components(component_starts=component_starts) + + def _orient_crossings(self, start_orientations=None): + if self.all_crossings_oriented(): + return + if start_orientations is None: + start_orientations = list() + else: # copy as algorithm modifies this list + start_orientations = list(start_orientations) + + remaining = OrderedSet( + [(c, i) for c in self.crossings for i in range(4) if c.sign == 0]) + + while len(remaining): + if len(start_orientations) > 0: + c, i = start = start_orientations.pop() + else: + c, i = start = remaining.pop() + + reversed = False + finished = False + while not finished: + if reversed: + c.make_tail(i) + else: + c.make_head(i) + + if c.adjacent[i] is not None: + d, j = c.adjacent[i] + remaining.discard((c, i)), remaining.discard((d, j)) + c, i = d, (j + 2) % 4 + finished = (c, i) == start + else: + if reversed: + # Hit the boundary of the tangle from both sides, + # done with this component + finished = True + else: + # Hit the boundary of the tangle, + # now go back and orient reversely + reversed = True + c, i = start + c, i = c, (i + 2) % 4 + + for c in self.crossings: + c.orient() + + def _build_components(self, component_starts=None): + #TODO + pass + + def crossing_entries(self): + ans = [] + for C in self.crossings: + if isinstance(C, Crossing): + ans += C.entry_points() + return ans + + def _crossings_from_PD_code(self, code, entry_points): + """ + entry_points as INPUT: a list of labels of arcs left open in the tangle + as OUTPUT: a list of CrossingStrands + """ + labels = set() + for X in code: + for i in X: + labels.add(i) + + gluings = OrderedDict() + + for c, X in enumerate(code): + for i, x in enumerate(X): + if x in gluings: + gluings[x].append((c, i)) + else: + gluings[x] = [(c, i)] + + if any(len(v) > 2 for v in gluings.values()): + raise ValueError("PD code isn't consistent") + + component_starts = self._component_starts_from_PD( + code, labels, gluings) + + crossings = [Crossing(i) for i, d in enumerate(code)] + + for item in gluings.values(): + if len(item) > 1: + (c, i), (d, j) = item + crossings[c][i] = crossings[d][j] + + entry_points = [crossings[gluings[x][0]].crossing_strands()[gluings[x][1]] + for x in entry_points] + + component_starts = [crossings[c].crossing_strands()[i] + for (c, i) in component_starts] + + return crossings, component_starts, entry_points + + def _component_starts_from_PD(self, code, labels, gluings): + """ + A PD code determines an order and orientation on the tangle + components as follows, where we view the code as labels on the + strands at the point where two crossings are stuck together. + + 1. The minimum label on each component is used to order the + components. + + 2. Each component is oriented by finding its minimal label, + looking at the labels of its two neighbors, and then + orienting the component towards the smaller of those two. + + This is designed so that a PLink-generated PD_code results in a + link with the same component order and orientation. + """ + starts = [] + while labels: + m = min(labels) + labels.remove(m) + + if len(gluings[m]) == 1: + # entrance strand of the tangle + [(c, index)] = gluings[m] + + j = (index + 2) % 4 + next_label = code[c][j] + direction = (c, j) + + starts.append(direction) + else: + (c1, index1), (c2, index2) = gluings[m] + if c1 == c2: + # loop at strand, take next strand to be next smallest label + # on crossing + next_label = min(set(code[c1]) - {m}) + direction = (c1, code[c1].index(next_label)) + starts.append(direction) + else: + # strand connects two different crossings, take next strand to + # be next smallest label on two 'opposite' strands + j1, j2 = (index1 + 2) % 4, (index2 + 2) % 4 + l1, l2 = code[c1][j1], code[c2][j2] + if l1 < l2: + next_label = l1 + direction = (c1, j1) + elif l2 < l1: + next_label = l2 + direction = (c2, j2) + else: + # We have a component of length 2, so now rely on + # the convention that the first position at a PD + # crossing is a directed entry point. (If both + # crossings are over or both under, the + # orientation is arbitrary anyway.) + next_label = l1 + + # The strand labeled m is oriented c2 --> c1 if + # and only if either l1 = l2 is the incoming + # understrand of c2 or m is incoming understrand + # at c1. + if code[c2][0] == l1 or code[c1][0] == m: + direction = (c1, j1) + else: + direction = (c2, j2) + + starts.append(direction) + + # Component start recorded. Erase the rest of the component + # by traversing along it and remove the labels + while next_label != m: + labels.remove(next_label) + g = gluings[next_label] + if len(g) == 1: + break + other_direction = g[1 - g.index(direction)] + direction = (other_direction[0], (other_direction[1] + 2) % 4) + next_label = code[direction[0]][direction[1]] + + return starts + def __add__(self, other): """Put self to left of other and fuse the top-right strand of self to the top-left strand of other and the bottom-right strand of self to the bottom-left strand of other. diff --git a/spherogram_src/version.py b/spherogram_src/version.py index 7501145..dc4633d 100644 --- a/spherogram_src/version.py +++ b/spherogram_src/version.py @@ -1 +1 @@ -version = '2.4.1' +version = '2.4.2b' From 04222fa591eaa47ae05061cf8abab12069ff7111 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Mon, 11 May 2026 02:10:10 -0500 Subject: [PATCH 02/53] First running version with components and PD_code for Tangles More tests pending Co-authored-by: Copilot --- spherogram_src/links/links_base.py | 106 +++++++++++++-- spherogram_src/links/tangles.py | 198 +++++++++++++++++++++++++---- 2 files changed, 265 insertions(+), 39 deletions(-) diff --git a/spherogram_src/links/links_base.py b/spherogram_src/links/links_base.py index b3b3a9f..a94cda6 100644 --- a/spherogram_src/links/links_base.py +++ b/spherogram_src/links/links_base.py @@ -276,7 +276,6 @@ def oriented(self): def __repr__(self): return "" % (self.crossing, self.strand_index) - class CrossingEntryPoint(CrossingStrand): """ One of the two entry points of an oriented crossing @@ -288,13 +287,21 @@ def next(self): return CrossingEntryPoint(*c.adjacent[(e + s) % (2 * s)]) def previous(self): - s = self.crossing._adjacent_len // 2 - return CrossingEntryPoint(*self.opposite().rotate(s)) + d, j = self.opposite() + + if isinstance(d, (Crossing, Strand)): + s = d._adjacent_len // 2 + return CrossingEntryPoint(*self.opposite().rotate(s)) + else: + return CrossingEntryPoint(d, j) def other(self): - nonzero_entry_point = 1 if self.crossing.sign == -1 else 3 - other = nonzero_entry_point if self.strand_index == 0 else 0 - return CrossingEntryPoint(self.crossing, other) + if isinstance(self.crossing, Crossing): + nonzero_entry_point = 1 if self.crossing.sign == -1 else 3 + other = nonzero_entry_point if self.strand_index == 0 else 0 + return CrossingEntryPoint(self.crossing, other) + else: + return None def is_under_crossing(self): return self.strand_index == 0 @@ -304,12 +311,27 @@ def is_over_crossing(self): def component(self): ans = [self] + + reversed = False while True: - next = ans[-1].next() - if next == self: + if reversed: + d = ans[0].previous() + else: + d = ans[-1].next() + + if d == self: break else: - ans.append(next) + if reversed: + ans.insert(0, d) + else: + ans.append(d) + + if not isinstance(d.crossing, (Crossing, Strand)): + if reversed: + break + else: + reversed = True return ans @@ -318,9 +340,15 @@ def component_label(self): def label_crossing(self, comp, labels): c, e = self.crossing, self.strand_index - f = (e + 2) % 4 - c.strand_labels[e], c.strand_components[e] = labels[self], comp - c.strand_labels[f], c.strand_components[f] = labels[self.next()], comp + + if isinstance(c, Crossing): + f = (e + 2) % 4 + c.strand_labels[e], c.strand_components[e] = labels[self], comp + c.strand_labels[f], c.strand_components[f] = labels[self.next()], comp + elif isinstance(c, Strand): + c.strand_label, c.strand_component = labels[self], comp + else: + c.strand_labels[e], c.strand_components[e] = labels[self], comp def __repr__(self): return "" % (self.crossing, self.strand_index) @@ -348,6 +376,7 @@ class Strand: def __init__(self, label=None, component_idx=None): self.label = label self.adjacent = [None, None] + self._clear() self._adjacent_len = 2 self.component_idx = component_idx @@ -380,6 +409,59 @@ def format_adjacent(a): def is_loop(self): return self == self.adjacent[0][0] + def _clear(self): + self.sign, self.direction = 0, None + self._clear_strand_info() + + def _clear_strand_info(self): + self.strand_label = None + self.strand_component = None + + def make_tail(self, a): + """ + Orients the strand joining input "a" to input" a+1" to start at "a" and end at + "a+1". + """ + b = (a, (a + 1) % 2) + if self.direction: + raise ValueError("Can only orient a strand once.") + self.direction = b + + def make_head(self, a): + """ + Orients the strand joining input "a" to input" a+1" to start at "a" and end at + "a+1". + """ + b = ((a + 1) % 2, a) + if self.direction: + raise ValueError("Can only orient a strand once.") + self.direction = b + + def rotate(self, s): + """ + Rotate the incoming connections by 180*s degrees anticlockwise. + """ + def rotate(v): + return (v + s) % 2 + new_adjacent = [self.adjacent[rotate(i)] for i in range(4)] + for i, (o, j) in enumerate(new_adjacent): + if o != self: + o.adjacent[j] = (self, i) + self.adjacent[i] = (o, j) + else: + self.adjacent[i] = (self, (j - s) % 2) + + a,b = self.direction + self.direction = (rotate(a), rotate(b)) + + def orient(self): + if self.direction == (1, 0): + self.rotate(1) + + self.sign = 1 + + def entry_points(self): + return [CrossingEntryPoint(self, 0)] def enumerate_lists(lists, n=0, filter=lambda x: True): ans = [] diff --git a/spherogram_src/links/tangles.py b/spherogram_src/links/tangles.py index 750e166..975a898 100644 --- a/spherogram_src/links/tangles.py +++ b/spherogram_src/links/tangles.py @@ -23,9 +23,15 @@ from collections import OrderedDict from .ordered_set import OrderedSet -from .links import Crossing, Strand, Link, Labels +from .links_base import Crossing, Strand, Link from . import planar_isotopy +class CyclicList(list): + def __init__(self, iterable): + super().__init__(iterable) + + def __getitem__(self, i): + return super().__getitem__(i % len(self)) def join_strands(x, y): """ @@ -78,9 +84,25 @@ def decode_boundary(boundary): raise ValueError("Number of top boundary strands cannot be negative") return (m, n) -class TangleComponents: - #TODO - pass +class ArcLabels(OrderedDict): + def __init__(self, iterable = []): + super().__init__(iterable) + + self.counter = 0 + + def add(self, c, advance): + if c not in self: + self[c] = self.counter + if advance: + self.counter += 1 + else: + raise ValueError("Each CEP should only be labeled once") + +class TangleComponents(list): + def add(self, c): + component = c.component() + self.append(component) + return component class Tangle: def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, label=None): @@ -113,6 +135,8 @@ def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, m, n = decode_boundary(boundary) component_starts = None start_orientations = None + self.strand_labels = CyclicList(m * [None] + n * [None]) + self.strand_components = CyclicList(m * [None] + n * [None]) if crossings is None: crossings = [] @@ -126,10 +150,13 @@ def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, if not all(isinstance(c, (Crossing, Strand)) for c in crossings): raise ValueError("Every element of crossings must be a Crossing or a Strand") + # Note that crossings in Tangle can contain Strands self.crossings = crossings # the pair for the number of lower strands and the number of upper strands self.boundary = (m, n) + # -1 if entering Tangle, 1 if exiting Tangle, 0 if not yet oriented. + self.boundary_signs = CyclicList(m * [0] + n * [0]) # a list of (c, i) pairs for the boundary strands. Each c will reciprocally # contain (self, j) where j is the strand number. The fact this is called @@ -140,34 +167,66 @@ def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, if len(entry_points) != m + n: raise ValueError("The number of boundary strands is not equal to the length" " of entry_points") - - if build: - if start_orientations is None: - # By default, orient the components so that the Tangle is upward pointing - start_orientations = [(c, (i + 2) % 4) for (c,i) in entry_points[:m]] - - self._build(start_orientations, component_starts) for i, e in enumerate(entry_points): - # TODO: make it so that the entry points are attached to Strands + # TODO: make it so that the entry points are attached to Strands? join_strands((self, i), e) + if build: + self._build(start_orientations, component_starts, entry_points=entry_points) + assert self.is_oriented() + self.label = label - def _build(self, start_orientations=None, component_starts=None): - self._orient_crossings(start_orientations=start_orientations) + def is_upward(self): + return self.boundary_signs == CyclicList([-1] * self.boundary[0] + [1] * self.boundary[1]) + + def is_downward(self): + return self.boundary_signs == CyclicList([1] * self.boundary[0] + [-1] * self.boundary[1]) + + def is_oriented(self): + return all(s != 0 for s in self.boundary_signs) + + def _build(self, start_orientations=None, component_starts=None, entry_points = None): + self._orient_crossings(start_orientations=start_orientations, entry_points=entry_points) self._build_components(component_starts=component_starts) - def _orient_crossings(self, start_orientations=None): + def _rebuild(self, same_components_and_orientations = False): + if same_components_and_orientations: + # Hopefully we have enough of the original components left + # to figure out what this is. Otherwise, new choices will + # be made as in the default algorithm. + start_css = [] + for comp in self.components: + for cs in comp: + if cs.crossing in self.crossings: + start_css.append(cs) + break + self.components = None + for c in self.crossings: + c._clear() + if same_components_and_orientations: + self._build(start_orientations=start_css, + component_starts=start_css) + else: + self._build() + + + def all_crossings_oriented(self): + return all(c.sign != 0 for c in self.crossings) + + def _orient_crossings(self, start_orientations=None, entry_points = None): if self.all_crossings_oriented(): return if start_orientations is None: start_orientations = list() + remaining = OrderedSet( + sorted([(c, i) for c in self.crossings for i in range(c._adjacent_len) if c.sign == 0], + key = lambda x : any(x == (d, j) for d, j in entry_points[:self.boundary[0]]))) else: # copy as algorithm modifies this list start_orientations = list(start_orientations) - - remaining = OrderedSet( - [(c, i) for c in self.crossings for i in range(4) if c.sign == 0]) + remaining = OrderedSet( + [(c, i) for c in self.crossings for i in range(c._adjacent_len) if c.sign == 0]) while len(remaining): if len(start_orientations) > 0: @@ -182,11 +241,15 @@ def _orient_crossings(self, start_orientations=None): c.make_tail(i) else: c.make_head(i) + + remaining.discard((c, i)) - if c.adjacent[i] is not None: + if not c.adjacent[i][0] == self: d, j = c.adjacent[i] - remaining.discard((c, i)), remaining.discard((d, j)) - c, i = d, (j + 2) % 4 + remaining.discard((d, j)) + s = d._adjacent_len // 2 + c, i = d, (j + s) % (2 * s) + finished = (c, i) == start else: if reversed: @@ -198,20 +261,85 @@ def _orient_crossings(self, start_orientations=None): # now go back and orient reversely reversed = True c, i = start - c, i = c, (i + 2) % 4 + + s = c._adjacent_len // 2 + c, i = c, (i + s) % (2 * s) for c in self.crossings: c.orient() def _build_components(self, component_starts=None): - #TODO - pass + if component_starts is not None: + # Take all CrossingStrand and CrossingEntryPoint objects + # and turn them into CrossingEntryPoints + component_starts = [cs.crossing.entry_points()[cs.strand_index % 2] + for cs in component_starts] + remaining, components = OrderedSet(self.crossing_entries()), TangleComponents() + other_crossing_entries = [] + self.labels = labels = ArcLabels() + for c in self.crossings: + c._clear_strand_info() + + while len(remaining): + if component_starts: + d = component_starts[len(components)] + elif len(components) == 0: + d = remaining.pop() + else: # prioritize labeling crossing strands that are adjacent to already labeled ones + found, comp_index = False, 0 + while not found and comp_index < len(components): + others = other_crossing_entries[comp_index] + if others: + for j, d in enumerate(others): + if d.component_label() is None: + if labels[d.other()] % 2 == 0: + d = d.next() + found = True + break + other_crossing_entries[comp_index] = others[j:] + comp_index += 1 + + if not found: + d = remaining.pop() + + component = components.add(d) + + # Label arcs along the component + for i, c in enumerate(component): + if isinstance(c.crossing, Tangle): + assert len(component) > 2 + assert self.boundary_signs[c.strand_index] == 0 + + if i == 0: + self.boundary_signs[c.strand_index] = -1 + else: + assert i == len(component) - 1 + self.boundary_signs[c.strand_index] = 1 + + # if is a Crossing or at the end of the component, advance the label + # otherwise, don't advance the label since we will still be on the same arc + if isinstance(c.crossing, Crossing) or i == len(component) - 1: + advance = True + else: + advance = False + + labels.add(c, advance) + + others = [] + for c in component: + c.label_crossing(len(components) - 1, labels) + o = c.other() + if o is not None and o.component_label() is None: + others.append(o) + other_crossing_entries.append(others) + remaining.difference_update(component) + + self.components = components def crossing_entries(self): ans = [] for C in self.crossings: - if isinstance(C, Crossing): - ans += C.entry_points() + ans += C.entry_points() return ans def _crossings_from_PD_code(self, code, entry_points): @@ -246,14 +374,30 @@ def _crossings_from_PD_code(self, code, entry_points): (c, i), (d, j) = item crossings[c][i] = crossings[d][j] - entry_points = [crossings[gluings[x][0]].crossing_strands()[gluings[x][1]] + entry_points = [crossings[gluings[x][0][0]].crossing_strands()[gluings[x][0][1]] for x in entry_points] component_starts = [crossings[c].crossing_strands()[i] for (c, i) in component_starts] return crossings, component_starts, entry_points + + def PD_code(self, KnotTheory=False, min_strand_index = 0): + PD = [] + entry_info = [s + min_strand_index for s in self.strand_labels] + + for c in self.crossings: + if isinstance(c, Crossing): + PD.append([s + min_strand_index for s in c.strand_labels]) + + if KnotTheory: + PD = "PD" + repr(PD).replace('[', 'X[')[1:] + entry_info = "EP" + repr(entry_info) + else: + PD = [tuple(x) for x in PD] + return PD, entry_info + def _component_starts_from_PD(self, code, labels, gluings): """ A PD code determines an order and orientation on the tangle From 8a89b37d91685b5c826400c8ad87dca66362965b Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Tue, 12 May 2026 17:30:49 -0500 Subject: [PATCH 03/53] Reidemeister move I and II working Co-authored-by: Copilot --- spherogram_src/links/links_base.py | 30 ++++--- spherogram_src/links/simplify.py | 87 +++++++++++------- spherogram_src/links/tangles.py | 137 ++++++++++++++++++++++------- 3 files changed, 175 insertions(+), 79 deletions(-) diff --git a/spherogram_src/links/links_base.py b/spherogram_src/links/links_base.py index a94cda6..1f52ab9 100644 --- a/spherogram_src/links/links_base.py +++ b/spherogram_src/links/links_base.py @@ -261,7 +261,10 @@ def previous_corner(self): return self.opposite().rotate(-1) def strand_label(self): - return self.crossing.strand_labels[self.strand_index] + if isinstance(self.crossing, Strand): + return self.crossing.strand_label + else: + return self.crossing.strand_labels[self.strand_index] def oriented(self): """ @@ -282,9 +285,13 @@ class CrossingEntryPoint(CrossingStrand): """ def next(self): - c, e = self.crossing, self.strand_index - s = c._adjacent_len // 2 - return CrossingEntryPoint(*c.adjacent[(e + s) % (2 * s)]) + if isinstance(self.crossing, (Crossing, Strand)): + c, e = self.crossing, self.strand_index + s = c._adjacent_len // 2 + return CrossingEntryPoint(*c.adjacent[(e + s) % (2 * s)]) + else: + raise RuntimeError('This should not be reached') + return CrossingEntryPoint(*self.crossing.adjacent[self.strand_index]) def previous(self): d, j = self.opposite() @@ -292,7 +299,7 @@ def previous(self): if isinstance(d, (Crossing, Strand)): s = d._adjacent_len // 2 return CrossingEntryPoint(*self.opposite().rotate(s)) - else: + else: return CrossingEntryPoint(d, j) def other(self): @@ -312,9 +319,9 @@ def is_over_crossing(self): def component(self): ans = [self] - reversed = False + is_reversed = False while True: - if reversed: + if is_reversed: d = ans[0].previous() else: d = ans[-1].next() @@ -322,16 +329,16 @@ def component(self): if d == self: break else: - if reversed: + if is_reversed: ans.insert(0, d) else: ans.append(d) if not isinstance(d.crossing, (Crossing, Strand)): - if reversed: + if is_reversed: break else: - reversed = True + is_reversed = True return ans @@ -443,7 +450,7 @@ def rotate(self, s): """ def rotate(v): return (v + s) % 2 - new_adjacent = [self.adjacent[rotate(i)] for i in range(4)] + new_adjacent = [self.adjacent[rotate(i)] for i in range(2)] for i, (o, j) in enumerate(new_adjacent): if o != self: o.adjacent[j] = (self, i) @@ -461,6 +468,7 @@ def orient(self): self.sign = 1 def entry_points(self): + assert self.sign == 1 return [CrossingEntryPoint(self, 0)] def enumerate_lists(lists, n=0, filter=lambda x: True): diff --git a/spherogram_src/links/simplify.py b/spherogram_src/links/simplify.py index e6f91c5..edee721 100644 --- a/spherogram_src/links/simplify.py +++ b/spherogram_src/links/simplify.py @@ -74,7 +74,7 @@ def remove_crossings(link, eliminate): for C in eliminate: link.crossings.remove(C) new_components = [] - for component in link.link_components: + for component in link.link_components if isinstance(link, Link) else link.components: for C in eliminate: for cep in C.entry_points(): try: @@ -83,9 +83,12 @@ def remove_crossings(link, eliminate): pass if len(component): new_components.append(component) - components_removed = len(link.link_components) - len(new_components) + components_removed = len(link.link_components if isinstance(link, Link) else link.components) - len(new_components) link.unlinked_unknot_components += components_removed - link.link_components = new_components + if isinstance(link, Link): + link.link_components = new_components + else: + link.components = new_components def reidemeister_I(link, C): @@ -95,15 +98,18 @@ def reidemeister_I(link, C): Returns the pair: {crossings eliminated}, {crossings changed} """ elim, changed = set(), set() - for i in range(4): - if C.adjacent[i] == (C, (i + 1) % 4): - (A, a), (B, b) = C.adjacent[i + 2], C.adjacent[i + 3] - elim = {C} - if C != A: - A[a] = B[b] - changed = {A, B} - - remove_crossings(link, elim) + + if isinstance(C, Crossing): + for i in range(4): + if C.adjacent[i] == (C, (i + 1) % 4): + (A, a), (B, b) = C.adjacent[i + 2], C.adjacent[i + 3] + elim = {C} + if C != A: + A[a] = B[b] + changed = {A, B} + + remove_crossings(link, elim) + return elim, changed @@ -118,25 +124,28 @@ def reidemeister_I_and_II(link, A): if not eliminated: for a in range(4): (B, b), (C, c) = A.adjacent[a], A.adjacent[a + 1] - if B == C and (b - 1) % 4 == c and (a + b) % 2 == 0: - eliminated, changed = reidemeister_I(link, B) - if eliminated: - break - else: - W, w = A.adjacent[a + 2] - X, x = A.adjacent[a + 3] - Y, y = B.adjacent[b + 1] - Z, z = B.adjacent[b + 2] - eliminated = {A, B} - if W != B: - W[w] = Z[z] - changed.update({W, Z}) - if X != B: - X[x] = Y[y] - changed.update({X, Y}) - remove_crossings(link, eliminated) - break - + if all(isinstance(x, Crossing) for x in (B,C)): + if B == C and (b - 1) % 4 == c and (a + b) % 2 == 0: + eliminated, changed = reidemeister_I(link, B) + if eliminated: + break + else: + W, w = A.adjacent[a + 2] + X, x = A.adjacent[a + 3] + Y, y = B.adjacent[b + 1] + Z, z = B.adjacent[b + 2] + eliminated = {A, B} + if W != B: + W[w] = Z[z] + changed.update({W, Z}) + if X != B: + X[x] = Y[y] + changed.update({X, Y}) + remove_crossings(link, eliminated) + break + + + changed &= {x for x in changed if isinstance(x, Crossing)} return eliminated, changed @@ -177,11 +186,18 @@ def basic_simplify(link, build_components=True, to_visit=None, # Redo the strand labels (used for DT codes) if (success and build_components) or force_build_components: component_starts = [] - for component in link.link_components: + is_link = isinstance(link, Link) + + for component in link.link_components if is_link else link.components: assert len(component) > 0 if len(component) > 1: - a, b = component[:2] + if is_link or isinstance(component[0].crossing, (Strand, Crossing)): + a, b = component[:2] + else: + assert len(component) > 3 + a, b = component[1:3] else: + assert is_link a = component[0] b = a.next() if a.strand_label() % 2 == 0: @@ -820,7 +836,10 @@ def clear_orientations(link): """ Resets the orientations on the crossings of a link to default values """ - link.link_components = None + if isinstance(link, Link): + link.link_components = None + else: + link.components = None for i in link.crossings: i.sign = 0 i.directions.clear() diff --git a/spherogram_src/links/tangles.py b/spherogram_src/links/tangles.py index 975a898..710f463 100644 --- a/spherogram_src/links/tangles.py +++ b/spherogram_src/links/tangles.py @@ -23,7 +23,7 @@ from collections import OrderedDict from .ordered_set import OrderedSet -from .links_base import Crossing, Strand, Link +from .links_base import Crossing, Strand, Link, CrossingEntryPoint from . import planar_isotopy class CyclicList(list): @@ -96,7 +96,7 @@ def add(self, c, advance): if advance: self.counter += 1 else: - raise ValueError("Each CEP should only be labeled once") + raise ValueError(f"Each CEP should only be labeled once, but {c} is already labeled with {self[c]}") class TangleComponents(list): def add(self, c): @@ -131,8 +131,9 @@ def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, Usually tangles should not be created directly using this constructor since the tangle operations and various primitive tangles are sufficient to create any tangle. """ + self.label = label - m, n = decode_boundary(boundary) + m, n = decode_boundary(boundary) component_starts = None start_orientations = None self.strand_labels = CyclicList(m * [None] + n * [None]) @@ -150,11 +151,28 @@ def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, if not all(isinstance(c, (Crossing, Strand)) for c in crossings): raise ValueError("Every element of crossings must be a Crossing or a Strand") - # Note that crossings in Tangle can contain Strands - self.crossings = crossings + + self.unlinked_unknot_components = 0 + component_strands = [] + fused_strands = [] + for s in crossings: + if isinstance(s, Strand): + if s.component_idx is not None: + # defer fusing + # TODO: deal with this + component_strands.append(s) + elif s.is_loop(): + self.unlinked_unknot_components += 1 + else: + fused_strands.append(s) + s.fuse() + + for s in fused_strands: + crossings.remove(s) # the pair for the number of lower strands and the number of upper strands self.boundary = (m, n) + self.boundary_strands = CyclicList([]) # -1 if entering Tangle, 1 if exiting Tangle, 0 if not yet oriented. self.boundary_signs = CyclicList(m * [0] + n * [0]) @@ -162,21 +180,36 @@ def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, # contain (self, j) where j is the strand number. The fact this is called # 'adjacent' means that the Tangle can take part in the joining protocol # implemented in join_strands. - self.adjacent = (m + n) * [None] + self.adjacent = CyclicList((m + n) * [None]) entry_points = entry_points or [] if len(entry_points) != m + n: raise ValueError("The number of boundary strands is not equal to the length" " of entry_points") for i, e in enumerate(entry_points): - # TODO: make it so that the entry points are attached to Strands? - join_strands((self, i), e) + if isinstance(e.crossing, Strand): + self.boundary_strands.append(e.crossing) + join_strands(e, (self, i)) + else: + boundary_strand = Strand(label = f'TS({self}, {i})') + self.boundary_strands.append(boundary_strand) + join_strands(e, (boundary_strand, 0)) + join_strands((self, i), (boundary_strand, 1)) + + # Note that crossings in Tangle can contain Strands + self.crossings = crossings if build: self._build(start_orientations, component_starts, entry_points=entry_points) assert self.is_oriented() - self.label = label + def __getitem__(self, i): + return (self, i % (self.boundary[0] + self.boundary[1])) + + def __setitem__(self, i, other): + o, j = other + self.adjacent[i % (self.boundary[0] + self.boundary[1])] = other + o.adjacent[j] = (self, i) def is_upward(self): return self.boundary_signs == CyclicList([-1] * self.boundary[0] + [1] * self.boundary[1]) @@ -187,10 +220,26 @@ def is_downward(self): def is_oriented(self): return all(s != 0 for s in self.boundary_signs) + def entry_points(self): + assert self.is_oriented() + return [CrossingEntryPoint(self, i) for i in range(self.boundary[0] + self.boundary[1]) + if self.boundary_signs[i] == -1] + def _build(self, start_orientations=None, component_starts=None, entry_points = None): self._orient_crossings(start_orientations=start_orientations, entry_points=entry_points) self._build_components(component_starts=component_starts) + def _clear(self): + self.components = None + for c in self.crossings: + c._clear() + for s in self.boundary_strands: + s._clear() + + self.boundary_signs = CyclicList(self.boundary[0] * [0] + self.boundary[1] * [0]) + self.strand_labels = CyclicList(self.boundary[0] * [None] + self.boundary[1] * [None]) + self.strand_components = CyclicList(self.boundary[0] * [None] + self.boundary[1] * [None]) + def _rebuild(self, same_components_and_orientations = False): if same_components_and_orientations: # Hopefully we have enough of the original components left @@ -202,16 +251,13 @@ def _rebuild(self, same_components_and_orientations = False): if cs.crossing in self.crossings: start_css.append(cs) break - self.components = None - for c in self.crossings: - c._clear() + self._clear() if same_components_and_orientations: self._build(start_orientations=start_css, component_starts=start_css) else: self._build() - def all_crossings_oriented(self): return all(c.sign != 0 for c in self.crossings) @@ -220,13 +266,12 @@ def _orient_crossings(self, start_orientations=None, entry_points = None): return if start_orientations is None: start_orientations = list() - remaining = OrderedSet( - sorted([(c, i) for c in self.crossings for i in range(c._adjacent_len) if c.sign == 0], - key = lambda x : any(x == (d, j) for d, j in entry_points[:self.boundary[0]]))) else: # copy as algorithm modifies this list start_orientations = list(start_orientations) - remaining = OrderedSet( - [(c, i) for c in self.crossings for i in range(c._adjacent_len) if c.sign == 0]) + + remaining = OrderedSet( + [(c, i) for c in self.crossings + list(reversed(self.boundary_strands)) + for i in range(c._adjacent_len) if c.sign == 0]) while len(remaining): if len(start_orientations) > 0: @@ -234,10 +279,10 @@ def _orient_crossings(self, start_orientations=None, entry_points = None): else: c, i = start = remaining.pop() - reversed = False + is_reversed = False finished = False while not finished: - if reversed: + if is_reversed: c.make_tail(i) else: c.make_head(i) @@ -252,27 +297,35 @@ def _orient_crossings(self, start_orientations=None, entry_points = None): finished = (c, i) == start else: - if reversed: + boundary_index = c.adjacent[i][1] + assert self.boundary_signs[boundary_index] == 0 + + if is_reversed: # Hit the boundary of the tangle from both sides, # done with this component + self.boundary_signs[boundary_index] = -1 finished = True else: # Hit the boundary of the tangle, # now go back and orient reversely - reversed = True - c, i = start + self.boundary_signs[boundary_index] = 1 + is_reversed = True + c, i = start s = c._adjacent_len // 2 c, i = c, (i + s) % (2 * s) for c in self.crossings: c.orient() + for s in self.boundary_strands: + s.orient() + def _build_components(self, component_starts=None): if component_starts is not None: # Take all CrossingStrand and CrossingEntryPoint objects # and turn them into CrossingEntryPoints - component_starts = [cs.crossing.entry_points()[cs.strand_index % 2] + component_starts = [cs.crossing.entry_points()[cs.strand_index % 2 if isinstance(cs.crossing, Crossing) else 0] for cs in component_starts] remaining, components = OrderedSet(self.crossing_entries()), TangleComponents() other_crossing_entries = [] @@ -306,16 +359,6 @@ def _build_components(self, component_starts=None): # Label arcs along the component for i, c in enumerate(component): - if isinstance(c.crossing, Tangle): - assert len(component) > 2 - assert self.boundary_signs[c.strand_index] == 0 - - if i == 0: - self.boundary_signs[c.strand_index] = -1 - else: - assert i == len(component) - 1 - self.boundary_signs[c.strand_index] = 1 - # if is a Crossing or at the end of the component, advance the label # otherwise, don't advance the label since we will still be on the same arc if isinstance(c.crossing, Crossing) or i == len(component) - 1: @@ -340,6 +383,10 @@ def crossing_entries(self): ans = [] for C in self.crossings: ans += C.entry_points() + + for s in self.boundary_strands: + ans += s.entry_points() + return ans def _crossings_from_PD_code(self, code, entry_points): @@ -479,6 +526,8 @@ def _component_starts_from_PD(self, code, labels, gluings): return starts + + # TODO: make the following operations interact with orientations... def __add__(self, other): """Put self to left of other and fuse the top-right strand of self to the top-left strand of other and the bottom-right strand of self to the bottom-left strand of other. @@ -674,6 +723,26 @@ def isosig(self, root=None, over_or_under=False): copy._fuse_strands() return planar_isotopy.min_isosig(copy, root, over_or_under) + def reverse_orientation(self, component_index): + # TODO + pass + + def is_planar(self): + # TODO + pass + + def simplify(self, mode = 'basic', type_III_limit = 100): + # TODO: double check if this works + from . import simplify + if mode == 'basic': + return simplify.basic_simplify(self) + elif mode == 'level': + return simplify.simplify_via_level_type_III(self, type_III_limit) + elif mode == 'pickup': + return simplify.pickup_simplify(self) + elif mode == 'global': + return simplify.pickup_simplify(self, type_III_limit) + def is_planar_isotopic(self, other, root=None, over_or_under=False) -> bool: return self.isosig() == other.isosig() From a2683a19ceba49cced34e2ff8f522fa783258bdb Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Tue, 12 May 2026 18:25:41 -0500 Subject: [PATCH 04/53] Creation from PD_code works for tangle without crossings Co-authored-by: Copilot --- spherogram_src/links/links_base.py | 4 +- spherogram_src/links/tangles.py | 75 +++++++++++++++++++----------- 2 files changed, 50 insertions(+), 29 deletions(-) diff --git a/spherogram_src/links/links_base.py b/spherogram_src/links/links_base.py index 1f52ab9..92f81e1 100644 --- a/spherogram_src/links/links_base.py +++ b/spherogram_src/links/links_base.py @@ -430,7 +430,7 @@ def make_tail(self, a): "a+1". """ b = (a, (a + 1) % 2) - if self.direction: + if self.direction is not None and self.direction != b: raise ValueError("Can only orient a strand once.") self.direction = b @@ -440,7 +440,7 @@ def make_head(self, a): "a+1". """ b = ((a + 1) % 2, a) - if self.direction: + if self.direction is not None and self.direction != b: raise ValueError("Can only orient a strand once.") self.direction = b diff --git a/spherogram_src/links/tangles.py b/spherogram_src/links/tangles.py index 710f463..263201b 100644 --- a/spherogram_src/links/tangles.py +++ b/spherogram_src/links/tangles.py @@ -21,9 +21,9 @@ """ import pickle -from collections import OrderedDict +from collections import OrderedDict, Counter from .ordered_set import OrderedSet -from .links_base import Crossing, Strand, Link, CrossingEntryPoint +from .links_base import Crossing, Strand, Link, CrossingStrand, CrossingEntryPoint from . import planar_isotopy class CyclicList(list): @@ -142,10 +142,11 @@ def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, if crossings is None: crossings = [] else: - if isinstance(crossings, str): + if isinstance(crossings, str) or isinstance(entry_points, str): raise NotImplementedError("Not Implemented. If you are trying to create a tangle from a PD code, input the PD code as a list instead.") - if len(crossings) > 0 and not isinstance(crossings[0], (Strand, Crossing)): + if (len(crossings) > 0 and not isinstance(crossings[0], (Strand, Crossing)))\ + or (entry_points is not None and len(entry_points) > 0 and not isinstance(entry_points[0], CrossingEntryPoint)): crossings, component_starts, entry_points = self._crossings_from_PD_code(crossings, entry_points) start_orientations = component_starts[:] @@ -154,8 +155,7 @@ def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, self.unlinked_unknot_components = 0 component_strands = [] - fused_strands = [] - for s in crossings: + for s in reversed(crossings): if isinstance(s, Strand): if s.component_idx is not None: # defer fusing @@ -164,11 +164,8 @@ def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, elif s.is_loop(): self.unlinked_unknot_components += 1 else: - fused_strands.append(s) s.fuse() - - for s in fused_strands: - crossings.remove(s) + crossings.remove(s) # the pair for the number of lower strands and the number of upper strands self.boundary = (m, n) @@ -187,14 +184,14 @@ def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, " of entry_points") for i, e in enumerate(entry_points): - if isinstance(e.crossing, Strand): - self.boundary_strands.append(e.crossing) + if isinstance(e[0], Strand): + self.boundary_strands.append(e[0]) join_strands(e, (self, i)) else: - boundary_strand = Strand(label = f'TS({self}, {i})') - self.boundary_strands.append(boundary_strand) - join_strands(e, (boundary_strand, 0)) - join_strands((self, i), (boundary_strand, 1)) + this_strand = Strand(label = f'TS({self}, {i})') + self.boundary_strands.append(this_strand) + join_strands(e, (this_strand, 0)) + join_strands((self, i), (this_strand, 1)) # Note that crossings in Tangle can contain Strands self.crossings = crossings @@ -248,7 +245,7 @@ def _rebuild(self, same_components_and_orientations = False): start_css = [] for comp in self.components: for cs in comp: - if cs.crossing in self.crossings: + if cs.crossing in self.crossings + self.boundary_strands: start_css.append(cs) break self._clear() @@ -259,7 +256,7 @@ def _rebuild(self, same_components_and_orientations = False): self._build() def all_crossings_oriented(self): - return all(c.sign != 0 for c in self.crossings) + return all(c.sign != 0 for c in self.crossings + self.boundary_strands) def _orient_crossings(self, start_orientations=None, entry_points = None): if self.all_crossings_oriented(): @@ -332,6 +329,8 @@ def _build_components(self, component_starts=None): self.labels = labels = ArcLabels() for c in self.crossings: c._clear_strand_info() + for s in self.boundary_strands: + s._clear_strand_info() while len(remaining): if component_starts: @@ -384,7 +383,7 @@ def crossing_entries(self): for C in self.crossings: ans += C.entry_points() - for s in self.boundary_strands: + for s in reversed(self.boundary_strands): ans += s.entry_points() return ans @@ -394,10 +393,14 @@ def _crossings_from_PD_code(self, code, entry_points): entry_points as INPUT: a list of labels of arcs left open in the tangle as OUTPUT: a list of CrossingStrands """ + assert Counter(entry_points).most_common(1)[0][1] <= 2, "Each entry point label should appear at most twice" + labels = set() for X in code: for i in X: labels.add(i) + for x in entry_points: + labels.add(x) gluings = OrderedDict() @@ -411,8 +414,7 @@ def _crossings_from_PD_code(self, code, entry_points): if any(len(v) > 2 for v in gluings.values()): raise ValueError("PD code isn't consistent") - component_starts = self._component_starts_from_PD( - code, labels, gluings) + crossings = [Crossing(i) for i, d in enumerate(code)] @@ -421,13 +423,29 @@ def _crossings_from_PD_code(self, code, entry_points): (c, i), (d, j) = item crossings[c][i] = crossings[d][j] - entry_points = [crossings[gluings[x][0][0]].crossing_strands()[gluings[x][0][1]] - for x in entry_points] + entry_strands = [] + entry_dict = dict() + + for i, x in enumerate(entry_points): + if x in gluings: + entry_strands.append(crossings[gluings[x][0][0]].crossing_strands()[gluings[x][0][1]]) + else: + this_strand = Strand(label = f'TS({self}, {i})') + if x not in entry_dict: + entry_strands.append((this_strand, 0)) + entry_dict[x] = (this_strand, 1) + else: + entry_strands.append((this_strand, 1)) + join_strands(entry_dict[x], (this_strand, 0)) + + component_starts = self._component_starts_from_PD( + code, labels, gluings, entry_dict) - component_starts = [crossings[c].crossing_strands()[i] + component_starts = [crossings[c].crossing_strands()[i] + if not isinstance(c, Strand) else CrossingStrand(c, i) for (c, i) in component_starts] - return crossings, component_starts, entry_points + return crossings, component_starts, entry_strands def PD_code(self, KnotTheory=False, min_strand_index = 0): PD = [] @@ -445,7 +463,7 @@ def PD_code(self, KnotTheory=False, min_strand_index = 0): return PD, entry_info - def _component_starts_from_PD(self, code, labels, gluings): + def _component_starts_from_PD(self, code, labels, gluings, entry_dict): """ A PD code determines an order and orientation on the tangle components as follows, where we view the code as labels on the @@ -466,7 +484,10 @@ def _component_starts_from_PD(self, code, labels, gluings): m = min(labels) labels.remove(m) - if len(gluings[m]) == 1: + if m not in gluings: + next_label = m + starts.append(entry_dict[m]) + elif len(gluings[m]) == 1: # entrance strand of the tangle [(c, index)] = gluings[m] From c19ee1c34f6c478d09bdcdd42308a79fb772b750 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Mon, 25 May 2026 18:47:34 -0500 Subject: [PATCH 05/53] Basic operations of Tangles working _rebuild() bug fixed simplify() bug fixed. All three Reidemeister moves work for Tangles but not the pickup moves TODO: 1. decide whether to fix describe() or replace it entirely with PD_code() 2. fix isosig() for Tangles 3. implement rot_num() --- spherogram_src/links/links_base.py | 17 +- spherogram_src/links/simplify.py | 22 +-- spherogram_src/links/tangles.py | 303 ++++++++++++++++++++++------- 3 files changed, 261 insertions(+), 81 deletions(-) diff --git a/spherogram_src/links/links_base.py b/spherogram_src/links/links_base.py index 92f81e1..2f3d506 100644 --- a/spherogram_src/links/links_base.py +++ b/spherogram_src/links/links_base.py @@ -414,7 +414,7 @@ def format_adjacent(a): (self.label, [format_adjacent(a) for a in self.adjacent])) def is_loop(self): - return self == self.adjacent[0][0] + return self.adjacent[0] is not None and self == self.adjacent[0][0] def _clear(self): self.sign, self.direction = 0, None @@ -464,6 +464,7 @@ def rotate(v): def orient(self): if self.direction == (1, 0): self.rotate(1) + self.direction = (0,1) self.sign = 1 @@ -877,7 +878,8 @@ def _rebuild(self, same_components_and_orientations=False): for comp in self.link_components: for cs in comp: if cs.crossing in self.crossings: - start_css.append(cs) + s = cs.crossing._adjacent_len // 2 + start_css.append(cs.rotate(s)) break self.link_components = None for c in self.crossings: @@ -1039,6 +1041,17 @@ def _build_components(self, component_starts=None): self.link_components = components + @property + def components(self): + """ + Synonym for link_components + """ + return self.link_components + + @components.setter + def components(self, value): + self.link_components = value + def digraph(self): """ The underlying directed graph for the link diagram. diff --git a/spherogram_src/links/simplify.py b/spherogram_src/links/simplify.py index edee721..bb2c122 100644 --- a/spherogram_src/links/simplify.py +++ b/spherogram_src/links/simplify.py @@ -74,7 +74,7 @@ def remove_crossings(link, eliminate): for C in eliminate: link.crossings.remove(C) new_components = [] - for component in link.link_components if isinstance(link, Link) else link.components: + for component in link.components: for C in eliminate: for cep in C.entry_points(): try: @@ -83,12 +83,10 @@ def remove_crossings(link, eliminate): pass if len(component): new_components.append(component) - components_removed = len(link.link_components if isinstance(link, Link) else link.components) - len(new_components) + components_removed = len(link.components) - len(new_components) link.unlinked_unknot_components += components_removed - if isinstance(link, Link): - link.link_components = new_components - else: - link.components = new_components + + link.components = new_components def reidemeister_I(link, C): @@ -186,18 +184,16 @@ def basic_simplify(link, build_components=True, to_visit=None, # Redo the strand labels (used for DT codes) if (success and build_components) or force_build_components: component_starts = [] - is_link = isinstance(link, Link) - for component in link.link_components if is_link else link.components: + for component in link.components: assert len(component) > 0 if len(component) > 1: - if is_link or isinstance(component[0].crossing, (Strand, Crossing)): + if isinstance(component[0].crossing, (Strand, Crossing)): a, b = component[:2] else: assert len(component) > 3 a, b = component[1:3] else: - assert is_link a = component[0] b = a.next() if a.strand_label() % 2 == 0: @@ -836,10 +832,8 @@ def clear_orientations(link): """ Resets the orientations on the crossings of a link to default values """ - if isinstance(link, Link): - link.link_components = None - else: - link.components = None + link.components = None + for i in link.crossings: i.sign = 0 i.directions.clear() diff --git a/spherogram_src/links/tangles.py b/spherogram_src/links/tangles.py index 263201b..c31c88c 100644 --- a/spherogram_src/links/tangles.py +++ b/spherogram_src/links/tangles.py @@ -31,6 +31,8 @@ def __init__(self, iterable): super().__init__(iterable) def __getitem__(self, i): + if isinstance(i, slice): + return super().__getitem__(i) return super().__getitem__(i % len(self)) def join_strands(x, y): @@ -131,7 +133,10 @@ def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, Usually tangles should not be created directly using this constructor since the tangle operations and various primitive tangles are sufficient to create any tangle. """ - self.label = label + if label is None: + self.label = id(self) + else: + self.label = label m, n = decode_boundary(boundary) component_starts = None @@ -146,27 +151,10 @@ def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, raise NotImplementedError("Not Implemented. If you are trying to create a tangle from a PD code, input the PD code as a list instead.") if (len(crossings) > 0 and not isinstance(crossings[0], (Strand, Crossing)))\ - or (entry_points is not None and len(entry_points) > 0 and not isinstance(entry_points[0], CrossingEntryPoint)): + or (entry_points is not None and len(entry_points) > 0 and not isinstance(entry_points[0], (CrossingStrand, list, tuple))): crossings, component_starts, entry_points = self._crossings_from_PD_code(crossings, entry_points) start_orientations = component_starts[:] - if not all(isinstance(c, (Crossing, Strand)) for c in crossings): - raise ValueError("Every element of crossings must be a Crossing or a Strand") - - self.unlinked_unknot_components = 0 - component_strands = [] - for s in reversed(crossings): - if isinstance(s, Strand): - if s.component_idx is not None: - # defer fusing - # TODO: deal with this - component_strands.append(s) - elif s.is_loop(): - self.unlinked_unknot_components += 1 - else: - s.fuse() - crossings.remove(s) - # the pair for the number of lower strands and the number of upper strands self.boundary = (m, n) self.boundary_strands = CyclicList([]) @@ -184,21 +172,54 @@ def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, " of entry_points") for i, e in enumerate(entry_points): - if isinstance(e[0], Strand): - self.boundary_strands.append(e[0]) - join_strands(e, (self, i)) - else: - this_strand = Strand(label = f'TS({self}, {i})') - self.boundary_strands.append(this_strand) - join_strands(e, (this_strand, 0)) - join_strands((self, i), (this_strand, 1)) + this_strand = Strand(label = f'TSE({self}, {i})') + self.boundary_strands.append(this_strand) + join_strands(e, (this_strand, 1)) + join_strands((self, i), (this_strand, 0)) + + if not all(isinstance(c, (Crossing, Strand)) for c in crossings): + raise ValueError("Every element of crossings must be a Crossing or a Strand") - # Note that crossings in Tangle can contain Strands + self.unlinked_unknot_components = 0 + component_strands = [] + for s in reversed(crossings): + if isinstance(s, Strand): + if s.component_idx is not None: + # defer fusing + component_strands.append(s) + elif s.is_loop(): + self.unlinked_unknot_components += 1 + else: + s.fuse() + crossings.remove(s) + + # Note that crossings in Tangle can contain Strands with comp_idx for now self.crossings = crossings if build: - self._build(start_orientations, component_starts, entry_points=entry_points) - assert self.is_oriented() + self._build(start_orientations, component_starts) + assert self.is_oriented(), 'Tangle is not oriented after build' + + for s in component_strands: + comp_id = s.component_idx + comp = self.components[s.strand_component] + + if isinstance(comp[0].crossing, Tangle): + for cep in reversed(comp): + if cep.crossing == s: + comp.remove(cep) + break + else: + raise RuntimeError(f"Component strand {s} not found in component {comp}") + + # Note that the components are always built following the orientation + # hence below always insists that the comp_id is labeled on the entrance strand + if comp[1].component_idx is not None: + assert comp[1].component_idx == comp_id + else: + comp[1].component_idx = comp_id + + self.crossings.remove(s) def __getitem__(self, i): return (self, i % (self.boundary[0] + self.boundary[1])) @@ -216,14 +237,32 @@ def is_downward(self): def is_oriented(self): return all(s != 0 for s in self.boundary_signs) + + def make_upward(self): + if self.is_upward(): + return + + assert self.is_oriented(), 'Tangle should be oriented to tell if it is upward' + + to_reverse = set() + for i in range(self.boundary[0]): + if self.boundary_signs[i] == 1: + to_reverse.add(self.boundary_strands[i].strand_component) + + self.reverse_orientation(to_reverse) def entry_points(self): - assert self.is_oriented() + assert self.is_oriented(), 'Tangle should be oriented to tell the entry points' return [CrossingEntryPoint(self, i) for i in range(self.boundary[0] + self.boundary[1]) if self.boundary_signs[i] == -1] + + def update_label(self, label): + self.label = label + for i, s in enumerate(self.boundary_strands): + s.label = f'TSE({self}, {i})' - def _build(self, start_orientations=None, component_starts=None, entry_points = None): - self._orient_crossings(start_orientations=start_orientations, entry_points=entry_points) + def _build(self, start_orientations=None, component_starts=None): + self._orient_crossings(start_orientations=start_orientations) self._build_components(component_starts=component_starts) def _clear(self): @@ -246,7 +285,8 @@ def _rebuild(self, same_components_and_orientations = False): for comp in self.components: for cs in comp: if cs.crossing in self.crossings + self.boundary_strands: - start_css.append(cs) + s = cs.crossing._adjacent_len // 2 + start_css.append(cs.rotate(s)) break self._clear() if same_components_and_orientations: @@ -258,7 +298,7 @@ def _rebuild(self, same_components_and_orientations = False): def all_crossings_oriented(self): return all(c.sign != 0 for c in self.crossings + self.boundary_strands) - def _orient_crossings(self, start_orientations=None, entry_points = None): + def _orient_crossings(self, start_orientations=None): if self.all_crossings_oriented(): return if start_orientations is None: @@ -275,7 +315,7 @@ def _orient_crossings(self, start_orientations=None, entry_points = None): c, i = start = start_orientations.pop() else: c, i = start = remaining.pop() - + is_reversed = False finished = False while not finished: @@ -283,7 +323,6 @@ def _orient_crossings(self, start_orientations=None, entry_points = None): c.make_tail(i) else: c.make_head(i) - remaining.discard((c, i)) if not c.adjacent[i][0] == self: @@ -295,7 +334,7 @@ def _orient_crossings(self, start_orientations=None, entry_points = None): finished = (c, i) == start else: boundary_index = c.adjacent[i][1] - assert self.boundary_signs[boundary_index] == 0 + assert self.boundary_signs[boundary_index] == 0, f'Boundary {boundary_index} is unexpectedly signed, something is wrong with the gluings' if is_reversed: # Hit the boundary of the tangle from both sides, @@ -358,6 +397,14 @@ def _build_components(self, component_starts=None): # Label arcs along the component for i, c in enumerate(component): + if isinstance(c.crossing, Tangle) and self.boundary_signs[c.strand_index] == 0: + assert len(component) > 2 + + if i == 0: + self.boundary_signs[c.strand_index] = -1 + else: + assert i == len(component) - 1 + self.boundary_signs[c.strand_index] = 1 # if is a Crossing or at the end of the component, advance the label # otherwise, don't advance the label since we will still be on the same arc if isinstance(c.crossing, Crossing) or i == len(component) - 1: @@ -430,7 +477,7 @@ def _crossings_from_PD_code(self, code, entry_points): if x in gluings: entry_strands.append(crossings[gluings[x][0][0]].crossing_strands()[gluings[x][0][1]]) else: - this_strand = Strand(label = f'TS({self}, {i})') + this_strand = Strand(label = f'PDSE({self}, {i})') if x not in entry_dict: entry_strands.append((this_strand, 0)) entry_dict[x] = (this_strand, 1) @@ -463,6 +510,10 @@ def PD_code(self, KnotTheory=False, min_strand_index = 0): return PD, entry_info + def rot_num(self): + #TODO + pass + def _component_starts_from_PD(self, code, labels, gluings, entry_dict): """ A PD code determines an order and orientation on the tangle @@ -548,7 +599,8 @@ def _component_starts_from_PD(self, code, labels, gluings, entry_dict): return starts - # TODO: make the following operations interact with orientations... + # The following operators always clear the current orientations on both tangles + # and recreate an orientation with default behaviour. def __add__(self, other): """Put self to left of other and fuse the top-right strand of self to the top-left strand of other and the bottom-right strand of self to the bottom-left strand of other. @@ -564,7 +616,15 @@ def __add__(self, other): join_strands(a[mA - 1], b[0]) join_strands(a[mA + nA - 1], b[mB]) entry_points = a[:mA - 1] + b[1:mB] + a[mA:mA + nA - 1] + b[mB + 1:] - return Tangle((mA + mB - 2, nA + nB - 2), A.crossings + B.crossings, entry_points) + + crossings = A.crossings + A.boundary_strands + B.crossings + B.boundary_strands + + for c in crossings: + c._clear() + + return Tangle((mA + mB - 2, nA + nB - 2), + crossings, + entry_points) def __mul__(self, other): """Join with self *above* other, as with braid multiplication. @@ -583,7 +643,15 @@ def __mul__(self, other): a, b = A.adjacent, B.adjacent for i in range(mA): join_strands(a[i], b[mB + i]) - return Tangle((mB, nA), A.crossings + B.crossings, b[:mB] + a[mA:]) + + crossings = A.crossings + A.boundary_strands + B.crossings + B.boundary_strands + + for c in crossings: + c._clear() + + return Tangle((mB, nA), + crossings, + b[:mB] + a[mA:]) def __neg__(self): """Mirror image of self. @@ -594,6 +662,7 @@ def __neg__(self): for c in T.crossings: if not isinstance(c, Strand): c.rotate_by_90() + c.orient() return T def __or__(self, other): @@ -606,7 +675,11 @@ def __or__(self, other): (mA, nA), (mB, nB) = A.boundary, B.boundary a, b = A.adjacent, B.adjacent entry_points = a[:mA] + b[:mB] + a[mA:] + b[mB:] - return Tangle((mA + mB, nA + nB), A.crossings + B.crossings, entry_points) + crossings = A.crossings + A.boundary_strands + B.crossings + B.boundary_strands + + return Tangle((mA + mB, nA + nB), + crossings, + entry_points) def copy(self): return pickle.loads(pickle.dumps(self)) @@ -620,9 +693,14 @@ def rotate(self, s): anticlockwise = [0, 1, 3, 2] rotate = dict(zip(anticlockwise, rotate_list(anticlockwise, s))) T = self.copy() + T.adjacent = [T.adjacent[rotate[i]] for i in range(4)] for i, (o, j) in enumerate(T.adjacent): o.adjacent[j] = (T, i) + + T.boundary_strands = [T.boundary_strands[rotate[i]] for i in range(4)] + T._rebuild(True) + return T def invert(self): @@ -651,7 +729,12 @@ def numerator_closure(self): join_strands(T.adjacent[i], T.adjacent[i + 1]) for i in range(0, n, 2): join_strands(T.adjacent[m + i], T.adjacent[m + i + 1]) - return Link(T.crossings, check_planarity=False) + + crossings = T.crossings + T.boundary_strands + for c in crossings: + c._clear() + + return Link(crossings, check_planarity=False) def denominator_closure(self): """The braid closure, where corresponding strands between the top and bottom @@ -673,14 +756,26 @@ def denominator_closure(self): T = self.copy() for i in range(n): join_strands(T.adjacent[i], T.adjacent[m + i]) - return Link(T.crossings, check_planarity=False) + + crossings = T.crossings + T.boundary_strands + for c in crossings: + c._clear() + + return Link(crossings, check_planarity=False) def link(self): """If its boundary is (0, 0), return this Tangle as a Link.""" if self.boundary != (0, 0): raise ValueError("The boundary must be (0, 0)") - return Link(self.copy().crossings, check_planarity=False) + + crossings = self.copy().crossings + for c in crossings: + c._clear() + return Link(crossings, check_planarity=False) + + + # TODO: test reshape def reshape(self, boundary, displace=0): """Renumber the boundary strands so that the tangle has the new boundary shape. This is performed by either repeatedly moving the last strands from the @@ -693,18 +788,31 @@ def reshape(self, boundary, displace=0): """ m, n = self.boundary Tm, Tn = decode_boundary(boundary) - if (m, n) == (Tm, Tn): + if (m, n) == (Tm, Tn) and displace == 0: return self if m + n != Tm + Tn: raise ValueError("Reshaping requires the tangle have the same number of boundary" " strands as in the new boundary.") + anticlockwise = [i for i in range(m)] + list(reversed([m + i for i in range(n)])) + rotate = dict(zip(anticlockwise, rotate_list(anticlockwise, displace))) + T = self.copy() + + displaced_adj = [T.adjacent[rotate[i]] for i in range(m + n)] # The 'adjacent' array but in total counterclockwise order - adj_ccw = T.adjacent[:m] + list(reversed(T.adjacent[m:])) - adj_ccw = rotate_list(adj_ccw, displace) + adj_ccw = displaced_adj[:m] + list(reversed(displaced_adj[m:])) + T.adjacent = adj_ccw[:Tm] + list(reversed(adj_ccw[Tm:])) + T.boundary = (Tm, Tn) + for i, (o, j) in enumerate(T.adjacent): + o.adjacent[j] = (T, i) - return Tangle((Tm, Tn), T.crossings, - adj_ccw[:Tm] + list(reversed(adj_ccw[Tm:]))) + displaced_bd_strands = [T.boundary_strands[rotate[i]] for i in range(m + n)] + bd_strands_ccw = displaced_bd_strands[:m] + list(reversed(displaced_bd_strands[m:])) + T.boundary_strands = bd_strands_ccw[:Tm] + list(reversed(bd_strands_ccw[Tm:])) + + T._rebuild(True) + + return T def circular_rotate(self, n): """ @@ -726,6 +834,7 @@ def circular_sum(self, other, n=0): raise ValueError("Tangles must have compatible boundary shapes") return (self * (other.circular_rotate(n))).denominator_closure() + # TODO: check if isosig still works def isosig(self, root=None, over_or_under=False): """ Return a bunch of data which encodes the planar isotopy class of the @@ -745,8 +854,68 @@ def isosig(self, root=None, over_or_under=False): return planar_isotopy.min_isosig(copy, root, over_or_under) def reverse_orientation(self, component_index): - # TODO - pass + """ + component_index: either a single index of component or a list of indices of components + """ + if not isinstance(component_index, (set, list)): + component_index = [component_index] + + org_entries = [] + for comp in self.components: + for cs in comp: + if cs.crossing in self.crossings + self.boundary_strands: + org_entries.append(cs) + break + + new_starts = [] + for i, cs in enumerate(org_entries): + if i not in component_index: + c, e = cs.crossing, cs.strand_index + s = c._adjacent_len // 2 + reversed_cs = CrossingStrand(c, (e + s) % (2 * s)) + new_starts.append(reversed_cs) + else: + new_starts.append(cs) + + self._clear() + self._build(start_orientations = new_starts, + component_starts = new_starts) + + def faces(self): + """ + + """ + corners = OrderedSet([CrossingStrand(c, i) + for c in self.crossings for i in range(4)]) + faces = [] + while len(corners): + cs0 = corners.pop() + face = [cs0] + next = cs0 + while True: + # Next two lines equiv to: next = next.next_corner() + c, e = next.crossing, next.strand_index + if isinstance(c, Tangle): + if e == 0: + next = CrossingStrand(*c.adjacent[c.boundary[0]]) + elif e < c.boundary[0]: + next = CrossingStrand(*c.adjacent[e-1]) + elif e < c.boundary[0] + c.boundary[1] - 1: + next = CrossingStrand(*c.adjacent[e+1]) + else: + assert e == c.boundary[0] + c.boundary[1] - 1 + next = CrossingStrand(*c.adjacent[c.boundary[0]-1]) + else: + next = next.next_corner() + + if next == cs0: + faces.append(face) + break + else: + corners.discard(next) + face.append(next) + + return faces def is_planar(self): # TODO @@ -759,10 +928,8 @@ def simplify(self, mode = 'basic', type_III_limit = 100): return simplify.basic_simplify(self) elif mode == 'level': return simplify.simplify_via_level_type_III(self, type_III_limit) - elif mode == 'pickup': - return simplify.pickup_simplify(self) - elif mode == 'global': - return simplify.pickup_simplify(self, type_III_limit) + else: + raise NotImplementedError() def is_planar_isotopic(self, other, root=None, over_or_under=False) -> bool: return self.isosig() == other.isosig() @@ -786,6 +953,7 @@ def _fuse_strands(self, preserve_boundary=False, preserve_components=False): def __repr__(self): return "" % self.label + # TODO: fix describe, or remove it? def describe(self, fuse_strands=True): """Give a PD-like description of the tangle in the form Tangle[{lower arcs}, {upper arcs}, P and X codes]. @@ -928,11 +1096,11 @@ def IntegerTangle(n): T = OneTangle() for i in range(n - 1): T += OneTangle() - T.label = f"IntegerTangle({n})" + T.update_label(f"IntegerTangle({n})") return T elif n < 0: T = -IntegerTangle(-n) - T.label = f"IntegerTangle({n})" + T.update_label(f"IntegerTangle({n})") return T else: raise ValueError("Expecting int") @@ -982,8 +1150,11 @@ def __init__(self, a, b=1): T = IntegerTangle(p) + T.invert() if a < 0: T = -T - Tangle.__init__(self, 2, T.crossings, T.adjacent, - f"RationalTangle({a}, {b})") + + Tangle.__init__(self, 2, + T.crossings + T.boundary_strands, + T.adjacent, + label = f"RationalTangle({a}, {b})") # --------------------------------------------------- # @@ -1009,10 +1180,9 @@ def IdentityBraid(n): """ if n < 0: raise ValueError("Expecting non-negative int") - strands = [Strand() for i in range(n)] - entry_points = [(s, 0) for s in strands] + [(s, 1) for s in strands] - return Tangle(n, strands, entry_points, - f"IdentityBraid({n})") + entry_points = 2* [i for i in range(n)] + return Tangle(n, [], entry_points, + label = f"IdentityBraid({n})") def BraidTangle(gens, n=None): @@ -1056,4 +1226,7 @@ def gen(i): if abs(i) >= n: raise ValueError("Generators must have magnitude less than n") b = b * gen(i) + + b.make_upward() + return b From 75c0c5d040dac47ddacbfb5c644e1f779c0f0e49 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Tue, 26 May 2026 02:45:38 -0500 Subject: [PATCH 06/53] First version passing all previously written doctests describe and isosig kept using old_tangles TODO: 1. rot_num 2. flip 3. computation of RT invariants 4. is_planar --- spherogram_src/links/old_tangles.py | 394 ++++++++++++++++++++++++++++ spherogram_src/links/tangles.py | 154 ++++------- 2 files changed, 448 insertions(+), 100 deletions(-) create mode 100644 spherogram_src/links/old_tangles.py diff --git a/spherogram_src/links/old_tangles.py b/spherogram_src/links/old_tangles.py new file mode 100644 index 0000000..6688579 --- /dev/null +++ b/spherogram_src/links/old_tangles.py @@ -0,0 +1,394 @@ +""" +A tangle is piece of a knot diagram in a disk where some of the +strands meet the boundary. Tangles can be composed by gluing them +along arcs in each boundary that have the same number of incident +strands. + +This module gives a version of tangles where there are four distinguished +boundary arcs used for gluing: the bottom and top, which can have incident +strands, and the left and right, which cannot. Tangles can be glued +vertically using ``*`` and horizontally using ``|``. There is also a second +kind of horizontal composition using ``+`` where the rightmost strands of the top +and bottom of the first tangle are glued to the leftmost strands of the top and +bottom of the second tangle. + +Rational tangles (created using ``RationalTangle``) are following the paper + +Classifying and Applying Rational Knots and Rational Tangles +http://homepages.math.uic.edu/~kauffman/VegasAMS.pdf + +See doc.pdf for conventions. +""" +import pickle + +from .links import Crossing, Strand, Link +from . import planar_isotopy + + +def join_strands(x, y): + """ + Input: two (c, i) pairs where c is a Crossing, Strand, or Tangle object and i is an index into + c.adjacent. Joins the objects by having them refer to each other at those positions. + + When c is a Tangle it is conceptually a special case since its c.adjacent is being + used to record the boundary strands. + + This function equivalent to creating a Strand s with s.adjacent = [x, y] and then + doing s.fuse() + """ + (a, i), (b, j) = x, y + a.adjacent[i] = (b, j) + b.adjacent[j] = (a, i) + + +def rotate_list(L, s): + """Rotate the list, putting L[s] into index 0.""" + n = len(L) + return [L[(i + s) % n] for i in range(n)] + + +def decode_boundary(boundary): + """The boundary is either a nonnegative integer or a pair of non-negative integers. + + * When the input is an integer n, this returns (n, n). + * When the input is a pair (m, n), then it returns (m, n). + + >>> decode_boundary(2) + (2, 2) + >>> decode_boundary((3,4)) + (3, 4) + >>> decode_boundary(-2) + Traceback (most recent call last): + ... + ValueError: Number of bottom boundary strands cannot be negative + >>> decode_boundary((3,-2)) + Traceback (most recent call last): + ... + ValueError: Number of top boundary strands cannot be negative + """ + if isinstance(boundary, tuple): + m, n = boundary + else: + m = n = boundary + if m < 0: + raise ValueError("Number of bottom boundary strands cannot be negative") + if n < 0: + raise ValueError("Number of top boundary strands cannot be negative") + return (m, n) + + +class Tangle: + def __init__(self, boundary=2, crossings=None, entry_points=None, label=None): + """ + A tangle is a fragment of a Link with some number of boundary + strands. Tangles can be composed in various ways along their boundary strands, + including the horizontal and vertical compositions of the tangle category. + + Inputs: + + * When boundary is an integer, then the tangle has n strands coming into both + the top and the bottom of the tangle. When boundary is a pair of integers + (m, n), then the tangle has m strands coming into the bottom and n coming + into the top. + + The strands are numbered 0 to m-1 on the bottom and m to m+n-1 on the + top, both from left to right. + + * crossings is a list of Crossing or Strand objects that comprise the tangle. + * entry_points is a list of pairs (c, i) where c is a Crossing or Strand + and i indexes into c.adjacent. These pairs describe the boundary strands + in order of the strand numbering. + * label is an arbitrary label for the tangle for informational purposes, which + appears in the ``repr`` form of the tangle. + + Usually tangles should not be created directly using this constructor since the + tangle operations and various primitive tangles are sufficient to create any tangle. + """ + + m, n = decode_boundary(boundary) + + if crossings is None: + crossings = [] + for c in crossings: + if not isinstance(c, (Crossing, Strand)): + raise ValueError("Every element of crossings must be a Crossing or a Strand") + self.crossings = crossings + + # the pair for the number of lower strands and the number of upper strands + self.boundary = (m, n) + + # a list of (c, i) pairs for the boundary strands. Each c will reciprocally + # contain (self, j) where j is the strand number. The fact this is called + # 'adjacent' means that the Tangle can take part in the joining protocol + # implemented in join_strands. + self.adjacent = (m + n) * [None] + entry_points = entry_points or [] + if len(entry_points) != m + n: + raise ValueError("The number of boundary strands is not equal to the length" + " of entry_points") + for i, e in enumerate(entry_points): + join_strands((self, i), e) + + self.label = label + + def __add__(self, other): + """Put self to left of other and fuse the top-right strand of self to the top-left + strand of other and the bottom-right strand of self to the bottom-left strand of other. + + >>> (IdentityBraid(2) + BraidTangle([1])).describe() + 'Tangle[{1,2}, {3,4}, P[1,3], X[2,4,5,5]]' + """ + A, B = self.copy(), other.copy() + (mA, nA), (mB, nB) = A.boundary, B.boundary + if mA == 0 or mB == 0 or nA == 0 or nB == 0: + raise ValueError("Tangles must have at least one top and bottom strand each.") + a, b = A.adjacent, B.adjacent + join_strands(a[mA - 1], b[0]) + join_strands(a[mA + nA - 1], b[mB]) + entry_points = a[:mA - 1] + b[1:mB] + a[mA:mA + nA - 1] + b[mB + 1:] + return Tangle((mA + mB - 2, nA + nB - 2), A.crossings + B.crossings, entry_points) + + def __mul__(self, other): + """Join with self *above* other, as with braid multiplication. + (See doc.pdf) + + >>> BraidTangle([1,1]).describe() + 'Tangle[{1,2}, {3,4}, X[5,4,3,6], X[2,5,6,1]]' + >>> (BraidTangle([1])*BraidTangle([1])).describe() + 'Tangle[{1,2}, {3,4}, X[5,4,3,6], X[2,5,6,1]]' + """ + A, B = self.copy(), other.copy() + (mA, nA), (mB, nB) = A.boundary, B.boundary + if mA != nB: + raise ValueError("Tangles must have a compatible number of strands to multiply them") + + a, b = A.adjacent, B.adjacent + for i in range(mA): + join_strands(a[i], b[mB + i]) + return Tangle((mB, nA), A.crossings + B.crossings, b[:mB] + a[mA:]) + + def __neg__(self): + """Mirror image of self. + + >>> (-BraidTangle([1])).describe() + 'Tangle[{1,2}, {3,4}, X[4,3,1,2]]'""" + T = self.copy() + for c in T.crossings: + if not isinstance(c, Strand): + c.rotate_by_90() + return T + + def __or__(self, other): + """Put self to left of other. This is like tangle addition but without the fusing of strands. + + >>> (IdentityBraid(1) | CupTangle()).describe() + 'Tangle[{1}, {2,3,4}, P[1,2], P[3,4]]' + """ + A, B = self.copy(), other.copy() + (mA, nA), (mB, nB) = A.boundary, B.boundary + a, b = A.adjacent, B.adjacent + entry_points = a[:mA] + b[:mB] + a[mA:] + b[mB:] + return Tangle((mA + mB, nA + nB), A.crossings + B.crossings, entry_points) + + def copy(self): + return pickle.loads(pickle.dumps(self)) + + def rotate(self, s): + """Rotate anticlockwise by s*90 degrees. This is only for (2,2) tangles. + + See ``Tangle.reshape()`` for a generalization to all tangle shapes.""" + if self.boundary != (2, 2): + raise ValueError("Only boundary=(2,2) tangles can be rotated") + anticlockwise = [0, 1, 3, 2] + rotate = dict(zip(anticlockwise, rotate_list(anticlockwise, s))) + T = self.copy() + T.adjacent = [T.adjacent[rotate[i]] for i in range(4)] + for i, (o, j) in enumerate(T.adjacent): + o.adjacent[j] = (T, i) + return T + + def invert(self): + """Rotate anticlockwise by 90 and take the mirror image. This is only for (2,2) tangles.""" + if self.boundary != (2, 2): + raise ValueError("Only boundary=(2,2) tangles can be inverted") + return -self.rotate(1) + + def numerator_closure(self): + """The bridge closure, where consecutive pairs of strands at both the top and + at the bottom are respectively joined by caps and cups. The numbers of + strands at both the top and the bottom must be even. Returns a Link. + + A synonym for this is ``Tangle.bridge_closure()``. + + sage: BraidTangle([2,-1,2],4).numerator_closure().alexander_polynomial() + t^2 - t + 1 + sage: BraidTangle([1,1,1]).rotate(1).numerator_closure().alexander_polynomial() + t^2 - t + 1 + """ + m, n = self.boundary + if m % 2 or n % 2: + raise ValueError("To do bridge closure, both the top and bottom must have an even number of strands") + T = self.copy() + for i in range(0, m, 2): + join_strands(T.adjacent[i], T.adjacent[i + 1]) + for i in range(0, n, 2): + join_strands(T.adjacent[m + i], T.adjacent[m + i + 1]) + return Link(T.crossings, check_planarity=False) + + def denominator_closure(self): + """The braid closure, where corresponding strands between the top and bottom + are joined. The number of strands at the top must equal the number of strands at + the bottom. Returns a Link. + + A synonym for this is ``Tangle.braid_closure()``. + + sage: BraidTangle([1,1,1]).braid_closure().alexander_polynomial() + t^2 - t + 1 + sage: BraidTangle([1,-2,1,-2]).braid_closure().alexander_polynomial() + t^2 - 3*t + 1 + >>> BraidTangle([1,-2,1,-2]).braid_closure().exterior().identify() # doctest: +SNAPPY + [m004(0,0), 4_1(0,0), K2_1(0,0), K4a1(0,0), otet02_00001(0,0)] + """ + m, n = self.boundary + if m != n: + raise ValueError("To do braid closure, both the top and bottom numbers of strands must be equal") + T = self.copy() + for i in range(n): + join_strands(T.adjacent[i], T.adjacent[m + i]) + return Link(T.crossings, check_planarity=False) + + def link(self): + """If its boundary is (0, 0), return this Tangle as a Link.""" + if self.boundary != (0, 0): + raise ValueError("The boundary must be (0, 0)") + return Link(self.copy().crossings, check_planarity=False) + + def reshape(self, boundary, displace=0): + """Renumber the boundary strands so that the tangle has the new boundary + shape. This is performed by either repeatedly moving the last strands from the + bottom right to the top right or vice versa. Simultaneously, displace controls + a rotation of the tangle where the tangle is rotated clockwise by ``displace`` steps + (so, for example, if 0 <= displace < m then the strand numbered ``displace`` + becomes the new lower-left strand). + + This is a generalization of ``Tangle.rotate()``. + """ + m, n = self.boundary + Tm, Tn = decode_boundary(boundary) + if m + n != Tm + Tn: + raise ValueError("Reshaping requires the tangle have the same number of boundary" + " strands as in the new boundary.") + T = self.copy() + # The 'adjacent' array but in total counterclockwise order + adj_ccw = T.adjacent[:m] + list(reversed(T.adjacent[m:])) + adj_ccw = rotate_list(adj_ccw, displace) + + return Tangle((Tm, Tn), T.crossings, + adj_ccw[:Tm] + list(reversed(adj_ccw[Tm:]))) + + def circular_rotate(self, n): + """ + Rotate a tangle in a circular fashion clockwise, keeping the same boundary. + + This generalizes ``Tangle.rotate()``, and it is a mild specialization of ``Tangle.reshape()``. + """ + return self.reshape(self.boundary, n) + + def circular_sum(self, other, n=0): + """ + Glue two tangles together to form a link by gluing them vertically and then taking + the braid closure (the ``Tangle.denominator_closure()``). + The second tangle is rotated clockwise by n strands using ``Tangle.circular_rotate()``. + """ + Am, An = self.boundary + Bm, Bn = self.boundary + if (Am, An) != (Bn, Bm): + raise ValueError("Tangles must have compatible boundary shapes") + return (self * (other.circular_rotate(n))).denominator_closure() + + def isosig(self, root=None, over_or_under=False): + """ + Return a bunch of data which encodes the planar isotopy class of the + tangle. Of course, this is just up to isotopy of the plane + (no Reidemeister moves). A root can be specified with a CrossingStrand + and ``over_or_under`` toggles whether only the underlying + shadow (4-valent planar map) is considered or the tangle with the + over/under data at each crossing. + + >>> BraidTangle([1]).isosig() == BraidTangle([1]).circular_rotate(1).isosig() + True + >>> BraidTangle([1]).isosig() == BraidTangle([-1]).isosig() + True + """ + copy = self.copy() + copy._fuse_strands() + return planar_isotopy.min_isosig(copy, root, over_or_under) + + def is_planar_isotopic(self, other, root=None, over_or_under=False) -> bool: + return self.isosig() == other.isosig() + + def _fuse_strands(self, preserve_boundary=False, preserve_components=False): + """Fuse all strands and delete them, even ones incident to only the boundary (unless + ``preserve_boundary`` is True). This will eliminate Strands that are loops as well. + + If ``preserve_components`` is True, then do not fuse strands that have the + ``component_idx`` attribute.""" + for s in reversed(self.crossings): + if isinstance(s, Strand): + # check that the strand is not only incident to the boundary + if preserve_boundary and all(a[0] == self for a in s.adjacent): + continue + if preserve_components and s.component_idx is not None: + continue + s.fuse() + self.crossings.remove(s) + + def __repr__(self): + return "" % self.label + + def describe(self, fuse_strands=True): + """Give a PD-like description of the tangle in the form + Tangle[{lower arcs}, {upper arcs}, P and X codes]. + + If fuse_strands is True, then fuse all internal Strand nodes first. + + >>> BraidTangle([1]).describe() + 'Tangle[{1,2}, {3,4}, X[2,4,3,1]]' + """ + T = self.copy() + if fuse_strands: + T._fuse_strands(preserve_boundary=True, preserve_components=True) + T.label = 0 + # give each crossing/strand a unique identifier, which + # is used for calculating ids for arcs + for i, c in enumerate(T.crossings): + c.label = i + 1 + arc_ids = {} + + def arc_key(c, i): + """For the given entity c and index into c.adjacent, + create a name for the incident arc. This gives something + that's suitable for use as a dictionary key.""" + d, j = c.adjacent[i] + return tuple(sorted([(c.label, i), (d.label, j)])) + + def arc_id(c, i): + """Get the unique integer id associated to the arc, generating + a fresh one if needed.""" + return arc_ids.setdefault(arc_key(c, i), len(arc_ids) + 1) + m, n = T.boundary + lower = "{" + ",".join(str(arc_id(T, i)) for i in range(m)) + "}" + upper = "{" + ",".join(str(arc_id(T, i)) for i in range(m, m + n)) + "}" + parts = [] + for c in T.crossings: + arcs = [arc_id(c, i) for i in range(len(c.adjacent))] + if isinstance(c, Crossing): + parts.append("X[%s,%s,%s,%s]" % tuple(arcs)) + elif isinstance(c, Strand): + if c.component_idx is not None: + parts.append(f"P[{arcs[0]},{arcs[1]}, component->{c.component_idx}]") + else: + parts.append(f"P[{arcs[0]},{arcs[1]}]") + else: + raise TypeError("Unexpected entity") + return f"Tangle[{lower}, {upper}{''.join(', ' + p for p in parts)}]" \ No newline at end of file diff --git a/spherogram_src/links/tangles.py b/spherogram_src/links/tangles.py index c31c88c..a0759ba 100644 --- a/spherogram_src/links/tangles.py +++ b/spherogram_src/links/tangles.py @@ -23,8 +23,7 @@ from collections import OrderedDict, Counter from .ordered_set import OrderedSet -from .links_base import Crossing, Strand, Link, CrossingStrand, CrossingEntryPoint -from . import planar_isotopy +from .links import Crossing, Strand, Link, CrossingStrand, CrossingEntryPoint class CyclicList(list): def __init__(self, iterable): @@ -181,44 +180,40 @@ def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, raise ValueError("Every element of crossings must be a Crossing or a Strand") self.unlinked_unknot_components = 0 - component_strands = [] - for s in reversed(crossings): - if isinstance(s, Strand): - if s.component_idx is not None: - # defer fusing - component_strands.append(s) - elif s.is_loop(): - self.unlinked_unknot_components += 1 - else: - s.fuse() - crossings.remove(s) - - # Note that crossings in Tangle can contain Strands with comp_idx for now + # Note that crossings in Tangle can contain Strands for now + # which will be removed after build self.crossings = crossings if build: self._build(start_orientations, component_starts) assert self.is_oriented(), 'Tangle is not oriented after build' - for s in component_strands: - comp_id = s.component_idx - comp = self.components[s.strand_component] + # Remove all Strands from crossings and components. + # Note that this will not affect strands in boundary_strands + for s in reversed(crossings): + if isinstance(s, Strand): + comp = self.components[s.strand_component] - if isinstance(comp[0].crossing, Tangle): - for cep in reversed(comp): - if cep.crossing == s: - comp.remove(cep) - break - else: - raise RuntimeError(f"Component strand {s} not found in component {comp}") - - # Note that the components are always built following the orientation - # hence below always insists that the comp_id is labeled on the entrance strand - if comp[1].component_idx is not None: - assert comp[1].component_idx == comp_id + if isinstance(comp[0].crossing, Tangle): + for cep in reversed(comp): + if cep.crossing == s: + comp.remove(cep) + break + else: + raise RuntimeError(f"Component strand {s} not found in component {comp}") + + # Note that the components are always built following the orientation + # hence below always insists that the comp_id is labeled on the entrance strand + if s.component_idx is not None: + comp_id = s.component_idx + if comp[1].crossing.component_idx is not None: + assert comp[1].crossing.component_idx == comp_id + else: + comp[1].crossing.component_idx = comp_id + if s.is_loop(): + self.unlinked_unknot_components += 1 else: - comp[1].component_idx = comp_id - + s.fuse() self.crossings.remove(s) def __getitem__(self, i): @@ -478,6 +473,7 @@ def _crossings_from_PD_code(self, code, entry_points): entry_strands.append(crossings[gluings[x][0][0]].crossing_strands()[gluings[x][0][1]]) else: this_strand = Strand(label = f'PDSE({self}, {i})') + crossings.append(this_strand) if x not in entry_dict: entry_strands.append((this_strand, 0)) entry_dict[x] = (this_strand, 1) @@ -606,7 +602,7 @@ def __add__(self, other): strand of other and the bottom-right strand of self to the bottom-left strand of other. >>> (IdentityBraid(2) + BraidTangle([1])).describe() - 'Tangle[{1,2}, {3,4}, P[1,3], X[2,4,5,5]]' + 'Tangle[{1,2}, {3,4}, X[2,4,5,5], P[1,3]]' """ A, B = self.copy(), other.copy() (mA, nA), (mB, nB) = A.boundary, B.boundary @@ -774,8 +770,6 @@ def link(self): return Link(crossings, check_planarity=False) - - # TODO: test reshape def reshape(self, boundary, displace=0): """Renumber the boundary strands so that the tangle has the new boundary shape. This is performed by either repeatedly moving the last strands from the @@ -834,7 +828,15 @@ def circular_sum(self, other, n=0): raise ValueError("Tangles must have compatible boundary shapes") return (self * (other.circular_rotate(n))).denominator_closure() - # TODO: check if isosig still works + def to_old_tangle(self): + from . import old_tangles + copy = self.copy() + + return old_tangles.Tangle(copy.boundary, + copy.crossings + copy.boundary_strands, + copy.adjacent, + copy.label) + def isosig(self, root=None, over_or_under=False): """ Return a bunch of data which encodes the planar isotopy class of the @@ -849,9 +851,9 @@ def isosig(self, root=None, over_or_under=False): >>> BraidTangle([1]).isosig() == BraidTangle([-1]).isosig() True """ - copy = self.copy() - copy._fuse_strands() - return planar_isotopy.min_isosig(copy, root, over_or_under) + + return self.to_old_tangle().isosig(root = root, + over_or_under=over_or_under) def reverse_orientation(self, component_index): """ @@ -922,7 +924,6 @@ def is_planar(self): pass def simplify(self, mode = 'basic', type_III_limit = 100): - # TODO: double check if this works from . import simplify if mode == 'basic': return simplify.basic_simplify(self) @@ -932,28 +933,11 @@ def simplify(self, mode = 'basic', type_III_limit = 100): raise NotImplementedError() def is_planar_isotopic(self, other, root=None, over_or_under=False) -> bool: - return self.isosig() == other.isosig() - - def _fuse_strands(self, preserve_boundary=False, preserve_components=False): - """Fuse all strands and delete them, even ones incident to only the boundary (unless - ``preserve_boundary`` is True). This will eliminate Strands that are loops as well. - - If ``preserve_components`` is True, then do not fuse strands that have the - ``component_idx`` attribute.""" - for s in reversed(self.crossings): - if isinstance(s, Strand): - # check that the strand is not only incident to the boundary - if preserve_boundary and all(a[0] == self for a in s.adjacent): - continue - if preserve_components and s.component_idx is not None: - continue - s.fuse() - self.crossings.remove(s) + return self.isosig(root = root, over_or_under=over_or_under) == other.isosig(root = root, over_or_under = over_or_under) def __repr__(self): return "" % self.label - # TODO: fix describe, or remove it? def describe(self, fuse_strands=True): """Give a PD-like description of the tangle in the form Tangle[{lower arcs}, {upper arcs}, P and X codes]. @@ -963,43 +947,8 @@ def describe(self, fuse_strands=True): >>> BraidTangle([1]).describe() 'Tangle[{1,2}, {3,4}, X[2,4,3,1]]' """ - T = self.copy() - if fuse_strands: - T._fuse_strands(preserve_boundary=True, preserve_components=True) - T.label = 0 - # give each crossing/strand a unique identifier, which - # is used for calculating ids for arcs - for i, c in enumerate(T.crossings): - c.label = i + 1 - arc_ids = {} - - def arc_key(c, i): - """For the given entity c and index into c.adjacent, - create a name for the incident arc. This gives something - that's suitable for use as a dictionary key.""" - d, j = c.adjacent[i] - return tuple(sorted([(c.label, i), (d.label, j)])) - - def arc_id(c, i): - """Get the unique integer id associated to the arc, generating - a fresh one if needed.""" - return arc_ids.setdefault(arc_key(c, i), len(arc_ids) + 1) - m, n = T.boundary - lower = "{" + ",".join(str(arc_id(T, i)) for i in range(m)) + "}" - upper = "{" + ",".join(str(arc_id(T, i)) for i in range(m, m + n)) + "}" - parts = [] - for c in T.crossings: - arcs = [arc_id(c, i) for i in range(len(c.adjacent))] - if isinstance(c, Crossing): - parts.append("X[%s,%s,%s,%s]" % tuple(arcs)) - elif isinstance(c, Strand): - if c.component_idx is not None: - parts.append(f"P[{arcs[0]},{arcs[1]}, component->{c.component_idx}]") - else: - parts.append(f"P[{arcs[0]},{arcs[1]}]") - else: - raise TypeError("Unexpected entity") - return f"Tangle[{lower}, {upper}{''.join(', ' + p for p in parts)}]" + + return self.to_old_tangle().describe(fuse_strands=fuse_strands) Tangle.bridge_closure = Tangle.numerator_closure @@ -1015,7 +964,7 @@ def ComponentTangle(component_idx): >>> T=(RationalTangle(2,3)+IdentityBraid(1))|(RationalTangle(2,5)+ComponentTangle(-1)) >>> T.describe() - 'Tangle[{1,2}, {3,4}, X[5,6,7,3], X[8,7,6,9], X[1,8,9,5], X[10,11,12,4], X[13,14,11,10], X[15,16,14,17], X[2,15,17,13], P[16,12, component->-1]]' + 'Tangle[{1,2}, {3,4}, X[5,3,6,7], X[8,5,7,9], X[1,8,9,6], X[10,4,11,12], X[13,14,12,11], X[14,15,16,10], X[17,16,15,13], P[2,17, component->-1]]' >>> M=T.braid_closure().exterior() # doctest: +SNAPPY >>> M.dehn_fill([(1,0),(0,0)]) # doctest: +SNAPPY @@ -1024,7 +973,7 @@ def ComponentTangle(component_idx): >>> T=(RationalTangle(2,3)+IdentityBraid(1))|(RationalTangle(2,5)+ComponentTangle(0)) >>> T.describe() - 'Tangle[{1,2}, {3,4}, X[5,6,7,3], X[8,7,6,9], X[1,8,9,5], X[10,11,12,4], X[13,14,11,10], X[15,16,14,17], X[2,15,17,13], P[16,12, component->0]]' + 'Tangle[{1,2}, {3,4}, X[5,3,6,7], X[8,5,7,9], X[1,8,9,6], X[10,4,11,12], X[13,14,12,11], X[14,15,16,10], X[17,16,15,13], P[2,17, component->0]]' >>> M=T.braid_closure().exterior() # doctest: +SNAPPY >>> M.dehn_fill([(0,0),(1,0)]) # doctest: +SNAPPY @@ -1151,8 +1100,13 @@ def __init__(self, a, b=1): if a < 0: T = -T + crossings = T.crossings + T.boundary_strands + + for c in crossings: + c._clear() + Tangle.__init__(self, 2, - T.crossings + T.boundary_strands, + crossings, T.adjacent, label = f"RationalTangle({a}, {b})") @@ -1203,9 +1157,9 @@ def BraidTangle(gens, n=None): >>> BraidTangle([-1]).describe() 'Tangle[{1,2}, {3,4}, X[1,2,4,3]]' >>> BraidTangle([1],3).describe() - 'Tangle[{1,2,3}, {4,5,6}, P[3,6], X[2,5,4,1]]' + 'Tangle[{1,2,3}, {4,5,6}, X[2,5,4,1], P[3,6]]' >>> BraidTangle([2],3).describe() - 'Tangle[{1,2,3}, {4,5,6}, P[1,4], X[3,6,5,2]]' + 'Tangle[{1,2,3}, {4,5,6}, X[3,6,5,2], P[1,4]]' >>> BraidTangle([1,2]).describe() 'Tangle[{1,2,3}, {4,5,6}, X[7,5,4,1], X[3,6,7,2]]' >>> BraidTangle([1,2,1]).describe() From 7b14645ec37342100a05d2e507477509731b35ca Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Thu, 28 May 2026 17:16:29 -0500 Subject: [PATCH 07/53] Add doctests and allow SnapPy to import BraidTangle and ComponentTangle --- spherogram_src/__init__.py | 2 +- spherogram_src/links/__init__.py | 4 +- spherogram_src/links/links_base.py | 33 ++++++ spherogram_src/links/tangles.py | 173 ++++++++++++++++++++++++++--- 4 files changed, 193 insertions(+), 19 deletions(-) diff --git a/spherogram_src/__init__.py b/spherogram_src/__init__.py index fd4644b..fc98bef 100644 --- a/spherogram_src/__init__.py +++ b/spherogram_src/__init__.py @@ -23,4 +23,4 @@ def version(): # from spherogram.links.tangles: 'Tangle', 'CapTangle', 'CupTangle', 'RationalTangle', 'ZeroTangle', 'InfinityTangle', 'MinusOneTangle', 'OneTangle', 'IntegerTangle', - 'IdentityBraid', 'ComponentTangle', 'join_strands'] + 'IdentityBraid', 'BraidTangle', 'ComponentTangle', 'join_strands'] diff --git a/spherogram_src/links/__init__.py b/spherogram_src/links/__init__.py index 91a5c36..5fa6bd3 100644 --- a/spherogram_src/links/__init__.py +++ b/spherogram_src/links/__init__.py @@ -2,7 +2,7 @@ import sys from .links import Crossing, Strand, Link, ClosedBraid -from .tangles import Tangle, CapTangle, CupTangle, RationalTangle, ZeroTangle, InfinityTangle, MinusOneTangle, OneTangle, IntegerTangle, IdentityBraid, ComponentTangle, join_strands +from .tangles import Tangle, CapTangle, CupTangle, RationalTangle, ZeroTangle, InfinityTangle, MinusOneTangle, OneTangle, IntegerTangle, IdentityBraid, BraidTangle, ComponentTangle, join_strands from . import orthogonal from .random_links import random_link from . import bands @@ -25,5 +25,5 @@ def pdf_docs(): __all__ = ['Crossing', 'Strand', 'Link', 'ClosedBraid', 'Tangle', 'CapTangle', 'CupTangle', 'RationalTangle', 'ZeroTangle', 'InfinityTangle', 'MinusOneTangle', 'OneTangle', 'IntegerTangle', - 'IdentityBraid', 'join_strands', + 'IdentityBraid', 'BraidTangle', 'ComponentTangle','join_strands', 'pdf_docs', 'random_link', 'bands'] diff --git a/spherogram_src/links/links_base.py b/spherogram_src/links/links_base.py index 2f3d506..ef65b34 100644 --- a/spherogram_src/links/links_base.py +++ b/spherogram_src/links/links_base.py @@ -890,6 +890,39 @@ def _rebuild(self, same_components_and_orientations=False): else: self._build() + def reverse_orientation(self, component_index): + """ + Reverse the orientation of components specified by component_index. + + component_index: either a single index of component or a list of indices of components + + """ + if not isinstance(component_index, (set, list, tuple)): + component_index = [component_index] + + org_entries = [] + for comp in self.components: + for cs in comp: + if cs.crossing in self.crossings: + org_entries.append(cs) + break + + new_starts = [] + for i, cs in enumerate(org_entries): + if i not in component_index: + c, e = cs.crossing, cs.strand_index + s = c._adjacent_len // 2 + reversed_cs = CrossingStrand(c, (e + s) % (2 * s)) + new_starts.append(reversed_cs) + else: + new_starts.append(cs) + + self.link_components = None + for c in self.crossings: + c._clear() + self._build(start_orientations = new_starts, + component_starts = new_starts) + def _check_crossing_orientations(self): for C in self.crossings: if C.sign == 1: diff --git a/spherogram_src/links/tangles.py b/spherogram_src/links/tangles.py index a0759ba..9e3c1d0 100644 --- a/spherogram_src/links/tangles.py +++ b/spherogram_src/links/tangles.py @@ -129,8 +129,12 @@ def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, * label is an arbitrary label for the tangle for informational purposes, which appears in the ``repr`` form of the tangle. - Usually tangles should not be created directly using this constructor since the - tangle operations and various primitive tangles are sufficient to create any tangle. + Tangles now support creation from PD_code, for example: + + >>> Tangle(3, [[0,4,1,5],[1,8,2,9],[2,7,3,6],[5,9,6,10]], [0,4,8,10,3,7], label = 'RIII') + + + see doc of ``PD_code`` for more details. """ if label is None: self.label = id(self) @@ -171,7 +175,7 @@ def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, " of entry_points") for i, e in enumerate(entry_points): - this_strand = Strand(label = f'TSE({self}, {i})') + this_strand = Strand(label = f'TSE({str(self)}, {i})') self.boundary_strands.append(this_strand) join_strands(e, (this_strand, 1)) join_strands((self, i), (this_strand, 0)) @@ -353,6 +357,23 @@ def _orient_crossings(self, start_orientations=None): s.orient() def _build_components(self, component_starts=None): + """ + Each component is stored as a list of *entry points* to crossings. + If the component starts and ends at the boundary of tangles, + the corresponding CrossingEntryPoint(self, boundary_index) + will be put at the tail and the head of the list. + + If provided, the component_starts must consist of one + CrossingEntryPoint per component. + + >>> len(RationalTangle(-2, 3).components) + 2 + >>> len(Tangle(3, [[0,4,1,5],[1,8,2,9],[2,7,3,6],[5,9,6,10]], + ... [0,4,8,10,3,7], label = 'RIII').components) + 3 + >>> len(((RationalTangle(2,3)+IdentityBraid(1))|(RationalTangle(2,5)+ComponentTangle(-1))).components) + 2 + """ if component_starts is not None: # Take all CrossingStrand and CrossingEntryPoint objects # and turn them into CrossingEntryPoints @@ -456,8 +477,6 @@ def _crossings_from_PD_code(self, code, entry_points): if any(len(v) > 2 for v in gluings.values()): raise ValueError("PD code isn't consistent") - - crossings = [Crossing(i) for i, d in enumerate(code)] for item in gluings.values(): @@ -472,7 +491,7 @@ def _crossings_from_PD_code(self, code, entry_points): if x in gluings: entry_strands.append(crossings[gluings[x][0][0]].crossing_strands()[gluings[x][0][1]]) else: - this_strand = Strand(label = f'PDSE({self}, {i})') + this_strand = Strand(label = f'PDSE({str(self)}, {i})') crossings.append(this_strand) if x not in entry_dict: entry_strands.append((this_strand, 0)) @@ -491,6 +510,22 @@ def _crossings_from_PD_code(self, code, entry_points): return crossings, component_starts, entry_strands def PD_code(self, KnotTheory=False, min_strand_index = 0): + """ + The planar diagram code for the tangle. Unlike for links, it returns two extra fields, + boundary and entry_info in addition to the PD code of crossings, in order to specify + how the boundary and entries of the tangle is arranged. The fields are ordered as follows: + + boundary, PD, entry_info + + so that they can be unpacked immediately for creating Tangles. + + >>> RationalTangle(-1,2).PD_code() + ((2, 2), [(1, 5, 2, 4), (3, 1, 4, 0)], [0, 3, 2, 5]) + >>> BraidTangle([1,2,1]).PD_code() + ((3, 3), [(7, 5, 8, 4), (6, 2, 7, 1), (3, 1, 4, 0)], [0, 3, 6, 8, 5, 2]) + >>> Tangle(*RationalTangle(-1,2).PD_code()).PD_code() + ((2, 2), [(1, 5, 2, 4), (3, 1, 4, 0)], [0, 3, 2, 5]) + """ PD = [] entry_info = [s + min_strand_index for s in self.strand_labels] @@ -504,7 +539,7 @@ def PD_code(self, KnotTheory=False, min_strand_index = 0): else: PD = [tuple(x) for x in PD] - return PD, entry_info + return self.boundary, PD, entry_info def rot_num(self): #TODO @@ -779,6 +814,14 @@ def reshape(self, boundary, displace=0): becomes the new lower-left strand). This is a generalization of ``Tangle.rotate()``. + + >>> T = BraidTangle([1,2,1]) + >>> T.PD_code() + ((3, 3), [(7, 5, 8, 4), (6, 2, 7, 1), (3, 1, 4, 0)], [0, 3, 6, 8, 5, 2]) + >>> T.reshape((4,2)).PD_code() + ((4, 2), [(7, 5, 8, 4), (6, 2, 7, 1), (3, 1, 4, 0)], [0, 3, 6, 2, 8, 5]) + >>> T.reshape((4,2), displace = 1).PD_code() + ((4, 2), [(7, 5, 8, 4), (6, 2, 7, 1), (3, 1, 4, 0)], [3, 6, 2, 5, 0, 8]) """ m, n = self.boundary Tm, Tn = decode_boundary(boundary) @@ -828,7 +871,7 @@ def circular_sum(self, other, n=0): raise ValueError("Tangles must have compatible boundary shapes") return (self * (other.circular_rotate(n))).denominator_closure() - def to_old_tangle(self): + def _to_old_tangle(self): from . import old_tangles copy = self.copy() @@ -850,16 +893,41 @@ def isosig(self, root=None, over_or_under=False): True >>> BraidTangle([1]).isosig() == BraidTangle([-1]).isosig() True + >>> BraidTangle([1,1]).isosig() == BraidTangle([-1,-1]).isosig() + True + >>> BraidTangle([1,1]).isosig(over_or_under=True) == BraidTangle([-1,-1]).isosig(over_or_under=True) + False """ - return self.to_old_tangle().isosig(root = root, + return self._to_old_tangle().isosig(root = root, over_or_under=over_or_under) def reverse_orientation(self, component_index): """ + Reverse the orientation of components specified by component_index, + changing the current tangle and the signs of crossings. + component_index: either a single index of component or a list of indices of components + + >>> T = BraidTangle([1,2,1]) + >>> T + + >>> T.PD_code() + ((3, 3), [(7, 5, 8, 4), (6, 2, 7, 1), (3, 1, 4, 0)], [0, 3, 6, 8, 5, 2]) + >>> T.reverse_orientation(1) + >>> T.PD_code() + ((3, 3), [(7, 3, 8, 4), (6, 2, 7, 1), (4, 0, 5, 1)], [0, 5, 6, 8, 3, 2]) + >>> T.reverse_orientation([1,2]) + >>> T.PD_code() + ((3, 3), [(6, 4, 7, 5), (7, 1, 8, 2), (3, 1, 4, 0)], [0, 3, 8, 6, 5, 2]) + >>> T.reverse_orientation([0,2]) + >>> T.PD_code() + ((3, 3), [(7, 5, 8, 4), (6, 0, 7, 1), (3, 1, 4, 2)], [2, 3, 6, 8, 5, 0]) + >>> T.reverse_orientation([0]) + >>> T.PD_code() + ((3, 3), [(7, 5, 8, 4), (6, 2, 7, 1), (3, 1, 4, 0)], [0, 3, 6, 8, 5, 2]) """ - if not isinstance(component_index, (set, list)): + if not isinstance(component_index, (set, list, tuple)): component_index = [component_index] org_entries = [] @@ -885,10 +953,26 @@ def reverse_orientation(self, component_index): def faces(self): """ - + The faces are the complementary regions of the tangle diagram in the disk, + where the boundary of the disk is thought of as the cusp. + + Each face is given as a list of corners of crossings as one + goes around *clockwise*. These corners are recorded as + CrossingStrands, where CrossingStrand(c, j) denotes the corner + of the face abutting crossing c between strand j and j + 1; + similarly, if c is the tangle itself, it denots the corner + as one stands at the j-th boundary entry and look *counterclockwisely*. + + Alternatively, the sequence of CrossingStrands can be regarded + as the *heads* of the oriented edges of the face. + + >>> len(IdentityBraid(2).faces()) + 3 + >>> len(BraidTangle([1,2,1]).faces()) + 7 """ corners = OrderedSet([CrossingStrand(c, i) - for c in self.crossings for i in range(4)]) + for c in self.crossings + self.boundary_strands for i in range(c._adjacent_len)]) faces = [] while len(corners): cs0 = corners.pop() @@ -924,6 +1008,49 @@ def is_planar(self): pass def simplify(self, mode = 'basic', type_III_limit = 100): + """ + Tries to simplify the tangle diagram. Returns whether it succeeded + in reducing the number of crossings. Modifies the tangle in place, + and unknot components which are also unlinked may be silently discarded. + The ordering of ``components`` is not always preserved. + + The following strategies can be employed. + + 1. In the default ``basic`` mode, it does Reidemeister I and II moves + until none are possible. + + 2. In ``level`` mode, it does random Reidemeister III moves, reducing + the number of crossings via type I and II moves whenever possible. + The process stops when it has done ``type_III_limit`` *consecutive* + type III moves without any simplification. + + The ``pickup`` and ``global`` modes are currently not available for tangles. + + Some examples: + + >>> T = Tangle(2, [[0,3,1,4],[1,5,2,4]], [0,3,2,5], label = 'RII') + >>> T + + >>> T.simplify('basic') + True + >>> T + + >>> T.simplify('basic') # Already done all it can + False + + >>> T = Tangle(3, [[0,4,1,5],[1,8,2,9],[2,7,3,6],[5,9,6,10]], + ... [0,4,8,10,3,7], label = 'RIII') + >>> T + + >>> T.simplify('basic') + False + >>> T # No change happens + + >>> T.simplify('level') + True + >>> T + + """ from . import simplify if mode == 'basic': return simplify.basic_simplify(self) @@ -936,6 +1063,9 @@ def is_planar_isotopic(self, other, root=None, over_or_under=False) -> bool: return self.isosig(root = root, over_or_under=over_or_under) == other.isosig(root = root, over_or_under = over_or_under) def __repr__(self): + return "" % (self.label, len(self.components), len(self.crossings), self.boundary[0], self.boundary[1]) + + def __str__(self): return "" % self.label def describe(self, fuse_strands=True): @@ -948,10 +1078,11 @@ def describe(self, fuse_strands=True): 'Tangle[{1,2}, {3,4}, X[2,4,3,1]]' """ - return self.to_old_tangle().describe(fuse_strands=fuse_strands) + return self._to_old_tangle().describe(fuse_strands=fuse_strands) Tangle.bridge_closure = Tangle.numerator_closure + Tangle.braid_closure = Tangle.denominator_closure @@ -962,6 +1093,9 @@ def ComponentTangle(component_idx): this tangle should be the last component when it is turned into a Link. + >>> ComponentTangle(2) + + >>> T=(RationalTangle(2,3)+IdentityBraid(1))|(RationalTangle(2,5)+ComponentTangle(-1)) >>> T.describe() 'Tangle[{1,2}, {3,4}, X[5,3,6,7], X[8,5,7,9], X[1,8,9,6], X[10,4,11,12], X[13,14,12,11], X[14,15,16,10], X[17,16,15,13], P[2,17, component->-1]]' @@ -985,10 +1119,9 @@ def ComponentTangle(component_idx): Traceback (most recent call last): ... ValueError: Two Strand objects in different components have the same component_idx values - """ s = Strand(component_idx=component_idx) - return Tangle((1, 1), [s], [(s, 0), (s, 1)]) + return Tangle((1, 1), [s], [(s, 0), (s, 1)], label = f'ComponentTangle({component_idx})') def CapTangle(): @@ -1084,6 +1217,9 @@ class RationalTangle(Tangle): attributes: ``fraction`` gives (a, b) and ``partial_quotients`` gives the continued fraction expansion of ``abs(a)/b``. + >>> RationalTangle(-2,3) + + >>> RationalTangle(2,5).braid_closure().exterior().identify() # doctest: +SNAPPY [m004(0,0), 4_1(0,0), K2_1(0,0), K4a1(0,0), otet02_00001(0,0)] """ @@ -1127,6 +1263,8 @@ def IdentityBraid(n): 'Tangle[{1}, {2}, P[1,2]]' >>> IdentityBraid(2).describe() 'Tangle[{1,2}, {3,4}, P[1,3], P[2,4]]' + >>> IdentityBraid(5) + >>> IdentityBraid(-1) Traceback (most recent call last): ... @@ -1151,7 +1289,7 @@ def BraidTangle(gens, n=None): number of strands that works for the given list of generators >>> BraidTangle([], 1) - + >>> BraidTangle([1]).describe() 'Tangle[{1,2}, {3,4}, X[2,4,3,1]]' >>> BraidTangle([-1]).describe() @@ -1164,6 +1302,8 @@ def BraidTangle(gens, n=None): 'Tangle[{1,2,3}, {4,5,6}, X[7,5,4,1], X[3,6,7,2]]' >>> BraidTangle([1,2,1]).describe() 'Tangle[{1,2,3}, {4,5,6}, X[7,5,4,8], X[3,6,7,9], X[2,9,8,1]]' + >>> BraidTangle([1,2,1]) + """ if n is None: n = max(-min(gens), max(gens)) + 1 @@ -1182,5 +1322,6 @@ def gen(i): b = b * gen(i) b.make_upward() + b.update_label(f'BraidTangle({gens}, {n})') return b From 53e7cdf49f622a9a10d6774c1e8d95d7e9e7368f Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Thu, 28 May 2026 17:18:03 -0500 Subject: [PATCH 08/53] Remove is_planar and rot_num in TODO phase temporarly to prepare for creating pull request --- spherogram_src/links/tangles.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/spherogram_src/links/tangles.py b/spherogram_src/links/tangles.py index 9e3c1d0..7f602ba 100644 --- a/spherogram_src/links/tangles.py +++ b/spherogram_src/links/tangles.py @@ -540,10 +540,6 @@ def PD_code(self, KnotTheory=False, min_strand_index = 0): PD = [tuple(x) for x in PD] return self.boundary, PD, entry_info - - def rot_num(self): - #TODO - pass def _component_starts_from_PD(self, code, labels, gluings, entry_dict): """ @@ -1003,10 +999,6 @@ def faces(self): return faces - def is_planar(self): - # TODO - pass - def simplify(self, mode = 'basic', type_III_limit = 100): """ Tries to simplify the tangle diagram. Returns whether it succeeded From b57bc22dadaf19d04c94dc637e257db6dd8b756e Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Thu, 28 May 2026 17:19:08 -0500 Subject: [PATCH 09/53] Layout the structure for implementation --- .../links/reshetikhin_turaev/R_matrices.py | 5 + .../reshetikhin_turaev/reshetikhin_turaev.py | 16 ++ .../links/reshetikhin_turaev/sparse_array.py | 215 ++++++++++++++++++ spherogram_src/links/tangles.py | 8 + 4 files changed, 244 insertions(+) create mode 100644 spherogram_src/links/reshetikhin_turaev/R_matrices.py create mode 100644 spherogram_src/links/reshetikhin_turaev/reshetikhin_turaev.py create mode 100644 spherogram_src/links/reshetikhin_turaev/sparse_array.py diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices.py b/spherogram_src/links/reshetikhin_turaev/R_matrices.py new file mode 100644 index 0000000..8ec2de8 --- /dev/null +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices.py @@ -0,0 +1,5 @@ +from ...sage_helper import _within_sage + +from .sparse_array import SparseTensor + +cache = dict() \ No newline at end of file diff --git a/spherogram_src/links/reshetikhin_turaev/reshetikhin_turaev.py b/spherogram_src/links/reshetikhin_turaev/reshetikhin_turaev.py new file mode 100644 index 0000000..77e3b11 --- /dev/null +++ b/spherogram_src/links/reshetikhin_turaev/reshetikhin_turaev.py @@ -0,0 +1,16 @@ +from .sparse_array import SparseTensor + +import R_matrices + +# tangle to graph where nodes are SparseTensors and edges are paris of +# tuples (SparseTensor, index in tensor) +# also, create a dictionary of labels of arcs to the edges + +# For curls, do an honest implementation as SparseTensors, so that rot_num +# is only used when creating the tensor network. +# The said dictionary should then use honest lables of edges instead of labels of arcs... + +# The contraction sequence can then still be presented as a list of lists of labels + +# When doing contractions, need to update the info in adjacent edges accordingly...ff + diff --git a/spherogram_src/links/reshetikhin_turaev/sparse_array.py b/spherogram_src/links/reshetikhin_turaev/sparse_array.py new file mode 100644 index 0000000..a5d202b --- /dev/null +++ b/spherogram_src/links/reshetikhin_turaev/sparse_array.py @@ -0,0 +1,215 @@ +class SparseArray: + """ + A sparse array supporting arbitrary-dimensional indexing via tuples. + + Internally stores only non-default entries in a dict keyed by index tuples. + Indices can be integers or tuples of integers of any length. + """ + __slots__ = ('_data', '_default', '_rank') + + def __init__(self, data= None, default=0, rank = None): + self._data = {} + self._default = default + self._rank = rank + if data is not None: + if isinstance(data, dict): + data = data.items() + for k, v in data: + self[k] = v + + def _key(self, index): + if isinstance(index, tuple): + return index + return (index,) + + def __getitem__(self, index): + return self._data.get(self._key(index), self._default) + + def __setitem__(self, index, value): + key = self._key(index) + if value == self._default: + self._data.pop(key, None) + else: + if self._rank is None: + self._rank = len(key) + elif len(key) != self._rank: + raise ValueError( + f'key length {len(key)} does not match rank {self._rank}') + self._data[key] = value + + def __delitem__(self, index): + key = self._key(index) + if key not in self._data: + raise KeyError(index) + del self._data[key] + if not self._data: + self._rank = None + + def __contains__(self, index): + return self._key(index) in self._data + + def __len__(self): + return len(self._data) + + def __iter__(self): + return iter(self._data) + + def __repr__(self): + return f'SparseArray({self._data!r}, default={self._default!r})' + + def keys(self): + return self._data.keys() + + def values(self): + return self._data.values() + + def items(self): + return self._data.items() + + def get(self, index, default=None): + if default is None: + default = self._default + return self._data.get(self._key(index), default) + + def clear(self): + self._data.clear() + self._rank = None + + def copy(self): + return SparseArray(data=self._data.copy(), + default=self._default, + rank = self.rank) + + @property + def rank(self): + """Dimension of the indices.""" + return self._rank + + def nonzero_indices(self): + """Return list of all indices with non-default values.""" + return list(self._data.keys()) + + def to_dict(self): + return dict(self._data) + + @classmethod + def from_dict(cls, d, default=0): + result = cls(default=default) + for k, v in d.items(): + result[k] = v + return result + + +class SparseTensor(SparseArray): + """ + A sparse tensor supporting pairwise and multi-tensor contraction. + + Indices are tuples of integers. contract(other, pairs) contracts two + tensors over specified index pairs (summing the product over those axes). + multi_contract handles networks of arbitrarily many tensors at once. + """ + + def __repr__(self): + return f'SparseTensor({self._data!r}, default={self._default!r})' + + def copy(self): + return SparseTensor(data=self._data.copy(), + default=self._default, + rank=self.rank) + + @property + def rank(self): + """Number of indices (tensor rank)""" + return self._rank + + def _set(self, key, value): + """Write self[key] = value, dropping the entry if it equals default.""" + assert self.rank == len(key) + + if value == self._default: + self._data.pop(key, None) + if not self._data: + self._rank = None + else: + self._data[key] = value + + def _accumulate(self, key, value): + """Add value into self[key], maintaining sparsity.""" + self._set(key, self._data.get(key, self._default) + value) + + def contract(self, other: SparseTensor, pairs): + """ + Contract self with other over the specified index pairs, returning a + new SparseTensor whose axes are the free axes of self followed by the + free axes of other. + + pairs: iterable of (self_axis, other_axis) to be summed over. + Only entries where self[..., v, ...] == other[..., v, ...] on + every contracted axis contribute; their products are accumulated. + + Result axes: free axes of self (in order) + free axes of other (in order). + + Examples: + # matrix multiply A[i,j] * B[j,k] -> C[i,k] + A.contract(B, [(1, 0)]) + + # double contraction A[i,j,k] * B[j,k,l] -> C[i,l] + A.contract(B, [(1, 0), (2, 1)]) + """ + pairs = list(pairs) + if not self._data or not other._data: + return SparseTensor(default=self._default) + + self_contracted = {ai for ai, _ in pairs} + other_contracted = {bj for _, bj in pairs} + + self_free = [i for i in range(self.rank) if i not in self_contracted] + other_free = [i for i in range(other.rank) if i not in other_contracted] + + # Group self entries by their values at the contracted axes (in pairs order). + # For each contraction key, we only need to visit other entries that match. + self_groups = {} + for key, val in self.items(): + c_key = tuple(key[ai] for ai, _ in pairs) + f_key = tuple(key[i] for i in self_free) + self_groups.setdefault(c_key, []).append((f_key, val)) + + result = SparseTensor(default=self._default, + rank = len(self_free) + len(other_free)) + for key_b, val_b in other.items(): + c_key = tuple(key_b[bj] for _, bj in pairs) + group = self_groups.get(c_key) + if group is None: + continue + assert len(group) > 0 + + f_key_b = tuple(key_b[i] for i in other_free) + for f_key_a, val_a in group: + result._accumulate(f_key_a + f_key_b, val_a * val_b) + return result + + def trace(self, i, j): + """ + Single-tensor contraction: set indices i and j equal and sum, + returning a SparseTensor of rank reduced by 2. + + Example: T[i,j,k].trace(0, 2) -> result[j] = sum_k T[k, j, k] + """ + assert self.rank - 2 >= 0 + if not self._data: + return SparseTensor(default=self._default, + rank = self.rank - 2) + + n = self.rank + i, j = i % n, j % n + if i == j: + raise ValueError("trace indices must be distinct") + + result = SparseTensor(default=self._default, + rank = self.rank - 2) + for key, value in self.items(): + if key[i] != key[j]: + continue + free_key = tuple(v for idx, v in enumerate(key) if idx != i and idx != j) + result._accumulate(free_key, value) + return result \ No newline at end of file diff --git a/spherogram_src/links/tangles.py b/spherogram_src/links/tangles.py index 7f602ba..9e3c1d0 100644 --- a/spherogram_src/links/tangles.py +++ b/spherogram_src/links/tangles.py @@ -540,6 +540,10 @@ def PD_code(self, KnotTheory=False, min_strand_index = 0): PD = [tuple(x) for x in PD] return self.boundary, PD, entry_info + + def rot_num(self): + #TODO + pass def _component_starts_from_PD(self, code, labels, gluings, entry_dict): """ @@ -999,6 +1003,10 @@ def faces(self): return faces + def is_planar(self): + # TODO + pass + def simplify(self, mode = 'basic', type_III_limit = 100): """ Tries to simplify the tangle diagram. Returns whether it succeeded From 76b157ebab8a6a1f865d5febbf405cfe5a43a841 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Thu, 28 May 2026 17:55:51 -0500 Subject: [PATCH 10/53] Update docstring for make_upward --- spherogram_src/links/links_base.py | 1 - spherogram_src/links/tangles.py | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/spherogram_src/links/links_base.py b/spherogram_src/links/links_base.py index ef65b34..c14acb7 100644 --- a/spherogram_src/links/links_base.py +++ b/spherogram_src/links/links_base.py @@ -291,7 +291,6 @@ def next(self): return CrossingEntryPoint(*c.adjacent[(e + s) % (2 * s)]) else: raise RuntimeError('This should not be reached') - return CrossingEntryPoint(*self.crossing.adjacent[self.strand_index]) def previous(self): d, j = self.opposite() diff --git a/spherogram_src/links/tangles.py b/spherogram_src/links/tangles.py index 7f602ba..e21df77 100644 --- a/spherogram_src/links/tangles.py +++ b/spherogram_src/links/tangles.py @@ -238,6 +238,26 @@ def is_oriented(self): return all(s != 0 for s in self.boundary_signs) def make_upward(self): + """ + Change the orientation of the tangle, trying to make it upwardly oriented. + The order of components is preserved. + + >>> T = BraidTangle([1,2,1]) + >>> T.reverse_orientation([1,2]) + >>> T.is_upward() + False + >>> T.make_upward() + >>> T.is_upward() + True + + Like alternating() for Links, this may fail silently if there is no orientation + which makes the tangle upwardly oriented. + + >>> T = RationalTangle(-2,3) + >>> T.make_upward() + >>> T.is_upward() + False + """ if self.is_upward(): return From e3632027543eb84c8b4fb12608e6d0fd66f6bbf1 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Thu, 28 May 2026 18:23:42 -0500 Subject: [PATCH 11/53] Make __or__ preserve orientations on the original tangles --- spherogram_src/links/tangles.py | 54 +++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/spherogram_src/links/tangles.py b/spherogram_src/links/tangles.py index e21df77..2ebd433 100644 --- a/spherogram_src/links/tangles.py +++ b/spherogram_src/links/tangles.py @@ -106,7 +106,7 @@ def add(self, c): return component class Tangle: - def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, label=None): + def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, label=None, start_orientations = None, component_starts = None): """ A tangle is a fragment of a Link with some number of boundary strands. Tangles can be composed in various ways along their boundary strands, @@ -142,8 +142,8 @@ def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, self.label = label m, n = decode_boundary(boundary) - component_starts = None - start_orientations = None + component_starts = component_starts + start_orientations = start_orientations self.strand_labels = CyclicList(m * [None] + n * [None]) self.strand_components = CyclicList(m * [None] + n * [None]) @@ -155,6 +155,8 @@ def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, if (len(crossings) > 0 and not isinstance(crossings[0], (Strand, Crossing)))\ or (entry_points is not None and len(entry_points) > 0 and not isinstance(entry_points[0], (CrossingStrand, list, tuple))): + assert component_starts is None and start_orientations is None, "Specifying components_starts and start_orientations is not compatible with creating from PD codes" + crossings, component_starts, entry_points = self._crossings_from_PD_code(crossings, entry_points) start_orientations = component_starts[:] @@ -300,13 +302,8 @@ def _rebuild(self, same_components_and_orientations = False): # Hopefully we have enough of the original components left # to figure out what this is. Otherwise, new choices will # be made as in the default algorithm. - start_css = [] - for comp in self.components: - for cs in comp: - if cs.crossing in self.crossings + self.boundary_strands: - s = cs.crossing._adjacent_len // 2 - start_css.append(cs.rotate(s)) - break + start_css = self._start_orientations() + self._clear() if same_components_and_orientations: self._build(start_orientations=start_css, @@ -712,11 +709,40 @@ def __neg__(self): c.orient() return T + def _start_orientations(self): + """ + Obtain the start orientations according to the current orientation + and components (the latter may be outdated) + """ + start_css = [] + for comp in self.components: + for cs in comp: + if cs.crossing in self.crossings + self.boundary_strands: + s = cs.crossing._adjacent_len // 2 + start_css.append(cs.rotate(s)) + break + + return start_css + def __or__(self, other): - """Put self to left of other. This is like tangle addition but without the fusing of strands. + """ + Put self to left of other. This is like tangle addition but without the fusing of strands. + Preserves the orientations of both tangles, since no gluing happens. >>> (IdentityBraid(1) | CupTangle()).describe() 'Tangle[{1}, {2,3,4}, P[1,2], P[3,4]]' + + >>> T = BraidTangle([1,2,1]) + >>> T.reverse_orientation([1,2]) + >>> T.is_upward() + False + >>> T.boundary_signs + [-1, 1, 1, -1, -1, 1] + >>> TT = T | snappy.RationalTangle(1,2) + >>> TT.is_upward() + False + >>> TT.boundary_signs + [-1, 1, 1, -1, -1, -1, -1, 1, 1, 1] """ A, B = self.copy(), other.copy() (mA, nA), (mB, nB) = A.boundary, B.boundary @@ -724,9 +750,13 @@ def __or__(self, other): entry_points = a[:mA] + b[:mB] + a[mA:] + b[mB:] crossings = A.crossings + A.boundary_strands + B.crossings + B.boundary_strands + start_css = A._start_orientations() + B._start_orientations() + return Tangle((mA + mB, nA + nB), crossings, - entry_points) + entry_points, + start_orientations=start_css, + component_starts=start_css) def copy(self): return pickle.loads(pickle.dumps(self)) From a1aec955be18b23825d73978e15aea768111b7e1 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Thu, 28 May 2026 18:24:17 -0500 Subject: [PATCH 12/53] Fix docstring --- spherogram_src/links/tangles.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spherogram_src/links/tangles.py b/spherogram_src/links/tangles.py index 2ebd433..44c5dcf 100644 --- a/spherogram_src/links/tangles.py +++ b/spherogram_src/links/tangles.py @@ -738,7 +738,7 @@ def __or__(self, other): False >>> T.boundary_signs [-1, 1, 1, -1, -1, 1] - >>> TT = T | snappy.RationalTangle(1,2) + >>> TT = T | RationalTangle(1,2) >>> TT.is_upward() False >>> TT.boundary_signs From dcddb54253592a2ea55666bf8456e28cc6ceb38d Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Sun, 21 Jun 2026 00:11:51 -0500 Subject: [PATCH 13/53] rot_num and long_diagram implemented --- spherogram_src/links/links_base.py | 27 +++++++++ spherogram_src/links/tangles.py | 93 +++++++++++++++++++++++++++++- 2 files changed, 117 insertions(+), 3 deletions(-) diff --git a/spherogram_src/links/links_base.py b/spherogram_src/links/links_base.py index c14acb7..670843c 100644 --- a/spherogram_src/links/links_base.py +++ b/spherogram_src/links/links_base.py @@ -1356,6 +1356,33 @@ def keep(C): A[a] = B[b] return type(self)(final_crossings, check_planarity=False) + + def long_diagram(self, cut_at = None): + """ + Returns the long diagram of self obtained by cutting open the strand specified by cut_at. + + cut_at should be a pair of integers (i, j) representing the j-th strand of the i-th crossing. + If not specified, the first strand of the first crossing will be chosen by default. + + >>> T = Link('4_1').long_diagram() + >>> T.PD_code() + ((1, 1), [(0, 5, 1, 6), (4, 1, 5, 2), (2, 8, 3, 7), (6, 4, 7, 3)], [0, 8]) + """ + from .tangles import Tangle + L = self.copy() + + if cut_at is None: + strand = L.crossings[0].crossing_strands()[0] + else: + i, j = cut_at + strand = L.crossings[i].crossing_strands()[j] + + open_strands = [strand, strand.opposite()] + + for c in L.crossings: + c._clear() + + return Tangle((1,1), L.crossings, open_strands) def __len__(self): return len(self.crossings) diff --git a/spherogram_src/links/tangles.py b/spherogram_src/links/tangles.py index edf60bc..a6be296 100644 --- a/spherogram_src/links/tangles.py +++ b/spherogram_src/links/tangles.py @@ -271,10 +271,15 @@ def make_upward(self): self.reverse_orientation(to_reverse) def entry_points(self): - assert self.is_oriented(), 'Tangle should be oriented to tell the entry points' + assert self.is_oriented(), 'Tangle should be oriented for the entry points to make sense' return [CrossingEntryPoint(self, i) for i in range(self.boundary[0] + self.boundary[1]) if self.boundary_signs[i] == -1] + def exit_points(self): + assert self.is_oriented(), 'Tangle should be oriented for the exit points to make sense' + return [CrossingStrand(self, i) for i in range(self.boundary[0] + self.boundary[1]) + if self.boundary_signs[i] == 1] + def update_label(self, label): self.label = label for i, s in enumerate(self.boundary_strands): @@ -562,8 +567,90 @@ def PD_code(self, KnotTheory=False, min_strand_index = 0): return self.boundary, PD, entry_info def rot_num(self): - #TODO - pass + """ + Requires self to be upward oriented so that the entry strands + of crossings make sense for computing rotation numbers. + + Rotation numbers should always be all zeros for BraidTangles: + + >>> BraidTangle([1,2,1]).rot_num() + [0, 0, 0, 0, 0, 0, 0, 0, 0] + + The following gives the rotation number of a long diagram of the 4_1 knot: + + >>> T1 = Tangle((1,1), [(5,1,6,0), (1,5,2,4),(7,2,8,3),(3,6,4,7)], [0,8]) + >>> T1.rot_num() + [0, 0, 0, 0, 1, -1, -1, 1, 0] + + A (2,2)-tangle obtained by cutting 4_1 twice: + + >>> T2 = Tangle((2,2), [(6,1,7,0), (1,5,2,4),(8,2,9,3),(3,7,4,8)], [0,6,9,5]) + >>> T2.rot_num() + [0, 0, 0, 0, 1, 0, 0, -1, 1, 0] + + Works also for tangles with disconnected shadow graphs: + + >>> (T1|T2).rot_num() + [0, 0, 0, 0, 1, -1, -1, 1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 1, 0] + """ + assert self.is_upward(), 'Tangle should be upward oriented in order to compute rotation numbers' + assert self.boundary[0] == self.boundary[1] + + n = len(self.crossings) + ans = [0 for i in range(2 * n + self.boundary[0])] + + front = [0] + + entry_strands = set([cep.strand_label() for cep in self.entry_points()]) + exit_strands = set([cs.strand_label() for cs in self.exit_points()]) + + to_do = set([s for s in self.strand_labels] + [s for c in self.crossings for s in c.strand_labels]) - exit_strands + + def next_arc(): + inter = set(front) & to_do + if inter: + return min(inter) + else: + arc = min(to_do) + front.append(arc) + return arc + + def entry_crossing(k): + for c in self.crossings: + if k in [cep.strand_label() for cep in c.entry_points()]: + return c + + raise ValueError(f'No crossing contains strand labeled {k}') + + while to_do: + k = next_arc() + + if ~k not in front: + c = entry_crossing(k) + entry_arcs = c.entry_points() if c.sign == -1 else list(reversed(c.entry_points())) + left_label = entry_arcs[0].strand_label() + i = front.index(k) + + if left_label == k: + front[i:i+1] = entry_arcs[1].rotate(2).strand_label(), \ + entry_arcs[0].rotate(2).strand_label(), \ + ~entry_arcs[1].strand_label() + else: + if left_label not in entry_strands: + ans[left_label] += 1 + front[i:i+1] = ~left_label, \ + entry_arcs[1].rotate(2).strand_label(), \ + entry_arcs[0].rotate(2).strand_label() + + elif [s for s in front if s in [k, ~k]] == [k, ~k]: + ans[k] += -1 + + to_do.remove(k) + + for s in self.strand_labels: + assert ans[s] == 0 + + return ans def _component_starts_from_PD(self, code, labels, gluings, entry_dict): """ From a73c8f0c7f686d58a97e057279de243939ed019e Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Tue, 23 Jun 2026 20:09:01 -0500 Subject: [PATCH 14/53] Running version with DictLaurentPolynomial --- setup.py | 6 +- spherogram_src/links/invariants.py | 6 +- .../links/reshetikhin_turaev/RT_network.py | 208 +++ .../links/reshetikhin_turaev/R_matrices.py | 82 +- .../reshetikhin_turaev/R_matrices/V1/Rn.csv | 26 + .../reshetikhin_turaev/R_matrices/V1/Rp.csv | 26 + .../reshetikhin_turaev/R_matrices/V1/hn.csv | 5 + .../reshetikhin_turaev/R_matrices/V1/hp.csv | 5 + .../reshetikhin_turaev/R_matrices/V2/Rn.csv | 178 +++ .../reshetikhin_turaev/R_matrices/V2/Rp.csv | 178 +++ .../reshetikhin_turaev/R_matrices/V2/hn.csv | 9 + .../reshetikhin_turaev/R_matrices/V2/hp.csv | 9 + .../reshetikhin_turaev/R_matrices/V3/Rn.csv | 586 +++++++ .../reshetikhin_turaev/R_matrices/V3/Rp.csv | 586 +++++++ .../reshetikhin_turaev/R_matrices/V3/hn.csv | 13 + .../reshetikhin_turaev/R_matrices/V3/hp.csv | 13 + .../reshetikhin_turaev/R_matrices/V4/Rn.csv | 1378 +++++++++++++++++ .../reshetikhin_turaev/R_matrices/V4/Rp.csv | 1378 +++++++++++++++++ .../reshetikhin_turaev/R_matrices/V4/hn.csv | 17 + .../reshetikhin_turaev/R_matrices/V4/hp.csv | 17 + .../links/reshetikhin_turaev/__init__.py | 6 + .../dict_laurent_polynomial.py | 434 ++++++ .../reshetikhin_turaev/reshetikhin_turaev.py | 16 - .../links/reshetikhin_turaev/sparse_array.py | 271 +++- spherogram_src/links/tangles.py | 4 + 25 files changed, 5393 insertions(+), 64 deletions(-) create mode 100644 spherogram_src/links/reshetikhin_turaev/RT_network.py create mode 100644 spherogram_src/links/reshetikhin_turaev/R_matrices/V1/Rn.csv create mode 100644 spherogram_src/links/reshetikhin_turaev/R_matrices/V1/Rp.csv create mode 100644 spherogram_src/links/reshetikhin_turaev/R_matrices/V1/hn.csv create mode 100644 spherogram_src/links/reshetikhin_turaev/R_matrices/V1/hp.csv create mode 100644 spherogram_src/links/reshetikhin_turaev/R_matrices/V2/Rn.csv create mode 100644 spherogram_src/links/reshetikhin_turaev/R_matrices/V2/Rp.csv create mode 100644 spherogram_src/links/reshetikhin_turaev/R_matrices/V2/hn.csv create mode 100644 spherogram_src/links/reshetikhin_turaev/R_matrices/V2/hp.csv create mode 100644 spherogram_src/links/reshetikhin_turaev/R_matrices/V3/Rn.csv create mode 100644 spherogram_src/links/reshetikhin_turaev/R_matrices/V3/Rp.csv create mode 100644 spherogram_src/links/reshetikhin_turaev/R_matrices/V3/hn.csv create mode 100644 spherogram_src/links/reshetikhin_turaev/R_matrices/V3/hp.csv create mode 100644 spherogram_src/links/reshetikhin_turaev/R_matrices/V4/Rn.csv create mode 100644 spherogram_src/links/reshetikhin_turaev/R_matrices/V4/Rp.csv create mode 100644 spherogram_src/links/reshetikhin_turaev/R_matrices/V4/hn.csv create mode 100644 spherogram_src/links/reshetikhin_turaev/R_matrices/V4/hp.csv create mode 100644 spherogram_src/links/reshetikhin_turaev/__init__.py create mode 100644 spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py delete mode 100644 spherogram_src/links/reshetikhin_turaev/reshetikhin_turaev.py diff --git a/setup.py b/setup.py index b20748a..f232a8d 100644 --- a/setup.py +++ b/setup.py @@ -214,9 +214,11 @@ def run(self): dependency_links = [], packages = ['spherogram', 'spherogram.links', 'spherogram.links.bands', 'spherogram.links.test', 'spherogram.codecs', - 'spherogram.dev', 'spherogram.dev.dev_jennet'], + 'spherogram.dev', 'spherogram.dev.dev_jennet', + 'spherogram.links.reshetikhin_turaev'], package_dir = {'spherogram' : 'spherogram_src', 'spherogram.dev':'dev'}, - package_data = {'spherogram.links' : ['doc.pdf']}, + package_data = {'spherogram.links' : ['doc.pdf'], + 'spherogram.links.reshetikhin_turaev': ['R_matrices/*/*']}, ext_modules = ext_modules, cmdclass = {'clean': SpherogramClean, 'test': SpherogramTest, diff --git a/spherogram_src/links/invariants.py b/spherogram_src/links/invariants.py index 289275b..4779c1d 100644 --- a/spherogram_src/links/invariants.py +++ b/spherogram_src/links/invariants.py @@ -88,7 +88,6 @@ def sage_braid_as_int_word(braid): see the documentation for the "sage_link" method for details. """ - class Link(links_base.Link): __doc__ = links_base.Link.__doc__ + extra_docstring @@ -331,6 +330,11 @@ def alexander_polynomial(self, multivar=True, v='no', method='default', if multivar and factored: # it's easier to view this way return p.factor() return p + + def colored_links_gould_polynomial(self, n): + from .reshetikhin_turaev import colored_links_gould_R_matrices + + return self.long_diagram().apply_reshetikhin_turaev_functor(colored_links_gould_R_matrices(n)).evaluate() def knot_floer_homology(self, prime=2, complex=False): """ diff --git a/spherogram_src/links/reshetikhin_turaev/RT_network.py b/spherogram_src/links/reshetikhin_turaev/RT_network.py new file mode 100644 index 0000000..6c69892 --- /dev/null +++ b/spherogram_src/links/reshetikhin_turaev/RT_network.py @@ -0,0 +1,208 @@ +class DirectedEdge: + __slots__ = ['label', 'index', 'sign', 'reversed_edge'] + + def __init__(self, label, reversed_edge = None): + self.label = label + self.index = max(label, ~label) + self.sign = 1 if label == self.index else -1 + + if reversed_edge is None: + reversed_edge = DirectedEdge(~self.label, self) + self.reversed_edge = reversed_edge + + def __str__(self): + return ('' if self.sign == 1 else '~') + str(self.index) + + def __repr__(self): + return str(self) + + def __hash__(self): + return hash(self.label) + + def __eq__(self, other): + return self.label == other.label + + def __invert__(self): + return self.reversed_edge + +class RTNetwork: + + def __init__(self, tensors, T = None, network = None, rot_num = None, boundary = None, boundary_labels = None): + """ + Represent the tensor network obtained by applying + the Reshetikhin--Turaev functor determined by the + given RMatrix tensors to the tangle T. + + The network is represented as a list of pairs (tensor, legs) + """ + self.tensors = tensors + + if T is not None: + assert T.is_upward(), 'Tangle should be upward for the Reshetikhin--Turaev functor to apply' + self.rot_num = T.rot_num() + self.tangle = T.copy() + + self.boundary = T.boundary + self.boundary_labels = T.strand_labels + + self.edge = edge = dict() + + self.network = network = [] + for c in T.crossings: + labels = c.strand_labels + for lab in labels: + if lab not in edge.keys(): + edge[lab] = DirectedEdge(lab) + + if c.sign == 1: + key = (~edge[labels[3]], + ~edge[labels[0]], + edge[labels[2]], + edge[labels[1]]) + else: + assert c.sign == -1, f'Crossing {c} is not oriented' + key = (~edge[labels[0]], + ~edge[labels[1]], + edge[labels[3]], + edge[labels[2]]) + + network.append((tensors.R(c.sign), key)) + + self.idle_labels = set(self.boundary_labels) + + for arc in self.idle_labels: + if arc not in edge.keys(): + edge[arc] = DirectedEdge(arc) + network.append((tensors.h(0), (~edge[arc], edge[arc]))) + else: + assert all(item is not None for item in (network, rot_num, boundary, boundary_labels)) + self.network = network + self.rot_num = rot_num + self.boundary = boundary + self.boundary_labels = boundary_labels + self.idle_labels = set(boundary_labels) + + self.edge = edge = dict() + for _, key in network: + for e in key: + if e.index not in edge.keys(): + edge[e.index] = e if e.sign == 1 else ~e + + def optimal_contraction_sequence(self): + try: + import numpy as np + import opt_einsum as oe + except ImportError: + raise ModuleNotFoundError('Modules numpy and opt_einsum is required for computing the optimal contraction sequences') + + oe_network = [] + idle = set(self.idle_labels) + for tensor, key in self.network: + if len(key) == 2 and key[0].index == key[1].index: + try: + idle.remove(key[0].index) + except: + raise ValueError(f'key {key[0].index} not found in {idle}') + else: + oe_network.append(np.empty(tensor.shape)) + oe_network.append([edge.index for edge in key]) + + return oe.contract_path(*oe_network, idle)[0] + + def contract_nodes(self, indices): + idx1, idx2 = indices + tensor1, key1 = self.network[idx1] + tensor2, key2 = self.network[idx2] + + pairs = {} + for pos_i, ei in enumerate(key1): + for pos_j, ej in enumerate(key2): + if ei.index == ej.index and ei.sign * ej.sign == -1: + side = 0 if ei.sign == 1 else 1 + pairs[(pos_i, pos_j)] = (side, self.tensors.h(self.rot_num[ei.index])) + + result_tensor = tensor1.decorated_contract(tensor2, pairs) + + contracted1 = {pos_i for pos_i, _ in pairs} + contracted2 = {pos_j for _, pos_j in pairs} + + if idx1 == idx2: + contracted_all = contracted1 | contracted2 + new_key = tuple(e for pos, e in enumerate(key1) if pos not in contracted_all) + self.network.pop(idx1) + else: + new_key = (tuple(e for pos, e in enumerate(key1) if pos not in contracted1) + + tuple(e for pos, e in enumerate(key2) if pos not in contracted2)) + hi, lo = max(idx1, idx2), min(idx1, idx2) + self.network.pop(hi) + self.network.pop(lo) + + del tensor1, tensor2 + + self.network.append((result_tensor, new_key)) + self._resolve_self_loops(len(self.network) - 1) + + def _resolve_self_loops(self, idx): + while True: + _, key = self.network[idx] + pairs = {} + seen = {} + for pos, e in enumerate(key): + if e.index in seen: + other_pos, other_e = seen[e.index] + if e.sign * other_e.sign == -1: + side = 0 if other_e.sign == 1 else 1 + pairs[(other_pos, pos)] = (side, self.tensors.h(self.rot_num[e.index])) + else: + seen[e.index] = (pos, e) + if not pairs: + break + self.contract_nodes((idx, idx)) + idx = len(self.network) - 1 + + def contract_sequence(self, seq): + for indices in seq: + self.contract_nodes(indices) + + def contract_all(self): + for i in range(len(self.network)): + self._resolve_self_loops(i) + self.contract_sequence(self.optimal_contraction_sequence()) + + def evaluate(self): + """ + Fixate all idle labels at value 0, obtaining a new RTNework with (0,0) boundary, + contract all and return the product of all values of the resulting tensors. + """ + assert self.boundary == (1,1) + + new_network = [] + prefactor = 1 + + for tensor, key in self.network: + idle_positions = sorted( + [pos for pos, e in enumerate(key) if e.index in self.idle_labels], + reverse=True + ) + non_idle_key = tuple(e for e in key if e.index not in self.idle_labels) + t = tensor + for pos in idle_positions: + t = t.fixate(pos, 0) + if t.rank == 0: + prefactor *= t[()] + else: + new_network.append((t, non_idle_key)) + + reduced = RTNetwork( + self.tensors, + network=new_network, + rot_num=self.rot_num, + boundary=(0, 0), + boundary_labels=[] + ) + reduced.contract_all() + + result = prefactor + for tensor, _ in reduced.network: + result *= tensor[()] + return result diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices.py b/spherogram_src/links/reshetikhin_turaev/R_matrices.py index 8ec2de8..b750443 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices.py +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices.py @@ -1,5 +1,83 @@ -from ...sage_helper import _within_sage +from .dict_laurent_polynomial import DictLaurentPolynomial from .sparse_array import SparseTensor -cache = dict() \ No newline at end of file +import csv, ast, pathlib, os + +dir_path = pathlib.Path(__file__).resolve().parent + +_cache = dict() + +def laurent_sparse_tensor_from_file(file, vars = ['t', 'q']): + reader = csv.reader(file) + header = next(reader) + shape = ast.literal_eval(header[0]) + + assert header[1] == 'LaurentPolynomial', f'Expected type LaurentPolynomial, got {header[1]}' + + data = dict() + for line in reader: + key, value = line + key = tuple(ast.literal_eval(key)) + assert key not in data.keys(), f'{key} appeared multiple times in {file.name}' + value = DictLaurentPolynomial.from_str(value, vars = vars) + data[key] = value + + return SparseTensor(shape = shape, data = data) + +def laurent_sparse_tensor_from_path(path, vars = ['t', 'q'], compressed = False): + if compressed: + import bz2 + with bz2.open(path, 'rt') as f: + return laurent_sparse_tensor_from_file(f, vars = vars) + else: + with open(path, 'r') as f: + return laurent_sparse_tensor_from_file(f, vars = vars) + +class RMatrix: + __slots__ = ['_R', '_h', '_id'] + + def __init__(self, Rp, Rm, hp, hm): + self._R = (Rp, Rm) + self._h = (hp, hm) + + self._id = SparseTensor(hp.shape, data = {(i,i): 1 for i in range(hp.shape[0])}) + + def R(self, sign): + if sign == 1: + return self._R[0].copy() + else: + assert sign == -1 + return self._R[1].copy() + + def h(self, sign): + if sign == 1: + return self._h[0].copy() + elif sign == -1: + return self._h[1].copy() + else: + assert sign == 0 + return self._id + + @staticmethod + def laurent_R_from_directory(dir_path, vars = ['t', 'q'], compressed = False): + names = [name + '.csv' + ('.bz2' if compressed else '') + for name in ['Rp', 'Rn', 'hp', 'hn']] + + tensors = [laurent_sparse_tensor_from_path(os.path.join(dir_path, name), + vars = vars, + compressed = compressed) + for name in names] + + return RMatrix(*tensors) + +def colored_links_gould_R_matrices(n): + if 0 < n <= 4: + key = f'V{n}' + if key in _cache.keys(): + return _cache[key] + else: + _cache[key] = RMatrix.laurent_R_from_directory(dir_path = os.path.join(dir_path, f'R_matrices/V{n}/')) + return _cache[key] + else: + raise NotImplementedError \ No newline at end of file diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/Rn.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/Rn.csv new file mode 100644 index 0000000..d74d959 --- /dev/null +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/Rn.csv @@ -0,0 +1,26 @@ +"(4,4,4,4)","LaurentPolynomial" +"(0,0,0,0)","1" +"(0,1,1,0)","1" +"(0,2,2,0)","1" +"(0,3,3,0)","1" +"(1,0,0,1)","q*t" +"(1,0,1,0)","1-q*t" +"(1,1,1,1)","-(q*t)" +"(1,2,2,1)","t/q" +"(1,2,3,0)","1" +"(1,3,3,1)","-(t/q)" +"(2,0,0,2)","q/t" +"(2,1,1,2)","q/t" +"(2,0,2,0)","1-q/t" +"(2,2,2,2)","-(q/t)" +"(2,1,3,0)","-(q/t)" +"(2,3,3,2)","-(q/t)" +"(3,0,0,3)","q^2" +"(3,0,1,2)","-1-q^2+q/t+q*t" +"(3,1,1,3)","-q^2" +"(3,0,2,1)","-1+t/q+q*t-t^2" +"(3,2,2,3)","-1" +"(3,0,3,0)","2-q/t-q*t" +"(3,1,3,1)","1-q*t" +"(3,2,3,2)","1-q/t" +"(3,3,3,3)","1" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/Rp.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/Rp.csv new file mode 100644 index 0000000..b48550e --- /dev/null +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/Rp.csv @@ -0,0 +1,26 @@ +"(4,4,4,4)","LaurentPolynomial" +"(0,0,0,0)","1" +"(0,1,0,1)","1-1/(q*t)" +"(0,2,0,2)","1-t/q" +"(0,3,0,3)","2-1/(q*t)-t/q" +"(0,1,1,0)","1/(q*t)" +"(0,3,1,2)","-1-q^(-2)+1/(q*t)+t/q" +"(0,2,2,0)","t/q" +"(0,3,2,1)","-q^(-2)+t/q^3+t/q-t^2/q^2" +"(0,3,3,0)","q^(-2)" +"(1,0,0,1)","1" +"(1,2,0,3)","1" +"(1,1,1,1)","-(1/(q*t))" +"(1,3,1,3)","1-1/(q*t)" +"(1,2,2,1)","t/q" +"(1,3,3,1)","-q^(-2)" +"(2,0,0,2)","1" +"(2,1,0,3)","-(q/t)" +"(2,1,1,2)","q/t" +"(2,2,2,2)","-(t/q)" +"(2,3,2,3)","1-t/q" +"(2,3,3,2)","-1" +"(3,0,0,3)","1" +"(3,1,1,3)","-(q/t)" +"(3,2,2,3)","-(t/q)" +"(3,3,3,3)","1" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/hn.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/hn.csv new file mode 100644 index 0000000..fd6c3b1 --- /dev/null +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/hn.csv @@ -0,0 +1,5 @@ +"(4,4)","LaurentPolynomial" +"(0,0)","1" +"(1,1)","-1" +"(2,2)","-1" +"(3,3)","1" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/hp.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/hp.csv new file mode 100644 index 0000000..fd6c3b1 --- /dev/null +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/hp.csv @@ -0,0 +1,5 @@ +"(4,4)","LaurentPolynomial" +"(0,0)","1" +"(1,1)","-1" +"(2,2)","-1" +"(3,3)","1" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/Rn.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/Rn.csv new file mode 100644 index 0000000..5673d80 --- /dev/null +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/Rn.csv @@ -0,0 +1,178 @@ +"(8,8,8,8)","LaurentPolynomial" +"(0,0,0,0)","1" +"(0,1,1,0)","1" +"(0,2,2,0)","1" +"(0,3,3,0)","1" +"(0,4,4,0)","1" +"(0,5,5,0)","1" +"(0,6,6,0)","1" +"(0,7,7,0)","1" +"(1,0,0,1)","q*t" +"(1,0,1,0)","1-q*t" +"(1,1,1,1)","-(q*t)" +"(1,2,2,1)","t" +"(1,2,3,0)","1" +"(1,3,3,1)","-t" +"(1,2,4,0)","-t" +"(1,4,4,1)","-t" +"(1,3,5,0)","t" +"(1,4,5,0)","1" +"(1,5,5,1)","t" +"(1,6,6,1)","-(t/q)" +"(1,6,7,0)","1" +"(1,7,7,1)","t/q" +"(2,0,0,2)","q/t" +"(2,1,1,2)","q/t" +"(2,0,2,0)","1-q/t" +"(2,2,2,2)","-(q/t)" +"(2,1,3,0)","-(q/t)" +"(2,3,3,2)","-(q/t)" +"(2,1,4,0)","1" +"(2,4,4,2)","-(q/t)" +"(2,5,5,2)","-(q/t)" +"(2,3,6,0)","1" +"(2,4,6,0)","q/t" +"(2,6,6,2)","q/t" +"(2,5,7,0)","q/t" +"(2,7,7,2)","q/t" +"(3,0,0,3)","q^2" +"(3,0,1,2)","-q^2+q/t" +"(3,1,1,3)","-q^2" +"(3,0,2,1)","-q+t" +"(3,2,2,3)","-q" +"(3,0,3,0)","1-q/t" +"(3,1,3,1)","q" +"(3,2,3,2)","-(q/t)" +"(3,3,3,3)","q" +"(3,0,4,0)","q-t" +"(3,1,4,1)","-t" +"(3,2,4,2)","q" +"(3,4,4,3)","q" +"(3,1,5,0)","1-q" +"(3,3,5,2)","-q" +"(3,4,5,2)","-(q/t)" +"(3,5,5,3)","-q" +"(3,3,6,1)","-(t/q)" +"(3,4,6,1)","-1" +"(3,6,6,3)","-1" +"(3,3,7,0)","1" +"(3,4,7,0)","q/t" +"(3,5,7,1)","1" +"(3,6,7,2)","q/t" +"(3,7,7,3)","1" +"(4,0,0,4)","q^2" +"(4,0,1,2)","-q^2+q/t" +"(4,1,1,4)","-q^2" +"(4,0,2,1)","-q^2+q*t" +"(4,2,2,4)","-q" +"(4,0,3,0)","q^2-q/t" +"(4,1,3,1)","q^2" +"(4,2,3,2)","-(q/t)" +"(4,3,3,4)","q" +"(4,0,4,0)","1-q*t" +"(4,1,4,1)","-(q*t)" +"(4,2,4,2)","q" +"(4,4,4,4)","q" +"(4,3,5,2)","-q" +"(4,4,5,2)","-(q/t)" +"(4,5,5,4)","-q" +"(4,2,6,0)","1-q" +"(4,3,6,1)","-t" +"(4,4,6,1)","-q" +"(4,6,6,4)","-1" +"(4,3,7,0)","q" +"(4,4,7,0)","q/t" +"(4,5,7,1)","q" +"(4,6,7,2)","q/t" +"(4,7,7,4)","1" +"(5,0,0,5)","q^3*t" +"(5,0,1,3)","-q^2+q^3*t" +"(5,0,1,4)","q^2-q^3*t" +"(5,1,1,5)","q^3*t" +"(5,2,2,5)","-(q*t)" +"(5,0,3,1)","q-q^2+q*t-q^2*t" +"(5,2,3,3)","q" +"(5,2,3,4)","-q" +"(5,3,3,5)","-(q*t)" +"(5,0,4,1)","-t+q^2*t" +"(5,2,4,3)","-(q*t)" +"(5,2,4,4)","q*t" +"(5,4,4,5)","-(q*t)" +"(5,0,5,0)","1-q-q*t+q^2*t" +"(5,1,5,1)","-(q*t)+q^2*t" +"(5,3,5,3)","-(q*t)" +"(5,4,5,3)","-q" +"(5,3,5,4)","q*t" +"(5,4,5,4)","q" +"(5,5,5,5)","-(q*t)" +"(5,2,6,1)","t-t/q" +"(5,6,6,5)","t/q" +"(5,2,7,0)","1-q" +"(5,4,7,1)","1-q" +"(5,6,7,3)","1" +"(5,6,7,4)","-1" +"(5,7,7,5)","t/q" +"(6,0,0,6)","q^3/t" +"(6,1,1,6)","-(q^3/t)" +"(6,0,2,3)","q^2-q^3/t" +"(6,0,2,4)","-q+q^2/t" +"(6,2,2,6)","q^2/t" +"(6,0,3,2)","-(q/t)+q^3/t" +"(6,1,3,3)","q^3/t" +"(6,1,3,4)","-(q^2/t)" +"(6,3,3,6)","-(q^2/t)" +"(6,0,4,2)","q-q^2+q/t-q^2/t" +"(6,1,4,3)","-q^2" +"(6,1,4,4)","q" +"(6,4,4,6)","-(q^2/t)" +"(6,1,5,2)","-(q/t)+q^2/t" +"(6,5,5,6)","q^2/t" +"(6,0,6,0)","1-q-q/t+q^2/t" +"(6,2,6,2)","-(q/t)+q^2/t" +"(6,3,6,3)","q" +"(6,4,6,3)","q^2/t" +"(6,3,6,4)","-1" +"(6,4,6,4)","-(q/t)" +"(6,6,6,6)","-(q/t)" +"(6,1,7,0)","q/t-q^2/t" +"(6,3,7,2)","q/t-q^2/t" +"(6,5,7,3)","-(q^2/t)" +"(6,5,7,4)","q/t" +"(6,7,7,6)","q/t" +"(7,0,0,7)","q^4" +"(7,0,1,6)","-q^2-q^4+q^3/t+q^3*t" +"(7,1,1,7)","q^4" +"(7,0,2,5)","q^2-q*t-q^3*t+q^2*t^2" +"(7,2,2,7)","q^2" +"(7,0,3,3)","q+2*q^2-q^3-q^3/t-q^3*t" +"(7,0,3,4)","-2*q+q^2/t-q*t+q^2*t+q^3*t" +"(7,1,3,5)","q^2-q^3*t" +"(7,2,3,6)","-q+q^2/t" +"(7,3,3,7)","q^2" +"(7,0,4,3)","-q+q^2+q^3-2*q*t+q^2*t^2" +"(7,0,4,4)","-q^2+t+2*q*t-q^2*t-q^2*t^2" +"(7,1,4,5)","-(q*t)+q^2*t^2" +"(7,2,4,6)","-q^2+q*t" +"(7,4,4,7)","q^2" +"(7,0,5,2)","-1+q-q^2+q^3+q/t-q^2/t+q*t-q^2*t" +"(7,1,5,3)","-q+q^3" +"(7,1,5,4)","q-q^2+q*t-q^2*t" +"(7,3,5,6)","-q^2+q*t" +"(7,4,5,6)","q-q^2/t" +"(7,5,5,7)","q^2" +"(7,0,6,1)","1-q+t-t/q-q*t+q^2*t+t^2-q*t^2" +"(7,2,6,3)","1-q+t-q*t" +"(7,2,6,4)","-(t/q)+q*t" +"(7,3,6,5)","t/q-t^2" +"(7,4,6,5)","1-q*t" +"(7,6,6,7)","1" +"(7,0,7,0)","2-2*q-q/t+q^2/t-q*t+q^2*t" +"(7,1,7,1)","1-q-q*t+q^2*t" +"(7,2,7,2)","1-q-q/t+q^2/t" +"(7,3,7,3)","1-q*t" +"(7,4,7,3)","-q+q^2/t" +"(7,3,7,4)","-1+q*t" +"(7,4,7,4)","1-q/t" +"(7,5,7,5)","1-q*t" +"(7,6,7,6)","1-q/t" +"(7,7,7,7)","1" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/Rp.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/Rp.csv new file mode 100644 index 0000000..865785f --- /dev/null +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/Rp.csv @@ -0,0 +1,178 @@ +"(8,8,8,8)","LaurentPolynomial" +"(0,0,0,0)","1" +"(0,1,0,1)","1-1/(q*t)" +"(0,2,0,2)","1-t/q" +"(0,3,0,3)","1-1/(q*t)" +"(0,4,0,3)","q^(-1)-t^(-1)" +"(0,3,0,4)","q^(-2)-t/q" +"(0,4,0,4)","1-t/q" +"(0,5,0,5)","1-q^(-1)+1/(q^2*t)-1/(q*t)" +"(0,6,0,6)","1-q^(-1)+t/q^2-t/q" +"(0,7,0,7)","2-2/q+1/(q^2*t)-1/(q*t)+t/q^2-t/q" +"(0,1,1,0)","1/(q*t)" +"(0,3,1,2)","-q^(-2)+1/(q*t)" +"(0,4,1,2)","-q^(-1)+t^(-1)" +"(0,5,1,3)","-t^(-1)+1/(q^2*t)" +"(0,5,1,4)","-q^(-2)+q^(-1)-1/(q^2*t)+1/(q*t)" +"(0,7,1,6)","-1+q^(-3)-q^(-2)+q^(-1)-1/(q^2*t)+1/(q*t)-t/q^2+t/q" +"(0,2,2,0)","t/q" +"(0,3,2,1)","-q^(-2)+t/q" +"(0,4,2,1)","-q^(-2)+t/q" +"(0,6,2,3)","-q^(-2)+q^(-1)-t/q^2+t/q" +"(0,6,2,4)","t/q^3-t/q" +"(0,7,2,5)","-q^(-3)+q^(-2)+t/q^4-t/q^3+t/q^2-t/q-t^2/q^3+t^2/q^2" +"(0,3,3,0)","q^(-2)" +"(0,5,3,1)","q^(-2)-1/(q^3*t)" +"(0,6,3,2)","-q^(-1)+t/q^2" +"(0,7,3,3)","-q^(-3)+2/q^2+q^(-1)-1/(q^3*t)-t/q^3" +"(0,7,3,4)","q^(-4)+q^(-3)-q^(-2)-(2*t)/q^2+t^2/q^3" +"(0,4,4,0)","q^(-2)" +"(0,5,4,1)","-q^(-2)+1/(q^3*t)" +"(0,6,4,2)","q^(-2)-t/q^3" +"(0,7,4,3)","-2/q^2+1/(q^3*t)+t/q^4+t/q^3-t/q^2" +"(0,7,4,4)","-q^(-4)-t/q^4+(2*t)/q^3+t/q^2-t^2/q^4" +"(0,5,5,0)","1/(q^3*t)" +"(0,7,5,2)","-q^(-4)-q^(-2)+1/(q^3*t)+t/q^3" +"(0,6,6,0)","t/q^3" +"(0,7,6,1)","q^(-4)-t/q^5-t/q^3+t^2/q^4" +"(0,7,7,0)","q^(-4)" +"(1,0,0,1)","1" +"(1,2,0,3)","1" +"(1,2,0,4)","-(t/q)" +"(1,4,0,5)","1-q^(-1)" +"(1,6,0,7)","1-q^(-1)" +"(1,1,1,1)","-(1/(q*t))" +"(1,3,1,3)","-(1/(q*t))" +"(1,4,1,3)","-t^(-1)" +"(1,3,1,4)","q^(-2)" +"(1,4,1,4)","q^(-1)" +"(1,5,1,5)","1/(q^2*t)-1/(q*t)" +"(1,7,1,7)","1-q^(-1)+1/(q^2*t)-1/(q*t)" +"(1,2,2,1)","t/q" +"(1,6,2,5)","t/q^2-t/q" +"(1,3,3,1)","-q^(-2)" +"(1,6,3,3)","q^(-1)" +"(1,6,3,4)","-(t/q^2)" +"(1,7,3,5)","-q^(-3)+q^(-2)-t/q^3+t/q^2" +"(1,4,4,1)","-q^(-2)" +"(1,6,4,3)","-q^(-2)" +"(1,6,4,4)","t/q^3" +"(1,7,4,5)","t/q^4-t/q^2" +"(1,5,5,1)","1/(q^3*t)" +"(1,7,5,3)","-q^(-2)+1/(q^3*t)" +"(1,7,5,4)","-q^(-4)+t/q^3" +"(1,6,6,1)","-(t/q^3)" +"(1,7,7,1)","q^(-4)" +"(2,0,0,2)","1" +"(2,1,0,3)","-t^(-1)" +"(2,1,0,4)","1" +"(2,3,0,6)","1-q^(-1)" +"(2,5,0,7)","-t^(-1)+q/t" +"(2,1,1,2)","t^(-1)" +"(2,5,1,6)","t^(-1)-q/t" +"(2,2,2,2)","-(t/q)" +"(2,3,2,3)","q^(-1)" +"(2,4,2,3)","q^(-1)" +"(2,3,2,4)","-(t/q)" +"(2,4,2,4)","-(t/q)" +"(2,6,2,6)","t/q^2-t/q" +"(2,7,2,7)","1-q^(-1)+t/q^2-t/q" +"(2,3,3,2)","-q^(-1)" +"(2,5,3,3)","1/(q*t)" +"(2,5,3,4)","-q^(-1)" +"(2,7,3,6)","-1+q^(-2)" +"(2,4,4,2)","-q^(-1)" +"(2,5,4,3)","-(1/(q*t))" +"(2,5,4,4)","q^(-1)" +"(2,7,4,6)","-q^(-2)+q^(-1)-t/q^2+t/q" +"(2,5,5,2)","-(1/(q*t))" +"(2,6,6,2)","t/q^2" +"(2,7,6,3)","q^(-2)-t/q^3" +"(2,7,6,4)","-(t/q^2)+t^2/q^3" +"(2,7,7,2)","q^(-2)" +"(3,0,0,3)","1" +"(3,1,0,5)","1" +"(3,2,0,6)","t/q" +"(3,3,0,7)","1" +"(3,4,0,7)","1" +"(3,1,1,3)","-t^(-1)" +"(3,3,1,6)","-q^(-1)" +"(3,4,1,6)","-1" +"(3,5,1,7)","-t^(-1)+q/t" +"(3,2,2,3)","-(t/q)" +"(3,3,2,5)","-(t/q)" +"(3,4,2,5)","-(t/q)" +"(3,3,3,3)","q^(-1)" +"(3,5,3,5)","q^(-1)" +"(3,6,3,6)","-(t/q)" +"(3,7,3,7)","1-t/q" +"(3,4,4,3)","q^(-1)" +"(3,5,4,5)","-q^(-1)" +"(3,6,4,6)","t/q^2" +"(3,7,4,7)","-q^(-1)+t/q^2" +"(3,5,5,3)","-(1/(q*t))" +"(3,7,5,6)","-q^(-2)+t/q" +"(3,6,6,3)","-(t/q^2)" +"(3,7,6,5)","t/q^2-t^2/q^3" +"(3,7,7,3)","q^(-2)" +"(4,0,0,4)","1" +"(4,1,0,5)","t^(-1)" +"(4,2,0,6)","1" +"(4,3,0,7)","t^(-1)" +"(4,4,0,7)","q/t" +"(4,1,1,4)","-t^(-1)" +"(4,3,1,6)","-t^(-1)" +"(4,4,1,6)","-(q/t)" +"(4,2,2,4)","-(t/q)" +"(4,3,2,5)","-q^(-1)" +"(4,4,2,5)","-q^(-1)" +"(4,6,2,7)","1-q^(-1)" +"(4,3,3,4)","q^(-1)" +"(4,5,3,5)","1/(q*t)" +"(4,6,3,6)","-1" +"(4,7,3,7)","-1+1/(q*t)" +"(4,4,4,4)","q^(-1)" +"(4,5,4,5)","-(1/(q*t))" +"(4,6,4,6)","q^(-1)" +"(4,7,4,7)","1-1/(q*t)" +"(4,5,5,4)","-(1/(q*t))" +"(4,7,5,6)","1-1/(q*t)" +"(4,6,6,4)","-(t/q^2)" +"(4,7,6,5)","q^(-2)-t/q^3" +"(4,7,7,4)","q^(-2)" +"(5,0,0,5)","1" +"(5,2,0,7)","1" +"(5,1,1,5)","t^(-1)" +"(5,3,1,7)","t^(-1)" +"(5,4,1,7)","q/t" +"(5,2,2,5)","-(t/q)" +"(5,3,3,5)","-q^(-1)" +"(5,6,3,7)","1" +"(5,4,4,5)","-q^(-1)" +"(5,6,4,7)","-q^(-1)" +"(5,5,5,5)","-(1/(q*t))" +"(5,7,5,7)","1-1/(q*t)" +"(5,6,6,5)","t/q^2" +"(5,7,7,5)","q^(-2)" +"(6,0,0,6)","1" +"(6,1,0,7)","q/t" +"(6,1,1,6)","-(q/t)" +"(6,2,2,6)","t/q" +"(6,3,2,7)","1" +"(6,4,2,7)","1" +"(6,3,3,6)","-1" +"(6,5,3,7)","-(q/t)" +"(6,4,4,6)","-1" +"(6,5,4,7)","q/t" +"(6,5,5,6)","q/t" +"(6,6,6,6)","-(t/q)" +"(6,7,6,7)","1-t/q" +"(6,7,7,6)","1" +"(7,0,0,7)","1" +"(7,1,1,7)","q/t" +"(7,2,2,7)","t/q" +"(7,3,3,7)","1" +"(7,4,4,7)","1" +"(7,5,5,7)","q/t" +"(7,6,6,7)","t/q" +"(7,7,7,7)","1" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/hn.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/hn.csv new file mode 100644 index 0000000..8c4464a --- /dev/null +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/hn.csv @@ -0,0 +1,9 @@ +"(8,8)","LaurentPolynomial" +"(0,0)","1" +"(1,1)","-1" +"(2,2)","-1" +"(3,3)","1" +"(4,4)","1" +"(5,5)","-1" +"(6,6)","-1" +"(7,7)","1" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/hp.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/hp.csv new file mode 100644 index 0000000..8c4464a --- /dev/null +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/hp.csv @@ -0,0 +1,9 @@ +"(8,8)","LaurentPolynomial" +"(0,0)","1" +"(1,1)","-1" +"(2,2)","-1" +"(3,3)","1" +"(4,4)","1" +"(5,5)","-1" +"(6,6)","-1" +"(7,7)","1" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/Rn.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/Rn.csv new file mode 100644 index 0000000..f8ce0ab --- /dev/null +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/Rn.csv @@ -0,0 +1,586 @@ +"(12,12,12,12)","LaurentPolynomial" +"(0,0,0,0)","1" +"(0,1,1,0)","1" +"(0,2,2,0)","1" +"(0,3,3,0)","1" +"(0,4,4,0)","1" +"(0,5,5,0)","1" +"(0,6,6,0)","1" +"(0,7,7,0)","1" +"(0,8,8,0)","1" +"(0,9,9,0)","1" +"(0,10,10,0)","1" +"(0,11,11,0)","1" +"(1,0,0,1)","q^3*t" +"(1,0,1,0)","1-q^3*t" +"(1,1,1,1)","-(q^3*t)" +"(1,2,2,1)","q*t*Subscript[q,1,2]" +"(1,2,3,0)","1" +"(1,3,3,1)","-(q*t*Subscript[q,1,2])" +"(1,2,4,0)","-(q*t*Subscript[q,1,2])" +"(1,4,4,1)","-(q*t*Subscript[q,1,2])" +"(1,3,5,0)","q*t*Subscript[q,1,2]" +"(1,4,5,0)","1" +"(1,5,5,1)","q*t*Subscript[q,1,2]" +"(1,6,6,1)","-((t*Subscript[q,1,2]^2)/q)" +"(1,6,7,0)","1" +"(1,7,7,1)","(t*Subscript[q,1,2]^2)/q" +"(1,6,8,0)","(t*Subscript[q,1,2]^2)/q" +"(1,8,8,1)","(t*Subscript[q,1,2]^2)/q" +"(1,7,9,0)","-((t*Subscript[q,1,2]^2)/q)" +"(1,8,9,0)","1" +"(1,9,9,1)","-((t*Subscript[q,1,2]^2)/q)" +"(1,10,10,1)","(t*Subscript[q,1,2]^3)/q^3" +"(1,10,11,0)","1" +"(1,11,11,1)","-((t*Subscript[q,1,2]^3)/q^3)" +"(2,0,0,2)","q^3/t" +"(2,1,1,2)","q^3/(t*Subscript[q,1,2])" +"(2,0,2,0)","1-q^3/t" +"(2,2,2,2)","-(q^3/t)" +"(2,1,3,0)","-(q^3/(t*Subscript[q,1,2]))" +"(2,3,3,2)","-(q^3/(t*Subscript[q,1,2]))" +"(2,1,4,0)","1" +"(2,4,4,2)","-(q^3/(t*Subscript[q,1,2]))" +"(2,5,5,2)","-(q^3/(t*Subscript[q,1,2]^2))" +"(2,3,6,0)","1" +"(2,4,6,0)","q^3/(t*Subscript[q,1,2])" +"(2,6,6,2)","q^3/(t*Subscript[q,1,2])" +"(2,5,7,0)","q^3/(t*Subscript[q,1,2]^2)" +"(2,7,7,2)","q^3/(t*Subscript[q,1,2]^2)" +"(2,5,8,0)","1" +"(2,8,8,2)","q^3/(t*Subscript[q,1,2]^2)" +"(2,9,9,2)","q^3/(t*Subscript[q,1,2]^3)" +"(2,7,10,0)","1" +"(2,8,10,0)","-(q^3/(t*Subscript[q,1,2]^2))" +"(2,10,10,2)","-(q^3/(t*Subscript[q,1,2]^2))" +"(2,9,11,0)","-(q^3/(t*Subscript[q,1,2]^3))" +"(2,11,11,2)","-(q^3/(t*Subscript[q,1,2]^3))" +"(3,0,0,3)","q^6" +"(3,0,1,2)","-q^6+q^3/t" +"(3,1,1,3)","-(q^6/Subscript[q,1,2])" +"(3,0,2,1)","-(q^4*Subscript[q,1,2])+q*t*Subscript[q,1,2]" +"(3,2,2,3)","-(q^4*Subscript[q,1,2])" +"(3,0,3,0)","1-q^3/t" +"(3,1,3,1)","q^4" +"(3,2,3,2)","-(q^3/t)" +"(3,3,3,3)","q^4" +"(3,0,4,0)","q^4*Subscript[q,1,2]-q*t*Subscript[q,1,2]" +"(3,1,4,1)","-(q*t*Subscript[q,1,2])" +"(3,2,4,2)","q^4*Subscript[q,1,2]" +"(3,4,4,3)","q^4" +"(3,1,5,0)","1-q^4" +"(3,3,5,2)","-q^4" +"(3,4,5,2)","-(q^3/(t*Subscript[q,1,2]))" +"(3,5,5,3)","-(q^4/Subscript[q,1,2])" +"(3,3,6,1)","-((t*Subscript[q,1,2]^2)/q)" +"(3,4,6,1)","-(q^2*Subscript[q,1,2])" +"(3,6,6,3)","-(q^2*Subscript[q,1,2])" +"(3,3,7,0)","1" +"(3,4,7,0)","q^3/(t*Subscript[q,1,2])" +"(3,5,7,1)","q^2" +"(3,6,7,2)","q^3/(t*Subscript[q,1,2])" +"(3,7,7,3)","q^2" +"(3,3,8,0)","(t*Subscript[q,1,2]^2)/q" +"(3,4,8,0)","q^2*Subscript[q,1,2]" +"(3,5,8,1)","(t*Subscript[q,1,2]^2)/q" +"(3,6,8,2)","q^2*Subscript[q,1,2]" +"(3,8,8,3)","q^2" +"(3,5,9,0)","1-q^2" +"(3,7,9,2)","-q^2" +"(3,8,9,2)","q^3/(t*Subscript[q,1,2]^2)" +"(3,9,9,3)","-(q^2/Subscript[q,1,2])" +"(3,7,10,1)","(t*Subscript[q,1,2]^3)/q^3" +"(3,8,10,1)","-Subscript[q,1,2]" +"(3,10,10,3)","-Subscript[q,1,2]" +"(3,7,11,0)","1" +"(3,8,11,0)","-(q^3/(t*Subscript[q,1,2]^2))" +"(3,9,11,1)","1" +"(3,10,11,2)","-(q^3/(t*Subscript[q,1,2]^2))" +"(3,11,11,3)","1" +"(4,0,0,4)","q^6" +"(4,0,1,2)","-(q^6/Subscript[q,1,2])+q^3/(t*Subscript[q,1,2])" +"(4,1,1,4)","-(q^6/Subscript[q,1,2])" +"(4,0,2,1)","-q^6+q^3*t" +"(4,2,2,4)","-(q^4*Subscript[q,1,2])" +"(4,0,3,0)","q^6/Subscript[q,1,2]-q^3/(t*Subscript[q,1,2])" +"(4,1,3,1)","q^6/Subscript[q,1,2]" +"(4,2,3,2)","-(q^3/(t*Subscript[q,1,2]))" +"(4,3,3,4)","q^4" +"(4,0,4,0)","1-q^3*t" +"(4,1,4,1)","-(q^3*t)" +"(4,2,4,2)","q^4" +"(4,4,4,4)","q^4" +"(4,3,5,2)","-(q^4/Subscript[q,1,2])" +"(4,4,5,2)","-(q^3/(t*Subscript[q,1,2]^2))" +"(4,5,5,4)","-(q^4/Subscript[q,1,2])" +"(4,2,6,0)","1-q^4" +"(4,3,6,1)","-(q*t*Subscript[q,1,2])" +"(4,4,6,1)","-q^4" +"(4,6,6,4)","-(q^2*Subscript[q,1,2])" +"(4,3,7,0)","q^4/Subscript[q,1,2]" +"(4,4,7,0)","q^3/(t*Subscript[q,1,2]^2)" +"(4,5,7,1)","q^4/Subscript[q,1,2]" +"(4,6,7,2)","q^3/(t*Subscript[q,1,2]^2)" +"(4,7,7,4)","q^2" +"(4,3,8,0)","q*t*Subscript[q,1,2]" +"(4,4,8,0)","1" +"(4,5,8,1)","q*t*Subscript[q,1,2]" +"(4,6,8,2)","q^2" +"(4,8,8,4)","q^2" +"(4,7,9,2)","-(q^2/Subscript[q,1,2])" +"(4,8,9,2)","q^3/(t*Subscript[q,1,2]^3)" +"(4,9,9,4)","-(q^2/Subscript[q,1,2])" +"(4,6,10,0)","1-q^2" +"(4,7,10,1)","(t*Subscript[q,1,2]^2)/q" +"(4,8,10,1)","-q^2" +"(4,10,10,4)","-Subscript[q,1,2]" +"(4,7,11,0)","q^2/Subscript[q,1,2]" +"(4,8,11,0)","-(q^3/(t*Subscript[q,1,2]^3))" +"(4,9,11,1)","q^2/Subscript[q,1,2]" +"(4,10,11,2)","-(q^3/(t*Subscript[q,1,2]^3))" +"(4,11,11,4)","1" +"(5,0,0,5)","q^9*t" +"(5,0,1,3)","-(q^6/Subscript[q,1,2])+(q^9*t)/Subscript[q,1,2]" +"(5,0,1,4)","q^6-q^9*t" +"(5,1,1,5)","(q^9*t)/Subscript[q,1,2]" +"(5,2,2,5)","-(q^5*t*Subscript[q,1,2]^2)" +"(5,0,3,1)","q^4-q^6+q^3*t-q^7*t" +"(5,2,3,3)","q^4" +"(5,2,3,4)","-(q^4*Subscript[q,1,2])" +"(5,3,3,5)","-(q^5*t*Subscript[q,1,2])" +"(5,0,4,1)","-(q*t*Subscript[q,1,2])+q^7*t*Subscript[q,1,2]" +"(5,2,4,3)","-(q^5*t*Subscript[q,1,2])" +"(5,2,4,4)","q^5*t*Subscript[q,1,2]^2" +"(5,4,4,5)","-(q^5*t*Subscript[q,1,2])" +"(5,0,5,0)","1-q^4-q^3*t+q^7*t" +"(5,1,5,1)","-(q^3*t)+q^7*t" +"(5,3,5,3)","-(q^5*t)" +"(5,4,5,3)","-(q^4/Subscript[q,1,2])" +"(5,3,5,4)","q^5*t*Subscript[q,1,2]" +"(5,4,5,4)","q^4" +"(5,5,5,5)","-(q^5*t)" +"(5,2,6,1)","-((t*Subscript[q,1,2]^2)/q)+q^3*t*Subscript[q,1,2]^2" +"(5,6,6,5)","q*t*Subscript[q,1,2]^3" +"(5,2,7,0)","1-q^4" +"(5,3,7,1)","-(q*t*Subscript[q,1,2])+q^3*t*Subscript[q,1,2]" +"(5,4,7,1)","q^2-q^4" +"(5,6,7,3)","q^2" +"(5,6,7,4)","-(q^2*Subscript[q,1,2])" +"(5,7,7,5)","q*t*Subscript[q,1,2]^2" +"(5,2,8,0)","(t*Subscript[q,1,2]^2)/q-q^3*t*Subscript[q,1,2]^2" +"(5,4,8,1)","(t*Subscript[q,1,2]^2)/q-q^3*t*Subscript[q,1,2]^2" +"(5,6,8,3)","q*t*Subscript[q,1,2]^2" +"(5,6,8,4)","-(q*t*Subscript[q,1,2]^3)" +"(5,8,8,5)","q*t*Subscript[q,1,2]^2" +"(5,3,9,0)","q*t*Subscript[q,1,2]-q^3*t*Subscript[q,1,2]" +"(5,4,9,0)","1-q^2" +"(5,5,9,1)","q*t*Subscript[q,1,2]-q^3*t*Subscript[q,1,2]" +"(5,7,9,3)","q*t*Subscript[q,1,2]" +"(5,8,9,3)","-(q^2/Subscript[q,1,2])" +"(5,7,9,4)","-(q*t*Subscript[q,1,2]^2)" +"(5,8,9,4)","q^2" +"(5,9,9,5)","q*t*Subscript[q,1,2]" +"(5,6,10,1)","(t*Subscript[q,1,2]^3)/q^3-(t*Subscript[q,1,2]^3)/q" +"(5,10,10,5)","-((t*Subscript[q,1,2]^4)/q^3)" +"(5,6,11,0)","1-q^2" +"(5,8,11,1)","1-q^2" +"(5,10,11,3)","1" +"(5,10,11,4)","-Subscript[q,1,2]" +"(5,11,11,5)","-((t*Subscript[q,1,2]^3)/q^3)" +"(6,0,0,6)","q^9/t" +"(6,1,1,6)","-(q^9/(t*Subscript[q,1,2]^2))" +"(6,0,2,3)","q^6-q^9/t" +"(6,0,2,4)","-(q^4*Subscript[q,1,2])+(q^7*Subscript[q,1,2])/t" +"(6,2,2,6)","(q^7*Subscript[q,1,2])/t" +"(6,0,3,2)","-(q^3/(t*Subscript[q,1,2]))+q^9/(t*Subscript[q,1,2])" +"(6,1,3,3)","q^9/(t*Subscript[q,1,2]^2)" +"(6,1,3,4)","-(q^7/(t*Subscript[q,1,2]))" +"(6,3,3,6)","-(q^7/(t*Subscript[q,1,2]))" +"(6,0,4,2)","q^4-q^6+q^3/t-q^7/t" +"(6,1,4,3)","-(q^6/Subscript[q,1,2])" +"(6,1,4,4)","q^4" +"(6,4,4,6)","-(q^7/(t*Subscript[q,1,2]))" +"(6,1,5,2)","-(q^3/(t*Subscript[q,1,2]^2))+q^7/(t*Subscript[q,1,2]^2)" +"(6,5,5,6)","q^7/(t*Subscript[q,1,2]^3)" +"(6,0,6,0)","1-q^4-q^3/t+q^7/t" +"(6,2,6,2)","-(q^3/t)+q^7/t" +"(6,3,6,3)","q^4" +"(6,4,6,3)","q^7/(t*Subscript[q,1,2])" +"(6,3,6,4)","-(q^2*Subscript[q,1,2])" +"(6,4,6,4)","-(q^5/t)" +"(6,6,6,6)","-(q^5/t)" +"(6,1,7,0)","q^3/(t*Subscript[q,1,2]^2)-q^7/(t*Subscript[q,1,2]^2)" +"(6,3,7,2)","q^3/(t*Subscript[q,1,2]^2)-q^7/(t*Subscript[q,1,2]^2)" +"(6,5,7,3)","-(q^7/(t*Subscript[q,1,2]^3))" +"(6,5,7,4)","q^5/(t*Subscript[q,1,2]^2)" +"(6,7,7,6)","q^5/(t*Subscript[q,1,2]^2)" +"(6,1,8,0)","1-q^4" +"(6,3,8,2)","q^2-q^4" +"(6,4,8,2)","-(q^3/(t*Subscript[q,1,2]))+q^5/(t*Subscript[q,1,2])" +"(6,5,8,3)","-(q^4/Subscript[q,1,2])" +"(6,5,8,4)","q^2" +"(6,8,8,6)","q^5/(t*Subscript[q,1,2]^2)" +"(6,5,9,2)","q^3/(t*Subscript[q,1,2]^3)-q^5/(t*Subscript[q,1,2]^3)" +"(6,9,9,6)","-(q^5/(t*Subscript[q,1,2]^4))" +"(6,3,10,0)","1-q^2" +"(6,4,10,0)","q^3/(t*Subscript[q,1,2])-q^5/(t*Subscript[q,1,2])" +"(6,6,10,2)","q^3/(t*Subscript[q,1,2])-q^5/(t*Subscript[q,1,2])" +"(6,7,10,3)","q^2" +"(6,8,10,3)","-(q^5/(t*Subscript[q,1,2]^2))" +"(6,7,10,4)","-Subscript[q,1,2]" +"(6,8,10,4)","q^3/(t*Subscript[q,1,2])" +"(6,10,10,6)","q^3/(t*Subscript[q,1,2])" +"(6,5,11,0)","-(q^3/(t*Subscript[q,1,2]^3))+q^5/(t*Subscript[q,1,2]^3)" +"(6,7,11,2)","-(q^3/(t*Subscript[q,1,2]^3))+q^5/(t*Subscript[q,1,2]^3)" +"(6,9,11,3)","q^5/(t*Subscript[q,1,2]^4)" +"(6,9,11,4)","-(q^3/(t*Subscript[q,1,2]^3))" +"(6,11,11,6)","-(q^3/(t*Subscript[q,1,2]^3))" +"(7,0,0,7)","q^12" +"(7,0,1,6)","-q^12+q^9/t" +"(7,1,1,7)","q^12/Subscript[q,1,2]^2" +"(7,0,2,5)","q^8*Subscript[q,1,2]^2-q^5*t*Subscript[q,1,2]^2" +"(7,2,2,7)","q^8*Subscript[q,1,2]^2" +"(7,0,3,3)","q^4+q^6-q^10-q^9/t" +"(7,0,3,4)","-(q^4*Subscript[q,1,2])+(q^7*Subscript[q,1,2])/t" +"(7,1,3,5)","q^8" +"(7,2,3,6)","(q^7*Subscript[q,1,2])/t" +"(7,3,3,7)","q^8" +"(7,0,4,3)","-(q^4*Subscript[q,1,2])+q^8*Subscript[q,1,2]+q^10*Subscript[q,1,2]-q^5*t*Subscript[q,1,2]" +"(7,0,4,4)","-(q^8*Subscript[q,1,2]^2)+q^5*t*Subscript[q,1,2]^2" +"(7,1,4,5)","-(q^5*t*Subscript[q,1,2])" +"(7,2,4,6)","-(q^8*Subscript[q,1,2]^2)" +"(7,4,4,7)","q^8" +"(7,0,5,2)","-q^6+q^10+q^3/t-q^7/t" +"(7,1,5,3)","-(q^4/Subscript[q,1,2])-q^6/Subscript[q,1,2]+q^8/Subscript[q,1,2]+q^10/Subscript[q,1,2]" +"(7,1,5,4)","q^4-q^8" +"(7,3,5,6)","-q^8" +"(7,4,5,6)","-(q^7/(t*Subscript[q,1,2]))" +"(7,5,5,7)","q^8/Subscript[q,1,2]^2" +"(7,0,6,1)","q^2*Subscript[q,1,2]^2-q^6*Subscript[q,1,2]^2-(t*Subscript[q,1,2]^2)/q+q^3*t*Subscript[q,1,2]^2" +"(7,2,6,3)","q^2*Subscript[q,1,2]^2-q^6*Subscript[q,1,2]^2" +"(7,3,6,5)","q*t*Subscript[q,1,2]^3" +"(7,4,6,5)","q^4*Subscript[q,1,2]^2" +"(7,6,6,7)","q^4*Subscript[q,1,2]^2" +"(7,0,7,0)","1-q^4-q^3/t+q^7/t" +"(7,1,7,1)","q^2-q^6" +"(7,2,7,2)","-(q^3/t)+q^7/t" +"(7,3,7,3)","q^2+q^4-q^6" +"(7,4,7,3)","q^7/(t*Subscript[q,1,2])" +"(7,3,7,4)","-(q^2*Subscript[q,1,2])" +"(7,4,7,4)","-(q^5/t)" +"(7,5,7,5)","q^4" +"(7,6,7,6)","-(q^5/t)" +"(7,7,7,7)","q^4" +"(7,0,8,0)","-(q^2*Subscript[q,1,2]^2)+q^6*Subscript[q,1,2]^2+(t*Subscript[q,1,2]^2)/q-q^3*t*Subscript[q,1,2]^2" +"(7,1,8,1)","(t*Subscript[q,1,2]^2)/q-q^3*t*Subscript[q,1,2]^2" +"(7,2,8,2)","-(q^2*Subscript[q,1,2]^2)+q^6*Subscript[q,1,2]^2" +"(7,3,8,3)","q*t*Subscript[q,1,2]^2" +"(7,4,8,3)","-(q^2*Subscript[q,1,2])+q^4*Subscript[q,1,2]+q^6*Subscript[q,1,2]" +"(7,3,8,4)","-(q*t*Subscript[q,1,2]^3)" +"(7,4,8,4)","-(q^4*Subscript[q,1,2]^2)" +"(7,5,8,5)","q*t*Subscript[q,1,2]^2" +"(7,6,8,6)","-(q^4*Subscript[q,1,2]^2)" +"(7,8,8,7)","q^4" +"(7,1,9,0)","1-q^2-q^4+q^6" +"(7,3,9,2)","-q^4+q^6" +"(7,4,9,2)","-(q^3/(t*Subscript[q,1,2]))+q^5/(t*Subscript[q,1,2])" +"(7,5,9,3)","-(q^2/Subscript[q,1,2])+q^6/Subscript[q,1,2]" +"(7,5,9,4)","q^2-q^4" +"(7,7,9,6)","-q^4" +"(7,8,9,6)","q^5/(t*Subscript[q,1,2]^2)" +"(7,9,9,7)","q^4/Subscript[q,1,2]^2" +"(7,3,10,1)","(t*Subscript[q,1,2]^3)/q^3-(t*Subscript[q,1,2]^3)/q" +"(7,4,10,1)","Subscript[q,1,2]^2-q^2*Subscript[q,1,2]^2" +"(7,6,10,3)","Subscript[q,1,2]^2-q^2*Subscript[q,1,2]^2" +"(7,7,10,5)","-((t*Subscript[q,1,2]^4)/q^3)" +"(7,8,10,5)","Subscript[q,1,2]^2" +"(7,10,10,7)","Subscript[q,1,2]^2" +"(7,3,11,0)","1-q^2" +"(7,4,11,0)","q^3/(t*Subscript[q,1,2])-q^5/(t*Subscript[q,1,2])" +"(7,5,11,1)","1-q^2" +"(7,6,11,2)","q^3/(t*Subscript[q,1,2])-q^5/(t*Subscript[q,1,2])" +"(7,7,11,3)","1" +"(7,8,11,3)","-(q^5/(t*Subscript[q,1,2]^2))" +"(7,7,11,4)","-Subscript[q,1,2]" +"(7,8,11,4)","q^3/(t*Subscript[q,1,2])" +"(7,9,11,5)","1" +"(7,10,11,6)","q^3/(t*Subscript[q,1,2])" +"(7,11,11,7)","1" +"(8,0,0,8)","q^12" +"(8,0,1,6)","q^12/Subscript[q,1,2]^2-q^9/(t*Subscript[q,1,2]^2)" +"(8,1,1,8)","q^12/Subscript[q,1,2]^2" +"(8,0,2,5)","-q^12+q^9*t" +"(8,2,2,8)","q^8*Subscript[q,1,2]^2" +"(8,0,3,3)","-(q^12/Subscript[q,1,2]^2)+q^9/(t*Subscript[q,1,2]^2)" +"(8,0,3,4)","-(q^6/Subscript[q,1,2])+q^10/Subscript[q,1,2]+q^12/Subscript[q,1,2]-q^7/(t*Subscript[q,1,2])" +"(8,1,3,5)","-(q^12/Subscript[q,1,2]^2)" +"(8,2,3,6)","-(q^7/(t*Subscript[q,1,2]))" +"(8,3,3,8)","q^8" +"(8,0,4,3)","-(q^6/Subscript[q,1,2])+(q^9*t)/Subscript[q,1,2]" +"(8,0,4,4)","q^4+q^6-q^10-q^9*t" +"(8,1,4,5)","(q^9*t)/Subscript[q,1,2]" +"(8,2,4,6)","q^8" +"(8,4,4,8)","q^8" +"(8,0,5,2)","q^6/Subscript[q,1,2]^2-q^10/Subscript[q,1,2]^2-q^3/(t*Subscript[q,1,2]^2)+q^7/(t*Subscript[q,1,2]^2)" +"(8,1,5,4)","q^6/Subscript[q,1,2]^2-q^10/Subscript[q,1,2]^2" +"(8,3,5,6)","q^8/Subscript[q,1,2]^2" +"(8,4,5,6)","q^7/(t*Subscript[q,1,2]^3)" +"(8,5,5,8)","q^8/Subscript[q,1,2]^2" +"(8,0,6,1)","-q^6+q^10+q^3*t-q^7*t" +"(8,2,6,3)","q^4-q^8" +"(8,2,6,4)","-(q^2*Subscript[q,1,2])-q^4*Subscript[q,1,2]+q^6*Subscript[q,1,2]+q^8*Subscript[q,1,2]" +"(8,3,6,5)","-(q^5*t*Subscript[q,1,2])" +"(8,4,6,5)","-q^8" +"(8,6,6,8)","q^4*Subscript[q,1,2]^2" +"(8,0,7,0)","-(q^6/Subscript[q,1,2]^2)+q^10/Subscript[q,1,2]^2+q^3/(t*Subscript[q,1,2]^2)-q^7/(t*Subscript[q,1,2]^2)" +"(8,1,7,1)","-(q^6/Subscript[q,1,2]^2)+q^10/Subscript[q,1,2]^2" +"(8,2,7,2)","q^3/(t*Subscript[q,1,2]^2)-q^7/(t*Subscript[q,1,2]^2)" +"(8,3,7,3)","-(q^8/Subscript[q,1,2]^2)" +"(8,4,7,3)","-(q^7/(t*Subscript[q,1,2]^3))" +"(8,3,7,4)","-(q^4/Subscript[q,1,2])+q^6/Subscript[q,1,2]+q^8/Subscript[q,1,2]" +"(8,4,7,4)","q^5/(t*Subscript[q,1,2]^2)" +"(8,5,7,5)","-(q^8/Subscript[q,1,2]^2)" +"(8,6,7,6)","q^5/(t*Subscript[q,1,2]^2)" +"(8,7,7,8)","q^4" +"(8,0,8,0)","1-q^4-q^3*t+q^7*t" +"(8,1,8,1)","-(q^3*t)+q^7*t" +"(8,2,8,2)","q^2-q^6" +"(8,3,8,3)","-(q^5*t)" +"(8,4,8,3)","-(q^4/Subscript[q,1,2])" +"(8,3,8,4)","q^5*t*Subscript[q,1,2]" +"(8,4,8,4)","q^2+q^4-q^6" +"(8,5,8,5)","-(q^5*t)" +"(8,6,8,6)","q^4" +"(8,8,8,8)","q^4" +"(8,3,9,2)","q^4/Subscript[q,1,2]^2-q^6/Subscript[q,1,2]^2" +"(8,4,9,2)","q^3/(t*Subscript[q,1,2]^3)-q^5/(t*Subscript[q,1,2]^3)" +"(8,5,9,4)","q^4/Subscript[q,1,2]^2-q^6/Subscript[q,1,2]^2" +"(8,7,9,6)","q^4/Subscript[q,1,2]^2" +"(8,8,9,6)","-(q^5/(t*Subscript[q,1,2]^4))" +"(8,9,9,8)","q^4/Subscript[q,1,2]^2" +"(8,2,10,0)","1-q^2-q^4+q^6" +"(8,3,10,1)","-(q*t*Subscript[q,1,2])+q^3*t*Subscript[q,1,2]" +"(8,4,10,1)","-q^4+q^6" +"(8,6,10,3)","q^2-q^4" +"(8,6,10,4)","-Subscript[q,1,2]+q^4*Subscript[q,1,2]" +"(8,7,10,5)","q*t*Subscript[q,1,2]^2" +"(8,8,10,5)","-q^4" +"(8,10,10,8)","Subscript[q,1,2]^2" +"(8,3,11,0)","-(q^4/Subscript[q,1,2]^2)+q^6/Subscript[q,1,2]^2" +"(8,4,11,0)","-(q^3/(t*Subscript[q,1,2]^3))+q^5/(t*Subscript[q,1,2]^3)" +"(8,5,11,1)","-(q^4/Subscript[q,1,2]^2)+q^6/Subscript[q,1,2]^2" +"(8,6,11,2)","-(q^3/(t*Subscript[q,1,2]^3))+q^5/(t*Subscript[q,1,2]^3)" +"(8,7,11,3)","-(q^4/Subscript[q,1,2]^2)" +"(8,8,11,3)","q^5/(t*Subscript[q,1,2]^4)" +"(8,7,11,4)","q^4/Subscript[q,1,2]" +"(8,8,11,4)","-(q^3/(t*Subscript[q,1,2]^3))" +"(8,9,11,5)","-(q^4/Subscript[q,1,2]^2)" +"(8,10,11,6)","-(q^3/(t*Subscript[q,1,2]^3))" +"(8,11,11,8)","1" +"(9,0,0,9)","q^15*t" +"(9,0,1,7)","q^12/Subscript[q,1,2]^2-(q^15*t)/Subscript[q,1,2]^2" +"(9,0,1,8)","q^12-q^15*t" +"(9,1,1,9)","-((q^15*t)/Subscript[q,1,2]^2)" +"(9,2,2,9)","q^9*t*Subscript[q,1,2]^3" +"(9,0,3,5)","q^8-q^12+q^7*t+q^9*t-q^11*t-q^13*t" +"(9,2,3,7)","q^8" +"(9,2,3,8)","q^8*Subscript[q,1,2]^2" +"(9,3,3,9)","-(q^9*t*Subscript[q,1,2])" +"(9,0,4,5)","-(q^5*t*Subscript[q,1,2])-q^7*t*Subscript[q,1,2]+q^11*t*Subscript[q,1,2]+q^13*t*Subscript[q,1,2]" +"(9,2,4,7)","-(q^9*t*Subscript[q,1,2])" +"(9,2,4,8)","-(q^9*t*Subscript[q,1,2]^3)" +"(9,4,4,9)","-(q^9*t*Subscript[q,1,2])" +"(9,0,5,3)","-(q^4/Subscript[q,1,2])-q^6/Subscript[q,1,2]+q^8/Subscript[q,1,2]+q^10/Subscript[q,1,2]+(q^7*t)/Subscript[q,1,2]+(q^9*t)/Subscript[q,1,2]-(q^11*t)/Subscript[q,1,2]-(q^13*t)/Subscript[q,1,2]" +"(9,0,5,4)","q^4+q^6-q^8-q^10-q^7*t-q^9*t+q^11*t+q^13*t" +"(9,1,5,5)","(q^7*t)/Subscript[q,1,2]+(q^9*t)/Subscript[q,1,2]-(q^11*t)/Subscript[q,1,2]-(q^13*t)/Subscript[q,1,2]" +"(9,3,5,7)","(q^9*t)/Subscript[q,1,2]" +"(9,4,5,7)","q^8/Subscript[q,1,2]^2" +"(9,3,5,8)","q^9*t*Subscript[q,1,2]" +"(9,4,5,8)","q^8" +"(9,5,5,9)","(q^9*t)/Subscript[q,1,2]" +"(9,2,6,5)","q*t*Subscript[q,1,2]^3+q^3*t*Subscript[q,1,2]^3-q^5*t*Subscript[q,1,2]^3-q^7*t*Subscript[q,1,2]^3" +"(9,6,6,9)","-(q^3*t*Subscript[q,1,2]^4)" +"(9,0,7,1)","q^2-2*q^6+q^10+q^3*t-q^5*t-q^7*t+q^9*t" +"(9,2,7,3)","q^2+q^4-q^6-q^8" +"(9,2,7,4)","-(q^2*Subscript[q,1,2])-q^4*Subscript[q,1,2]+q^6*Subscript[q,1,2]+q^8*Subscript[q,1,2]" +"(9,3,7,5)","-(q^3*t*Subscript[q,1,2])+q^7*t*Subscript[q,1,2]" +"(9,4,7,5)","q^4-q^8" +"(9,6,7,7)","q^4" +"(9,6,7,8)","q^4*Subscript[q,1,2]^2" +"(9,7,7,9)","q^3*t*Subscript[q,1,2]^2" +"(9,0,8,1)","(t*Subscript[q,1,2]^2)/q-q^3*t*Subscript[q,1,2]^2-q^5*t*Subscript[q,1,2]^2+q^9*t*Subscript[q,1,2]^2" +"(9,2,8,3)","q*t*Subscript[q,1,2]^2+q^3*t*Subscript[q,1,2]^2-q^5*t*Subscript[q,1,2]^2-q^7*t*Subscript[q,1,2]^2" +"(9,2,8,4)","-(q*t*Subscript[q,1,2]^3)-q^3*t*Subscript[q,1,2]^3+q^5*t*Subscript[q,1,2]^3+q^7*t*Subscript[q,1,2]^3" +"(9,4,8,5)","q*t*Subscript[q,1,2]^2+q^3*t*Subscript[q,1,2]^2-q^5*t*Subscript[q,1,2]^2-q^7*t*Subscript[q,1,2]^2" +"(9,6,8,7)","q^3*t*Subscript[q,1,2]^2" +"(9,6,8,8)","q^3*t*Subscript[q,1,2]^4" +"(9,8,8,9)","q^3*t*Subscript[q,1,2]^2" +"(9,0,9,0)","1-q^2-q^4+q^6-q^3*t+q^5*t+q^7*t-q^9*t" +"(9,1,9,1)","-(q^3*t)+q^5*t+q^7*t-q^9*t" +"(9,3,9,3)","-(q^3*t)+q^7*t" +"(9,4,9,3)","-(q^2/Subscript[q,1,2])+q^6/Subscript[q,1,2]" +"(9,3,9,4)","q^3*t*Subscript[q,1,2]-q^7*t*Subscript[q,1,2]" +"(9,4,9,4)","q^2-q^6" +"(9,5,9,5)","-(q^3*t)+q^7*t" +"(9,7,9,7)","-(q^3*t)" +"(9,8,9,7)","q^4/Subscript[q,1,2]^2" +"(9,7,9,8)","-(q^3*t*Subscript[q,1,2]^2)" +"(9,8,9,8)","q^4" +"(9,9,9,9)","-(q^3*t)" +"(9,2,10,1)","(t*Subscript[q,1,2]^3)/q^3-(t*Subscript[q,1,2]^3)/q-q*t*Subscript[q,1,2]^3+q^3*t*Subscript[q,1,2]^3" +"(9,6,10,5)","-((t*Subscript[q,1,2]^4)/q^3)+q*t*Subscript[q,1,2]^4" +"(9,10,10,9)","(t*Subscript[q,1,2]^5)/q^3" +"(9,2,11,0)","1-q^2-q^4+q^6" +"(9,4,11,1)","1-q^2-q^4+q^6" +"(9,6,11,3)","1-q^4" +"(9,6,11,4)","-Subscript[q,1,2]+q^4*Subscript[q,1,2]" +"(9,8,11,5)","1-q^4" +"(9,10,11,7)","1" +"(9,10,11,8)","Subscript[q,1,2]^2" +"(9,11,11,9)","-((t*Subscript[q,1,2]^3)/q^3)" +"(10,0,0,10)","q^15/t" +"(10,1,1,10)","q^15/(t*Subscript[q,1,2]^3)" +"(10,0,2,7)","q^12-q^15/t" +"(10,0,2,8)","q^8*Subscript[q,1,2]^2-(q^11*Subscript[q,1,2]^2)/t" +"(10,2,2,10)","-((q^11*Subscript[q,1,2]^2)/t)" +"(10,0,3,6)","-(q^7/(t*Subscript[q,1,2]))-q^9/(t*Subscript[q,1,2])+q^13/(t*Subscript[q,1,2])+q^15/(t*Subscript[q,1,2])" +"(10,1,3,7)","-(q^15/(t*Subscript[q,1,2]^3))" +"(10,1,3,8)","-(q^11/(t*Subscript[q,1,2]))" +"(10,3,3,10)","-(q^11/(t*Subscript[q,1,2]))" +"(10,0,4,6)","q^8-q^12+q^7/t+q^9/t-q^11/t-q^13/t" +"(10,1,4,7)","q^12/Subscript[q,1,2]^2" +"(10,1,4,8)","q^8" +"(10,4,4,10)","-(q^11/(t*Subscript[q,1,2]))" +"(10,1,5,6)","q^7/(t*Subscript[q,1,2]^3)+q^9/(t*Subscript[q,1,2]^3)-q^11/(t*Subscript[q,1,2]^3)-q^13/(t*Subscript[q,1,2]^3)" +"(10,5,5,10)","-(q^11/(t*Subscript[q,1,2]^4))" +"(10,0,6,3)","q^4+q^6-q^8-q^10-q^7/t-q^9/t+q^11/t+q^13/t" +"(10,0,6,4)","-(q^2*Subscript[q,1,2])-q^4*Subscript[q,1,2]+q^6*Subscript[q,1,2]+q^8*Subscript[q,1,2]+(q^5*Subscript[q,1,2])/t+(q^7*Subscript[q,1,2])/t-(q^9*Subscript[q,1,2])/t-(q^11*Subscript[q,1,2])/t" +"(10,2,6,6)","(q^5*Subscript[q,1,2])/t+(q^7*Subscript[q,1,2])/t-(q^9*Subscript[q,1,2])/t-(q^11*Subscript[q,1,2])/t" +"(10,3,6,7)","q^8" +"(10,4,6,7)","q^11/(t*Subscript[q,1,2])" +"(10,3,6,8)","q^4*Subscript[q,1,2]^2" +"(10,4,6,8)","(q^7*Subscript[q,1,2])/t" +"(10,6,6,10)","(q^7*Subscript[q,1,2])/t" +"(10,0,7,2)","q^3/(t*Subscript[q,1,2]^2)-q^7/(t*Subscript[q,1,2]^2)-q^9/(t*Subscript[q,1,2]^2)+q^13/(t*Subscript[q,1,2]^2)" +"(10,1,7,3)","-(q^7/(t*Subscript[q,1,2]^3))-q^9/(t*Subscript[q,1,2]^3)+q^11/(t*Subscript[q,1,2]^3)+q^13/(t*Subscript[q,1,2]^3)" +"(10,1,7,4)","q^5/(t*Subscript[q,1,2]^2)+q^7/(t*Subscript[q,1,2]^2)-q^9/(t*Subscript[q,1,2]^2)-q^11/(t*Subscript[q,1,2]^2)" +"(10,3,7,6)","q^5/(t*Subscript[q,1,2]^2)+q^7/(t*Subscript[q,1,2]^2)-q^9/(t*Subscript[q,1,2]^2)-q^11/(t*Subscript[q,1,2]^2)" +"(10,5,7,7)","q^11/(t*Subscript[q,1,2]^4)" +"(10,5,7,8)","q^7/(t*Subscript[q,1,2]^2)" +"(10,7,7,10)","q^7/(t*Subscript[q,1,2]^2)" +"(10,0,8,2)","q^2-2*q^6+q^10+q^3/t-q^5/t-q^7/t+q^9/t" +"(10,1,8,3)","-(q^4/Subscript[q,1,2])-q^6/Subscript[q,1,2]+q^8/Subscript[q,1,2]+q^10/Subscript[q,1,2]" +"(10,1,8,4)","q^2+q^4-q^6-q^8" +"(10,3,8,6)","q^4-q^8" +"(10,4,8,6)","-(q^5/(t*Subscript[q,1,2]))+q^9/(t*Subscript[q,1,2])" +"(10,5,8,7)","q^8/Subscript[q,1,2]^2" +"(10,5,8,8)","q^4" +"(10,8,8,10)","q^7/(t*Subscript[q,1,2]^2)" +"(10,1,9,2)","q^3/(t*Subscript[q,1,2]^3)-q^5/(t*Subscript[q,1,2]^3)-q^7/(t*Subscript[q,1,2]^3)+q^9/(t*Subscript[q,1,2]^3)" +"(10,5,9,6)","-(q^5/(t*Subscript[q,1,2]^4))+q^9/(t*Subscript[q,1,2]^4)" +"(10,9,9,10)","q^7/(t*Subscript[q,1,2]^5)" +"(10,0,10,0)","1-q^2-q^4+q^6-q^3/t+q^5/t+q^7/t-q^9/t" +"(10,2,10,2)","-(q^3/t)+q^5/t+q^7/t-q^9/t" +"(10,3,10,3)","q^2-q^6" +"(10,4,10,3)","q^5/(t*Subscript[q,1,2])-q^9/(t*Subscript[q,1,2])" +"(10,3,10,4)","-Subscript[q,1,2]+q^4*Subscript[q,1,2]" +"(10,4,10,4)","-(q^3/t)+q^7/t" +"(10,6,10,6)","-(q^3/t)+q^7/t" +"(10,7,10,7)","q^4" +"(10,8,10,7)","-(q^7/(t*Subscript[q,1,2]^2))" +"(10,7,10,8)","Subscript[q,1,2]^2" +"(10,8,10,8)","-(q^3/t)" +"(10,10,10,10)","-(q^3/t)" +"(10,1,11,0)","-(q^3/(t*Subscript[q,1,2]^3))+q^5/(t*Subscript[q,1,2]^3)+q^7/(t*Subscript[q,1,2]^3)-q^9/(t*Subscript[q,1,2]^3)" +"(10,3,11,2)","-(q^3/(t*Subscript[q,1,2]^3))+q^5/(t*Subscript[q,1,2]^3)+q^7/(t*Subscript[q,1,2]^3)-q^9/(t*Subscript[q,1,2]^3)" +"(10,5,11,3)","q^5/(t*Subscript[q,1,2]^4)-q^9/(t*Subscript[q,1,2]^4)" +"(10,5,11,4)","-(q^3/(t*Subscript[q,1,2]^3))+q^7/(t*Subscript[q,1,2]^3)" +"(10,7,11,6)","-(q^3/(t*Subscript[q,1,2]^3))+q^7/(t*Subscript[q,1,2]^3)" +"(10,9,11,7)","-(q^7/(t*Subscript[q,1,2]^5))" +"(10,9,11,8)","-(q^3/(t*Subscript[q,1,2]^3))" +"(10,11,11,10)","-(q^3/(t*Subscript[q,1,2]^3))" +"(11,0,0,11)","q^18" +"(11,0,1,10)","-q^12-q^18+q^15/t+q^15*t" +"(11,1,1,11)","-(q^18/Subscript[q,1,2]^3)" +"(11,0,2,9)","-(q^12*Subscript[q,1,2]^3)+q^9*t*Subscript[q,1,2]^3+q^15*t*Subscript[q,1,2]^3-q^12*t^2*Subscript[q,1,2]^3" +"(11,2,2,11)","-(q^12*Subscript[q,1,2]^3)" +"(11,0,3,7)","q^8+q^10+2*q^12-q^14-q^16-q^15/t-q^15*t" +"(11,0,3,8)","2*q^8*Subscript[q,1,2]^2-(q^11*Subscript[q,1,2]^2)/t+q^7*t*Subscript[q,1,2]^2+q^9*t*Subscript[q,1,2]^2-q^11*t*Subscript[q,1,2]^2-q^13*t*Subscript[q,1,2]^2-q^15*t*Subscript[q,1,2]^2" +"(11,1,3,9)","q^12-q^15*t" +"(11,2,3,10)","q^8*Subscript[q,1,2]^2-(q^11*Subscript[q,1,2]^2)/t" +"(11,3,3,11)","q^12" +"(11,0,4,7)","-(q^8*Subscript[q,1,2])-q^10*Subscript[q,1,2]+q^12*Subscript[q,1,2]+q^14*Subscript[q,1,2]+q^16*Subscript[q,1,2]-2*q^9*t*Subscript[q,1,2]+q^12*t^2*Subscript[q,1,2]" +"(11,0,4,8)","q^12*Subscript[q,1,2]^3-q^5*t*Subscript[q,1,2]^3-q^7*t*Subscript[q,1,2]^3-2*q^9*t*Subscript[q,1,2]^3+q^11*t*Subscript[q,1,2]^3+q^13*t*Subscript[q,1,2]^3+q^12*t^2*Subscript[q,1,2]^3" +"(11,1,4,9)","-(q^9*t*Subscript[q,1,2])+q^12*t^2*Subscript[q,1,2]" +"(11,2,4,10)","q^12*Subscript[q,1,2]^3-q^9*t*Subscript[q,1,2]^3" +"(11,4,4,11)","q^12" +"(11,0,5,6)","-q^4-q^6+q^8-q^12+q^14+q^16+q^7/t+q^9/t-q^11/t-q^13/t+q^7*t+q^9*t-q^11*t-q^13*t" +"(11,1,5,7)","q^8/Subscript[q,1,2]^2+q^10/Subscript[q,1,2]^2-q^14/Subscript[q,1,2]^2-q^16/Subscript[q,1,2]^2" +"(11,1,5,8)","q^8-q^12+q^7*t+q^9*t-q^11*t-q^13*t" +"(11,3,5,10)","-q^12+q^9*t" +"(11,4,5,10)","q^8/Subscript[q,1,2]-q^11/(t*Subscript[q,1,2])" +"(11,5,5,11)","-(q^12/Subscript[q,1,2]^3)" +"(11,0,6,5)","-(q^4*Subscript[q,1,2]^3)-q^6*Subscript[q,1,2]^3+q^8*Subscript[q,1,2]^3+q^10*Subscript[q,1,2]^3+q*t*Subscript[q,1,2]^3+q^3*t*Subscript[q,1,2]^3-q^5*t*Subscript[q,1,2]^3+q^9*t*Subscript[q,1,2]^3-q^11*t*Subscript[q,1,2]^3-q^13*t*Subscript[q,1,2]^3-q^4*t^2*Subscript[q,1,2]^3-q^6*t^2*Subscript[q,1,2]^3+q^8*t^2*Subscript[q,1,2]^3+q^10*t^2*Subscript[q,1,2]^3" +"(11,2,6,7)","-(q^4*Subscript[q,1,2]^3)-q^6*Subscript[q,1,2]^3+q^8*Subscript[q,1,2]^3+q^10*Subscript[q,1,2]^3-q^5*t*Subscript[q,1,2]^3+q^9*t*Subscript[q,1,2]^3" +"(11,2,6,8)","-(q*t*Subscript[q,1,2]^5)-q^3*t*Subscript[q,1,2]^5+q^7*t*Subscript[q,1,2]^5+q^9*t*Subscript[q,1,2]^5" +"(11,3,6,9)","-(q^3*t*Subscript[q,1,2]^4)+q^6*t^2*Subscript[q,1,2]^4" +"(11,4,6,9)","-(q^6*Subscript[q,1,2]^3)+q^9*t*Subscript[q,1,2]^3" +"(11,6,6,11)","-(q^6*Subscript[q,1,2]^3)" +"(11,0,7,3)","q^2+2*q^4+q^6-3*q^8-2*q^10+q^12-q^7/t-q^9/t+q^11/t+q^13/t-q^7*t-q^9*t+q^11*t+q^13*t" +"(11,0,7,4)","-2*q^2*Subscript[q,1,2]-2*q^4*Subscript[q,1,2]+2*q^6*Subscript[q,1,2]+2*q^8*Subscript[q,1,2]+(q^5*Subscript[q,1,2])/t+(q^7*Subscript[q,1,2])/t-(q^9*Subscript[q,1,2])/t-(q^11*Subscript[q,1,2])/t-q^3*t*Subscript[q,1,2]+q^5*t*Subscript[q,1,2]+2*q^7*t*Subscript[q,1,2]-q^11*t*Subscript[q,1,2]-q^13*t*Subscript[q,1,2]" +"(11,1,7,5)","q^4+q^6-q^8-q^10-q^7*t-q^9*t+q^11*t+q^13*t" +"(11,2,7,6)","-(q^2*Subscript[q,1,2])-q^4*Subscript[q,1,2]+q^6*Subscript[q,1,2]+q^8*Subscript[q,1,2]+(q^5*Subscript[q,1,2])/t+(q^7*Subscript[q,1,2])/t-(q^9*Subscript[q,1,2])/t-(q^11*Subscript[q,1,2])/t" +"(11,3,7,7)","q^4+q^6-q^10-q^9*t" +"(11,4,7,7)","-(q^8/Subscript[q,1,2])+q^11/(t*Subscript[q,1,2])" +"(11,3,7,8)","q^4*Subscript[q,1,2]^2+q^3*t*Subscript[q,1,2]^2-q^7*t*Subscript[q,1,2]^2-q^9*t*Subscript[q,1,2]^2" +"(11,4,7,8)","-(q^4*Subscript[q,1,2])+(q^7*Subscript[q,1,2])/t" +"(11,5,7,9)","q^6-q^9*t" +"(11,6,7,10)","-(q^4*Subscript[q,1,2])+(q^7*Subscript[q,1,2])/t" +"(11,7,7,11)","q^6" +"(11,0,8,3)","q^2*Subscript[q,1,2]^2-q^4*Subscript[q,1,2]^2-2*q^6*Subscript[q,1,2]^2+q^10*Subscript[q,1,2]^2+q^12*Subscript[q,1,2]^2+2*q*t*Subscript[q,1,2]^2+2*q^3*t*Subscript[q,1,2]^2-2*q^5*t*Subscript[q,1,2]^2-2*q^7*t*Subscript[q,1,2]^2-q^4*t^2*Subscript[q,1,2]^2-q^6*t^2*Subscript[q,1,2]^2+q^8*t^2*Subscript[q,1,2]^2+q^10*t^2*Subscript[q,1,2]^2" +"(11,0,8,4)","q^4*Subscript[q,1,2]^3+q^6*Subscript[q,1,2]^3-q^8*Subscript[q,1,2]^3-q^10*Subscript[q,1,2]^3-(t*Subscript[q,1,2]^3)/q-2*q*t*Subscript[q,1,2]^3-q^3*t*Subscript[q,1,2]^3+3*q^5*t*Subscript[q,1,2]^3+2*q^7*t*Subscript[q,1,2]^3-q^9*t*Subscript[q,1,2]^3+q^4*t^2*Subscript[q,1,2]^3+q^6*t^2*Subscript[q,1,2]^3-q^8*t^2*Subscript[q,1,2]^3-q^10*t^2*Subscript[q,1,2]^3" +"(11,1,8,5)","q*t*Subscript[q,1,2]^2+q^3*t*Subscript[q,1,2]^2-q^5*t*Subscript[q,1,2]^2-q^7*t*Subscript[q,1,2]^2-q^4*t^2*Subscript[q,1,2]^2-q^6*t^2*Subscript[q,1,2]^2+q^8*t^2*Subscript[q,1,2]^2+q^10*t^2*Subscript[q,1,2]^2" +"(11,2,8,6)","q^4*Subscript[q,1,2]^3+q^6*Subscript[q,1,2]^3-q^8*Subscript[q,1,2]^3-q^10*Subscript[q,1,2]^3-q*t*Subscript[q,1,2]^3-q^3*t*Subscript[q,1,2]^3+q^5*t*Subscript[q,1,2]^3+q^7*t*Subscript[q,1,2]^3" +"(11,3,8,7)","q^3*t*Subscript[q,1,2]^2-q^6*t^2*Subscript[q,1,2]^2" +"(11,4,8,7)","-(q^4*Subscript[q,1,2])+q^8*Subscript[q,1,2]+q^10*Subscript[q,1,2]-q^5*t*Subscript[q,1,2]" +"(11,3,8,8)","q^3*t*Subscript[q,1,2]^4-q^6*t^2*Subscript[q,1,2]^4" +"(11,4,8,8)","q^6*Subscript[q,1,2]^3-q*t*Subscript[q,1,2]^3-q^3*t*Subscript[q,1,2]^3+q^7*t*Subscript[q,1,2]^3" +"(11,5,8,9)","q^3*t*Subscript[q,1,2]^2-q^6*t^2*Subscript[q,1,2]^2" +"(11,6,8,10)","q^6*Subscript[q,1,2]^3-q^3*t*Subscript[q,1,2]^3" +"(11,8,8,11)","q^6" +"(11,0,9,2)","-1+q^2+q^4-2*q^6+q^8+q^10-q^12+q^3/t-q^5/t-q^7/t+q^9/t+q^3*t-q^5*t-q^7*t+q^9*t" +"(11,1,9,3)","-(q^2/Subscript[q,1,2])+q^6/Subscript[q,1,2]+q^8/Subscript[q,1,2]-q^12/Subscript[q,1,2]" +"(11,1,9,4)","q^2-2*q^6+q^10+q^3*t-q^5*t-q^7*t+q^9*t" +"(11,3,9,6)","-q^6+q^10+q^3*t-q^7*t" +"(11,4,9,6)","q^2/Subscript[q,1,2]-q^6/Subscript[q,1,2]-q^5/(t*Subscript[q,1,2])+q^9/(t*Subscript[q,1,2])" +"(11,5,9,7)","q^4/Subscript[q,1,2]^2-q^10/Subscript[q,1,2]^2" +"(11,5,9,8)","q^4-q^6+q^3*t-q^7*t" +"(11,7,9,10)","-q^6+q^3*t" +"(11,8,9,10)","-(q^4/Subscript[q,1,2]^2)+q^7/(t*Subscript[q,1,2]^2)" +"(11,9,9,11)","-(q^6/Subscript[q,1,2]^3)" +"(11,0,10,1)","-Subscript[q,1,2]^3+q^2*Subscript[q,1,2]^3+q^4*Subscript[q,1,2]^3-q^6*Subscript[q,1,2]^3+(t*Subscript[q,1,2]^3)/q^3-(t*Subscript[q,1,2]^3)/q-q*t*Subscript[q,1,2]^3+2*q^3*t*Subscript[q,1,2]^3-q^5*t*Subscript[q,1,2]^3-q^7*t*Subscript[q,1,2]^3+q^9*t*Subscript[q,1,2]^3-t^2*Subscript[q,1,2]^3+q^2*t^2*Subscript[q,1,2]^3+q^4*t^2*Subscript[q,1,2]^3-q^6*t^2*Subscript[q,1,2]^3" +"(11,2,10,3)","-Subscript[q,1,2]^3+q^2*Subscript[q,1,2]^3+q^4*Subscript[q,1,2]^3-q^6*Subscript[q,1,2]^3-(t*Subscript[q,1,2]^3)/q+2*q^3*t*Subscript[q,1,2]^3-q^7*t*Subscript[q,1,2]^3" +"(11,2,10,4)","(t*Subscript[q,1,2]^4)/q^3-q*t*Subscript[q,1,2]^4-q^3*t*Subscript[q,1,2]^4+q^7*t*Subscript[q,1,2]^4" +"(11,3,10,5)","-((t*Subscript[q,1,2]^4)/q^3)+q*t*Subscript[q,1,2]^4+t^2*Subscript[q,1,2]^4-q^4*t^2*Subscript[q,1,2]^4" +"(11,4,10,5)","-Subscript[q,1,2]^3+q^4*Subscript[q,1,2]^3+q^3*t*Subscript[q,1,2]^3-q^7*t*Subscript[q,1,2]^3" +"(11,6,10,7)","-Subscript[q,1,2]^3+q^4*Subscript[q,1,2]^3-q*t*Subscript[q,1,2]^3+q^3*t*Subscript[q,1,2]^3" +"(11,6,10,8)","-((t*Subscript[q,1,2]^5)/q^3)+q^3*t*Subscript[q,1,2]^5" +"(11,7,10,9)","(t*Subscript[q,1,2]^5)/q^3-t^2*Subscript[q,1,2]^5" +"(11,8,10,9)","-Subscript[q,1,2]^3+q^3*t*Subscript[q,1,2]^3" +"(11,10,10,11)","-Subscript[q,1,2]^3" +"(11,0,11,0)","2-2*q^2-2*q^4+2*q^6-q^3/t+q^5/t+q^7/t-q^9/t-q^3*t+q^5*t+q^7*t-q^9*t" +"(11,1,11,1)","1-q^2-q^4+q^6-q^3*t+q^5*t+q^7*t-q^9*t" +"(11,2,11,2)","1-q^2-q^4+q^6-q^3/t+q^5/t+q^7/t-q^9/t" +"(11,3,11,3)","1-q^4-q^3*t+q^7*t" +"(11,4,11,3)","-(q^2/Subscript[q,1,2])+q^6/Subscript[q,1,2]+q^5/(t*Subscript[q,1,2])-q^9/(t*Subscript[q,1,2])" +"(11,3,11,4)","-Subscript[q,1,2]+q^4*Subscript[q,1,2]+q^3*t*Subscript[q,1,2]-q^7*t*Subscript[q,1,2]" +"(11,4,11,4)","1-q^4-q^3/t+q^7/t" +"(11,5,11,5)","1-q^4-q^3*t+q^7*t" +"(11,6,11,6)","1-q^4-q^3/t+q^7/t" +"(11,7,11,7)","1-q^3*t" +"(11,8,11,7)","q^4/Subscript[q,1,2]^2-q^7/(t*Subscript[q,1,2]^2)" +"(11,7,11,8)","Subscript[q,1,2]^2-q^3*t*Subscript[q,1,2]^2" +"(11,8,11,8)","1-q^3/t" +"(11,9,11,9)","1-q^3*t" +"(11,10,11,10)","1-q^3/t" +"(11,11,11,11)","1" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/Rp.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/Rp.csv new file mode 100644 index 0000000..672d8c0 --- /dev/null +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/Rp.csv @@ -0,0 +1,586 @@ +"(12,12,12,12)","LaurentPolynomial" +"(0,0,0,0)","1" +"(0,1,0,1)","1-1/(q^3*t)" +"(0,2,0,2)","1-t/q^3" +"(0,3,0,3)","1-1/(q^3*t)" +"(0,4,0,3)","1/(q^4*Subscript[q,1,2])-1/(q*t*Subscript[q,1,2])" +"(0,3,0,4)","Subscript[q,1,2]/q^6-(t*Subscript[q,1,2])/q^3" +"(0,4,0,4)","1-t/q^3" +"(0,5,0,5)","1-q^(-4)+1/(q^7*t)-1/(q^3*t)" +"(0,6,0,6)","1-q^(-4)+t/q^7-t/q^3" +"(0,7,0,7)","1-q^(-4)+1/(q^7*t)-1/(q^3*t)" +"(0,8,0,7)","1/(q^6*Subscript[q,1,2]^2)-1/(q^2*Subscript[q,1,2]^2)-1/(q^3*t*Subscript[q,1,2]^2)+q/(t*Subscript[q,1,2]^2)" +"(0,7,0,8)","Subscript[q,1,2]^2/q^10-Subscript[q,1,2]^2/q^6-(t*Subscript[q,1,2]^2)/q^7+(t*Subscript[q,1,2]^2)/q^3" +"(0,8,0,8)","1-q^(-4)+t/q^7-t/q^3" +"(0,9,0,9)","1+q^(-6)-q^(-4)-q^(-2)-1/(q^9*t)+1/(q^7*t)+1/(q^5*t)-1/(q^3*t)" +"(0,10,0,10)","1+q^(-6)-q^(-4)-q^(-2)-t/q^9+t/q^7+t/q^5-t/q^3" +"(0,11,0,11)","2+2/q^6-2/q^4-2/q^2-1/(q^9*t)+1/(q^7*t)+1/(q^5*t)-1/(q^3*t)-t/q^9+t/q^7+t/q^5-t/q^3" +"(0,1,1,0)","1/(q^3*t)" +"(0,3,1,2)","-q^(-6)+1/(q^3*t)" +"(0,4,1,2)","-(1/(q^4*Subscript[q,1,2]))+1/(q*t*Subscript[q,1,2])" +"(0,5,1,3)","1/(q^7*t*Subscript[q,1,2])-1/(q*t*Subscript[q,1,2])" +"(0,5,1,4)","-q^(-6)+q^(-4)-1/(q^7*t)+1/(q^3*t)" +"(0,7,1,6)","q^(-10)-q^(-6)-1/(q^7*t)+1/(q^3*t)" +"(0,8,1,6)","-(1/(q^6*Subscript[q,1,2]^2))+1/(q^2*Subscript[q,1,2]^2)+1/(q^3*t*Subscript[q,1,2]^2)-q/(t*Subscript[q,1,2]^2)" +"(0,9,1,7)","1/(q^9*t*Subscript[q,1,2]^2)-1/(q^5*t*Subscript[q,1,2]^2)-1/(q^3*t*Subscript[q,1,2]^2)+q/(t*Subscript[q,1,2]^2)" +"(0,9,1,8)","q^(-10)-2/q^6+q^(-2)+1/(q^9*t)-1/(q^7*t)-1/(q^5*t)+1/(q^3*t)" +"(0,11,1,10)","-1-q^(-12)+q^(-10)+q^(-8)-2/q^6+q^(-4)+q^(-2)+1/(q^9*t)-1/(q^7*t)-1/(q^5*t)+1/(q^3*t)+t/q^9-t/q^7-t/q^5+t/q^3" +"(0,2,2,0)","t/q^3" +"(0,3,2,1)","-(Subscript[q,1,2]/q^6)+(t*Subscript[q,1,2])/q^3" +"(0,4,2,1)","-q^(-6)+t/q^3" +"(0,6,2,3)","-q^(-6)+q^(-4)-t/q^7+t/q^3" +"(0,6,2,4)","(t*Subscript[q,1,2])/q^9-(t*Subscript[q,1,2])/q^3" +"(0,7,2,5)","-(Subscript[q,1,2]^2/q^10)+Subscript[q,1,2]^2/q^6+(t*Subscript[q,1,2]^2)/q^7-(t*Subscript[q,1,2]^2)/q^3" +"(0,8,2,5)","q^(-10)-q^(-6)-t/q^7+t/q^3" +"(0,10,2,7)","q^(-10)-2/q^6+q^(-2)+t/q^9-t/q^7-t/q^5+t/q^3" +"(0,10,2,8)","(t*Subscript[q,1,2]^2)/q^13-(t*Subscript[q,1,2]^2)/q^9-(t*Subscript[q,1,2]^2)/q^7+(t*Subscript[q,1,2]^2)/q^3" +"(0,11,2,9)","-(Subscript[q,1,2]^3/q^12)+Subscript[q,1,2]^3/q^10+Subscript[q,1,2]^3/q^8-Subscript[q,1,2]^3/q^6+(t*Subscript[q,1,2]^3)/q^15-(t*Subscript[q,1,2]^3)/q^13-(t*Subscript[q,1,2]^3)/q^11+(2*t*Subscript[q,1,2]^3)/q^9-(t*Subscript[q,1,2]^3)/q^7-(t*Subscript[q,1,2]^3)/q^5+(t*Subscript[q,1,2]^3)/q^3-(t^2*Subscript[q,1,2]^3)/q^12+(t^2*Subscript[q,1,2]^3)/q^10+(t^2*Subscript[q,1,2]^3)/q^8-(t^2*Subscript[q,1,2]^3)/q^6" +"(0,3,3,0)","q^(-6)" +"(0,5,3,1)","q^(-6)-1/(q^9*t)" +"(0,6,3,2)","-(1/(q^4*Subscript[q,1,2]))+t/(q^7*Subscript[q,1,2])" +"(0,7,3,3)","-q^(-10)+q^(-6)+q^(-4)-1/(q^9*t)" +"(0,8,3,3)","-(1/(q^8*Subscript[q,1,2]^2))+1/(q^5*t*Subscript[q,1,2]^2)" +"(0,7,3,4)","Subscript[q,1,2]/q^12+Subscript[q,1,2]/q^10-Subscript[q,1,2]/q^6-(t*Subscript[q,1,2])/q^7" +"(0,8,3,4)","-(1/(q^4*Subscript[q,1,2]))+t/(q^7*Subscript[q,1,2])" +"(0,9,3,5)","-q^(-10)-q^(-8)+q^(-6)+q^(-4)+1/(q^13*t)+1/(q^11*t)-1/(q^9*t)-1/(q^7*t)" +"(0,10,3,6)","1/(q^8*Subscript[q,1,2])+1/(q^6*Subscript[q,1,2])-1/(q^4*Subscript[q,1,2])-1/(q^2*Subscript[q,1,2])-t/(q^11*Subscript[q,1,2])-t/(q^9*Subscript[q,1,2])+t/(q^7*Subscript[q,1,2])+t/(q^5*Subscript[q,1,2])" +"(0,11,3,7)","q^(-12)-2/q^10-3/q^8+q^(-6)+2/q^4+q^(-2)+1/(q^13*t)+1/(q^11*t)-1/(q^9*t)-1/(q^7*t)+t/q^13+t/q^11-t/q^9-t/q^7" +"(0,11,3,8)","Subscript[q,1,2]^2/q^16+Subscript[q,1,2]^2/q^14-(2*Subscript[q,1,2]^2)/q^10-Subscript[q,1,2]^2/q^8+Subscript[q,1,2]^2/q^6-(2*t*Subscript[q,1,2]^2)/q^11-(2*t*Subscript[q,1,2]^2)/q^9+(2*t*Subscript[q,1,2]^2)/q^7+(2*t*Subscript[q,1,2]^2)/q^5+(t^2*Subscript[q,1,2]^2)/q^14+(t^2*Subscript[q,1,2]^2)/q^12-(t^2*Subscript[q,1,2]^2)/q^10-(t^2*Subscript[q,1,2]^2)/q^8" +"(0,4,4,0)","q^(-6)" +"(0,5,4,1)","-(Subscript[q,1,2]/q^6)+Subscript[q,1,2]/(q^9*t)" +"(0,6,4,2)","q^(-6)-t/q^9" +"(0,7,4,3)","-(Subscript[q,1,2]/q^6)+Subscript[q,1,2]/(q^9*t)" +"(0,8,4,3)","1/(q^10*Subscript[q,1,2])+1/(q^8*Subscript[q,1,2])-1/(q^4*Subscript[q,1,2])-1/(q^5*t*Subscript[q,1,2])" +"(0,7,4,4)","-(Subscript[q,1,2]^2/q^12)+(t*Subscript[q,1,2]^2)/q^9" +"(0,8,4,4)","-q^(-10)+q^(-6)+q^(-4)-t/q^9" +"(0,9,4,5)","Subscript[q,1,2]/q^10+Subscript[q,1,2]/q^8-Subscript[q,1,2]/q^6-Subscript[q,1,2]/q^4-Subscript[q,1,2]/(q^13*t)-Subscript[q,1,2]/(q^11*t)+Subscript[q,1,2]/(q^9*t)+Subscript[q,1,2]/(q^7*t)" +"(0,10,4,6)","-q^(-10)-q^(-8)+q^(-6)+q^(-4)+t/q^13+t/q^11-t/q^9-t/q^7" +"(0,11,4,7)","(2*Subscript[q,1,2])/q^10+(2*Subscript[q,1,2])/q^8-(2*Subscript[q,1,2])/q^6-(2*Subscript[q,1,2])/q^4-Subscript[q,1,2]/(q^13*t)-Subscript[q,1,2]/(q^11*t)+Subscript[q,1,2]/(q^9*t)+Subscript[q,1,2]/(q^7*t)-(t*Subscript[q,1,2])/q^15-(t*Subscript[q,1,2])/q^13+(2*t*Subscript[q,1,2])/q^9+(t*Subscript[q,1,2])/q^7-(t*Subscript[q,1,2])/q^5" +"(0,11,4,8)","-(Subscript[q,1,2]^3/q^16)-Subscript[q,1,2]^3/q^14+Subscript[q,1,2]^3/q^12+Subscript[q,1,2]^3/q^10-(t*Subscript[q,1,2]^3)/q^15+(2*t*Subscript[q,1,2]^3)/q^13+(3*t*Subscript[q,1,2]^3)/q^11-(t*Subscript[q,1,2]^3)/q^9-(2*t*Subscript[q,1,2]^3)/q^7-(t*Subscript[q,1,2]^3)/q^5-(t^2*Subscript[q,1,2]^3)/q^16-(t^2*Subscript[q,1,2]^3)/q^14+(t^2*Subscript[q,1,2]^3)/q^12+(t^2*Subscript[q,1,2]^3)/q^10" +"(0,5,5,0)","1/(q^9*t)" +"(0,7,5,2)","-q^(-12)+1/(q^9*t)" +"(0,8,5,2)","1/(q^8*Subscript[q,1,2]^2)-1/(q^5*t*Subscript[q,1,2]^2)" +"(0,9,5,3)","1/(q^13*t*Subscript[q,1,2])+1/(q^11*t*Subscript[q,1,2])-1/(q^7*t*Subscript[q,1,2])-1/(q^5*t*Subscript[q,1,2])" +"(0,9,5,4)","-q^(-12)+q^(-8)-1/(q^13*t)-1/(q^11*t)+1/(q^9*t)+1/(q^7*t)" +"(0,11,5,6)","q^(-16)+q^(-14)-q^(-12)+q^(-8)-q^(-6)-q^(-4)-1/(q^13*t)-1/(q^11*t)+1/(q^9*t)+1/(q^7*t)-t/q^13-t/q^11+t/q^9+t/q^7" +"(0,6,6,0)","t/q^9" +"(0,7,6,1)","Subscript[q,1,2]^2/q^12-(t*Subscript[q,1,2]^2)/q^9" +"(0,8,6,1)","-q^(-12)+t/q^9" +"(0,10,6,3)","-q^(-12)+q^(-8)-t/q^13-t/q^11+t/q^9+t/q^7" +"(0,10,6,4)","(t*Subscript[q,1,2])/q^15+(t*Subscript[q,1,2])/q^13-(t*Subscript[q,1,2])/q^9-(t*Subscript[q,1,2])/q^7" +"(0,11,6,5)","Subscript[q,1,2]^3/q^16+Subscript[q,1,2]^3/q^14-Subscript[q,1,2]^3/q^12-Subscript[q,1,2]^3/q^10-(t*Subscript[q,1,2]^3)/q^19-(t*Subscript[q,1,2]^3)/q^17+(t*Subscript[q,1,2]^3)/q^15-(t*Subscript[q,1,2]^3)/q^11+(t*Subscript[q,1,2]^3)/q^9+(t*Subscript[q,1,2]^3)/q^7+(t^2*Subscript[q,1,2]^3)/q^16+(t^2*Subscript[q,1,2]^3)/q^14-(t^2*Subscript[q,1,2]^3)/q^12-(t^2*Subscript[q,1,2]^3)/q^10" +"(0,7,7,0)","q^(-12)" +"(0,9,7,1)","q^(-12)-1/(q^15*t)" +"(0,10,7,2)","1/(q^8*Subscript[q,1,2]^2)-t/(q^11*Subscript[q,1,2]^2)" +"(0,11,7,3)","-q^(-16)-q^(-14)+2/q^12+q^(-10)+q^(-8)-1/(q^15*t)-t/q^15" +"(0,11,7,4)","Subscript[q,1,2]/q^18+Subscript[q,1,2]/q^16+Subscript[q,1,2]/q^14-Subscript[q,1,2]/q^12-Subscript[q,1,2]/q^10-(2*t*Subscript[q,1,2])/q^11+(t^2*Subscript[q,1,2])/q^14" +"(0,8,8,0)","q^(-12)" +"(0,9,8,1)","Subscript[q,1,2]^2/q^12-Subscript[q,1,2]^2/(q^15*t)" +"(0,10,8,2)","q^(-12)-t/q^15" +"(0,11,8,3)","(2*Subscript[q,1,2]^2)/q^12-Subscript[q,1,2]^2/(q^15*t)-(t*Subscript[q,1,2]^2)/q^19-(t*Subscript[q,1,2]^2)/q^17-(t*Subscript[q,1,2]^2)/q^15+(t*Subscript[q,1,2]^2)/q^13+(t*Subscript[q,1,2]^2)/q^11" +"(0,11,8,4)","Subscript[q,1,2]^3/q^18+(t*Subscript[q,1,2]^3)/q^19+(t*Subscript[q,1,2]^3)/q^17-(2*t*Subscript[q,1,2]^3)/q^15-(t*Subscript[q,1,2]^3)/q^13-(t*Subscript[q,1,2]^3)/q^11+(t^2*Subscript[q,1,2]^3)/q^18" +"(0,9,9,0)","1/(q^15*t)" +"(0,11,9,2)","-q^(-18)-q^(-12)+1/(q^15*t)+t/q^15" +"(0,10,10,0)","t/q^15" +"(0,11,10,1)","-(Subscript[q,1,2]^3/q^18)+(t*Subscript[q,1,2]^3)/q^21+(t*Subscript[q,1,2]^3)/q^15-(t^2*Subscript[q,1,2]^3)/q^18" +"(0,11,11,0)","q^(-18)" +"(1,0,0,1)","1" +"(1,2,0,3)","1" +"(1,2,0,4)","-((t*Subscript[q,1,2])/q^3)" +"(1,4,0,5)","1-q^(-4)" +"(1,6,0,7)","1-q^(-4)" +"(1,6,0,8)","-((t*Subscript[q,1,2]^2)/q^7)+(t*Subscript[q,1,2]^2)/q^3" +"(1,8,0,9)","1+q^(-6)-q^(-4)-q^(-2)" +"(1,10,0,11)","1+q^(-6)-q^(-4)-q^(-2)" +"(1,1,1,1)","-(1/(q^3*t))" +"(1,3,1,3)","-(1/(q^3*t))" +"(1,4,1,3)","-(1/(q*t*Subscript[q,1,2]))" +"(1,3,1,4)","Subscript[q,1,2]/q^6" +"(1,4,1,4)","q^(-4)" +"(1,5,1,5)","1/(q^7*t)-1/(q^3*t)" +"(1,7,1,7)","1/(q^7*t)-1/(q^3*t)" +"(1,8,1,7)","-(1/(q^3*t*Subscript[q,1,2]^2))+q/(t*Subscript[q,1,2]^2)" +"(1,7,1,8)","Subscript[q,1,2]^2/q^10-Subscript[q,1,2]^2/q^6" +"(1,8,1,8)","-q^(-6)+q^(-2)" +"(1,9,1,9)","-(1/(q^9*t))+1/(q^7*t)+1/(q^5*t)-1/(q^3*t)" +"(1,11,1,11)","1+q^(-6)-q^(-4)-q^(-2)-1/(q^9*t)+1/(q^7*t)+1/(q^5*t)-1/(q^3*t)" +"(1,2,2,1)","(t*Subscript[q,1,2])/q^3" +"(1,6,2,5)","(t*Subscript[q,1,2]^2)/q^7-(t*Subscript[q,1,2]^2)/q^3" +"(1,10,2,9)","(t*Subscript[q,1,2]^3)/q^9-(t*Subscript[q,1,2]^3)/q^7-(t*Subscript[q,1,2]^3)/q^5+(t*Subscript[q,1,2]^3)/q^3" +"(1,3,3,1)","-(Subscript[q,1,2]/q^6)" +"(1,6,3,3)","q^(-4)" +"(1,6,3,4)","-((t*Subscript[q,1,2])/q^7)" +"(1,7,3,5)","-(Subscript[q,1,2]^2/q^10)+Subscript[q,1,2]^2/q^6" +"(1,8,3,5)","-q^(-8)+q^(-4)" +"(1,10,3,7)","-q^(-8)-q^(-6)+q^(-4)+q^(-2)" +"(1,10,3,8)","-((t*Subscript[q,1,2]^2)/q^11)-(t*Subscript[q,1,2]^2)/q^9+(t*Subscript[q,1,2]^2)/q^7+(t*Subscript[q,1,2]^2)/q^5" +"(1,11,3,9)","-(Subscript[q,1,2]^3/q^12)+Subscript[q,1,2]^3/q^10+Subscript[q,1,2]^3/q^8-Subscript[q,1,2]^3/q^6-(t*Subscript[q,1,2]^3)/q^13+(2*t*Subscript[q,1,2]^3)/q^9-(t*Subscript[q,1,2]^3)/q^5" +"(1,4,4,1)","-(Subscript[q,1,2]/q^6)" +"(1,6,4,3)","-(Subscript[q,1,2]/q^6)" +"(1,6,4,4)","(t*Subscript[q,1,2]^2)/q^9" +"(1,8,4,5)","Subscript[q,1,2]/q^10+Subscript[q,1,2]/q^8-Subscript[q,1,2]/q^6-Subscript[q,1,2]/q^4" +"(1,10,4,7)","Subscript[q,1,2]/q^10+Subscript[q,1,2]/q^8-Subscript[q,1,2]/q^6-Subscript[q,1,2]/q^4" +"(1,10,4,8)","(t*Subscript[q,1,2]^3)/q^13+(t*Subscript[q,1,2]^3)/q^11-(t*Subscript[q,1,2]^3)/q^9-(t*Subscript[q,1,2]^3)/q^7" +"(1,11,4,9)","(t*Subscript[q,1,2]^4)/q^15-(t*Subscript[q,1,2]^4)/q^11-(t*Subscript[q,1,2]^4)/q^9+(t*Subscript[q,1,2]^4)/q^5" +"(1,5,5,1)","Subscript[q,1,2]/(q^9*t)" +"(1,7,5,3)","Subscript[q,1,2]/(q^9*t)" +"(1,8,5,3)","-(1/(q^5*t*Subscript[q,1,2]))" +"(1,7,5,4)","-(Subscript[q,1,2]^2/q^12)" +"(1,8,5,4)","q^(-8)" +"(1,9,5,5)","-(Subscript[q,1,2]/(q^13*t))-Subscript[q,1,2]/(q^11*t)+Subscript[q,1,2]/(q^9*t)+Subscript[q,1,2]/(q^7*t)" +"(1,11,5,7)","Subscript[q,1,2]/q^10+Subscript[q,1,2]/q^8-Subscript[q,1,2]/q^6-Subscript[q,1,2]/q^4-Subscript[q,1,2]/(q^13*t)-Subscript[q,1,2]/(q^11*t)+Subscript[q,1,2]/(q^9*t)+Subscript[q,1,2]/(q^7*t)" +"(1,11,5,8)","-(Subscript[q,1,2]^3/q^16)-Subscript[q,1,2]^3/q^14+Subscript[q,1,2]^3/q^12+Subscript[q,1,2]^3/q^10+(t*Subscript[q,1,2]^3)/q^13+(t*Subscript[q,1,2]^3)/q^11-(t*Subscript[q,1,2]^3)/q^9-(t*Subscript[q,1,2]^3)/q^7" +"(1,6,6,1)","-((t*Subscript[q,1,2]^2)/q^9)" +"(1,10,6,5)","-((t*Subscript[q,1,2]^3)/q^13)-(t*Subscript[q,1,2]^3)/q^11+(t*Subscript[q,1,2]^3)/q^9+(t*Subscript[q,1,2]^3)/q^7" +"(1,7,7,1)","Subscript[q,1,2]^2/q^12" +"(1,10,7,3)","q^(-8)" +"(1,10,7,4)","-((t*Subscript[q,1,2])/q^11)" +"(1,11,7,5)","Subscript[q,1,2]^3/q^16+Subscript[q,1,2]^3/q^14-Subscript[q,1,2]^3/q^12-Subscript[q,1,2]^3/q^10+(t*Subscript[q,1,2]^3)/q^15-(t*Subscript[q,1,2]^3)/q^11" +"(1,8,8,1)","Subscript[q,1,2]^2/q^12" +"(1,10,8,3)","Subscript[q,1,2]^2/q^12" +"(1,10,8,4)","-((t*Subscript[q,1,2]^3)/q^15)" +"(1,11,8,5)","(t*Subscript[q,1,2]^5)/q^19+(t*Subscript[q,1,2]^5)/q^17-(t*Subscript[q,1,2]^5)/q^13-(t*Subscript[q,1,2]^5)/q^11" +"(1,9,9,1)","-(Subscript[q,1,2]^2/(q^15*t))" +"(1,11,9,3)","Subscript[q,1,2]^2/q^12-Subscript[q,1,2]^2/(q^15*t)" +"(1,11,9,4)","Subscript[q,1,2]^3/q^18-(t*Subscript[q,1,2]^3)/q^15" +"(1,10,10,1)","(t*Subscript[q,1,2]^3)/q^15" +"(1,11,11,1)","-(Subscript[q,1,2]^3/q^18)" +"(2,0,0,2)","1" +"(2,1,0,3)","-(1/(q*t*Subscript[q,1,2]))" +"(2,1,0,4)","1" +"(2,3,0,6)","1-q^(-4)" +"(2,5,0,7)","-(1/(q^3*t*Subscript[q,1,2]^2))+q/(t*Subscript[q,1,2]^2)" +"(2,5,0,8)","1-q^(-4)" +"(2,7,0,10)","1+q^(-6)-q^(-4)-q^(-2)" +"(2,9,0,11)","-(1/(q^3*t*Subscript[q,1,2]^3))+1/(q*t*Subscript[q,1,2]^3)+q/(t*Subscript[q,1,2]^3)-q^3/(t*Subscript[q,1,2]^3)" +"(2,1,1,2)","1/(q*t*Subscript[q,1,2])" +"(2,5,1,6)","1/(q^3*t*Subscript[q,1,2]^2)-q/(t*Subscript[q,1,2]^2)" +"(2,9,1,10)","1/(q^3*t*Subscript[q,1,2]^3)-1/(q*t*Subscript[q,1,2]^3)-q/(t*Subscript[q,1,2]^3)+q^3/(t*Subscript[q,1,2]^3)" +"(2,2,2,2)","-(t/q^3)" +"(2,3,2,3)","q^(-4)" +"(2,4,2,3)","1/(q^4*Subscript[q,1,2])" +"(2,3,2,4)","-((t*Subscript[q,1,2])/q^3)" +"(2,4,2,4)","-(t/q^3)" +"(2,6,2,6)","t/q^7-t/q^3" +"(2,7,2,7)","-q^(-6)+q^(-2)" +"(2,8,2,7)","1/(q^6*Subscript[q,1,2]^2)-1/(q^2*Subscript[q,1,2]^2)" +"(2,7,2,8)","-((t*Subscript[q,1,2]^2)/q^7)+(t*Subscript[q,1,2]^2)/q^3" +"(2,8,2,8)","t/q^7-t/q^3" +"(2,10,2,10)","-(t/q^9)+t/q^7+t/q^5-t/q^3" +"(2,11,2,11)","1+q^(-6)-q^(-4)-q^(-2)-t/q^9+t/q^7+t/q^5-t/q^3" +"(2,3,3,2)","-(1/(q^4*Subscript[q,1,2]))" +"(2,5,3,3)","1/(q^5*t*Subscript[q,1,2]^2)" +"(2,5,3,4)","-(1/(q^4*Subscript[q,1,2]))" +"(2,7,3,6)","1/(q^8*Subscript[q,1,2])+1/(q^6*Subscript[q,1,2])-1/(q^4*Subscript[q,1,2])-1/(q^2*Subscript[q,1,2])" +"(2,9,3,7)","1/(q^7*t*Subscript[q,1,2]^3)+1/(q^5*t*Subscript[q,1,2]^3)-1/(q^3*t*Subscript[q,1,2]^3)-1/(q*t*Subscript[q,1,2]^3)" +"(2,9,3,8)","1/(q^8*Subscript[q,1,2])+1/(q^6*Subscript[q,1,2])-1/(q^4*Subscript[q,1,2])-1/(q^2*Subscript[q,1,2])" +"(2,11,3,10)","-Subscript[q,1,2]^(-1)-1/(q^10*Subscript[q,1,2])+1/(q^6*Subscript[q,1,2])+1/(q^4*Subscript[q,1,2])" +"(2,4,4,2)","-(1/(q^4*Subscript[q,1,2]))" +"(2,5,4,3)","-(1/(q^5*t*Subscript[q,1,2]))" +"(2,5,4,4)","q^(-4)" +"(2,7,4,6)","-q^(-8)+q^(-4)" +"(2,8,4,6)","-(1/(q^6*Subscript[q,1,2]^2))+1/(q^2*Subscript[q,1,2]^2)" +"(2,9,4,7)","-(1/(q^7*t*Subscript[q,1,2]^2))-1/(q^5*t*Subscript[q,1,2]^2)+1/(q^3*t*Subscript[q,1,2]^2)+1/(q*t*Subscript[q,1,2]^2)" +"(2,9,4,8)","-q^(-8)-q^(-6)+q^(-4)+q^(-2)" +"(2,11,4,10)","q^(-10)-2/q^6+q^(-2)+t/q^9-t/q^7-t/q^5+t/q^3" +"(2,5,5,2)","-(1/(q^5*t*Subscript[q,1,2]^2))" +"(2,9,5,6)","-(1/(q^7*t*Subscript[q,1,2]^3))-1/(q^5*t*Subscript[q,1,2]^3)+1/(q^3*t*Subscript[q,1,2]^3)+1/(q*t*Subscript[q,1,2]^3)" +"(2,6,6,2)","t/(q^7*Subscript[q,1,2])" +"(2,7,6,3)","q^(-8)" +"(2,8,6,3)","-(1/(q^8*Subscript[q,1,2]^2))" +"(2,7,6,4)","-((t*Subscript[q,1,2])/q^7)" +"(2,8,6,4)","t/(q^7*Subscript[q,1,2])" +"(2,10,6,6)","-(t/(q^11*Subscript[q,1,2]))-t/(q^9*Subscript[q,1,2])+t/(q^7*Subscript[q,1,2])+t/(q^5*Subscript[q,1,2])" +"(2,11,6,7)","-q^(-10)-q^(-8)+q^(-6)+q^(-4)+t/q^13+t/q^11-t/q^9-t/q^7" +"(2,11,6,8)","-((t*Subscript[q,1,2]^2)/q^11)-(t*Subscript[q,1,2]^2)/q^9+(t*Subscript[q,1,2]^2)/q^7+(t*Subscript[q,1,2]^2)/q^5+(t^2*Subscript[q,1,2]^2)/q^14+(t^2*Subscript[q,1,2]^2)/q^12-(t^2*Subscript[q,1,2]^2)/q^10-(t^2*Subscript[q,1,2]^2)/q^8" +"(2,7,7,2)","1/(q^8*Subscript[q,1,2]^2)" +"(2,9,7,3)","-(1/(q^9*t*Subscript[q,1,2]^3))" +"(2,9,7,4)","1/(q^8*Subscript[q,1,2]^2)" +"(2,11,7,6)","-(1/(q^12*Subscript[q,1,2]^2))-1/(q^10*Subscript[q,1,2]^2)+1/(q^6*Subscript[q,1,2]^2)+1/(q^4*Subscript[q,1,2]^2)" +"(2,8,8,2)","1/(q^8*Subscript[q,1,2]^2)" +"(2,9,8,3)","-(1/(q^9*t*Subscript[q,1,2]))" +"(2,9,8,4)","q^(-8)" +"(2,11,8,6)","-q^(-12)+q^(-8)-t/q^13-t/q^11+t/q^9+t/q^7" +"(2,9,9,2)","1/(q^9*t*Subscript[q,1,2]^3)" +"(2,10,10,2)","-(t/(q^11*Subscript[q,1,2]^2))" +"(2,11,10,3)","q^(-12)-t/q^15" +"(2,11,10,4)","-((t*Subscript[q,1,2])/q^11)+(t^2*Subscript[q,1,2])/q^14" +"(2,11,11,2)","-(1/(q^12*Subscript[q,1,2]^3))" +"(3,0,0,3)","1" +"(3,1,0,5)","1" +"(3,2,0,6)","(t*Subscript[q,1,2])/q^3" +"(3,3,0,7)","1" +"(3,4,0,7)","1/(q^2*Subscript[q,1,2])" +"(3,3,0,8)","(t*Subscript[q,1,2]^2)/q^3" +"(3,4,0,8)","(t*Subscript[q,1,2])/q^3" +"(3,5,0,9)","1-q^(-2)" +"(3,6,0,10)","-((t*Subscript[q,1,2])/q^5)+(t*Subscript[q,1,2])/q^3" +"(3,7,0,11)","1-q^(-2)" +"(3,8,0,11)","-Subscript[q,1,2]^(-2)+1/(q^2*Subscript[q,1,2]^2)" +"(3,1,1,3)","-(1/(q*t*Subscript[q,1,2]))" +"(3,3,1,6)","-q^(-4)" +"(3,4,1,6)","-(1/(q^2*Subscript[q,1,2]))" +"(3,5,1,7)","-(1/(q^3*t*Subscript[q,1,2]^2))+q/(t*Subscript[q,1,2]^2)" +"(3,5,1,8)","-q^(-4)+q^(-2)" +"(3,7,1,10)","q^(-6)-q^(-4)" +"(3,8,1,10)","Subscript[q,1,2]^(-2)-1/(q^2*Subscript[q,1,2]^2)" +"(3,9,1,11)","-(1/(q^3*t*Subscript[q,1,2]^3))+1/(q*t*Subscript[q,1,2]^3)+q/(t*Subscript[q,1,2]^3)-q^3/(t*Subscript[q,1,2]^3)" +"(3,2,2,3)","-((t*Subscript[q,1,2])/q^3)" +"(3,3,2,5)","-((t*Subscript[q,1,2]^2)/q^3)" +"(3,4,2,5)","-((t*Subscript[q,1,2])/q^3)" +"(3,6,2,7)","(t*Subscript[q,1,2])/q^5-(t*Subscript[q,1,2])/q^3" +"(3,7,2,9)","-((t*Subscript[q,1,2]^3)/q^5)+(t*Subscript[q,1,2]^3)/q^3" +"(3,8,2,9)","(t*Subscript[q,1,2])/q^5-(t*Subscript[q,1,2])/q^3" +"(3,3,3,3)","q^(-4)" +"(3,5,3,5)","q^(-4)" +"(3,6,3,6)","-(t/q^5)" +"(3,7,3,7)","-q^(-6)+q^(-4)+q^(-2)" +"(3,8,3,7)","-(1/(q^4*Subscript[q,1,2]^2))" +"(3,7,3,8)","(t*Subscript[q,1,2]^2)/q^5" +"(3,8,3,8)","-(t/q^5)" +"(3,9,3,9)","-q^(-6)+q^(-2)" +"(3,10,3,10)","t/q^7-t/q^3" +"(3,11,3,11)","1-q^(-4)+t/q^7-t/q^3" +"(3,4,4,3)","q^(-4)" +"(3,5,4,5)","-(Subscript[q,1,2]/q^4)" +"(3,6,4,6)","(t*Subscript[q,1,2])/q^7" +"(3,7,4,7)","-(Subscript[q,1,2]/q^4)" +"(3,8,4,7)","1/(q^6*Subscript[q,1,2])+1/(q^4*Subscript[q,1,2])-1/(q^2*Subscript[q,1,2])" +"(3,7,4,8)","-((t*Subscript[q,1,2]^3)/q^7)" +"(3,8,4,8)","(t*Subscript[q,1,2])/q^7" +"(3,9,4,9)","Subscript[q,1,2]/q^6-Subscript[q,1,2]/q^2" +"(3,10,4,10)","-((t*Subscript[q,1,2])/q^9)+(t*Subscript[q,1,2])/q^5" +"(3,11,4,11)","Subscript[q,1,2]/q^6-Subscript[q,1,2]/q^2-(t*Subscript[q,1,2])/q^9+(t*Subscript[q,1,2])/q^5" +"(3,5,5,3)","-(1/(q^5*t*Subscript[q,1,2]))" +"(3,7,5,6)","-q^(-8)" +"(3,8,5,6)","1/(q^4*Subscript[q,1,2]^2)" +"(3,9,5,7)","-(1/(q^7*t*Subscript[q,1,2]^2))-1/(q^5*t*Subscript[q,1,2]^2)+1/(q^3*t*Subscript[q,1,2]^2)+1/(q*t*Subscript[q,1,2]^2)" +"(3,9,5,8)","-q^(-8)+q^(-4)" +"(3,11,5,10)","q^(-10)-q^(-6)-t/q^7+t/q^3" +"(3,6,6,3)","-((t*Subscript[q,1,2])/q^7)" +"(3,7,6,5)","(t*Subscript[q,1,2]^3)/q^7" +"(3,8,6,5)","-((t*Subscript[q,1,2])/q^7)" +"(3,10,6,7)","(t*Subscript[q,1,2])/q^9-(t*Subscript[q,1,2])/q^5" +"(3,11,6,9)","(t*Subscript[q,1,2]^4)/q^9-(t*Subscript[q,1,2]^4)/q^5-(t^2*Subscript[q,1,2]^4)/q^12+(t^2*Subscript[q,1,2]^4)/q^8" +"(3,7,7,3)","q^(-8)" +"(3,9,7,5)","q^(-8)" +"(3,10,7,6)","t/(q^7*Subscript[q,1,2])" +"(3,11,7,7)","-q^(-10)+q^(-6)+q^(-4)-t/q^9" +"(3,11,7,8)","(t*Subscript[q,1,2]^2)/q^7-(t^2*Subscript[q,1,2]^2)/q^10" +"(3,8,8,3)","q^(-8)" +"(3,9,8,5)","Subscript[q,1,2]^2/q^8" +"(3,10,8,6)","(t*Subscript[q,1,2])/q^11" +"(3,11,8,7)","Subscript[q,1,2]^2/q^8-(t*Subscript[q,1,2]^2)/q^13-(t*Subscript[q,1,2]^2)/q^11+(t*Subscript[q,1,2]^2)/q^7" +"(3,11,8,8)","(t*Subscript[q,1,2]^4)/q^11-(t^2*Subscript[q,1,2]^4)/q^14" +"(3,9,9,3)","-(1/(q^9*t*Subscript[q,1,2]))" +"(3,11,9,6)","-q^(-12)+t/q^9" +"(3,10,10,3)","-((t*Subscript[q,1,2])/q^11)" +"(3,11,10,5)","-((t*Subscript[q,1,2]^4)/q^11)+(t^2*Subscript[q,1,2]^4)/q^14" +"(3,11,11,3)","q^(-12)" +"(4,0,0,4)","1" +"(4,1,0,5)","1/(q*t*Subscript[q,1,2])" +"(4,2,0,6)","1" +"(4,3,0,7)","1/(q*t*Subscript[q,1,2])" +"(4,4,0,7)","q/(t*Subscript[q,1,2]^2)" +"(4,3,0,8)","Subscript[q,1,2]/q^4" +"(4,4,0,8)","1" +"(4,5,0,9)","-(1/(q^3*t*Subscript[q,1,2]))+1/(q*t*Subscript[q,1,2])" +"(4,6,0,10)","1-q^(-2)" +"(4,7,0,11)","-(1/(q^3*t*Subscript[q,1,2]))+1/(q*t*Subscript[q,1,2])" +"(4,8,0,11)","q/(t*Subscript[q,1,2]^3)-q^3/(t*Subscript[q,1,2]^3)" +"(4,1,1,4)","-(1/(q*t*Subscript[q,1,2]))" +"(4,3,1,6)","-(1/(q*t*Subscript[q,1,2]))" +"(4,4,1,6)","-(q/(t*Subscript[q,1,2]^2))" +"(4,5,1,8)","1/(q^3*t*Subscript[q,1,2])-1/(q*t*Subscript[q,1,2])" +"(4,7,1,10)","1/(q^3*t*Subscript[q,1,2])-1/(q*t*Subscript[q,1,2])" +"(4,8,1,10)","-(q/(t*Subscript[q,1,2]^3))+q^3/(t*Subscript[q,1,2]^3)" +"(4,2,2,4)","-((t*Subscript[q,1,2])/q^3)" +"(4,3,2,5)","-(Subscript[q,1,2]/q^4)" +"(4,4,2,5)","-q^(-4)" +"(4,6,2,7)","-q^(-4)+q^(-2)" +"(4,6,2,8)","-((t*Subscript[q,1,2]^2)/q^7)+(t*Subscript[q,1,2]^2)/q^3" +"(4,7,2,9)","-(Subscript[q,1,2]^2/q^6)+Subscript[q,1,2]^2/q^4" +"(4,8,2,9)","q^(-6)-q^(-4)" +"(4,10,2,11)","1+q^(-6)-q^(-4)-q^(-2)" +"(4,3,3,4)","q^(-4)" +"(4,5,3,5)","1/(q^5*t*Subscript[q,1,2])" +"(4,6,3,6)","-(1/(q^2*Subscript[q,1,2]))" +"(4,7,3,7)","1/(q^5*t*Subscript[q,1,2])" +"(4,8,3,7)","-(1/(q*t*Subscript[q,1,2]^3))" +"(4,7,3,8)","Subscript[q,1,2]/q^8+Subscript[q,1,2]/q^6-Subscript[q,1,2]/q^4" +"(4,8,3,8)","-(1/(q^2*Subscript[q,1,2]))" +"(4,9,3,9)","-(1/(q^7*t*Subscript[q,1,2]))+1/(q^3*t*Subscript[q,1,2])" +"(4,10,3,10)","-Subscript[q,1,2]^(-1)+1/(q^4*Subscript[q,1,2])" +"(4,11,3,11)","-Subscript[q,1,2]^(-1)+1/(q^4*Subscript[q,1,2])-1/(q^7*t*Subscript[q,1,2])+1/(q^3*t*Subscript[q,1,2])" +"(4,4,4,4)","q^(-4)" +"(4,5,4,5)","-(1/(q^5*t))" +"(4,6,4,6)","q^(-4)" +"(4,7,4,7)","-(1/(q^5*t))" +"(4,8,4,7)","1/(q*t*Subscript[q,1,2]^2)" +"(4,7,4,8)","-(Subscript[q,1,2]^2/q^8)" +"(4,8,4,8)","-q^(-6)+q^(-4)+q^(-2)" +"(4,9,4,9)","1/(q^7*t)-1/(q^3*t)" +"(4,10,4,10)","-q^(-6)+q^(-2)" +"(4,11,4,11)","1-q^(-4)+1/(q^7*t)-1/(q^3*t)" +"(4,5,5,4)","-(1/(q^5*t*Subscript[q,1,2]))" +"(4,7,5,6)","-(1/(q^5*t*Subscript[q,1,2]))" +"(4,8,5,6)","1/(q*t*Subscript[q,1,2]^3)" +"(4,9,5,8)","1/(q^7*t*Subscript[q,1,2])-1/(q^3*t*Subscript[q,1,2])" +"(4,11,5,10)","Subscript[q,1,2]^(-1)-1/(q^4*Subscript[q,1,2])+1/(q^7*t*Subscript[q,1,2])-1/(q^3*t*Subscript[q,1,2])" +"(4,6,6,4)","-((t*Subscript[q,1,2])/q^7)" +"(4,7,6,5)","Subscript[q,1,2]^2/q^8" +"(4,8,6,5)","-q^(-8)" +"(4,10,6,7)","-q^(-8)+q^(-4)" +"(4,10,6,8)","-((t*Subscript[q,1,2]^2)/q^11)-(t*Subscript[q,1,2]^2)/q^9+(t*Subscript[q,1,2]^2)/q^7+(t*Subscript[q,1,2]^2)/q^5" +"(4,11,6,9)","Subscript[q,1,2]^3/q^10-Subscript[q,1,2]^3/q^6-(t*Subscript[q,1,2]^3)/q^13+(t*Subscript[q,1,2]^3)/q^9" +"(4,7,7,4)","q^(-8)" +"(4,9,7,5)","1/(q^9*t*Subscript[q,1,2])" +"(4,10,7,6)","1/(q^4*Subscript[q,1,2]^2)" +"(4,11,7,7)","-(1/(q^6*Subscript[q,1,2]))+1/(q^9*t*Subscript[q,1,2])" +"(4,11,7,8)","Subscript[q,1,2]/q^12+Subscript[q,1,2]/q^10-Subscript[q,1,2]/q^6-(t*Subscript[q,1,2])/q^7" +"(4,8,8,4)","q^(-8)" +"(4,9,8,5)","Subscript[q,1,2]/(q^9*t)" +"(4,10,8,6)","q^(-8)" +"(4,11,8,7)","-(Subscript[q,1,2]/q^6)+Subscript[q,1,2]/(q^9*t)" +"(4,11,8,8)","Subscript[q,1,2]^3/q^12+(t*Subscript[q,1,2]^3)/q^13-(t*Subscript[q,1,2]^3)/q^9-(t*Subscript[q,1,2]^3)/q^7" +"(4,9,9,4)","-(1/(q^9*t*Subscript[q,1,2]))" +"(4,11,9,6)","1/(q^6*Subscript[q,1,2])-1/(q^9*t*Subscript[q,1,2])" +"(4,10,10,4)","-((t*Subscript[q,1,2])/q^11)" +"(4,11,10,5)","-(Subscript[q,1,2]^3/q^12)+(t*Subscript[q,1,2]^3)/q^15" +"(4,11,11,4)","q^(-12)" +"(5,0,0,5)","1" +"(5,2,0,7)","1" +"(5,2,0,8)","(t*Subscript[q,1,2]^2)/q^3" +"(5,4,0,9)","1-q^(-2)" +"(5,6,0,11)","1-q^(-2)" +"(5,1,1,5)","1/(q*t*Subscript[q,1,2])" +"(5,3,1,7)","1/(q*t*Subscript[q,1,2])" +"(5,4,1,7)","q/(t*Subscript[q,1,2]^2)" +"(5,3,1,8)","Subscript[q,1,2]/q^4" +"(5,4,1,8)","q^(-2)" +"(5,5,1,9)","-(1/(q^3*t*Subscript[q,1,2]))+1/(q*t*Subscript[q,1,2])" +"(5,7,1,11)","-(1/(q^3*t*Subscript[q,1,2]))+1/(q*t*Subscript[q,1,2])" +"(5,8,1,11)","q/(t*Subscript[q,1,2]^3)-q^3/(t*Subscript[q,1,2]^3)" +"(5,2,2,5)","-((t*Subscript[q,1,2]^2)/q^3)" +"(5,6,2,9)","-((t*Subscript[q,1,2]^3)/q^5)+(t*Subscript[q,1,2]^3)/q^3" +"(5,3,3,5)","-(Subscript[q,1,2]/q^4)" +"(5,6,3,7)","q^(-2)" +"(5,6,3,8)","(t*Subscript[q,1,2]^2)/q^5" +"(5,7,3,9)","-(Subscript[q,1,2]^2/q^6)+Subscript[q,1,2]^2/q^4" +"(5,8,3,9)","-q^(-4)+q^(-2)" +"(5,10,3,11)","1-q^(-4)" +"(5,4,4,5)","-(Subscript[q,1,2]/q^4)" +"(5,6,4,7)","-(Subscript[q,1,2]/q^4)" +"(5,6,4,8)","-((t*Subscript[q,1,2]^3)/q^7)" +"(5,8,4,9)","Subscript[q,1,2]/q^6-Subscript[q,1,2]/q^2" +"(5,10,4,11)","Subscript[q,1,2]/q^6-Subscript[q,1,2]/q^2" +"(5,5,5,5)","-(1/(q^5*t))" +"(5,7,5,7)","-(1/(q^5*t))" +"(5,8,5,7)","1/(q*t*Subscript[q,1,2]^2)" +"(5,7,5,8)","-(Subscript[q,1,2]^2/q^8)" +"(5,8,5,8)","q^(-4)" +"(5,9,5,9)","1/(q^7*t)-1/(q^3*t)" +"(5,11,5,11)","1-q^(-4)+1/(q^7*t)-1/(q^3*t)" +"(5,6,6,5)","(t*Subscript[q,1,2]^3)/q^7" +"(5,10,6,9)","(t*Subscript[q,1,2]^4)/q^9-(t*Subscript[q,1,2]^4)/q^5" +"(5,7,7,5)","Subscript[q,1,2]^2/q^8" +"(5,10,7,7)","q^(-4)" +"(5,10,7,8)","(t*Subscript[q,1,2]^2)/q^7" +"(5,11,7,9)","Subscript[q,1,2]^3/q^10-Subscript[q,1,2]^3/q^6+(t*Subscript[q,1,2]^3)/q^9-(t*Subscript[q,1,2]^3)/q^7" +"(5,8,8,5)","Subscript[q,1,2]^2/q^8" +"(5,10,8,7)","Subscript[q,1,2]^2/q^8" +"(5,10,8,8)","(t*Subscript[q,1,2]^4)/q^11" +"(5,11,8,9)","(t*Subscript[q,1,2]^5)/q^13-(t*Subscript[q,1,2]^5)/q^7" +"(5,9,9,5)","Subscript[q,1,2]/(q^9*t)" +"(5,11,9,7)","-(Subscript[q,1,2]/q^6)+Subscript[q,1,2]/(q^9*t)" +"(5,11,9,8)","Subscript[q,1,2]^3/q^12-(t*Subscript[q,1,2]^3)/q^9" +"(5,10,10,5)","-((t*Subscript[q,1,2]^4)/q^11)" +"(5,11,11,5)","-(Subscript[q,1,2]^3/q^12)" +"(6,0,0,6)","1" +"(6,1,0,7)","q/(t*Subscript[q,1,2]^2)" +"(6,1,0,8)","1" +"(6,3,0,10)","1-q^(-2)" +"(6,5,0,11)","q/(t*Subscript[q,1,2]^3)-q^3/(t*Subscript[q,1,2]^3)" +"(6,1,1,6)","-(q/(t*Subscript[q,1,2]^2))" +"(6,5,1,10)","-(q/(t*Subscript[q,1,2]^3))+q^3/(t*Subscript[q,1,2]^3)" +"(6,2,2,6)","(t*Subscript[q,1,2])/q^3" +"(6,3,2,7)","q^(-2)" +"(6,4,2,7)","1/(q^2*Subscript[q,1,2])" +"(6,3,2,8)","(t*Subscript[q,1,2]^2)/q^3" +"(6,4,2,8)","(t*Subscript[q,1,2])/q^3" +"(6,6,2,10)","-((t*Subscript[q,1,2])/q^5)+(t*Subscript[q,1,2])/q^3" +"(6,7,2,11)","1-q^(-2)" +"(6,8,2,11)","-Subscript[q,1,2]^(-2)+1/(q^2*Subscript[q,1,2]^2)" +"(6,3,3,6)","-(1/(q^2*Subscript[q,1,2]))" +"(6,5,3,7)","-(1/(q*t*Subscript[q,1,2]^3))" +"(6,5,3,8)","-(1/(q^2*Subscript[q,1,2]))" +"(6,7,3,10)","-Subscript[q,1,2]^(-1)+1/(q^4*Subscript[q,1,2])" +"(6,9,3,11)","-(1/(q*t*Subscript[q,1,2]^4))+q^3/(t*Subscript[q,1,2]^4)" +"(6,4,4,6)","-(1/(q^2*Subscript[q,1,2]))" +"(6,5,4,7)","1/(q*t*Subscript[q,1,2]^2)" +"(6,5,4,8)","q^(-2)" +"(6,7,4,10)","-q^(-4)+q^(-2)" +"(6,8,4,10)","Subscript[q,1,2]^(-2)-1/(q^2*Subscript[q,1,2]^2)" +"(6,9,4,11)","1/(q*t*Subscript[q,1,2]^3)-q^3/(t*Subscript[q,1,2]^3)" +"(6,5,5,6)","1/(q*t*Subscript[q,1,2]^3)" +"(6,9,5,10)","1/(q*t*Subscript[q,1,2]^4)-q^3/(t*Subscript[q,1,2]^4)" +"(6,6,6,6)","-(t/q^5)" +"(6,7,6,7)","q^(-4)" +"(6,8,6,7)","-(1/(q^4*Subscript[q,1,2]^2))" +"(6,7,6,8)","(t*Subscript[q,1,2]^2)/q^5" +"(6,8,6,8)","-(t/q^5)" +"(6,10,6,10)","t/q^7-t/q^3" +"(6,11,6,11)","1-q^(-4)+t/q^7-t/q^3" +"(6,7,7,6)","1/(q^4*Subscript[q,1,2]^2)" +"(6,9,7,7)","1/(q^3*t*Subscript[q,1,2]^4)" +"(6,9,7,8)","1/(q^4*Subscript[q,1,2]^2)" +"(6,11,7,10)","Subscript[q,1,2]^(-2)-1/(q^6*Subscript[q,1,2]^2)" +"(6,8,8,6)","1/(q^4*Subscript[q,1,2]^2)" +"(6,9,8,7)","1/(q^3*t*Subscript[q,1,2]^2)" +"(6,9,8,8)","q^(-4)" +"(6,11,8,10)","-q^(-6)+q^(-4)-t/q^7+t/q^3" +"(6,9,9,6)","-(1/(q^3*t*Subscript[q,1,2]^4))" +"(6,10,10,6)","t/(q^7*Subscript[q,1,2])" +"(6,11,10,7)","q^(-6)-t/q^9" +"(6,11,10,8)","(t*Subscript[q,1,2]^2)/q^7-(t^2*Subscript[q,1,2]^2)/q^10" +"(6,11,11,6)","-(1/(q^6*Subscript[q,1,2]^3))" +"(7,0,0,7)","1" +"(7,1,0,9)","1" +"(7,2,0,10)","-((t*Subscript[q,1,2]^2)/q^3)" +"(7,3,0,11)","1" +"(7,4,0,11)","Subscript[q,1,2]^(-1)" +"(7,1,1,7)","q/(t*Subscript[q,1,2]^2)" +"(7,3,1,10)","-q^(-2)" +"(7,4,1,10)","-Subscript[q,1,2]^(-1)" +"(7,5,1,11)","q/(t*Subscript[q,1,2]^3)-q^3/(t*Subscript[q,1,2]^3)" +"(7,2,2,7)","(t*Subscript[q,1,2]^2)/q^3" +"(7,3,2,9)","(t*Subscript[q,1,2]^3)/q^3" +"(7,4,2,9)","(t*Subscript[q,1,2]^2)/q^3" +"(7,3,3,7)","q^(-2)" +"(7,5,3,9)","q^(-2)" +"(7,6,3,10)","(t*Subscript[q,1,2])/q^3" +"(7,7,3,11)","1" +"(7,8,3,11)","-Subscript[q,1,2]^(-2)" +"(7,4,4,7)","q^(-2)" +"(7,5,4,9)","-(Subscript[q,1,2]/q^2)" +"(7,6,4,10)","-((t*Subscript[q,1,2]^2)/q^5)" +"(7,7,4,11)","-(Subscript[q,1,2]/q^2)" +"(7,8,4,11)","1/(q^2*Subscript[q,1,2])" +"(7,5,5,7)","1/(q*t*Subscript[q,1,2]^2)" +"(7,7,5,10)","-q^(-4)" +"(7,8,5,10)","Subscript[q,1,2]^(-2)" +"(7,9,5,11)","1/(q*t*Subscript[q,1,2]^3)-q^3/(t*Subscript[q,1,2]^3)" +"(7,6,6,7)","(t*Subscript[q,1,2]^2)/q^5" +"(7,7,6,9)","-((t*Subscript[q,1,2]^4)/q^5)" +"(7,8,6,9)","(t*Subscript[q,1,2]^2)/q^5" +"(7,7,7,7)","q^(-4)" +"(7,9,7,9)","q^(-4)" +"(7,10,7,10)","-(t/q^3)" +"(7,11,7,11)","1-t/q^3" +"(7,8,8,7)","q^(-4)" +"(7,9,8,9)","Subscript[q,1,2]^2/q^4" +"(7,10,8,10)","-((t*Subscript[q,1,2]^2)/q^7)" +"(7,11,8,11)","Subscript[q,1,2]^2/q^4-(t*Subscript[q,1,2]^2)/q^7" +"(7,9,9,7)","1/(q^3*t*Subscript[q,1,2]^2)" +"(7,11,9,10)","-q^(-6)+t/q^3" +"(7,10,10,7)","(t*Subscript[q,1,2]^2)/q^7" +"(7,11,10,9)","(t*Subscript[q,1,2]^5)/q^7-(t^2*Subscript[q,1,2]^5)/q^10" +"(7,11,11,7)","q^(-6)" +"(8,0,0,8)","1" +"(8,1,0,9)","-(q/(t*Subscript[q,1,2]^2))" +"(8,2,0,10)","1" +"(8,3,0,11)","-(q/(t*Subscript[q,1,2]^2))" +"(8,4,0,11)","-(q^3/(t*Subscript[q,1,2]^3))" +"(8,1,1,8)","q/(t*Subscript[q,1,2]^2)" +"(8,3,1,10)","q/(t*Subscript[q,1,2]^2)" +"(8,4,1,10)","q^3/(t*Subscript[q,1,2]^3)" +"(8,2,2,8)","(t*Subscript[q,1,2]^2)/q^3" +"(8,3,2,9)","-(Subscript[q,1,2]/q^2)" +"(8,4,2,9)","-q^(-2)" +"(8,6,2,11)","1-q^(-2)" +"(8,3,3,8)","q^(-2)" +"(8,5,3,9)","-(1/(q*t*Subscript[q,1,2]^2))" +"(8,6,3,10)","-Subscript[q,1,2]^(-1)" +"(8,7,3,11)","-(1/(q*t*Subscript[q,1,2]^2))" +"(8,8,3,11)","q^3/(t*Subscript[q,1,2]^4)" +"(8,4,4,8)","q^(-2)" +"(8,5,4,9)","1/(q*t*Subscript[q,1,2])" +"(8,6,4,10)","q^(-2)" +"(8,7,4,11)","1/(q*t*Subscript[q,1,2])" +"(8,8,4,11)","-(q^3/(t*Subscript[q,1,2]^3))" +"(8,5,5,8)","1/(q*t*Subscript[q,1,2]^2)" +"(8,7,5,10)","1/(q*t*Subscript[q,1,2]^2)" +"(8,8,5,10)","-(q^3/(t*Subscript[q,1,2]^4))" +"(8,6,6,8)","(t*Subscript[q,1,2]^2)/q^5" +"(8,7,6,9)","Subscript[q,1,2]^2/q^4" +"(8,8,6,9)","-q^(-4)" +"(8,10,6,11)","1-q^(-4)" +"(8,7,7,8)","q^(-4)" +"(8,9,7,9)","-(1/(q^3*t*Subscript[q,1,2]^2))" +"(8,10,7,10)","Subscript[q,1,2]^(-2)" +"(8,11,7,11)","Subscript[q,1,2]^(-2)-1/(q^3*t*Subscript[q,1,2]^2)" +"(8,8,8,8)","q^(-4)" +"(8,9,8,9)","-(1/(q^3*t))" +"(8,10,8,10)","q^(-4)" +"(8,11,8,11)","1-1/(q^3*t)" +"(8,9,9,8)","1/(q^3*t*Subscript[q,1,2]^2)" +"(8,11,9,10)","-Subscript[q,1,2]^(-2)+1/(q^3*t*Subscript[q,1,2]^2)" +"(8,10,10,8)","(t*Subscript[q,1,2]^2)/q^7" +"(8,11,10,9)","-(Subscript[q,1,2]^3/q^6)+(t*Subscript[q,1,2]^3)/q^9" +"(8,11,11,8)","q^(-6)" +"(9,0,0,9)","1" +"(9,2,0,11)","1" +"(9,1,1,9)","-(q/(t*Subscript[q,1,2]^2))" +"(9,3,1,11)","-(q/(t*Subscript[q,1,2]^2))" +"(9,4,1,11)","-(q^3/(t*Subscript[q,1,2]^3))" +"(9,2,2,9)","(t*Subscript[q,1,2]^3)/q^3" +"(9,3,3,9)","-(Subscript[q,1,2]/q^2)" +"(9,6,3,11)","1" +"(9,4,4,9)","-(Subscript[q,1,2]/q^2)" +"(9,6,4,11)","-(Subscript[q,1,2]/q^2)" +"(9,5,5,9)","1/(q*t*Subscript[q,1,2])" +"(9,7,5,11)","1/(q*t*Subscript[q,1,2])" +"(9,8,5,11)","-(q^3/(t*Subscript[q,1,2]^3))" +"(9,6,6,9)","-((t*Subscript[q,1,2]^4)/q^5)" +"(9,7,7,9)","Subscript[q,1,2]^2/q^4" +"(9,10,7,11)","1" +"(9,8,8,9)","Subscript[q,1,2]^2/q^4" +"(9,10,8,11)","Subscript[q,1,2]^2/q^4" +"(9,9,9,9)","-(1/(q^3*t))" +"(9,11,9,11)","1-1/(q^3*t)" +"(9,10,10,9)","(t*Subscript[q,1,2]^5)/q^7" +"(9,11,11,9)","-(Subscript[q,1,2]^3/q^6)" +"(10,0,0,10)","1" +"(10,1,0,11)","-(q^3/(t*Subscript[q,1,2]^3))" +"(10,1,1,10)","q^3/(t*Subscript[q,1,2]^3)" +"(10,2,2,10)","-((t*Subscript[q,1,2]^2)/q^3)" +"(10,3,2,11)","1" +"(10,4,2,11)","Subscript[q,1,2]^(-1)" +"(10,3,3,10)","-Subscript[q,1,2]^(-1)" +"(10,5,3,11)","q^3/(t*Subscript[q,1,2]^4)" +"(10,4,4,10)","-Subscript[q,1,2]^(-1)" +"(10,5,4,11)","-(q^3/(t*Subscript[q,1,2]^3))" +"(10,5,5,10)","-(q^3/(t*Subscript[q,1,2]^4))" +"(10,6,6,10)","(t*Subscript[q,1,2])/q^3" +"(10,7,6,11)","1" +"(10,8,6,11)","-Subscript[q,1,2]^(-2)" +"(10,7,7,10)","Subscript[q,1,2]^(-2)" +"(10,9,7,11)","-(q^3/(t*Subscript[q,1,2]^5))" +"(10,8,8,10)","Subscript[q,1,2]^(-2)" +"(10,9,8,11)","-(q^3/(t*Subscript[q,1,2]^3))" +"(10,9,9,10)","q^3/(t*Subscript[q,1,2]^5)" +"(10,10,10,10)","-(t/q^3)" +"(10,11,10,11)","1-t/q^3" +"(10,11,11,10)","-Subscript[q,1,2]^(-3)" +"(11,0,0,11)","1" +"(11,1,1,11)","-(q^3/(t*Subscript[q,1,2]^3))" +"(11,2,2,11)","-((t*Subscript[q,1,2]^3)/q^3)" +"(11,3,3,11)","1" +"(11,4,4,11)","1" +"(11,5,5,11)","-(q^3/(t*Subscript[q,1,2]^3))" +"(11,6,6,11)","-((t*Subscript[q,1,2]^3)/q^3)" +"(11,7,7,11)","1" +"(11,8,8,11)","1" +"(11,9,9,11)","-(q^3/(t*Subscript[q,1,2]^3))" +"(11,10,10,11)","-((t*Subscript[q,1,2]^3)/q^3)" +"(11,11,11,11)","1" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/hn.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/hn.csv new file mode 100644 index 0000000..7b97704 --- /dev/null +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/hn.csv @@ -0,0 +1,13 @@ +"(12,12)","LaurentPolynomial" +"(0,0)","1" +"(1,1)","-1" +"(2,2)","-1" +"(3,3)","1" +"(4,4)","1" +"(5,5)","-1" +"(6,6)","-1" +"(7,7)","1" +"(8,8)","1" +"(9,9)","-1" +"(10,10)","-1" +"(11,11)","1" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/hp.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/hp.csv new file mode 100644 index 0000000..7b97704 --- /dev/null +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/hp.csv @@ -0,0 +1,13 @@ +"(12,12)","LaurentPolynomial" +"(0,0)","1" +"(1,1)","-1" +"(2,2)","-1" +"(3,3)","1" +"(4,4)","1" +"(5,5)","-1" +"(6,6)","-1" +"(7,7)","1" +"(8,8)","1" +"(9,9)","-1" +"(10,10)","-1" +"(11,11)","1" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/Rn.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/Rn.csv new file mode 100644 index 0000000..b7f72b9 --- /dev/null +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/Rn.csv @@ -0,0 +1,1378 @@ +"(16,16,16,16)","LaurentPolynomial" +"(0,0,0,0)","1" +"(0,1,1,0)","1" +"(0,2,2,0)","1" +"(0,3,3,0)","1" +"(0,4,4,0)","1" +"(0,5,5,0)","1" +"(0,6,6,0)","1" +"(0,7,7,0)","1" +"(0,8,8,0)","1" +"(0,9,9,0)","1" +"(0,10,10,0)","1" +"(0,11,11,0)","1" +"(0,12,12,0)","1" +"(0,13,13,0)","1" +"(0,14,14,0)","1" +"(0,15,15,0)","1" +"(1,0,0,1)","q^2*t" +"(1,0,1,0)","1-q^2*t" +"(1,1,1,1)","-(q^2*t)" +"(1,2,2,1)","q*t" +"(1,2,3,0)","1" +"(1,3,3,1)","-(q*t)" +"(1,2,4,0)","-(q*t)" +"(1,4,4,1)","-(q*t)" +"(1,3,5,0)","q*t" +"(1,4,5,0)","1" +"(1,5,5,1)","q*t" +"(1,6,6,1)","-t" +"(1,6,7,0)","1" +"(1,7,7,1)","t" +"(1,6,8,0)","t" +"(1,8,8,1)","t" +"(1,7,9,0)","-t" +"(1,8,9,0)","1" +"(1,9,9,1)","-t" +"(1,10,10,1)","t/q" +"(1,10,11,0)","1" +"(1,11,11,1)","-(t/q)" +"(1,10,12,0)","-(t/q)" +"(1,12,12,1)","-(t/q)" +"(1,11,13,0)","t/q" +"(1,12,13,0)","1" +"(1,13,13,1)","t/q" +"(1,14,14,1)","-(t/q^2)" +"(1,14,15,0)","1" +"(1,15,15,1)","t/q^2" +"(2,0,0,2)","q^2/t" +"(2,1,1,2)","q^2/t" +"(2,0,2,0)","1-q^2/t" +"(2,2,2,2)","-(q^2/t)" +"(2,1,3,0)","-(q^2/t)" +"(2,3,3,2)","-(q^2/t)" +"(2,1,4,0)","1" +"(2,4,4,2)","-(q^2/t)" +"(2,5,5,2)","-(q^2/t)" +"(2,3,6,0)","1" +"(2,4,6,0)","q^2/t" +"(2,6,6,2)","q^2/t" +"(2,5,7,0)","q^2/t" +"(2,7,7,2)","q^2/t" +"(2,5,8,0)","1" +"(2,8,8,2)","q^2/t" +"(2,9,9,2)","q^2/t" +"(2,7,10,0)","1" +"(2,8,10,0)","-(q^2/t)" +"(2,10,10,2)","-(q^2/t)" +"(2,9,11,0)","-(q^2/t)" +"(2,11,11,2)","-(q^2/t)" +"(2,9,12,0)","1" +"(2,12,12,2)","-(q^2/t)" +"(2,13,13,2)","-(q^2/t)" +"(2,11,14,0)","1" +"(2,12,14,0)","q^2/t" +"(2,14,14,2)","q^2/t" +"(2,13,15,0)","q^2/t" +"(2,15,15,2)","q^2/t" +"(3,0,0,3)","q^4" +"(3,0,1,2)","-q^4+q^2/t" +"(3,1,1,3)","-q^4" +"(3,0,2,1)","-q^3+q*t" +"(3,2,2,3)","-q^3" +"(3,0,3,0)","1-q^2/t" +"(3,1,3,1)","q^3" +"(3,2,3,2)","-(q^2/t)" +"(3,3,3,3)","q^3" +"(3,0,4,0)","q^3-q*t" +"(3,1,4,1)","-(q*t)" +"(3,2,4,2)","q^3" +"(3,4,4,3)","q^3" +"(3,1,5,0)","1-q^3" +"(3,3,5,2)","-q^3" +"(3,4,5,2)","-(q^2/t)" +"(3,5,5,3)","-q^3" +"(3,3,6,1)","-t" +"(3,4,6,1)","-q^2" +"(3,6,6,3)","-q^2" +"(3,3,7,0)","1" +"(3,4,7,0)","q^2/t" +"(3,5,7,1)","q^2" +"(3,6,7,2)","q^2/t" +"(3,7,7,3)","q^2" +"(3,3,8,0)","t" +"(3,4,8,0)","q^2" +"(3,5,8,1)","t" +"(3,6,8,2)","q^2" +"(3,8,8,3)","q^2" +"(3,5,9,0)","1-q^2" +"(3,7,9,2)","-q^2" +"(3,8,9,2)","q^2/t" +"(3,9,9,3)","-q^2" +"(3,7,10,1)","t/q" +"(3,8,10,1)","-q" +"(3,10,10,3)","-q" +"(3,7,11,0)","1" +"(3,8,11,0)","-(q^2/t)" +"(3,9,11,1)","q" +"(3,10,11,2)","-(q^2/t)" +"(3,11,11,3)","q" +"(3,7,12,0)","-(t/q)" +"(3,8,12,0)","q" +"(3,9,12,1)","-(t/q)" +"(3,10,12,2)","q" +"(3,12,12,3)","q" +"(3,9,13,0)","1-q" +"(3,11,13,2)","-q" +"(3,12,13,2)","-(q^2/t)" +"(3,13,13,3)","-q" +"(3,11,14,1)","-(t/q^2)" +"(3,12,14,1)","-1" +"(3,14,14,3)","-1" +"(3,11,15,0)","1" +"(3,12,15,0)","q^2/t" +"(3,13,15,1)","1" +"(3,14,15,2)","q^2/t" +"(3,15,15,3)","1" +"(4,0,0,4)","q^4" +"(4,0,1,2)","-q^4+q^2/t" +"(4,1,1,4)","-q^4" +"(4,0,2,1)","-q^4+q^2*t" +"(4,2,2,4)","-q^3" +"(4,0,3,0)","q^4-q^2/t" +"(4,1,3,1)","q^4" +"(4,2,3,2)","-(q^2/t)" +"(4,3,3,4)","q^3" +"(4,0,4,0)","1-q^2*t" +"(4,1,4,1)","-(q^2*t)" +"(4,2,4,2)","q^3" +"(4,4,4,4)","q^3" +"(4,3,5,2)","-q^3" +"(4,4,5,2)","-(q^2/t)" +"(4,5,5,4)","-q^3" +"(4,2,6,0)","1-q^3" +"(4,3,6,1)","-(q*t)" +"(4,4,6,1)","-q^3" +"(4,6,6,4)","-q^2" +"(4,3,7,0)","q^3" +"(4,4,7,0)","q^2/t" +"(4,5,7,1)","q^3" +"(4,6,7,2)","q^2/t" +"(4,7,7,4)","q^2" +"(4,3,8,0)","q*t" +"(4,4,8,0)","1" +"(4,5,8,1)","q*t" +"(4,6,8,2)","q^2" +"(4,8,8,4)","q^2" +"(4,7,9,2)","-q^2" +"(4,8,9,2)","q^2/t" +"(4,9,9,4)","-q^2" +"(4,6,10,0)","1-q^2" +"(4,7,10,1)","t" +"(4,8,10,1)","-q^2" +"(4,10,10,4)","-q" +"(4,7,11,0)","q^2" +"(4,8,11,0)","-(q^2/t)" +"(4,9,11,1)","q^2" +"(4,10,11,2)","-(q^2/t)" +"(4,11,11,4)","q" +"(4,7,12,0)","-t" +"(4,8,12,0)","1" +"(4,9,12,1)","-t" +"(4,10,12,2)","q" +"(4,12,12,4)","q" +"(4,11,13,2)","-q" +"(4,12,13,2)","-(q^2/t)" +"(4,13,13,4)","-q" +"(4,10,14,0)","1-q" +"(4,11,14,1)","-(t/q)" +"(4,12,14,1)","-q" +"(4,14,14,4)","-1" +"(4,11,15,0)","q" +"(4,12,15,0)","q^2/t" +"(4,13,15,1)","q" +"(4,14,15,2)","q^2/t" +"(4,15,15,4)","1" +"(5,0,0,5)","q^6*t" +"(5,0,1,3)","-q^4+q^6*t" +"(5,0,1,4)","q^4-q^6*t" +"(5,1,1,5)","q^6*t" +"(5,2,2,5)","-(q^4*t)" +"(5,0,3,1)","q^3-q^4+q^2*t-q^5*t" +"(5,2,3,3)","q^3" +"(5,2,3,4)","-q^3" +"(5,3,3,5)","-(q^4*t)" +"(5,0,4,1)","-(q*t)+q^5*t" +"(5,2,4,3)","-(q^4*t)" +"(5,2,4,4)","q^4*t" +"(5,4,4,5)","-(q^4*t)" +"(5,0,5,0)","1-q^3-q^2*t+q^5*t" +"(5,1,5,1)","-(q^2*t)+q^5*t" +"(5,3,5,3)","-(q^4*t)" +"(5,4,5,3)","-q^3" +"(5,3,5,4)","q^4*t" +"(5,4,5,4)","q^3" +"(5,5,5,5)","-(q^4*t)" +"(5,2,6,1)","-t+q^3*t" +"(5,6,6,5)","q^2*t" +"(5,2,7,0)","1-q^3" +"(5,3,7,1)","-(q*t)+q^3*t" +"(5,4,7,1)","q^2-q^3" +"(5,6,7,3)","q^2" +"(5,6,7,4)","-q^2" +"(5,7,7,5)","q^2*t" +"(5,2,8,0)","t-q^3*t" +"(5,4,8,1)","t-q^3*t" +"(5,6,8,3)","q^2*t" +"(5,6,8,4)","-(q^2*t)" +"(5,8,8,5)","q^2*t" +"(5,3,9,0)","q*t-q^3*t" +"(5,4,9,0)","1-q^2" +"(5,5,9,1)","q*t-q^3*t" +"(5,7,9,3)","q^2*t" +"(5,8,9,3)","-q^2" +"(5,7,9,4)","-(q^2*t)" +"(5,8,9,4)","q^2" +"(5,9,9,5)","q^2*t" +"(5,6,10,1)","t/q-q*t" +"(5,10,10,5)","-t" +"(5,6,11,0)","1-q^2" +"(5,7,11,1)","t-q*t" +"(5,8,11,1)","q-q^2" +"(5,10,11,3)","q" +"(5,10,11,4)","-q" +"(5,11,11,5)","-t" +"(5,6,12,0)","-(t/q)+q*t" +"(5,8,12,1)","-(t/q)+q*t" +"(5,10,12,3)","-t" +"(5,10,12,4)","t" +"(5,12,12,5)","-t" +"(5,7,13,0)","-t+q*t" +"(5,8,13,0)","1-q" +"(5,9,13,1)","-t+q*t" +"(5,11,13,3)","-t" +"(5,12,13,3)","-q" +"(5,11,13,4)","t" +"(5,12,13,4)","q" +"(5,13,13,5)","-t" +"(5,10,14,1)","-(t/q^2)+t/q" +"(5,14,14,5)","t/q^2" +"(5,10,15,0)","1-q" +"(5,12,15,1)","1-q" +"(5,14,15,3)","1" +"(5,14,15,4)","-1" +"(5,15,15,5)","t/q^2" +"(6,0,0,6)","q^6/t" +"(6,1,1,6)","-(q^6/t)" +"(6,0,2,3)","q^4-q^6/t" +"(6,0,2,4)","-q^3+q^5/t" +"(6,2,2,6)","q^5/t" +"(6,0,3,2)","-(q^2/t)+q^6/t" +"(6,1,3,3)","q^6/t" +"(6,1,3,4)","-(q^5/t)" +"(6,3,3,6)","-(q^5/t)" +"(6,0,4,2)","q^3-q^4+q^2/t-q^5/t" +"(6,1,4,3)","-q^4" +"(6,1,4,4)","q^3" +"(6,4,4,6)","-(q^5/t)" +"(6,1,5,2)","-(q^2/t)+q^5/t" +"(6,5,5,6)","q^5/t" +"(6,0,6,0)","1-q^3-q^2/t+q^5/t" +"(6,2,6,2)","-(q^2/t)+q^5/t" +"(6,3,6,3)","q^3" +"(6,4,6,3)","q^5/t" +"(6,3,6,4)","-q^2" +"(6,4,6,4)","-(q^4/t)" +"(6,6,6,6)","-(q^4/t)" +"(6,1,7,0)","q^2/t-q^5/t" +"(6,3,7,2)","q^2/t-q^5/t" +"(6,5,7,3)","-(q^5/t)" +"(6,5,7,4)","q^4/t" +"(6,7,7,6)","q^4/t" +"(6,1,8,0)","1-q^3" +"(6,3,8,2)","q^2-q^3" +"(6,4,8,2)","-(q^2/t)+q^4/t" +"(6,5,8,3)","-q^3" +"(6,5,8,4)","q^2" +"(6,8,8,6)","q^4/t" +"(6,5,9,2)","q^2/t-q^4/t" +"(6,9,9,6)","-(q^4/t)" +"(6,3,10,0)","1-q^2" +"(6,4,10,0)","q^2/t-q^4/t" +"(6,6,10,2)","q^2/t-q^4/t" +"(6,7,10,3)","q^2" +"(6,8,10,3)","-(q^4/t)" +"(6,7,10,4)","-q" +"(6,8,10,4)","q^3/t" +"(6,10,10,6)","q^3/t" +"(6,5,11,0)","-(q^2/t)+q^4/t" +"(6,7,11,2)","-(q^2/t)+q^4/t" +"(6,9,11,3)","q^4/t" +"(6,9,11,4)","-(q^3/t)" +"(6,11,11,6)","-(q^3/t)" +"(6,5,12,0)","1-q^2" +"(6,7,12,2)","q-q^2" +"(6,8,12,2)","q^2/t-q^3/t" +"(6,9,12,3)","-q^2" +"(6,9,12,4)","q" +"(6,12,12,6)","-(q^3/t)" +"(6,9,13,2)","-(q^2/t)+q^3/t" +"(6,13,13,6)","q^3/t" +"(6,7,14,0)","1-q" +"(6,8,14,0)","-(q^2/t)+q^3/t" +"(6,10,14,2)","-(q^2/t)+q^3/t" +"(6,11,14,3)","q" +"(6,12,14,3)","q^3/t" +"(6,11,14,4)","-1" +"(6,12,14,4)","-(q^2/t)" +"(6,14,14,6)","-(q^2/t)" +"(6,9,15,0)","q^2/t-q^3/t" +"(6,11,15,2)","q^2/t-q^3/t" +"(6,13,15,3)","-(q^3/t)" +"(6,13,15,4)","q^2/t" +"(6,15,15,6)","q^2/t" +"(7,0,0,7)","q^8" +"(7,0,1,6)","-q^8+q^6/t" +"(7,1,1,7)","q^8" +"(7,0,2,5)","q^6-q^4*t" +"(7,2,2,7)","q^6" +"(7,0,3,3)","q^3+q^4-q^7-q^6/t" +"(7,0,3,4)","-q^3+q^5/t" +"(7,1,3,5)","q^6" +"(7,2,3,6)","q^5/t" +"(7,3,3,7)","q^6" +"(7,0,4,3)","-q^3+q^6+q^7-q^4*t" +"(7,0,4,4)","-q^6+q^4*t" +"(7,1,4,5)","-(q^4*t)" +"(7,2,4,6)","-q^6" +"(7,4,4,7)","q^6" +"(7,0,5,2)","-q^4+q^7+q^2/t-q^5/t" +"(7,1,5,3)","-q^3-q^4+q^6+q^7" +"(7,1,5,4)","q^3-q^6" +"(7,3,5,6)","-q^6" +"(7,4,5,6)","-(q^5/t)" +"(7,5,5,7)","q^6" +"(7,0,6,1)","q^2-q^5-t+q^3*t" +"(7,2,6,3)","q^2-q^5" +"(7,3,6,5)","q^2*t" +"(7,4,6,5)","q^4" +"(7,6,6,7)","q^4" +"(7,0,7,0)","1-q^3-q^2/t+q^5/t" +"(7,1,7,1)","q^2-q^5" +"(7,2,7,2)","-(q^2/t)+q^5/t" +"(7,3,7,3)","q^2+q^3-q^5" +"(7,4,7,3)","q^5/t" +"(7,3,7,4)","-q^2" +"(7,4,7,4)","-(q^4/t)" +"(7,5,7,5)","q^4" +"(7,6,7,6)","-(q^4/t)" +"(7,7,7,7)","q^4" +"(7,0,8,0)","-q^2+q^5+t-q^3*t" +"(7,1,8,1)","t-q^3*t" +"(7,2,8,2)","-q^2+q^5" +"(7,3,8,3)","q^2*t" +"(7,4,8,3)","-q^2+q^4+q^5" +"(7,3,8,4)","-(q^2*t)" +"(7,4,8,4)","-q^4" +"(7,5,8,5)","q^2*t" +"(7,6,8,6)","-q^4" +"(7,8,8,7)","q^4" +"(7,1,9,0)","1-q^2-q^3+q^5" +"(7,3,9,2)","-q^3+q^5" +"(7,4,9,2)","-(q^2/t)+q^4/t" +"(7,5,9,3)","-q^2-q^3+q^4+q^5" +"(7,5,9,4)","q^2-q^4" +"(7,7,9,6)","-q^4" +"(7,8,9,6)","q^4/t" +"(7,9,9,7)","q^4" +"(7,3,10,1)","t/q-q*t" +"(7,4,10,1)","q-q^3" +"(7,6,10,3)","q-q^3" +"(7,7,10,5)","-t" +"(7,8,10,5)","q^2" +"(7,10,10,7)","q^2" +"(7,3,11,0)","1-q^2" +"(7,4,11,0)","q^2/t-q^4/t" +"(7,5,11,1)","q-q^3" +"(7,6,11,2)","q^2/t-q^4/t" +"(7,7,11,3)","q+q^2-q^3" +"(7,8,11,3)","-(q^4/t)" +"(7,7,11,4)","-q" +"(7,8,11,4)","q^3/t" +"(7,9,11,5)","q^2" +"(7,10,11,6)","q^3/t" +"(7,11,11,7)","q^2" +"(7,3,12,0)","-(t/q)+q*t" +"(7,4,12,0)","-q+q^3" +"(7,5,12,1)","-(t/q)+q*t" +"(7,6,12,2)","-q+q^3" +"(7,7,12,3)","-t" +"(7,8,12,3)","-q+q^2+q^3" +"(7,7,12,4)","t" +"(7,8,12,4)","-q^2" +"(7,9,12,5)","-t" +"(7,10,12,6)","-q^2" +"(7,12,12,7)","q^2" +"(7,5,13,0)","1-q-q^2+q^3" +"(7,7,13,2)","-q^2+q^3" +"(7,8,13,2)","q^2/t-q^3/t" +"(7,9,13,3)","-q+q^3" +"(7,9,13,4)","q-q^2" +"(7,11,13,6)","-q^2" +"(7,12,13,6)","-(q^3/t)" +"(7,13,13,7)","q^2" +"(7,7,14,1)","-(t/q^2)+t/q" +"(7,8,14,1)","1-q" +"(7,10,14,3)","1-q" +"(7,11,14,5)","t/q^2" +"(7,12,14,5)","1" +"(7,14,14,7)","1" +"(7,7,15,0)","1-q" +"(7,8,15,0)","-(q^2/t)+q^3/t" +"(7,9,15,1)","1-q" +"(7,10,15,2)","-(q^2/t)+q^3/t" +"(7,11,15,3)","1" +"(7,12,15,3)","q^3/t" +"(7,11,15,4)","-1" +"(7,12,15,4)","-(q^2/t)" +"(7,13,15,5)","1" +"(7,14,15,6)","-(q^2/t)" +"(7,15,15,7)","1" +"(8,0,0,8)","q^8" +"(8,0,1,6)","q^8-q^6/t" +"(8,1,1,8)","q^8" +"(8,0,2,5)","-q^8+q^6*t" +"(8,2,2,8)","q^6" +"(8,0,3,3)","-q^8+q^6/t" +"(8,0,3,4)","-q^4+q^7+q^8-q^5/t" +"(8,1,3,5)","-q^8" +"(8,2,3,6)","-(q^5/t)" +"(8,3,3,8)","q^6" +"(8,0,4,3)","-q^4+q^6*t" +"(8,0,4,4)","q^3+q^4-q^7-q^6*t" +"(8,1,4,5)","q^6*t" +"(8,2,4,6)","q^6" +"(8,4,4,8)","q^6" +"(8,0,5,2)","q^4-q^7-q^2/t+q^5/t" +"(8,1,5,4)","q^4-q^7" +"(8,3,5,6)","q^6" +"(8,4,5,6)","q^5/t" +"(8,5,5,8)","q^6" +"(8,0,6,1)","-q^4+q^7+q^2*t-q^5*t" +"(8,2,6,3)","q^3-q^6" +"(8,2,6,4)","-q^2-q^3+q^5+q^6" +"(8,3,6,5)","-(q^4*t)" +"(8,4,6,5)","-q^6" +"(8,6,6,8)","q^4" +"(8,0,7,0)","-q^4+q^7+q^2/t-q^5/t" +"(8,1,7,1)","-q^4+q^7" +"(8,2,7,2)","q^2/t-q^5/t" +"(8,3,7,3)","-q^6" +"(8,4,7,3)","-(q^5/t)" +"(8,3,7,4)","-q^3+q^5+q^6" +"(8,4,7,4)","q^4/t" +"(8,5,7,5)","-q^6" +"(8,6,7,6)","q^4/t" +"(8,7,7,8)","q^4" +"(8,0,8,0)","1-q^3-q^2*t+q^5*t" +"(8,1,8,1)","-(q^2*t)+q^5*t" +"(8,2,8,2)","q^2-q^5" +"(8,3,8,3)","-(q^4*t)" +"(8,4,8,3)","-q^3" +"(8,3,8,4)","q^4*t" +"(8,4,8,4)","q^2+q^3-q^5" +"(8,5,8,5)","-(q^4*t)" +"(8,6,8,6)","q^4" +"(8,8,8,8)","q^4" +"(8,3,9,2)","q^3-q^5" +"(8,4,9,2)","q^2/t-q^4/t" +"(8,5,9,4)","q^3-q^5" +"(8,7,9,6)","q^4" +"(8,8,9,6)","-(q^4/t)" +"(8,9,9,8)","q^4" +"(8,2,10,0)","1-q^2-q^3+q^5" +"(8,3,10,1)","-(q*t)+q^3*t" +"(8,4,10,1)","-q^3+q^5" +"(8,6,10,3)","q^2-q^4" +"(8,6,10,4)","-q-q^2+q^3+q^4" +"(8,7,10,5)","q^2*t" +"(8,8,10,5)","-q^4" +"(8,10,10,8)","q^2" +"(8,3,11,0)","-q^3+q^5" +"(8,4,11,0)","-(q^2/t)+q^4/t" +"(8,5,11,1)","-q^3+q^5" +"(8,6,11,2)","-(q^2/t)+q^4/t" +"(8,7,11,3)","-q^4" +"(8,8,11,3)","q^4/t" +"(8,7,11,4)","-q^2+q^3+q^4" +"(8,8,11,4)","-(q^3/t)" +"(8,9,11,5)","-q^4" +"(8,10,11,6)","-(q^3/t)" +"(8,11,11,8)","q^2" +"(8,3,12,0)","q*t-q^3*t" +"(8,4,12,0)","1-q^2" +"(8,5,12,1)","q*t-q^3*t" +"(8,6,12,2)","q-q^3" +"(8,7,12,3)","q^2*t" +"(8,8,12,3)","-q^2" +"(8,7,12,4)","-(q^2*t)" +"(8,8,12,4)","q+q^2-q^3" +"(8,9,12,5)","q^2*t" +"(8,10,12,6)","q^2" +"(8,12,12,8)","q^2" +"(8,7,13,2)","q^2-q^3" +"(8,8,13,2)","-(q^2/t)+q^3/t" +"(8,9,13,4)","q^2-q^3" +"(8,11,13,6)","q^2" +"(8,12,13,6)","q^3/t" +"(8,13,13,8)","q^2" +"(8,6,14,0)","1-q-q^2+q^3" +"(8,7,14,1)","t-q*t" +"(8,8,14,1)","-q^2+q^3" +"(8,10,14,3)","q-q^2" +"(8,10,14,4)","-1+q^2" +"(8,11,14,5)","-t" +"(8,12,14,5)","-q^2" +"(8,14,14,8)","1" +"(8,7,15,0)","-q^2+q^3" +"(8,8,15,0)","q^2/t-q^3/t" +"(8,9,15,1)","-q^2+q^3" +"(8,10,15,2)","q^2/t-q^3/t" +"(8,11,15,3)","-q^2" +"(8,12,15,3)","-(q^3/t)" +"(8,11,15,4)","q^2" +"(8,12,15,4)","q^2/t" +"(8,13,15,5)","-q^2" +"(8,14,15,6)","q^2/t" +"(8,15,15,8)","1" +"(9,0,0,9)","q^10*t" +"(9,0,1,7)","q^8-q^10*t" +"(9,0,1,8)","q^8-q^10*t" +"(9,1,1,9)","-(q^10*t)" +"(9,2,2,9)","q^7*t" +"(9,0,3,5)","q^6-q^8+q^5*t+q^6*t-q^8*t-q^9*t" +"(9,2,3,7)","q^6" +"(9,2,3,8)","q^6" +"(9,3,3,9)","-(q^7*t)" +"(9,0,4,5)","-(q^4*t)-q^5*t+q^8*t+q^9*t" +"(9,2,4,7)","-(q^7*t)" +"(9,2,4,8)","-(q^7*t)" +"(9,4,4,9)","-(q^7*t)" +"(9,0,5,3)","-q^3-q^4+q^6+q^7+q^5*t+q^6*t-q^8*t-q^9*t" +"(9,0,5,4)","q^3+q^4-q^6-q^7-q^5*t-q^6*t+q^8*t+q^9*t" +"(9,1,5,5)","q^5*t+q^6*t-q^8*t-q^9*t" +"(9,3,5,7)","q^7*t" +"(9,4,5,7)","q^6" +"(9,3,5,8)","q^7*t" +"(9,4,5,8)","q^6" +"(9,5,5,9)","q^7*t" +"(9,2,6,5)","q^2*t+q^3*t-q^5*t-q^6*t" +"(9,6,6,9)","-(q^4*t)" +"(9,0,7,1)","q^2-q^4-q^5+q^7+q^2*t-q^4*t-q^5*t+q^7*t" +"(9,2,7,3)","q^2+q^3-q^5-q^6" +"(9,2,7,4)","-q^2-q^3+q^5+q^6" +"(9,3,7,5)","-(q^3*t)-q^4*t+q^5*t+q^6*t" +"(9,4,7,5)","q^4-q^6" +"(9,6,7,7)","q^4" +"(9,6,7,8)","q^4" +"(9,7,7,9)","q^4*t" +"(9,0,8,1)","t-q^3*t-q^4*t+q^7*t" +"(9,2,8,3)","q^2*t+q^3*t-q^5*t-q^6*t" +"(9,2,8,4)","-(q^2*t)-q^3*t+q^5*t+q^6*t" +"(9,4,8,5)","q^2*t+q^3*t-q^5*t-q^6*t" +"(9,6,8,7)","q^4*t" +"(9,6,8,8)","q^4*t" +"(9,8,8,9)","q^4*t" +"(9,0,9,0)","1-q^2-q^3+q^5-q^2*t+q^4*t+q^5*t-q^7*t" +"(9,1,9,1)","-(q^2*t)+q^4*t+q^5*t-q^7*t" +"(9,3,9,3)","-(q^3*t)-q^4*t+q^5*t+q^6*t" +"(9,4,9,3)","-q^2-q^3+q^4+q^5" +"(9,3,9,4)","q^3*t+q^4*t-q^5*t-q^6*t" +"(9,4,9,4)","q^2+q^3-q^4-q^5" +"(9,5,9,5)","-(q^3*t)-q^4*t+q^5*t+q^6*t" +"(9,7,9,7)","-(q^4*t)" +"(9,8,9,7)","q^4" +"(9,7,9,8)","-(q^4*t)" +"(9,8,9,8)","q^4" +"(9,9,9,9)","-(q^4*t)" +"(9,2,10,1)","t/q-q*t-q^2*t+q^4*t" +"(9,6,10,5)","-t-q*t+q^2*t+q^3*t" +"(9,10,10,9)","q*t" +"(9,2,11,0)","1-q^2-q^3+q^5" +"(9,3,11,1)","-(q*t)+q^2*t+q^3*t-q^4*t" +"(9,4,11,1)","q-2*q^3+q^5" +"(9,6,11,3)","q+q^2-q^3-q^4" +"(9,6,11,4)","-q-q^2+q^3+q^4" +"(9,7,11,5)","q*t-q^3*t" +"(9,8,11,5)","q^2-q^4" +"(9,10,11,7)","q^2" +"(9,10,11,8)","q^2" +"(9,11,11,9)","-(q*t)" +"(9,2,12,0)","-(t/q)+q*t+q^2*t-q^4*t" +"(9,4,12,1)","-(t/q)+q*t+q^2*t-q^4*t" +"(9,6,12,3)","-t-q*t+q^2*t+q^3*t" +"(9,6,12,4)","t+q*t-q^2*t-q^3*t" +"(9,8,12,5)","-t-q*t+q^2*t+q^3*t" +"(9,10,12,7)","-(q*t)" +"(9,10,12,8)","-(q*t)" +"(9,12,12,9)","-(q*t)" +"(9,3,13,0)","q*t-q^2*t-q^3*t+q^4*t" +"(9,4,13,0)","1-q-q^2+q^3" +"(9,5,13,1)","q*t-q^2*t-q^3*t+q^4*t" +"(9,7,13,3)","q*t-q^3*t" +"(9,8,13,3)","-q+q^3" +"(9,7,13,4)","-(q*t)+q^3*t" +"(9,8,13,4)","q-q^3" +"(9,9,13,5)","q*t-q^3*t" +"(9,11,13,7)","q*t" +"(9,12,13,7)","q^2" +"(9,11,13,8)","q*t" +"(9,12,13,8)","q^2" +"(9,13,13,9)","q*t" +"(9,6,14,1)","t-t/q^2+t/q-q*t" +"(9,10,14,5)","-t+t/q^2" +"(9,14,14,9)","-(t/q^2)" +"(9,6,15,0)","1-q-q^2+q^3" +"(9,8,15,1)","1-q-q^2+q^3" +"(9,10,15,3)","1-q^2" +"(9,10,15,4)","-1+q^2" +"(9,12,15,5)","1-q^2" +"(9,14,15,7)","1" +"(9,14,15,8)","1" +"(9,15,15,9)","t/q^2" +"(10,0,0,10)","q^10/t" +"(10,1,1,10)","q^10/t" +"(10,0,2,7)","q^8-q^10/t" +"(10,0,2,8)","q^6-q^8/t" +"(10,2,2,10)","-(q^8/t)" +"(10,0,3,6)","-(q^5/t)-q^6/t+q^9/t+q^10/t" +"(10,1,3,7)","-(q^10/t)" +"(10,1,3,8)","-(q^8/t)" +"(10,3,3,10)","-(q^8/t)" +"(10,0,4,6)","q^6-q^8+q^5/t+q^6/t-q^8/t-q^9/t" +"(10,1,4,7)","q^8" +"(10,1,4,8)","q^6" +"(10,4,4,10)","-(q^8/t)" +"(10,1,5,6)","q^5/t+q^6/t-q^8/t-q^9/t" +"(10,5,5,10)","-(q^8/t)" +"(10,0,6,3)","q^3+q^4-q^6-q^7-q^5/t-q^6/t+q^8/t+q^9/t" +"(10,0,6,4)","-q^2-q^3+q^5+q^6+q^4/t+q^5/t-q^7/t-q^8/t" +"(10,2,6,6)","q^4/t+q^5/t-q^7/t-q^8/t" +"(10,3,6,7)","q^6" +"(10,4,6,7)","q^8/t" +"(10,3,6,8)","q^4" +"(10,4,6,8)","q^6/t" +"(10,6,6,10)","q^6/t" +"(10,0,7,2)","q^2/t-q^5/t-q^6/t+q^9/t" +"(10,1,7,3)","-(q^5/t)-q^6/t+q^8/t+q^9/t" +"(10,1,7,4)","q^4/t+q^5/t-q^7/t-q^8/t" +"(10,3,7,6)","q^4/t+q^5/t-q^7/t-q^8/t" +"(10,5,7,7)","q^8/t" +"(10,5,7,8)","q^6/t" +"(10,7,7,10)","q^6/t" +"(10,0,8,2)","q^2-q^4-q^5+q^7+q^2/t-q^4/t-q^5/t+q^7/t" +"(10,1,8,3)","-q^3-q^4+q^6+q^7" +"(10,1,8,4)","q^2+q^3-q^5-q^6" +"(10,3,8,6)","q^4-q^6" +"(10,4,8,6)","-(q^4/t)-q^5/t+q^6/t+q^7/t" +"(10,5,8,7)","q^6" +"(10,5,8,8)","q^4" +"(10,8,8,10)","q^6/t" +"(10,1,9,2)","q^2/t-q^4/t-q^5/t+q^7/t" +"(10,5,9,6)","-(q^4/t)-q^5/t+q^6/t+q^7/t" +"(10,9,9,10)","q^6/t" +"(10,0,10,0)","1-q^2-q^3+q^5-q^2/t+q^4/t+q^5/t-q^7/t" +"(10,2,10,2)","-(q^2/t)+q^4/t+q^5/t-q^7/t" +"(10,3,10,3)","q^2+q^3-q^4-q^5" +"(10,4,10,3)","q^4/t+q^5/t-q^6/t-q^7/t" +"(10,3,10,4)","-q-q^2+q^3+q^4" +"(10,4,10,4)","-(q^3/t)-q^4/t+q^5/t+q^6/t" +"(10,6,10,6)","-(q^3/t)-q^4/t+q^5/t+q^6/t" +"(10,7,10,7)","q^4" +"(10,8,10,7)","-(q^6/t)" +"(10,7,10,8)","q^2" +"(10,8,10,8)","-(q^4/t)" +"(10,10,10,10)","-(q^4/t)" +"(10,1,11,0)","-(q^2/t)+q^4/t+q^5/t-q^7/t" +"(10,3,11,2)","-(q^2/t)+q^4/t+q^5/t-q^7/t" +"(10,5,11,3)","q^4/t+q^5/t-q^6/t-q^7/t" +"(10,5,11,4)","-(q^3/t)-q^4/t+q^5/t+q^6/t" +"(10,7,11,6)","-(q^3/t)-q^4/t+q^5/t+q^6/t" +"(10,9,11,7)","-(q^6/t)" +"(10,9,11,8)","-(q^4/t)" +"(10,11,11,10)","-(q^4/t)" +"(10,1,12,0)","1-q^2-q^3+q^5" +"(10,3,12,2)","q-2*q^3+q^5" +"(10,4,12,2)","-(q^2/t)+q^3/t+q^4/t-q^5/t" +"(10,5,12,3)","-q^2-q^3+q^4+q^5" +"(10,5,12,4)","q+q^2-q^3-q^4" +"(10,7,12,6)","q^2-q^4" +"(10,8,12,6)","q^3/t-q^5/t" +"(10,9,12,7)","q^4" +"(10,9,12,8)","q^2" +"(10,12,12,10)","-(q^4/t)" +"(10,5,13,2)","-(q^2/t)+q^3/t+q^4/t-q^5/t" +"(10,9,13,6)","q^3/t-q^5/t" +"(10,13,13,10)","-(q^4/t)" +"(10,3,14,0)","1-q-q^2+q^3" +"(10,4,14,0)","q^2/t-q^3/t-q^4/t+q^5/t" +"(10,6,14,2)","q^2/t-q^3/t-q^4/t+q^5/t" +"(10,7,14,3)","q-q^3" +"(10,8,14,3)","-(q^3/t)+q^5/t" +"(10,7,14,4)","-1+q^2" +"(10,8,14,4)","q^2/t-q^4/t" +"(10,10,14,6)","q^2/t-q^4/t" +"(10,11,14,7)","q^2" +"(10,12,14,7)","q^4/t" +"(10,11,14,8)","1" +"(10,12,14,8)","q^2/t" +"(10,14,14,10)","q^2/t" +"(10,5,15,0)","q^2/t-q^3/t-q^4/t+q^5/t" +"(10,7,15,2)","q^2/t-q^3/t-q^4/t+q^5/t" +"(10,9,15,3)","-(q^3/t)+q^5/t" +"(10,9,15,4)","q^2/t-q^4/t" +"(10,11,15,6)","q^2/t-q^4/t" +"(10,13,15,7)","q^4/t" +"(10,13,15,8)","q^2/t" +"(10,15,15,10)","q^2/t" +"(11,0,0,11)","q^12" +"(11,0,1,10)","-q^12+q^10/t" +"(11,1,1,11)","-q^12" +"(11,0,2,9)","-q^9+q^7*t" +"(11,2,2,11)","-q^9" +"(11,0,3,7)","q^6+q^7+q^8-q^10-q^11-q^10/t" +"(11,0,3,8)","q^6-q^8/t" +"(11,1,3,9)","q^9" +"(11,2,3,10)","-(q^8/t)" +"(11,3,3,11)","q^9" +"(11,0,4,7)","-q^6-q^7+q^9+q^10+q^11-q^7*t" +"(11,0,4,8)","q^9-q^7*t" +"(11,1,4,9)","-(q^7*t)" +"(11,2,4,10)","q^9" +"(11,4,4,11)","q^9" +"(11,0,5,6)","-q^7-q^8+q^10+q^11+q^5/t+q^6/t-q^8/t-q^9/t" +"(11,1,5,7)","q^6+q^7+q^8-q^9-q^10-q^11" +"(11,1,5,8)","q^6-q^9" +"(11,3,5,10)","-q^9" +"(11,4,5,10)","-(q^8/t)" +"(11,5,5,11)","-q^9" +"(11,0,6,5)","-q^4-q^5+q^7+q^8+q^2*t+q^3*t-q^5*t-q^6*t" +"(11,2,6,7)","-q^4-q^5+q^7+q^8" +"(11,3,6,9)","-(q^4*t)" +"(11,4,6,9)","-q^6" +"(11,6,6,11)","-q^6" +"(11,0,7,3)","q^2+q^3+q^4-q^5-2*q^6-q^7+q^9-q^5/t-q^6/t+q^8/t+q^9/t" +"(11,0,7,4)","-q^2-q^3+q^5+q^6+q^4/t+q^5/t-q^7/t-q^8/t" +"(11,1,7,5)","q^4+q^5-q^7-q^8" +"(11,2,7,6)","q^4/t+q^5/t-q^7/t-q^8/t" +"(11,3,7,7)","q^4+q^5+q^6-q^7-q^8" +"(11,4,7,7)","q^8/t" +"(11,3,7,8)","q^4" +"(11,4,7,8)","q^6/t" +"(11,5,7,9)","q^6" +"(11,6,7,10)","q^6/t" +"(11,7,7,11)","q^6" +"(11,0,8,3)","q^2-q^4-2*q^5-q^6+q^7+q^8+q^9+q^2*t+q^3*t-q^5*t-q^6*t" +"(11,0,8,4)","q^4+q^5-q^7-q^8-q^2*t-q^3*t+q^5*t+q^6*t" +"(11,1,8,5)","q^2*t+q^3*t-q^5*t-q^6*t" +"(11,2,8,6)","q^4+q^5-q^7-q^8" +"(11,3,8,7)","q^4*t" +"(11,4,8,7)","-q^4-q^5+q^6+q^7+q^8" +"(11,3,8,8)","q^4*t" +"(11,4,8,8)","q^6" +"(11,5,8,9)","q^4*t" +"(11,6,8,10)","q^6" +"(11,8,8,11)","q^6" +"(11,0,9,2)","-q^4+q^6+q^7-q^9+q^2/t-q^4/t-q^5/t+q^7/t" +"(11,1,9,3)","-q^2-q^3+2*q^5+2*q^6-q^8-q^9" +"(11,1,9,4)","q^2+q^3-q^4-2*q^5-q^6+q^7+q^8" +"(11,3,9,6)","-q^5-q^6+q^7+q^8" +"(11,4,9,6)","-(q^4/t)-q^5/t+q^6/t+q^7/t" +"(11,5,9,7)","q^4+q^5-q^7-q^8" +"(11,5,9,8)","q^4-q^6" +"(11,7,9,10)","-q^6" +"(11,8,9,10)","q^6/t" +"(11,9,9,11)","-q^6" +"(11,0,10,1)","-q+q^3+q^4-q^6+t/q-q*t-q^2*t+q^4*t" +"(11,2,10,3)","-q+q^3+q^4-q^6" +"(11,3,10,5)","-t-q*t+q^2*t+q^3*t" +"(11,4,10,5)","-q^2-q^3+q^4+q^5" +"(11,6,10,7)","-q^2-q^3+q^4+q^5" +"(11,7,10,9)","q*t" +"(11,8,10,9)","-q^3" +"(11,10,10,11)","-q^3" +"(11,0,11,0)","1-q^2-q^3+q^5-q^2/t+q^4/t+q^5/t-q^7/t" +"(11,1,11,1)","q-q^3-q^4+q^6" +"(11,2,11,2)","-(q^2/t)+q^4/t+q^5/t-q^7/t" +"(11,3,11,3)","q+q^2-2*q^4-q^5+q^6" +"(11,4,11,3)","q^4/t+q^5/t-q^6/t-q^7/t" +"(11,3,11,4)","-q-q^2+q^3+q^4" +"(11,4,11,4)","-(q^3/t)-q^4/t+q^5/t+q^6/t" +"(11,5,11,5)","q^2+q^3-q^4-q^5" +"(11,6,11,6)","-(q^3/t)-q^4/t+q^5/t+q^6/t" +"(11,7,11,7)","q^2+q^3-q^5" +"(11,8,11,7)","-(q^6/t)" +"(11,7,11,8)","q^2" +"(11,8,11,8)","-(q^4/t)" +"(11,9,11,9)","q^3" +"(11,10,11,10)","-(q^4/t)" +"(11,11,11,11)","q^3" +"(11,0,12,0)","q-q^3-q^4+q^6-t/q+q*t+q^2*t-q^4*t" +"(11,1,12,1)","-(t/q)+q*t+q^2*t-q^4*t" +"(11,2,12,2)","q-q^3-q^4+q^6" +"(11,3,12,3)","-t-q*t+q^2*t+q^3*t" +"(11,4,12,3)","q-q^2-2*q^3+q^5+q^6" +"(11,3,12,4)","t+q*t-q^2*t-q^3*t" +"(11,4,12,4)","q^2+q^3-q^4-q^5" +"(11,5,12,5)","-t-q*t+q^2*t+q^3*t" +"(11,6,12,6)","q^2+q^3-q^4-q^5" +"(11,7,12,7)","-(q*t)" +"(11,8,12,7)","-q^2+q^4+q^5" +"(11,7,12,8)","-(q*t)" +"(11,8,12,8)","q^3" +"(11,9,12,9)","-(q*t)" +"(11,10,12,10)","q^3" +"(11,12,12,11)","q^3" +"(11,1,13,0)","1-q-q^2+q^4+q^5-q^6" +"(11,3,13,2)","-q^3+q^4+q^5-q^6" +"(11,4,13,2)","-(q^2/t)+q^3/t+q^4/t-q^5/t" +"(11,5,13,3)","-q+q^3+q^4-q^6" +"(11,5,13,4)","q-2*q^3+q^5" +"(11,7,13,6)","-q^3+q^5" +"(11,8,13,6)","q^3/t-q^5/t" +"(11,9,13,7)","q^2-q^5" +"(11,9,13,8)","q^2-q^3" +"(11,11,13,10)","-q^3" +"(11,12,13,10)","-(q^4/t)" +"(11,13,13,11)","-q^3" +"(11,3,14,1)","t-t/q^2+t/q-q*t" +"(11,4,14,1)","-1+q+q^2-q^3" +"(11,6,14,3)","-1+q+q^2-q^3" +"(11,7,14,5)","-t+t/q^2" +"(11,8,14,5)","-1+q^2" +"(11,10,14,7)","-1+q^2" +"(11,11,14,9)","-(t/q^2)" +"(11,12,14,9)","-1" +"(11,14,14,11)","-1" +"(11,3,15,0)","1-q-q^2+q^3" +"(11,4,15,0)","q^2/t-q^3/t-q^4/t+q^5/t" +"(11,5,15,1)","1-q-q^2+q^3" +"(11,6,15,2)","q^2/t-q^3/t-q^4/t+q^5/t" +"(11,7,15,3)","1-q^2" +"(11,8,15,3)","-(q^3/t)+q^5/t" +"(11,7,15,4)","-1+q^2" +"(11,8,15,4)","q^2/t-q^4/t" +"(11,9,15,5)","1-q^2" +"(11,10,15,6)","q^2/t-q^4/t" +"(11,11,15,7)","1" +"(11,12,15,7)","q^4/t" +"(11,11,15,8)","1" +"(11,12,15,8)","q^2/t" +"(11,13,15,9)","1" +"(11,14,15,10)","q^2/t" +"(11,15,15,11)","1" +"(12,0,0,12)","q^12" +"(12,0,1,10)","-q^12+q^10/t" +"(12,1,1,12)","-q^12" +"(12,0,2,9)","-q^12+q^10*t" +"(12,2,2,12)","-q^9" +"(12,0,3,7)","q^12-q^10/t" +"(12,0,3,8)","-q^7-q^8+q^10+q^11+q^12-q^8/t" +"(12,1,3,9)","q^12" +"(12,2,3,10)","-(q^8/t)" +"(12,3,3,12)","q^9" +"(12,0,4,7)","q^8-q^10*t" +"(12,0,4,8)","q^6+q^7+q^8-q^10-q^11-q^10*t" +"(12,1,4,9)","-(q^10*t)" +"(12,2,4,10)","q^9" +"(12,4,4,12)","q^9" +"(12,0,5,6)","-q^7-q^8+q^10+q^11+q^5/t+q^6/t-q^8/t-q^9/t" +"(12,1,5,8)","-q^7-q^8+q^10+q^11" +"(12,3,5,10)","-q^9" +"(12,4,5,10)","-(q^8/t)" +"(12,5,5,12)","-q^9" +"(12,0,6,5)","-q^7-q^8+q^10+q^11+q^5*t+q^6*t-q^8*t-q^9*t" +"(12,2,6,7)","q^6-q^9" +"(12,2,6,8)","q^4+q^5+q^6-q^7-q^8-q^9" +"(12,3,6,9)","-(q^7*t)" +"(12,4,6,9)","-q^9" +"(12,6,6,12)","-q^6" +"(12,0,7,3)","q^7+q^8-q^10-q^11-q^5/t-q^6/t+q^8/t+q^9/t" +"(12,0,7,4)","q^4-q^6-2*q^7-q^8+q^9+q^10+q^11+q^4/t+q^5/t-q^7/t-q^8/t" +"(12,1,7,5)","q^7+q^8-q^10-q^11" +"(12,2,7,6)","q^4/t+q^5/t-q^7/t-q^8/t" +"(12,3,7,7)","q^9" +"(12,4,7,7)","q^8/t" +"(12,3,7,8)","-q^5-q^6+q^7+q^8+q^9" +"(12,4,7,8)","q^6/t" +"(12,5,7,9)","q^9" +"(12,6,7,10)","q^6/t" +"(12,7,7,12)","q^6" +"(12,0,8,3)","-q^3-q^4+q^6+q^7+q^5*t+q^6*t-q^8*t-q^9*t" +"(12,0,8,4)","q^2+q^3+q^4-q^5-2*q^6-q^7+q^9-q^5*t-q^6*t+q^8*t+q^9*t" +"(12,1,8,5)","q^5*t+q^6*t-q^8*t-q^9*t" +"(12,2,8,6)","q^4+q^5-q^7-q^8" +"(12,3,8,7)","q^7*t" +"(12,4,8,7)","q^6" +"(12,3,8,8)","q^7*t" +"(12,4,8,8)","q^4+q^5+q^6-q^7-q^8" +"(12,5,8,9)","q^7*t" +"(12,6,8,10)","q^6" +"(12,8,8,12)","q^6" +"(12,0,9,2)","-q^4+q^6+q^7-q^9+q^2/t-q^4/t-q^5/t+q^7/t" +"(12,1,9,4)","-q^4+q^6+q^7-q^9" +"(12,3,9,6)","-q^5-q^6+q^7+q^8" +"(12,4,9,6)","-(q^4/t)-q^5/t+q^6/t+q^7/t" +"(12,5,9,8)","-q^5-q^6+q^7+q^8" +"(12,7,9,10)","-q^6" +"(12,8,9,10)","q^6/t" +"(12,9,9,12)","-q^6" +"(12,0,10,1)","-q^4+q^6+q^7-q^9+q^2*t-q^4*t-q^5*t+q^7*t" +"(12,2,10,3)","q^2+q^3-q^4-2*q^5-q^6+q^7+q^8" +"(12,2,10,4)","-q-q^2+2*q^4+2*q^5-q^7-q^8" +"(12,3,10,5)","-(q^3*t)-q^4*t+q^5*t+q^6*t" +"(12,4,10,5)","-q^5-q^6+q^7+q^8" +"(12,6,10,7)","q^4-q^6" +"(12,6,10,8)","q^2+q^3-q^5-q^6" +"(12,7,10,9)","q^4*t" +"(12,8,10,9)","-q^6" +"(12,10,10,12)","-q^3" +"(12,0,11,0)","q^4-q^6-q^7+q^9-q^2/t+q^4/t+q^5/t-q^7/t" +"(12,1,11,1)","q^4-q^6-q^7+q^9" +"(12,2,11,2)","-(q^2/t)+q^4/t+q^5/t-q^7/t" +"(12,3,11,3)","q^5+q^6-q^7-q^8" +"(12,4,11,3)","q^4/t+q^5/t-q^6/t-q^7/t" +"(12,3,11,4)","q^3-q^4-2*q^5+q^7+q^8" +"(12,4,11,4)","-(q^3/t)-q^4/t+q^5/t+q^6/t" +"(12,5,11,5)","q^5+q^6-q^7-q^8" +"(12,6,11,6)","-(q^3/t)-q^4/t+q^5/t+q^6/t" +"(12,7,11,7)","q^6" +"(12,8,11,7)","-(q^6/t)" +"(12,7,11,8)","-q^3+q^5+q^6" +"(12,8,11,8)","-(q^4/t)" +"(12,9,11,9)","q^6" +"(12,10,11,10)","-(q^4/t)" +"(12,11,11,12)","q^3" +"(12,0,12,0)","1-q^2-q^3+q^5-q^2*t+q^4*t+q^5*t-q^7*t" +"(12,1,12,1)","-(q^2*t)+q^4*t+q^5*t-q^7*t" +"(12,2,12,2)","q-q^3-q^4+q^6" +"(12,3,12,3)","-(q^3*t)-q^4*t+q^5*t+q^6*t" +"(12,4,12,3)","-q^2-q^3+q^4+q^5" +"(12,3,12,4)","q^3*t+q^4*t-q^5*t-q^6*t" +"(12,4,12,4)","q+q^2-2*q^4-q^5+q^6" +"(12,5,12,5)","-(q^3*t)-q^4*t+q^5*t+q^6*t" +"(12,6,12,6)","q^2+q^3-q^4-q^5" +"(12,7,12,7)","-(q^4*t)" +"(12,8,12,7)","q^4" +"(12,7,12,8)","-(q^4*t)" +"(12,8,12,8)","q^2+q^3-q^5" +"(12,9,12,9)","-(q^4*t)" +"(12,10,12,10)","q^3" +"(12,12,12,12)","q^3" +"(12,3,13,2)","-q^3+q^4+q^5-q^6" +"(12,4,13,2)","-(q^2/t)+q^3/t+q^4/t-q^5/t" +"(12,5,13,4)","-q^3+q^4+q^5-q^6" +"(12,7,13,6)","-q^3+q^5" +"(12,8,13,6)","q^3/t-q^5/t" +"(12,9,13,8)","-q^3+q^5" +"(12,11,13,10)","-q^3" +"(12,12,13,10)","-(q^4/t)" +"(12,13,13,12)","-q^3" +"(12,2,14,0)","1-q-q^2+q^4+q^5-q^6" +"(12,3,14,1)","-(q*t)+q^2*t+q^3*t-q^4*t" +"(12,4,14,1)","-q^3+q^4+q^5-q^6" +"(12,6,14,3)","q-2*q^3+q^5" +"(12,6,14,4)","-1+q^2+q^3-q^5" +"(12,7,14,5)","q*t-q^3*t" +"(12,8,14,5)","-q^3+q^5" +"(12,10,14,7)","q^2-q^3" +"(12,10,14,8)","1-q^3" +"(12,11,14,9)","-(q*t)" +"(12,12,14,9)","-q^3" +"(12,14,14,12)","-1" +"(12,3,15,0)","q^3-q^4-q^5+q^6" +"(12,4,15,0)","q^2/t-q^3/t-q^4/t+q^5/t" +"(12,5,15,1)","q^3-q^4-q^5+q^6" +"(12,6,15,2)","q^2/t-q^3/t-q^4/t+q^5/t" +"(12,7,15,3)","q^3-q^5" +"(12,8,15,3)","-(q^3/t)+q^5/t" +"(12,7,15,4)","-q^3+q^5" +"(12,8,15,4)","q^2/t-q^4/t" +"(12,9,15,5)","q^3-q^5" +"(12,10,15,6)","q^2/t-q^4/t" +"(12,11,15,7)","q^3" +"(12,12,15,7)","q^4/t" +"(12,11,15,8)","q^3" +"(12,12,15,8)","q^2/t" +"(12,13,15,9)","q^3" +"(12,14,15,10)","q^2/t" +"(12,15,15,12)","1" +"(13,0,0,13)","q^14*t" +"(13,0,1,11)","-q^12+q^14*t" +"(13,0,1,12)","q^12-q^14*t" +"(13,1,1,13)","q^14*t" +"(13,2,2,13)","-(q^10*t)" +"(13,0,3,9)","q^9-q^12+q^8*t+q^9*t+q^10*t-q^11*t-q^12*t-q^13*t" +"(13,2,3,11)","q^9" +"(13,2,3,12)","-q^9" +"(13,3,3,13)","-(q^10*t)" +"(13,0,4,9)","-(q^7*t)-q^8*t-q^9*t+q^11*t+q^12*t+q^13*t" +"(13,2,4,11)","-(q^10*t)" +"(13,2,4,12)","q^10*t" +"(13,4,4,13)","-(q^10*t)" +"(13,0,5,7)","q^6+q^7+q^8-q^9-q^10-q^11-q^8*t-q^9*t-q^10*t+q^11*t+q^12*t+q^13*t" +"(13,0,5,8)","q^6+q^7+q^8-q^9-q^10-q^11-q^8*t-q^9*t-q^10*t+q^11*t+q^12*t+q^13*t" +"(13,1,5,9)","-(q^8*t)-q^9*t-q^10*t+q^11*t+q^12*t+q^13*t" +"(13,3,5,11)","-(q^10*t)" +"(13,4,5,11)","-q^9" +"(13,3,5,12)","q^10*t" +"(13,4,5,12)","q^9" +"(13,5,5,13)","-(q^10*t)" +"(13,2,6,9)","-(q^4*t)-q^5*t-q^6*t+q^7*t+q^8*t+q^9*t" +"(13,6,6,13)","q^6*t" +"(13,0,7,5)","q^4+q^5-2*q^7-2*q^8+q^10+q^11+q^4*t+q^5*t-2*q^7*t-2*q^8*t+q^10*t+q^11*t" +"(13,2,7,7)","q^4+q^5+q^6-q^7-q^8-q^9" +"(13,2,7,8)","q^4+q^5+q^6-q^7-q^8-q^9" +"(13,3,7,9)","-(q^5*t)-q^6*t+q^8*t+q^9*t" +"(13,4,7,9)","q^6-q^9" +"(13,6,7,11)","q^6" +"(13,6,7,12)","-q^6" +"(13,7,7,13)","q^6*t" +"(13,0,8,5)","q^2*t+q^3*t+q^4*t-q^5*t-2*q^6*t-2*q^7*t-q^8*t+q^9*t+q^10*t+q^11*t" +"(13,2,8,7)","q^4*t+q^5*t+q^6*t-q^7*t-q^8*t-q^9*t" +"(13,2,8,8)","q^4*t+q^5*t+q^6*t-q^7*t-q^8*t-q^9*t" +"(13,4,8,9)","q^4*t+q^5*t+q^6*t-q^7*t-q^8*t-q^9*t" +"(13,6,8,11)","q^6*t" +"(13,6,8,12)","-(q^6*t)" +"(13,8,8,13)","q^6*t" +"(13,0,9,3)","-q^2-q^3+2*q^5+2*q^6-q^8-q^9+q^4*t+q^5*t-2*q^7*t-2*q^8*t+q^10*t+q^11*t" +"(13,0,9,4)","q^2+q^3-2*q^5-2*q^6+q^8+q^9-q^4*t-q^5*t+2*q^7*t+2*q^8*t-q^10*t-q^11*t" +"(13,1,9,5)","q^4*t+q^5*t-2*q^7*t-2*q^8*t+q^10*t+q^11*t" +"(13,3,9,7)","q^5*t+q^6*t-q^8*t-q^9*t" +"(13,4,9,7)","q^4+q^5-q^7-q^8" +"(13,3,9,8)","q^5*t+q^6*t-q^8*t-q^9*t" +"(13,4,9,8)","q^4+q^5-q^7-q^8" +"(13,5,9,9)","q^5*t+q^6*t-q^8*t-q^9*t" +"(13,7,9,11)","q^6*t" +"(13,8,9,11)","-q^6" +"(13,7,9,12)","-(q^6*t)" +"(13,8,9,12)","q^6" +"(13,9,9,13)","q^6*t" +"(13,2,10,5)","-t-q*t+2*q^3*t+2*q^4*t-q^6*t-q^7*t" +"(13,6,10,9)","q*t+q^2*t-q^4*t-q^5*t" +"(13,10,10,13)","-(q^2*t)" +"(13,0,11,1)","q-q^3-2*q^4+2*q^6+q^7-q^9+q^2*t-q^3*t-q^4*t+q^6*t+q^7*t-q^8*t" +"(13,2,11,3)","q+q^2-2*q^4-2*q^5+q^7+q^8" +"(13,2,11,4)","-q-q^2+2*q^4+2*q^5-q^7-q^8" +"(13,3,11,5)","-(q^2*t)+q^4*t+q^5*t-q^7*t" +"(13,4,11,5)","q^2+q^3-q^4-2*q^5-q^6+q^7+q^8" +"(13,6,11,7)","q^2+q^3-q^5-q^6" +"(13,6,11,8)","q^2+q^3-q^5-q^6" +"(13,7,11,9)","q^2*t-q^5*t" +"(13,8,11,9)","q^3-q^6" +"(13,10,11,11)","q^3" +"(13,10,11,12)","-q^3" +"(13,11,11,13)","-(q^2*t)" +"(13,0,12,1)","-(t/q)+q*t+q^2*t+q^3*t-q^4*t-q^5*t-q^6*t+q^8*t" +"(13,2,12,3)","-t-q*t+2*q^3*t+2*q^4*t-q^6*t-q^7*t" +"(13,2,12,4)","t+q*t-2*q^3*t-2*q^4*t+q^6*t+q^7*t" +"(13,4,12,5)","-t-q*t+2*q^3*t+2*q^4*t-q^6*t-q^7*t" +"(13,6,12,7)","-(q*t)-q^2*t+q^4*t+q^5*t" +"(13,6,12,8)","-(q*t)-q^2*t+q^4*t+q^5*t" +"(13,8,12,9)","-(q*t)-q^2*t+q^4*t+q^5*t" +"(13,10,12,11)","-(q^2*t)" +"(13,10,12,12)","q^2*t" +"(13,12,12,13)","-(q^2*t)" +"(13,0,13,0)","1-q-q^2+q^4+q^5-q^6-q^2*t+q^3*t+q^4*t-q^6*t-q^7*t+q^8*t" +"(13,1,13,1)","-(q^2*t)+q^3*t+q^4*t-q^6*t-q^7*t+q^8*t" +"(13,3,13,3)","-(q^2*t)+q^4*t+q^5*t-q^7*t" +"(13,4,13,3)","-q+q^3+q^4-q^6" +"(13,3,13,4)","q^2*t-q^4*t-q^5*t+q^7*t" +"(13,4,13,4)","q-q^3-q^4+q^6" +"(13,5,13,5)","-(q^2*t)+q^4*t+q^5*t-q^7*t" +"(13,7,13,7)","-(q^2*t)+q^5*t" +"(13,8,13,7)","q^2-q^5" +"(13,7,13,8)","-(q^2*t)+q^5*t" +"(13,8,13,8)","q^2-q^5" +"(13,9,13,9)","-(q^2*t)+q^5*t" +"(13,11,13,11)","-(q^2*t)" +"(13,12,13,11)","-q^3" +"(13,11,13,12)","q^2*t" +"(13,12,13,12)","q^3" +"(13,13,13,13)","-(q^2*t)" +"(13,2,14,1)","t-t/q^2+t/q-q^2*t-q^3*t+q^4*t" +"(13,6,14,5)","-t+t/q^2-q*t+q^3*t" +"(13,10,14,9)","-(t/q^2)+q*t" +"(13,14,14,13)","t/q^2" +"(13,2,15,0)","1-q-q^2+q^4+q^5-q^6" +"(13,4,15,1)","1-q-q^2+q^4+q^5-q^6" +"(13,6,15,3)","1-q^2-q^3+q^5" +"(13,6,15,4)","-1+q^2+q^3-q^5" +"(13,8,15,5)","1-q^2-q^3+q^5" +"(13,10,15,7)","1-q^3" +"(13,10,15,8)","1-q^3" +"(13,12,15,9)","1-q^3" +"(13,14,15,11)","1" +"(13,14,15,12)","-1" +"(13,15,15,13)","t/q^2" +"(14,0,0,14)","q^14/t" +"(14,1,1,14)","-(q^14/t)" +"(14,0,2,11)","q^12-q^14/t" +"(14,0,2,12)","-q^9+q^11/t" +"(14,2,2,14)","q^11/t" +"(14,0,3,10)","-(q^8/t)-q^9/t-q^10/t+q^12/t+q^13/t+q^14/t" +"(14,1,3,11)","q^14/t" +"(14,1,3,12)","-(q^11/t)" +"(14,3,3,14)","-(q^11/t)" +"(14,0,4,10)","q^9-q^12+q^8/t+q^9/t+q^10/t-q^11/t-q^12/t-q^13/t" +"(14,1,4,11)","-q^12" +"(14,1,4,12)","q^9" +"(14,4,4,14)","-(q^11/t)" +"(14,1,5,10)","-(q^8/t)-q^9/t-q^10/t+q^11/t+q^12/t+q^13/t" +"(14,5,5,14)","q^11/t" +"(14,0,6,7)","q^6+q^7+q^8-q^9-q^10-q^11-q^8/t-q^9/t-q^10/t+q^11/t+q^12/t+q^13/t" +"(14,0,6,8)","q^4+q^5+q^6-q^7-q^8-q^9-q^6/t-q^7/t-q^8/t+q^9/t+q^10/t+q^11/t" +"(14,2,6,10)","-(q^6/t)-q^7/t-q^8/t+q^9/t+q^10/t+q^11/t" +"(14,3,6,11)","q^9" +"(14,4,6,11)","q^11/t" +"(14,3,6,12)","-q^6" +"(14,4,6,12)","-(q^8/t)" +"(14,6,6,14)","-(q^8/t)" +"(14,0,7,6)","q^4/t+q^5/t+q^6/t-q^7/t-(2*q^8)/t-(2*q^9)/t-q^10/t+q^11/t+q^12/t+q^13/t" +"(14,1,7,7)","q^8/t+q^9/t+q^10/t-q^11/t-q^12/t-q^13/t" +"(14,1,7,8)","q^6/t+q^7/t+q^8/t-q^9/t-q^10/t-q^11/t" +"(14,3,7,10)","q^6/t+q^7/t+q^8/t-q^9/t-q^10/t-q^11/t" +"(14,5,7,11)","-(q^11/t)" +"(14,5,7,12)","q^8/t" +"(14,7,7,14)","q^8/t" +"(14,0,8,6)","q^4+q^5-2*q^7-2*q^8+q^10+q^11+q^4/t+q^5/t-(2*q^7)/t-(2*q^8)/t+q^10/t+q^11/t" +"(14,1,8,7)","q^6+q^7+q^8-q^9-q^10-q^11" +"(14,1,8,8)","q^4+q^5+q^6-q^7-q^8-q^9" +"(14,3,8,10)","q^6-q^9" +"(14,4,8,10)","-(q^6/t)-q^7/t+q^9/t+q^10/t" +"(14,5,8,11)","-q^9" +"(14,5,8,12)","q^6" +"(14,8,8,14)","q^8/t" +"(14,1,9,6)","-(q^4/t)-q^5/t+(2*q^7)/t+(2*q^8)/t-q^10/t-q^11/t" +"(14,5,9,10)","q^6/t+q^7/t-q^9/t-q^10/t" +"(14,9,9,14)","-(q^8/t)" +"(14,0,10,3)","q^2+q^3-2*q^5-2*q^6+q^8+q^9-q^4/t-q^5/t+(2*q^7)/t+(2*q^8)/t-q^10/t-q^11/t" +"(14,0,10,4)","-q-q^2+2*q^4+2*q^5-q^7-q^8+q^3/t+q^4/t-(2*q^6)/t-(2*q^7)/t+q^9/t+q^10/t" +"(14,2,10,6)","q^3/t+q^4/t-(2*q^6)/t-(2*q^7)/t+q^9/t+q^10/t" +"(14,3,10,7)","q^4+q^5-q^7-q^8" +"(14,4,10,7)","q^6/t+q^7/t-q^9/t-q^10/t" +"(14,3,10,8)","q^2+q^3-q^5-q^6" +"(14,4,10,8)","q^4/t+q^5/t-q^7/t-q^8/t" +"(14,6,10,10)","q^4/t+q^5/t-q^7/t-q^8/t" +"(14,7,10,11)","q^6" +"(14,8,10,11)","-(q^8/t)" +"(14,7,10,12)","-q^3" +"(14,8,10,12)","q^5/t" +"(14,10,10,14)","q^5/t" +"(14,0,11,2)","-(q^2/t)+q^4/t+q^5/t+q^6/t-q^7/t-q^8/t-q^9/t+q^11/t" +"(14,1,11,3)","q^4/t+q^5/t-(2*q^7)/t-(2*q^8)/t+q^10/t+q^11/t" +"(14,1,11,4)","-(q^3/t)-q^4/t+(2*q^6)/t+(2*q^7)/t-q^9/t-q^10/t" +"(14,3,11,6)","-(q^3/t)-q^4/t+(2*q^6)/t+(2*q^7)/t-q^9/t-q^10/t" +"(14,5,11,7)","-(q^6/t)-q^7/t+q^9/t+q^10/t" +"(14,5,11,8)","-(q^4/t)-q^5/t+q^7/t+q^8/t" +"(14,7,11,10)","-(q^4/t)-q^5/t+q^7/t+q^8/t" +"(14,9,11,11)","q^8/t" +"(14,9,11,12)","-(q^5/t)" +"(14,11,11,14)","-(q^5/t)" +"(14,0,12,2)","q-q^3-2*q^4+2*q^6+q^7-q^9+q^2/t-q^3/t-q^4/t+q^6/t+q^7/t-q^8/t" +"(14,1,12,3)","-q^2-q^3+2*q^5+2*q^6-q^8-q^9" +"(14,1,12,4)","q+q^2-2*q^4-2*q^5+q^7+q^8" +"(14,3,12,6)","q^2+q^3-q^4-2*q^5-q^6+q^7+q^8" +"(14,4,12,6)","-(q^3/t)+q^5/t+q^6/t-q^8/t" +"(14,5,12,7)","q^4+q^5-q^7-q^8" +"(14,5,12,8)","q^2+q^3-q^5-q^6" +"(14,7,12,10)","q^3-q^6" +"(14,8,12,10)","q^4/t-q^7/t" +"(14,9,12,11)","-q^6" +"(14,9,12,12)","q^3" +"(14,12,12,14)","-(q^5/t)" +"(14,1,13,2)","-(q^2/t)+q^3/t+q^4/t-q^6/t-q^7/t+q^8/t" +"(14,5,13,6)","q^3/t-q^5/t-q^6/t+q^8/t" +"(14,9,13,10)","-(q^4/t)+q^7/t" +"(14,13,13,14)","q^5/t" +"(14,0,14,0)","1-q-q^2+q^4+q^5-q^6-q^2/t+q^3/t+q^4/t-q^6/t-q^7/t+q^8/t" +"(14,2,14,2)","-(q^2/t)+q^3/t+q^4/t-q^6/t-q^7/t+q^8/t" +"(14,3,14,3)","q-q^3-q^4+q^6" +"(14,4,14,3)","q^3/t-q^5/t-q^6/t+q^8/t" +"(14,3,14,4)","-1+q^2+q^3-q^5" +"(14,4,14,4)","-(q^2/t)+q^4/t+q^5/t-q^7/t" +"(14,6,14,6)","-(q^2/t)+q^4/t+q^5/t-q^7/t" +"(14,7,14,7)","q^2-q^5" +"(14,8,14,7)","-(q^4/t)+q^7/t" +"(14,7,14,8)","1-q^3" +"(14,8,14,8)","-(q^2/t)+q^5/t" +"(14,10,14,10)","-(q^2/t)+q^5/t" +"(14,11,14,11)","q^3" +"(14,12,14,11)","q^5/t" +"(14,11,14,12)","-1" +"(14,12,14,12)","-(q^2/t)" +"(14,14,14,14)","-(q^2/t)" +"(14,1,15,0)","q^2/t-q^3/t-q^4/t+q^6/t+q^7/t-q^8/t" +"(14,3,15,2)","q^2/t-q^3/t-q^4/t+q^6/t+q^7/t-q^8/t" +"(14,5,15,3)","-(q^3/t)+q^5/t+q^6/t-q^8/t" +"(14,5,15,4)","q^2/t-q^4/t-q^5/t+q^7/t" +"(14,7,15,6)","q^2/t-q^4/t-q^5/t+q^7/t" +"(14,9,15,7)","q^4/t-q^7/t" +"(14,9,15,8)","q^2/t-q^5/t" +"(14,11,15,10)","q^2/t-q^5/t" +"(14,13,15,11)","-(q^5/t)" +"(14,13,15,12)","q^2/t" +"(14,15,15,14)","q^2/t" +"(15,0,0,15)","q^16" +"(15,0,1,14)","-q^12-q^16+q^14/t+q^14*t" +"(15,1,1,15)","q^16" +"(15,0,2,13)","q^12-q^10*t-q^14*t+q^12*t^2" +"(15,2,2,15)","q^12" +"(15,0,3,11)","q^9+q^10+q^11+2*q^12-q^13-q^14-q^15-q^14/t-q^14*t" +"(15,0,3,12)","-2*q^9+q^11/t-q^8*t-q^9*t-q^10*t+q^11*t+q^12*t+q^13*t+q^14*t" +"(15,1,3,13)","q^12-q^14*t" +"(15,2,3,14)","-q^9+q^11/t" +"(15,3,3,15)","q^12" +"(15,0,4,11)","-q^9-q^10-q^11+q^12+q^13+q^14+q^15-2*q^10*t+q^12*t^2" +"(15,0,4,12)","-q^12+q^7*t+q^8*t+q^9*t+2*q^10*t-q^11*t-q^12*t-q^13*t-q^12*t^2" +"(15,1,4,13)","-(q^10*t)+q^12*t^2" +"(15,2,4,14)","-q^12+q^10*t" +"(15,4,4,15)","q^12" +"(15,0,5,10)","-q^6-q^7-q^8+q^9-q^12+q^13+q^14+q^15+q^8/t+q^9/t+q^10/t-q^11/t-q^12/t-q^13/t+q^8*t+q^9*t+q^10*t-q^11*t-q^12*t-q^13*t" +"(15,1,5,11)","-q^9-q^10-q^11+q^13+q^14+q^15" +"(15,1,5,12)","q^9-q^12+q^8*t+q^9*t+q^10*t-q^11*t-q^12*t-q^13*t" +"(15,3,5,14)","-q^12+q^10*t" +"(15,4,5,14)","q^9-q^11/t" +"(15,5,5,15)","q^12" +"(15,0,6,9)","q^6+q^7+q^8-q^9-q^10-q^11-q^4*t-q^5*t-q^6*t+q^7*t-q^10*t+q^11*t+q^12*t+q^13*t+q^6*t^2+q^7*t^2+q^8*t^2-q^9*t^2-q^10*t^2-q^11*t^2" +"(15,2,6,11)","q^6+q^7+q^8-q^9-q^10-q^11+q^7*t-q^10*t" +"(15,2,6,12)","-(q^4*t)-q^5*t-q^6*t+q^8*t+q^9*t+q^10*t" +"(15,3,6,13)","q^6*t-q^8*t^2" +"(15,4,6,13)","q^8-q^10*t" +"(15,6,6,15)","q^8" +"(15,0,7,7)","q^4+q^5+3*q^6+q^7-4*q^9-3*q^10-q^11+q^12+q^13-q^8/t-q^9/t-q^10/t+q^11/t+q^12/t+q^13/t-q^8*t-q^9*t-q^10*t+q^11*t+q^12*t+q^13*t" +"(15,0,7,8)","2*q^4+2*q^5+2*q^6-2*q^7-2*q^8-2*q^9-q^6/t-q^7/t-q^8/t+q^9/t+q^10/t+q^11/t+q^4*t+q^5*t-2*q^7*t-3*q^8*t-q^9*t+2*q^11*t+q^12*t+q^13*t" +"(15,1,7,9)","q^6+q^7+q^8-q^9-q^10-q^11-q^8*t-q^9*t-q^10*t+q^11*t+q^12*t+q^13*t" +"(15,2,7,10)","q^4+q^5+q^6-q^7-q^8-q^9-q^6/t-q^7/t-q^8/t+q^9/t+q^10/t+q^11/t" +"(15,3,7,11)","q^6+q^7+q^8-q^10-q^11-q^10*t" +"(15,4,7,11)","-q^9+q^11/t" +"(15,3,7,12)","-q^6-q^5*t-q^6*t+q^8*t+q^9*t+q^10*t" +"(15,4,7,12)","q^6-q^8/t" +"(15,5,7,13)","q^8-q^10*t" +"(15,6,7,14)","q^6-q^8/t" +"(15,7,7,15)","q^8" +"(15,0,8,7)","q^4+q^5-2*q^7-3*q^8-q^9+2*q^11+q^12+q^13+2*q^4*t+2*q^5*t+2*q^6*t-2*q^7*t-2*q^8*t-2*q^9*t-q^6*t^2-q^7*t^2-q^8*t^2+q^9*t^2+q^10*t^2+q^11*t^2" +"(15,0,8,8)","-q^6-q^7-q^8+q^9+q^10+q^11+q^2*t+q^3*t+3*q^4*t+q^5*t-4*q^7*t-3*q^8*t-q^9*t+q^10*t+q^11*t-q^6*t^2-q^7*t^2-q^8*t^2+q^9*t^2+q^10*t^2+q^11*t^2" +"(15,1,8,9)","q^4*t+q^5*t+q^6*t-q^7*t-q^8*t-q^9*t-q^6*t^2-q^7*t^2-q^8*t^2+q^9*t^2+q^10*t^2+q^11*t^2" +"(15,2,8,10)","-q^6-q^7-q^8+q^9+q^10+q^11+q^4*t+q^5*t+q^6*t-q^7*t-q^8*t-q^9*t" +"(15,3,8,11)","q^6*t-q^8*t^2" +"(15,4,8,11)","-q^6-q^7+q^9+q^10+q^11-q^7*t" +"(15,3,8,12)","-(q^6*t)+q^8*t^2" +"(15,4,8,12)","-q^8+q^4*t+q^5*t+q^6*t-q^8*t-q^9*t" +"(15,5,8,13)","q^6*t-q^8*t^2" +"(15,6,8,14)","-q^8+q^6*t" +"(15,8,8,15)","q^8" +"(15,0,9,6)","-q^2-q^3+2*q^5+q^6-q^7-q^8+q^9+2*q^10-q^12-q^13+q^4/t+q^5/t-(2*q^7)/t-(2*q^8)/t+q^10/t+q^11/t+q^4*t+q^5*t-2*q^7*t-2*q^8*t+q^10*t+q^11*t" +"(15,1,9,7)","q^4+q^5+q^6-q^7-2*q^8-2*q^9-q^10+q^11+q^12+q^13" +"(15,1,9,8)","q^4+q^5-2*q^7-2*q^8+q^10+q^11+q^4*t+q^5*t-2*q^7*t-2*q^8*t+q^10*t+q^11*t" +"(15,3,9,10)","-q^7-q^8+q^10+q^11+q^5*t+q^6*t-q^8*t-q^9*t" +"(15,4,9,10)","q^4+q^5-q^7-q^8-q^6/t-q^7/t+q^9/t+q^10/t" +"(15,5,9,11)","-q^6-q^7+q^10+q^11" +"(15,5,9,12)","q^6-q^8+q^5*t+q^6*t-q^8*t-q^9*t" +"(15,7,9,14)","-q^8+q^6*t" +"(15,8,9,14)","-q^6+q^8/t" +"(15,9,9,15)","q^8" +"(15,0,10,5)","q^2+q^3-2*q^5-2*q^6+q^8+q^9-t-q*t+2*q^3*t+q^4*t-q^5*t-q^6*t+q^7*t+2*q^8*t-q^10*t-q^11*t+q^2*t^2+q^3*t^2-2*q^5*t^2-2*q^6*t^2+q^8*t^2+q^9*t^2" +"(15,2,10,7)","q^2+q^3-2*q^5-2*q^6+q^8+q^9+q^2*t+q^3*t-2*q^5*t-2*q^6*t+q^8*t+q^9*t" +"(15,2,10,8)","t+q*t+q^2*t-q^3*t-2*q^4*t-2*q^5*t-q^6*t+q^7*t+q^8*t+q^9*t" +"(15,3,10,9)","q*t+q^2*t-q^4*t-q^5*t-q^3*t^2-q^4*t^2+q^6*t^2+q^7*t^2" +"(15,4,10,9)","q^3+q^4-q^6-q^7-q^5*t-q^6*t+q^8*t+q^9*t" +"(15,6,10,11)","q^3+q^4-q^6-q^7+q^4*t-q^6*t" +"(15,6,10,12)","-(q*t)-q^2*t+q^5*t+q^6*t" +"(15,7,10,13)","-(q^2*t)+q^4*t^2" +"(15,8,10,13)","q^4-q^6*t" +"(15,10,10,15)","q^4" +"(15,0,11,3)","q+2*q^2+q^3-q^4-5*q^5-3*q^6+q^7+3*q^8+2*q^9-q^10-q^4/t-q^5/t+(2*q^7)/t+(2*q^8)/t-q^10/t-q^11/t-q^4*t-q^5*t+2*q^7*t+2*q^8*t-q^10*t-q^11*t" +"(15,0,11,4)","-2*q-2*q^2+4*q^4+4*q^5-2*q^7-2*q^8+q^3/t+q^4/t-(2*q^6)/t-(2*q^7)/t+q^9/t+q^10/t-q^2*t+q^3*t+2*q^4*t+q^5*t-q^6*t-3*q^7*t-q^8*t+q^10*t+q^11*t" +"(15,1,11,5)","q^2+q^3-2*q^5-2*q^6+q^8+q^9-q^4*t-q^5*t+2*q^7*t+2*q^8*t-q^10*t-q^11*t" +"(15,2,11,6)","-q-q^2+2*q^4+2*q^5-q^7-q^8+q^3/t+q^4/t-(2*q^6)/t-(2*q^7)/t+q^9/t+q^10/t" +"(15,3,11,7)","q^2+q^3+q^4-q^5-2*q^6-q^7+q^9-q^5*t-q^6*t+q^8*t+q^9*t" +"(15,4,11,7)","-q^4-q^5+q^7+q^8+q^6/t+q^7/t-q^9/t-q^10/t" +"(15,3,11,8)","q^2+q^3-q^5-q^6+q^2*t-q^4*t-2*q^5*t-q^6*t+q^7*t+q^8*t+q^9*t" +"(15,4,11,8)","-q^2-q^3+q^5+q^6+q^4/t+q^5/t-q^7/t-q^8/t" +"(15,5,11,9)","q^3+q^4-q^6-q^7-q^5*t-q^6*t+q^8*t+q^9*t" +"(15,6,11,10)","-q^2-q^3+q^5+q^6+q^4/t+q^5/t-q^7/t-q^8/t" +"(15,7,11,11)","q^3+q^4-q^7-q^6*t" +"(15,8,11,11)","q^6-q^8/t" +"(15,7,11,12)","-q^3-q^2*t+q^5*t+q^6*t" +"(15,8,11,12)","-q^3+q^5/t" +"(15,9,11,13)","q^4-q^6*t" +"(15,10,11,14)","-q^3+q^5/t" +"(15,11,11,15)","q^4" +"(15,0,12,3)","-q+q^2+2*q^3+q^4-q^5-3*q^6-q^7+q^9+q^10-2*t-2*q*t+4*q^3*t+4*q^4*t-2*q^6*t-2*q^7*t+q^2*t^2+q^3*t^2-2*q^5*t^2-2*q^6*t^2+q^8*t^2+q^9*t^2" +"(15,0,12,4)","-q^2-q^3+2*q^5+2*q^6-q^8-q^9+2*t+t/q+q*t-q^2*t-5*q^3*t-3*q^4*t+q^5*t+3*q^6*t+2*q^7*t-q^8*t-q^2*t^2-q^3*t^2+2*q^5*t^2+2*q^6*t^2-q^8*t^2-q^9*t^2" +"(15,1,12,5)","-t-q*t+2*q^3*t+2*q^4*t-q^6*t-q^7*t+q^2*t^2+q^3*t^2-2*q^5*t^2-2*q^6*t^2+q^8*t^2+q^9*t^2" +"(15,2,12,6)","-q^2-q^3+2*q^5+2*q^6-q^8-q^9+t+q*t-2*q^3*t-2*q^4*t+q^6*t+q^7*t" +"(15,3,12,7)","-(q*t)-q^2*t+q^4*t+q^5*t+q^3*t^2+q^4*t^2-q^6*t^2-q^7*t^2" +"(15,4,12,7)","q^2-q^4-2*q^5-q^6+q^7+q^8+q^9+q^2*t+q^3*t-q^5*t-q^6*t" +"(15,3,12,8)","-(q*t)-q^2*t+q^4*t+q^5*t+q^3*t^2+q^4*t^2-q^6*t^2-q^7*t^2" +"(15,4,12,8)","-q^3-q^4+q^6+q^7+t+q*t+q^2*t-q^3*t-2*q^4*t-q^5*t+q^7*t" +"(15,5,12,9)","-(q*t)-q^2*t+q^4*t+q^5*t+q^3*t^2+q^4*t^2-q^6*t^2-q^7*t^2" +"(15,6,12,10)","-q^3-q^4+q^6+q^7+q*t+q^2*t-q^4*t-q^5*t" +"(15,7,12,11)","-(q^2*t)+q^4*t^2" +"(15,8,12,11)","-q^3+q^6+q^7-q^4*t" +"(15,7,12,12)","q^2*t-q^4*t^2" +"(15,8,12,12)","-q^4+q*t+q^2*t-q^5*t" +"(15,9,12,13)","-(q^2*t)+q^4*t^2" +"(15,10,12,14)","-q^4+q^2*t" +"(15,12,12,15)","q^4" +"(15,0,13,2)","-1+q+q^2-2*q^4+2*q^6-q^8-q^9+q^10+q^2/t-q^3/t-q^4/t+q^6/t+q^7/t-q^8/t+q^2*t-q^3*t-q^4*t+q^6*t+q^7*t-q^8*t" +"(15,1,13,3)","-q+q^3+q^4+q^5-q^6-q^7-q^8+q^10" +"(15,1,13,4)","q-q^3-2*q^4+2*q^6+q^7-q^9+q^2*t-q^3*t-q^4*t+q^6*t+q^7*t-q^8*t" +"(15,3,13,6)","-q^4+q^6+q^7-q^9+q^2*t-q^4*t-q^5*t+q^7*t" +"(15,4,13,6)","q-q^3-q^4+q^6-q^3/t+q^5/t+q^6/t-q^8/t" +"(15,5,13,7)","q^2-q^5-q^6+q^9" +"(15,5,13,8)","q^2-q^4-q^5+q^7+q^2*t-q^4*t-q^5*t+q^7*t" +"(15,7,13,10)","-q^4+q^7+q^2*t-q^5*t" +"(15,8,13,10)","-q^2+q^5+q^4/t-q^7/t" +"(15,9,13,11)","-q^3+q^7" +"(15,9,13,12)","q^3-q^4+q^2*t-q^5*t" +"(15,11,13,14)","-q^4+q^2*t" +"(15,12,13,14)","q^3-q^5/t" +"(15,13,13,15)","q^4" +"(15,0,14,1)","1-q-q^2+q^4+q^5-q^6+t-t/q^2+t/q-2*q^2*t+2*q^4*t-q^6*t-q^7*t+q^8*t+t^2-q*t^2-q^2*t^2+q^4*t^2+q^5*t^2-q^6*t^2" +"(15,2,14,3)","1-q-q^2+q^4+q^5-q^6+t/q-q*t-2*q^2*t+2*q^4*t+q^5*t-q^7*t" +"(15,2,14,4)","t-t/q^2+q*t+q^2*t-q^3*t-q^4*t-q^5*t+q^7*t" +"(15,3,14,5)","-t+t/q^2-q*t+q^3*t-t^2+q^2*t^2+q^3*t^2-q^5*t^2" +"(15,4,14,5)","1-q^2-q^3+q^5-q^2*t+q^4*t+q^5*t-q^7*t" +"(15,6,14,7)","1-q^2-q^3+q^5+t-q^2*t-q^3*t+q^5*t" +"(15,6,14,8)","t/q^2-q*t-q^2*t+q^5*t" +"(15,7,14,9)","-(t/q^2)+q*t+t^2-q^3*t^2" +"(15,8,14,9)","1-q^3-q^2*t+q^5*t" +"(15,10,14,11)","1-q^3+q*t-q^2*t" +"(15,10,14,12)","-(t/q^2)+q^2*t" +"(15,11,14,13)","t/q^2-t^2" +"(15,12,14,13)","1-q^2*t" +"(15,14,14,15)","1" +"(15,0,15,0)","2-2*q-2*q^2+2*q^4+2*q^5-2*q^6-q^2/t+q^3/t+q^4/t-q^6/t-q^7/t+q^8/t-q^2*t+q^3*t+q^4*t-q^6*t-q^7*t+q^8*t" +"(15,1,15,1)","1-q-q^2+q^4+q^5-q^6-q^2*t+q^3*t+q^4*t-q^6*t-q^7*t+q^8*t" +"(15,2,15,2)","1-q-q^2+q^4+q^5-q^6-q^2/t+q^3/t+q^4/t-q^6/t-q^7/t+q^8/t" +"(15,3,15,3)","1-q^2-q^3+q^5-q^2*t+q^4*t+q^5*t-q^7*t" +"(15,4,15,3)","-q+q^3+q^4-q^6+q^3/t-q^5/t-q^6/t+q^8/t" +"(15,3,15,4)","-1+q^2+q^3-q^5+q^2*t-q^4*t-q^5*t+q^7*t" +"(15,4,15,4)","1-q^2-q^3+q^5-q^2/t+q^4/t+q^5/t-q^7/t" +"(15,5,15,5)","1-q^2-q^3+q^5-q^2*t+q^4*t+q^5*t-q^7*t" +"(15,6,15,6)","1-q^2-q^3+q^5-q^2/t+q^4/t+q^5/t-q^7/t" +"(15,7,15,7)","1-q^3-q^2*t+q^5*t" +"(15,8,15,7)","q^2-q^5-q^4/t+q^7/t" +"(15,7,15,8)","1-q^3-q^2*t+q^5*t" +"(15,8,15,8)","1-q^3-q^2/t+q^5/t" +"(15,9,15,9)","1-q^3-q^2*t+q^5*t" +"(15,10,15,10)","1-q^3-q^2/t+q^5/t" +"(15,11,15,11)","1-q^2*t" +"(15,12,15,11)","-q^3+q^5/t" +"(15,11,15,12)","-1+q^2*t" +"(15,12,15,12)","1-q^2/t" +"(15,13,15,13)","1-q^2*t" +"(15,14,15,14)","1-q^2/t" +"(15,15,15,15)","1" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/Rp.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/Rp.csv new file mode 100644 index 0000000..e50f0d4 --- /dev/null +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/Rp.csv @@ -0,0 +1,1378 @@ +"(16,16,16,16)","LaurentPolynomial" +"(0,0,0,0)","1" +"(0,1,0,1)","1-1/(q^2*t)" +"(0,2,0,2)","1-t/q^2" +"(0,3,0,3)","1-1/(q^2*t)" +"(0,4,0,3)","q^(-3)-1/(q*t)" +"(0,3,0,4)","q^(-4)-t/q^2" +"(0,4,0,4)","1-t/q^2" +"(0,5,0,5)","1-q^(-3)+1/(q^5*t)-1/(q^2*t)" +"(0,6,0,6)","1-q^(-3)+t/q^5-t/q^2" +"(0,7,0,7)","1-q^(-3)+1/(q^5*t)-1/(q^2*t)" +"(0,8,0,7)","q^(-5)-q^(-2)+t^(-1)-1/(q^3*t)" +"(0,7,0,8)","q^(-7)-q^(-4)-t/q^5+t/q^2" +"(0,8,0,8)","1-q^(-3)+t/q^5-t/q^2" +"(0,9,0,9)","1+q^(-5)-q^(-3)-q^(-2)-1/(q^7*t)+1/(q^5*t)+1/(q^4*t)-1/(q^2*t)" +"(0,10,0,10)","1+q^(-5)-q^(-3)-q^(-2)-t/q^7+t/q^5+t/q^4-t/q^2" +"(0,11,0,11)","1+q^(-5)-q^(-3)-q^(-2)-1/(q^7*t)+1/(q^5*t)+1/(q^4*t)-1/(q^2*t)" +"(0,12,0,11)","q^(-6)-q^(-4)-q^(-3)+q^(-1)-1/(q^4*t)+1/(q^2*t)+1/(q*t)-q/t" +"(0,11,0,12)","q^(-9)-q^(-7)-q^(-6)+q^(-4)-t/q^7+t/q^5+t/q^4-t/q^2" +"(0,12,0,12)","1+q^(-5)-q^(-3)-q^(-2)-t/q^7+t/q^5+t/q^4-t/q^2" +"(0,13,0,13)","1-q^(-6)+q^(-5)+q^(-4)-q^(-2)-q^(-1)+1/(q^8*t)-1/(q^7*t)-1/(q^6*t)+1/(q^4*t)+1/(q^3*t)-1/(q^2*t)" +"(0,14,0,14)","1-q^(-6)+q^(-5)+q^(-4)-q^(-2)-q^(-1)+t/q^8-t/q^7-t/q^6+t/q^4+t/q^3-t/q^2" +"(0,15,0,15)","2-2/q^6+2/q^5+2/q^4-2/q^2-2/q+1/(q^8*t)-1/(q^7*t)-1/(q^6*t)+1/(q^4*t)+1/(q^3*t)-1/(q^2*t)+t/q^8-t/q^7-t/q^6+t/q^4+t/q^3-t/q^2" +"(0,1,1,0)","1/(q^2*t)" +"(0,3,1,2)","-q^(-4)+1/(q^2*t)" +"(0,4,1,2)","-q^(-3)+1/(q*t)" +"(0,5,1,3)","1/(q^5*t)-1/(q*t)" +"(0,5,1,4)","-q^(-4)+q^(-3)-1/(q^5*t)+1/(q^2*t)" +"(0,7,1,6)","q^(-7)-q^(-4)-1/(q^5*t)+1/(q^2*t)" +"(0,8,1,6)","-q^(-5)+q^(-2)-t^(-1)+1/(q^3*t)" +"(0,9,1,7)","t^(-1)+1/(q^7*t)-1/(q^4*t)-1/(q^3*t)" +"(0,9,1,8)","q^(-7)-q^(-5)-q^(-4)+q^(-2)+1/(q^7*t)-1/(q^5*t)-1/(q^4*t)+1/(q^2*t)" +"(0,11,1,10)","-q^(-9)+q^(-7)+q^(-6)-q^(-4)+1/(q^7*t)-1/(q^5*t)-1/(q^4*t)+1/(q^2*t)" +"(0,12,1,10)","-q^(-6)+q^(-4)+q^(-3)-q^(-1)+1/(q^4*t)-1/(q^2*t)-1/(q*t)+q/t" +"(0,13,1,11)","1/(q^8*t)-1/(q^6*t)-1/(q^5*t)-1/(q^4*t)+1/(q^3*t)+1/(q^2*t)+1/(q*t)-q/t" +"(0,13,1,12)","-q^(-9)+q^(-7)+2/q^6-2/q^4-q^(-3)+q^(-1)-1/(q^8*t)+1/(q^7*t)+1/(q^6*t)-1/(q^4*t)-1/(q^3*t)+1/(q^2*t)" +"(0,15,1,14)","-1+q^(-10)-q^(-9)-q^(-8)+2/q^6-2/q^4+q^(-2)+q^(-1)-1/(q^8*t)+1/(q^7*t)+1/(q^6*t)-1/(q^4*t)-1/(q^3*t)+1/(q^2*t)-t/q^8+t/q^7+t/q^6-t/q^4-t/q^3+t/q^2" +"(0,2,2,0)","t/q^2" +"(0,3,2,1)","-q^(-4)+t/q^2" +"(0,4,2,1)","-q^(-4)+t/q^2" +"(0,6,2,3)","-q^(-4)+q^(-3)-t/q^5+t/q^2" +"(0,6,2,4)","t/q^6-t/q^2" +"(0,7,2,5)","-q^(-7)+q^(-4)+t/q^5-t/q^2" +"(0,8,2,5)","q^(-7)-q^(-4)-t/q^5+t/q^2" +"(0,10,2,7)","q^(-7)-q^(-5)-q^(-4)+q^(-2)+t/q^7-t/q^5-t/q^4+t/q^2" +"(0,10,2,8)","t/q^9-t/q^6-t/q^5+t/q^2" +"(0,11,2,9)","-q^(-9)+q^(-7)+q^(-6)-q^(-4)+t/q^7-t/q^5-t/q^4+t/q^2" +"(0,12,2,9)","-q^(-9)+q^(-7)+q^(-6)-q^(-4)+t/q^7-t/q^5-t/q^4+t/q^2" +"(0,14,2,11)","-q^(-9)+q^(-7)+2/q^6-2/q^4-q^(-3)+q^(-1)-t/q^8+t/q^7+t/q^6-t/q^4-t/q^3+t/q^2" +"(0,14,2,12)","t/q^11-t/q^9-t/q^8-t/q^7+t/q^6+t/q^5+t/q^4-t/q^2" +"(0,15,2,13)","-q^(-10)+q^(-9)+q^(-8)-q^(-6)-q^(-5)+q^(-4)+t/q^12-t/q^11-t/q^10+(2*t)/q^8-(2*t)/q^6+t/q^4+t/q^3-t/q^2-t^2/q^10+t^2/q^9+t^2/q^8-t^2/q^6-t^2/q^5+t^2/q^4" +"(0,3,3,0)","q^(-4)" +"(0,5,3,1)","q^(-4)-1/(q^6*t)" +"(0,6,3,2)","-q^(-3)+t/q^5" +"(0,7,3,3)","-q^(-7)+q^(-4)+q^(-3)-1/(q^6*t)" +"(0,8,3,3)","-q^(-6)+1/(q^4*t)" +"(0,7,3,4)","q^(-8)+q^(-7)-q^(-4)-t/q^5" +"(0,8,3,4)","-q^(-3)+t/q^5" +"(0,9,3,5)","-q^(-7)-q^(-6)+q^(-4)+q^(-3)+1/(q^9*t)+1/(q^8*t)-1/(q^6*t)-1/(q^5*t)" +"(0,10,3,6)","q^(-6)+q^(-5)-q^(-3)-q^(-2)-t/q^8-t/q^7+t/q^5+t/q^4" +"(0,11,3,7)","q^(-9)-q^(-7)-2/q^6-q^(-5)+q^(-4)+q^(-3)+q^(-2)+1/(q^9*t)+1/(q^8*t)-1/(q^6*t)-1/(q^5*t)" +"(0,12,3,7)","-q^(-8)-q^(-7)+q^(-5)+q^(-4)+1/(q^6*t)+1/(q^5*t)-1/(q^3*t)-1/(q^2*t)" +"(0,11,3,8)","q^(-11)+q^(-10)+q^(-9)-q^(-8)-2/q^7-q^(-6)+q^(-4)-t/q^8-t/q^7+t/q^5+t/q^4" +"(0,12,3,8)","q^(-6)+q^(-5)-q^(-3)-q^(-2)-t/q^8-t/q^7+t/q^5+t/q^4" +"(0,13,3,9)","q^(-9)+q^(-8)-2/q^6-2/q^5+q^(-3)+q^(-2)-1/(q^11*t)-1/(q^10*t)+2/(q^8*t)+2/(q^7*t)-1/(q^5*t)-1/(q^4*t)" +"(0,14,3,10)","-q^(-8)-q^(-7)+2/q^5+2/q^4-q^(-2)-q^(-1)+t/q^10+t/q^9-(2*t)/q^7-(2*t)/q^6+t/q^4+t/q^3" +"(0,15,3,11)","-q^(-10)+2/q^9+3/q^8+q^(-7)-3/q^6-5/q^5-q^(-4)+q^(-3)+2/q^2+q^(-1)-1/(q^11*t)-1/(q^10*t)+2/(q^8*t)+2/(q^7*t)-1/(q^5*t)-1/(q^4*t)-t/q^11-t/q^10+(2*t)/q^8+(2*t)/q^7-t/q^5-t/q^4" +"(0,15,3,12)","q^(-13)+q^(-12)-q^(-10)-3/q^9-q^(-8)+q^(-7)+2/q^6+q^(-5)-q^(-4)-(2*t)/q^10-(2*t)/q^9+(4*t)/q^7+(4*t)/q^6-(2*t)/q^4-(2*t)/q^3+t^2/q^12+t^2/q^11-(2*t^2)/q^9-(2*t^2)/q^8+t^2/q^6+t^2/q^5" +"(0,4,4,0)","q^(-4)" +"(0,5,4,1)","-q^(-4)+1/(q^6*t)" +"(0,6,4,2)","q^(-4)-t/q^6" +"(0,7,4,3)","-q^(-4)+1/(q^6*t)" +"(0,8,4,3)","q^(-7)+q^(-6)-q^(-3)-1/(q^4*t)" +"(0,7,4,4)","-q^(-8)+t/q^6" +"(0,8,4,4)","-q^(-7)+q^(-4)+q^(-3)-t/q^6" +"(0,9,4,5)","q^(-7)+q^(-6)-q^(-4)-q^(-3)-1/(q^9*t)-1/(q^8*t)+1/(q^6*t)+1/(q^5*t)" +"(0,10,4,6)","-q^(-7)-q^(-6)+q^(-4)+q^(-3)+t/q^9+t/q^8-t/q^6-t/q^5" +"(0,11,4,7)","q^(-7)+q^(-6)-q^(-4)-q^(-3)-1/(q^9*t)-1/(q^8*t)+1/(q^6*t)+1/(q^5*t)" +"(0,12,4,7)","q^(-9)+q^(-8)+q^(-7)-q^(-6)-2/q^5-q^(-4)+q^(-2)-1/(q^6*t)-1/(q^5*t)+1/(q^3*t)+1/(q^2*t)" +"(0,11,4,8)","-q^(-11)-q^(-10)+q^(-8)+q^(-7)+t/q^9+t/q^8-t/q^6-t/q^5" +"(0,12,4,8)","q^(-9)-q^(-7)-2/q^6-q^(-5)+q^(-4)+q^(-3)+q^(-2)+t/q^9+t/q^8-t/q^6-t/q^5" +"(0,13,4,9)","-q^(-9)-q^(-8)+2/q^6+2/q^5-q^(-3)-q^(-2)+1/(q^11*t)+1/(q^10*t)-2/(q^8*t)-2/(q^7*t)+1/(q^5*t)+1/(q^4*t)" +"(0,14,4,10)","q^(-9)+q^(-8)-2/q^6-2/q^5+q^(-3)+q^(-2)-t/q^11-t/q^10+(2*t)/q^8+(2*t)/q^7-t/q^5-t/q^4" +"(0,15,4,11)","-2/q^9-2/q^8+4/q^6+4/q^5-2/q^3-2/q^2+1/(q^11*t)+1/(q^10*t)-2/(q^8*t)-2/(q^7*t)+1/(q^5*t)+1/(q^4*t)+t/q^12+t/q^11-t/q^9-(3*t)/q^8-t/q^7+t/q^6+(2*t)/q^5+t/q^4-t/q^3" +"(0,15,4,12)","-q^(-13)-q^(-12)+2/q^10+2/q^9-q^(-7)-q^(-6)-t/q^12+(2*t)/q^11+(3*t)/q^10+t/q^9-(3*t)/q^8-(5*t)/q^7-t/q^6+t/q^5+(2*t)/q^4+t/q^3-t^2/q^13-t^2/q^12+(2*t^2)/q^10+(2*t^2)/q^9-t^2/q^7-t^2/q^6" +"(0,5,5,0)","1/(q^6*t)" +"(0,7,5,2)","-q^(-8)+1/(q^6*t)" +"(0,8,5,2)","q^(-6)-1/(q^4*t)" +"(0,9,5,3)","1/(q^9*t)+1/(q^8*t)-1/(q^5*t)-1/(q^4*t)" +"(0,9,5,4)","-q^(-8)+q^(-6)-1/(q^9*t)-1/(q^8*t)+1/(q^6*t)+1/(q^5*t)" +"(0,11,5,6)","q^(-11)+q^(-10)-q^(-8)-q^(-7)-1/(q^9*t)-1/(q^8*t)+1/(q^6*t)+1/(q^5*t)" +"(0,12,5,6)","q^(-8)+q^(-7)-q^(-5)-q^(-4)-1/(q^6*t)-1/(q^5*t)+1/(q^3*t)+1/(q^2*t)" +"(0,13,5,7)","1/(q^11*t)+1/(q^10*t)+1/(q^9*t)-1/(q^8*t)-2/(q^7*t)-2/(q^6*t)-1/(q^5*t)+1/(q^4*t)+1/(q^3*t)+1/(q^2*t)" +"(0,13,5,8)","q^(-11)+q^(-10)-2/q^8-2/q^7+q^(-5)+q^(-4)+1/(q^11*t)+1/(q^10*t)-2/(q^8*t)-2/(q^7*t)+1/(q^5*t)+1/(q^4*t)" +"(0,15,5,10)","-q^(-13)-q^(-12)+2/q^10+q^(-9)-q^(-8)-q^(-7)+q^(-6)+2/q^5-q^(-3)-q^(-2)+1/(q^11*t)+1/(q^10*t)-2/(q^8*t)-2/(q^7*t)+1/(q^5*t)+1/(q^4*t)+t/q^11+t/q^10-(2*t)/q^8-(2*t)/q^7+t/q^5+t/q^4" +"(0,6,6,0)","t/q^6" +"(0,7,6,1)","q^(-8)-t/q^6" +"(0,8,6,1)","-q^(-8)+t/q^6" +"(0,10,6,3)","-q^(-8)+q^(-6)-t/q^9-t/q^8+t/q^6+t/q^5" +"(0,10,6,4)","t/q^10+t/q^9-t/q^6-t/q^5" +"(0,11,6,5)","q^(-11)+q^(-10)-q^(-8)-q^(-7)-t/q^9-t/q^8+t/q^6+t/q^5" +"(0,12,6,5)","q^(-11)+q^(-10)-q^(-8)-q^(-7)-t/q^9-t/q^8+t/q^6+t/q^5" +"(0,14,6,7)","q^(-11)+q^(-10)-2/q^8-2/q^7+q^(-5)+q^(-4)+t/q^11+t/q^10-(2*t)/q^8-(2*t)/q^7+t/q^5+t/q^4" +"(0,14,6,8)","t/q^13+t/q^12+t/q^11-t/q^10-(2*t)/q^9-(2*t)/q^8-t/q^7+t/q^6+t/q^5+t/q^4" +"(0,15,6,9)","q^(-13)+q^(-12)-2/q^10-2/q^9+q^(-7)+q^(-6)-t/q^15-t/q^14+(2*t)/q^12+t/q^11-t/q^10-t/q^9+t/q^8+(2*t)/q^7-t/q^5-t/q^4+t^2/q^13+t^2/q^12-(2*t^2)/q^10-(2*t^2)/q^9+t^2/q^7+t^2/q^6" +"(0,7,7,0)","q^(-8)" +"(0,9,7,1)","q^(-8)-1/(q^10*t)" +"(0,10,7,2)","q^(-6)-t/q^8" +"(0,11,7,3)","-q^(-11)-q^(-10)+q^(-8)+q^(-7)+q^(-6)-1/(q^10*t)" +"(0,12,7,3)","q^(-9)-1/(q^7*t)" +"(0,11,7,4)","q^(-12)+q^(-11)+q^(-10)-q^(-8)-q^(-7)-t/q^8" +"(0,12,7,4)","q^(-6)-t/q^8" +"(0,13,7,5)","-q^(-11)-q^(-10)-q^(-9)+q^(-8)+q^(-7)+q^(-6)+1/(q^13*t)+1/(q^12*t)+1/(q^11*t)-1/(q^10*t)-1/(q^9*t)-1/(q^8*t)" +"(0,14,7,6)","-q^(-9)-q^(-8)-q^(-7)+q^(-6)+q^(-5)+q^(-4)+t/q^11+t/q^10+t/q^9-t/q^8-t/q^7-t/q^6" +"(0,15,7,7)","q^(-13)+q^(-12)-q^(-11)-3/q^10-4/q^9+q^(-7)+3/q^6+q^(-5)+q^(-4)+1/(q^13*t)+1/(q^12*t)+1/(q^11*t)-1/(q^10*t)-1/(q^9*t)-1/(q^8*t)+t/q^13+t/q^12+t/q^11-t/q^10-t/q^9-t/q^8" +"(0,15,7,8)","q^(-15)+q^(-14)+2/q^13-q^(-11)-3/q^10-2/q^9+q^(-7)+q^(-6)-(2*t)/q^11-(2*t)/q^10-(2*t)/q^9+(2*t)/q^8+(2*t)/q^7+(2*t)/q^6+t^2/q^13+t^2/q^12+t^2/q^11-t^2/q^10-t^2/q^9-t^2/q^8" +"(0,8,8,0)","q^(-8)" +"(0,9,8,1)","q^(-8)-1/(q^10*t)" +"(0,10,8,2)","q^(-8)-t/q^10" +"(0,11,8,3)","q^(-8)-1/(q^10*t)" +"(0,12,8,3)","q^(-11)+q^(-10)+q^(-9)-q^(-7)-q^(-6)-1/(q^7*t)" +"(0,11,8,4)","q^(-12)-t/q^10" +"(0,12,8,4)","-q^(-11)-q^(-10)+q^(-8)+q^(-7)+q^(-6)-t/q^10" +"(0,13,8,5)","-q^(-11)-q^(-10)-q^(-9)+q^(-8)+q^(-7)+q^(-6)+1/(q^13*t)+1/(q^12*t)+1/(q^11*t)-1/(q^10*t)-1/(q^9*t)-1/(q^8*t)" +"(0,14,8,6)","-q^(-11)-q^(-10)-q^(-9)+q^(-8)+q^(-7)+q^(-6)+t/q^13+t/q^12+t/q^11-t/q^10-t/q^9-t/q^8" +"(0,15,8,7)","-2/q^11-2/q^10-2/q^9+2/q^8+2/q^7+2/q^6+1/(q^13*t)+1/(q^12*t)+1/(q^11*t)-1/(q^10*t)-1/(q^9*t)-1/(q^8*t)+t/q^15+t/q^14+(2*t)/q^13-t/q^11-(3*t)/q^10-(2*t)/q^9+t/q^7+t/q^6" +"(0,15,8,8)","q^(-15)+q^(-14)+q^(-13)-q^(-12)-q^(-11)-q^(-10)+t/q^15+t/q^14-t/q^13-(3*t)/q^12-(4*t)/q^11+t/q^9+(3*t)/q^8+t/q^7+t/q^6+t^2/q^15+t^2/q^14+t^2/q^13-t^2/q^12-t^2/q^11-t^2/q^10" +"(0,9,9,0)","1/(q^10*t)" +"(0,11,9,2)","-q^(-12)+1/(q^10*t)" +"(0,12,9,2)","-q^(-9)+1/(q^7*t)" +"(0,13,9,3)","1/(q^13*t)+1/(q^12*t)+1/(q^11*t)-1/(q^9*t)-1/(q^8*t)-1/(q^7*t)" +"(0,13,9,4)","-q^(-12)+q^(-9)-1/(q^13*t)-1/(q^12*t)-1/(q^11*t)+1/(q^10*t)+1/(q^9*t)+1/(q^8*t)" +"(0,15,9,6)","q^(-15)+q^(-14)+q^(-13)-q^(-12)+q^(-9)-q^(-8)-q^(-7)-q^(-6)-1/(q^13*t)-1/(q^12*t)-1/(q^11*t)+1/(q^10*t)+1/(q^9*t)+1/(q^8*t)-t/q^13-t/q^12-t/q^11+t/q^10+t/q^9+t/q^8" +"(0,10,10,0)","t/q^10" +"(0,11,10,1)","-q^(-12)+t/q^10" +"(0,12,10,1)","-q^(-12)+t/q^10" +"(0,14,10,3)","-q^(-12)+q^(-9)-t/q^13-t/q^12-t/q^11+t/q^10+t/q^9+t/q^8" +"(0,14,10,4)","t/q^14+t/q^13+t/q^12-t/q^10-t/q^9-t/q^8" +"(0,15,10,5)","-q^(-15)-q^(-14)-q^(-13)+q^(-12)+q^(-11)+q^(-10)+t/q^17+t/q^16+t/q^15-t/q^14+t/q^11-t/q^10-t/q^9-t/q^8-t^2/q^15-t^2/q^14-t^2/q^13+t^2/q^12+t^2/q^11+t^2/q^10" +"(0,11,11,0)","q^(-12)" +"(0,13,11,1)","q^(-12)-1/(q^14*t)" +"(0,14,11,2)","-q^(-9)+t/q^11" +"(0,15,11,3)","-q^(-15)-q^(-14)-q^(-13)+2/q^12+q^(-11)+q^(-10)+q^(-9)-1/(q^14*t)-t/q^14" +"(0,15,11,4)","q^(-16)+q^(-15)+q^(-14)+q^(-13)-q^(-12)-q^(-11)-q^(-10)-(2*t)/q^11+t^2/q^13" +"(0,12,12,0)","q^(-12)" +"(0,13,12,1)","-q^(-12)+1/(q^14*t)" +"(0,14,12,2)","q^(-12)-t/q^14" +"(0,15,12,3)","-2/q^12+1/(q^14*t)+t/q^17+t/q^16+t/q^15+t/q^14-t/q^13-t/q^12-t/q^11" +"(0,15,12,4)","-q^(-16)-t/q^17-t/q^16-t/q^15+(2*t)/q^14+t/q^13+t/q^12+t/q^11-t^2/q^16" +"(0,13,13,0)","1/(q^14*t)" +"(0,15,13,2)","-q^(-16)-q^(-12)+1/(q^14*t)+t/q^14" +"(0,14,14,0)","t/q^14" +"(0,15,14,1)","q^(-16)-t/q^18-t/q^14+t^2/q^16" +"(0,15,15,0)","q^(-16)" +"(1,0,0,1)","1" +"(1,2,0,3)","1" +"(1,2,0,4)","-(t/q^2)" +"(1,4,0,5)","1-q^(-3)" +"(1,6,0,7)","1-q^(-3)" +"(1,6,0,8)","-(t/q^5)+t/q^2" +"(1,8,0,9)","1+q^(-5)-q^(-3)-q^(-2)" +"(1,10,0,11)","1+q^(-5)-q^(-3)-q^(-2)" +"(1,10,0,12)","-(t/q^7)+t/q^5+t/q^4-t/q^2" +"(1,12,0,13)","1-q^(-6)+q^(-5)+q^(-4)-q^(-2)-q^(-1)" +"(1,14,0,15)","1-q^(-6)+q^(-5)+q^(-4)-q^(-2)-q^(-1)" +"(1,1,1,1)","-(1/(q^2*t))" +"(1,3,1,3)","-(1/(q^2*t))" +"(1,4,1,3)","-(1/(q*t))" +"(1,3,1,4)","q^(-4)" +"(1,4,1,4)","q^(-3)" +"(1,5,1,5)","1/(q^5*t)-1/(q^2*t)" +"(1,7,1,7)","1/(q^5*t)-1/(q^2*t)" +"(1,8,1,7)","t^(-1)-1/(q^3*t)" +"(1,7,1,8)","q^(-7)-q^(-4)" +"(1,8,1,8)","-q^(-5)+q^(-2)" +"(1,9,1,9)","-(1/(q^7*t))+1/(q^5*t)+1/(q^4*t)-1/(q^2*t)" +"(1,11,1,11)","-(1/(q^7*t))+1/(q^5*t)+1/(q^4*t)-1/(q^2*t)" +"(1,12,1,11)","-(1/(q^4*t))+1/(q^2*t)+1/(q*t)-q/t" +"(1,11,1,12)","q^(-9)-q^(-7)-q^(-6)+q^(-4)" +"(1,12,1,12)","q^(-6)-q^(-4)-q^(-3)+q^(-1)" +"(1,13,1,13)","1/(q^8*t)-1/(q^7*t)-1/(q^6*t)+1/(q^4*t)+1/(q^3*t)-1/(q^2*t)" +"(1,15,1,15)","1-q^(-6)+q^(-5)+q^(-4)-q^(-2)-q^(-1)+1/(q^8*t)-1/(q^7*t)-1/(q^6*t)+1/(q^4*t)+1/(q^3*t)-1/(q^2*t)" +"(1,2,2,1)","t/q^2" +"(1,6,2,5)","t/q^5-t/q^2" +"(1,10,2,9)","t/q^7-t/q^5-t/q^4+t/q^2" +"(1,14,2,13)","t/q^8-t/q^7-t/q^6+t/q^4+t/q^3-t/q^2" +"(1,3,3,1)","-q^(-4)" +"(1,6,3,3)","q^(-3)" +"(1,6,3,4)","-(t/q^5)" +"(1,7,3,5)","-q^(-7)+q^(-4)" +"(1,8,3,5)","-q^(-6)+q^(-3)" +"(1,10,3,7)","-q^(-6)-q^(-5)+q^(-3)+q^(-2)" +"(1,10,3,8)","-(t/q^8)-t/q^7+t/q^5+t/q^4" +"(1,11,3,9)","-q^(-9)+q^(-7)+q^(-6)-q^(-4)" +"(1,12,3,9)","q^(-8)+q^(-7)-q^(-6)-2/q^5-q^(-4)+q^(-3)+q^(-2)" +"(1,14,3,11)","q^(-8)+q^(-7)-2/q^5-2/q^4+q^(-2)+q^(-1)" +"(1,14,3,12)","-(t/q^10)-t/q^9+(2*t)/q^7+(2*t)/q^6-t/q^4-t/q^3" +"(1,15,3,13)","-q^(-10)+q^(-9)+q^(-8)-q^(-6)-q^(-5)+q^(-4)-t/q^11+t/q^9+(2*t)/q^8-(2*t)/q^6-t/q^5+t/q^3" +"(1,4,4,1)","-q^(-4)" +"(1,6,4,3)","-q^(-4)" +"(1,6,4,4)","t/q^6" +"(1,8,4,5)","q^(-7)+q^(-6)-q^(-4)-q^(-3)" +"(1,10,4,7)","q^(-7)+q^(-6)-q^(-4)-q^(-3)" +"(1,10,4,8)","t/q^9+t/q^8-t/q^6-t/q^5" +"(1,12,4,9)","-q^(-9)-q^(-8)+2/q^6+2/q^5-q^(-3)-q^(-2)" +"(1,14,4,11)","-q^(-9)-q^(-8)+2/q^6+2/q^5-q^(-3)-q^(-2)" +"(1,14,4,12)","t/q^11+t/q^10-(2*t)/q^8-(2*t)/q^7+t/q^5+t/q^4" +"(1,15,4,13)","t/q^12-t/q^10-t/q^9-t/q^8+t/q^7+t/q^6+t/q^5-t/q^3" +"(1,5,5,1)","1/(q^6*t)" +"(1,7,5,3)","1/(q^6*t)" +"(1,8,5,3)","-(1/(q^4*t))" +"(1,7,5,4)","-q^(-8)" +"(1,8,5,4)","q^(-6)" +"(1,9,5,5)","-(1/(q^9*t))-1/(q^8*t)+1/(q^6*t)+1/(q^5*t)" +"(1,11,5,7)","-(1/(q^9*t))-1/(q^8*t)+1/(q^6*t)+1/(q^5*t)" +"(1,12,5,7)","-(1/(q^6*t))-1/(q^5*t)+1/(q^3*t)+1/(q^2*t)" +"(1,11,5,8)","-q^(-11)-q^(-10)+q^(-8)+q^(-7)" +"(1,12,5,8)","-q^(-8)-q^(-7)+q^(-5)+q^(-4)" +"(1,13,5,9)","1/(q^11*t)+1/(q^10*t)-2/(q^8*t)-2/(q^7*t)+1/(q^5*t)+1/(q^4*t)" +"(1,15,5,11)","-q^(-9)-q^(-8)+2/q^6+2/q^5-q^(-3)-q^(-2)+1/(q^11*t)+1/(q^10*t)-2/(q^8*t)-2/(q^7*t)+1/(q^5*t)+1/(q^4*t)" +"(1,15,5,12)","-q^(-13)-q^(-12)+2/q^10+2/q^9-q^(-7)-q^(-6)+t/q^11+t/q^10-(2*t)/q^8-(2*t)/q^7+t/q^5+t/q^4" +"(1,6,6,1)","-(t/q^6)" +"(1,10,6,5)","-(t/q^9)-t/q^8+t/q^6+t/q^5" +"(1,14,6,9)","-(t/q^11)-t/q^10+(2*t)/q^8+(2*t)/q^7-t/q^5-t/q^4" +"(1,7,7,1)","q^(-8)" +"(1,10,7,3)","q^(-6)" +"(1,10,7,4)","-(t/q^8)" +"(1,11,7,5)","q^(-11)+q^(-10)-q^(-8)-q^(-7)" +"(1,12,7,5)","-q^(-9)+q^(-6)" +"(1,14,7,7)","-q^(-9)-q^(-8)-q^(-7)+q^(-6)+q^(-5)+q^(-4)" +"(1,14,7,8)","-(t/q^11)-t/q^10-t/q^9+t/q^8+t/q^7+t/q^6" +"(1,15,7,9)","q^(-13)+q^(-12)-2/q^10-2/q^9+q^(-7)+q^(-6)+t/q^13+t/q^12-(2*t)/q^10-(2*t)/q^9+t/q^7+t/q^6" +"(1,8,8,1)","q^(-8)" +"(1,10,8,3)","q^(-8)" +"(1,10,8,4)","-(t/q^10)" +"(1,12,8,5)","-q^(-11)-q^(-10)-q^(-9)+q^(-8)+q^(-7)+q^(-6)" +"(1,14,8,7)","-q^(-11)-q^(-10)-q^(-9)+q^(-8)+q^(-7)+q^(-6)" +"(1,14,8,8)","-(t/q^13)-t/q^12-t/q^11+t/q^10+t/q^9+t/q^8" +"(1,15,8,9)","t/q^15+t/q^14+t/q^13-t/q^12-(2*t)/q^11-(2*t)/q^10-t/q^9+t/q^8+t/q^7+t/q^6" +"(1,9,9,1)","-(1/(q^10*t))" +"(1,11,9,3)","-(1/(q^10*t))" +"(1,12,9,3)","-(1/(q^7*t))" +"(1,11,9,4)","q^(-12)" +"(1,12,9,4)","q^(-9)" +"(1,13,9,5)","1/(q^13*t)+1/(q^12*t)+1/(q^11*t)-1/(q^10*t)-1/(q^9*t)-1/(q^8*t)" +"(1,15,9,7)","-q^(-11)-q^(-10)-q^(-9)+q^(-8)+q^(-7)+q^(-6)+1/(q^13*t)+1/(q^12*t)+1/(q^11*t)-1/(q^10*t)-1/(q^9*t)-1/(q^8*t)" +"(1,15,9,8)","q^(-15)+q^(-14)+q^(-13)-q^(-12)-q^(-11)-q^(-10)-t/q^13-t/q^12-t/q^11+t/q^10+t/q^9+t/q^8" +"(1,10,10,1)","t/q^10" +"(1,14,10,5)","t/q^13+t/q^12+t/q^11-t/q^10-t/q^9-t/q^8" +"(1,11,11,1)","-q^(-12)" +"(1,14,11,3)","q^(-9)" +"(1,14,11,4)","-(t/q^11)" +"(1,15,11,5)","-q^(-15)-q^(-14)-q^(-13)+q^(-12)+q^(-11)+q^(-10)-t/q^14+t/q^11" +"(1,12,12,1)","-q^(-12)" +"(1,14,12,3)","-q^(-12)" +"(1,14,12,4)","t/q^14" +"(1,15,12,5)","t/q^17+t/q^16+t/q^15-t/q^13-t/q^12-t/q^11" +"(1,13,13,1)","1/(q^14*t)" +"(1,15,13,3)","-q^(-12)+1/(q^14*t)" +"(1,15,13,4)","-q^(-16)+t/q^14" +"(1,14,14,1)","-(t/q^14)" +"(1,15,15,1)","q^(-16)" +"(2,0,0,2)","1" +"(2,1,0,3)","-(1/(q*t))" +"(2,1,0,4)","1" +"(2,3,0,6)","1-q^(-3)" +"(2,5,0,7)","t^(-1)-1/(q^3*t)" +"(2,5,0,8)","1-q^(-3)" +"(2,7,0,10)","1+q^(-5)-q^(-3)-q^(-2)" +"(2,9,0,11)","-(1/(q^4*t))+1/(q^2*t)+1/(q*t)-q/t" +"(2,9,0,12)","1+q^(-5)-q^(-3)-q^(-2)" +"(2,11,0,14)","1-q^(-6)+q^(-5)+q^(-4)-q^(-2)-q^(-1)" +"(2,13,0,15)","-t^(-1)-1/(q^4*t)+1/(q^3*t)+1/(q^2*t)-q/t+q^2/t" +"(2,1,1,2)","1/(q*t)" +"(2,5,1,6)","-t^(-1)+1/(q^3*t)" +"(2,9,1,10)","1/(q^4*t)-1/(q^2*t)-1/(q*t)+q/t" +"(2,13,1,14)","t^(-1)+1/(q^4*t)-1/(q^3*t)-1/(q^2*t)+q/t-q^2/t" +"(2,2,2,2)","-(t/q^2)" +"(2,3,2,3)","q^(-3)" +"(2,4,2,3)","q^(-3)" +"(2,3,2,4)","-(t/q^2)" +"(2,4,2,4)","-(t/q^2)" +"(2,6,2,6)","t/q^5-t/q^2" +"(2,7,2,7)","-q^(-5)+q^(-2)" +"(2,8,2,7)","q^(-5)-q^(-2)" +"(2,7,2,8)","-(t/q^5)+t/q^2" +"(2,8,2,8)","t/q^5-t/q^2" +"(2,10,2,10)","-(t/q^7)+t/q^5+t/q^4-t/q^2" +"(2,11,2,11)","q^(-6)-q^(-4)-q^(-3)+q^(-1)" +"(2,12,2,11)","q^(-6)-q^(-4)-q^(-3)+q^(-1)" +"(2,11,2,12)","-(t/q^7)+t/q^5+t/q^4-t/q^2" +"(2,12,2,12)","-(t/q^7)+t/q^5+t/q^4-t/q^2" +"(2,14,2,14)","t/q^8-t/q^7-t/q^6+t/q^4+t/q^3-t/q^2" +"(2,15,2,15)","1-q^(-6)+q^(-5)+q^(-4)-q^(-2)-q^(-1)+t/q^8-t/q^7-t/q^6+t/q^4+t/q^3-t/q^2" +"(2,3,3,2)","-q^(-3)" +"(2,5,3,3)","1/(q^4*t)" +"(2,5,3,4)","-q^(-3)" +"(2,7,3,6)","q^(-6)+q^(-5)-q^(-3)-q^(-2)" +"(2,9,3,7)","1/(q^6*t)+1/(q^5*t)-1/(q^3*t)-1/(q^2*t)" +"(2,9,3,8)","q^(-6)+q^(-5)-q^(-3)-q^(-2)" +"(2,11,3,10)","-q^(-8)-q^(-7)+2/q^5+2/q^4-q^(-2)-q^(-1)" +"(2,13,3,11)","t^(-1)+1/(q^7*t)+1/(q^6*t)-2/(q^4*t)-2/(q^3*t)+1/(q*t)" +"(2,13,3,12)","-q^(-8)-q^(-7)+2/q^5+2/q^4-q^(-2)-q^(-1)" +"(2,15,3,14)","-1+q^(-9)-q^(-7)-q^(-6)-q^(-5)+q^(-4)+q^(-3)+q^(-2)" +"(2,4,4,2)","-q^(-3)" +"(2,5,4,3)","-(1/(q^4*t))" +"(2,5,4,4)","q^(-3)" +"(2,7,4,6)","-q^(-6)+q^(-3)" +"(2,8,4,6)","-q^(-5)+q^(-2)" +"(2,9,4,7)","-(1/(q^6*t))-1/(q^5*t)+1/(q^3*t)+1/(q^2*t)" +"(2,9,4,8)","-q^(-6)-q^(-5)+q^(-3)+q^(-2)" +"(2,11,4,10)","q^(-8)+q^(-7)-q^(-6)-2/q^5-q^(-4)+q^(-3)+q^(-2)" +"(2,12,4,10)","-q^(-6)+q^(-4)+q^(-3)-q^(-1)" +"(2,13,4,11)","-t^(-1)-1/(q^7*t)-1/(q^6*t)+2/(q^4*t)+2/(q^3*t)-1/(q*t)" +"(2,13,4,12)","q^(-8)+q^(-7)-2/q^5-2/q^4+q^(-2)+q^(-1)" +"(2,15,4,14)","-q^(-9)+q^(-7)+2/q^6-2/q^4-q^(-3)+q^(-1)-t/q^8+t/q^7+t/q^6-t/q^4-t/q^3+t/q^2" +"(2,5,5,2)","-(1/(q^4*t))" +"(2,9,5,6)","-(1/(q^6*t))-1/(q^5*t)+1/(q^3*t)+1/(q^2*t)" +"(2,13,5,10)","-t^(-1)-1/(q^7*t)-1/(q^6*t)+2/(q^4*t)+2/(q^3*t)-1/(q*t)" +"(2,6,6,2)","t/q^5" +"(2,7,6,3)","q^(-6)" +"(2,8,6,3)","-q^(-6)" +"(2,7,6,4)","-(t/q^5)" +"(2,8,6,4)","t/q^5" +"(2,10,6,6)","-(t/q^8)-t/q^7+t/q^5+t/q^4" +"(2,11,6,7)","-q^(-8)-q^(-7)+q^(-5)+q^(-4)" +"(2,12,6,7)","-q^(-8)-q^(-7)+q^(-5)+q^(-4)" +"(2,11,6,8)","-(t/q^8)-t/q^7+t/q^5+t/q^4" +"(2,12,6,8)","-(t/q^8)-t/q^7+t/q^5+t/q^4" +"(2,14,6,10)","t/q^10+t/q^9-(2*t)/q^7-(2*t)/q^6+t/q^4+t/q^3" +"(2,15,6,11)","q^(-9)+q^(-8)-2/q^6-2/q^5+q^(-3)+q^(-2)-t/q^11-t/q^10+(2*t)/q^8+(2*t)/q^7-t/q^5-t/q^4" +"(2,15,6,12)","-(t/q^10)-t/q^9+(2*t)/q^7+(2*t)/q^6-t/q^4-t/q^3+t^2/q^12+t^2/q^11-(2*t^2)/q^9-(2*t^2)/q^8+t^2/q^6+t^2/q^5" +"(2,7,7,2)","q^(-6)" +"(2,9,7,3)","-(1/(q^7*t))" +"(2,9,7,4)","q^(-6)" +"(2,11,7,6)","-q^(-9)-q^(-8)-q^(-7)+q^(-6)+q^(-5)+q^(-4)" +"(2,13,7,7)","-(1/(q^9*t))-1/(q^8*t)-1/(q^7*t)+1/(q^6*t)+1/(q^5*t)+1/(q^4*t)" +"(2,13,7,8)","-q^(-9)-q^(-8)-q^(-7)+q^(-6)+q^(-5)+q^(-4)" +"(2,15,7,10)","q^(-11)+q^(-10)+q^(-9)-q^(-8)-2/q^7-2/q^6-q^(-5)+q^(-4)+q^(-3)+q^(-2)" +"(2,8,8,2)","q^(-6)" +"(2,9,8,3)","-(1/(q^7*t))" +"(2,9,8,4)","q^(-6)" +"(2,11,8,6)","-q^(-9)+q^(-6)" +"(2,12,8,6)","q^(-8)+q^(-7)-q^(-5)-q^(-4)" +"(2,13,8,7)","-(1/(q^9*t))-1/(q^8*t)-1/(q^7*t)+1/(q^6*t)+1/(q^5*t)+1/(q^4*t)" +"(2,13,8,8)","-q^(-9)-q^(-8)-q^(-7)+q^(-6)+q^(-5)+q^(-4)" +"(2,15,8,10)","q^(-11)+q^(-10)-2/q^8-2/q^7+q^(-5)+q^(-4)+t/q^11+t/q^10-(2*t)/q^8-(2*t)/q^7+t/q^5+t/q^4" +"(2,9,9,2)","1/(q^7*t)" +"(2,13,9,6)","1/(q^9*t)+1/(q^8*t)+1/(q^7*t)-1/(q^6*t)-1/(q^5*t)-1/(q^4*t)" +"(2,10,10,2)","-(t/q^8)" +"(2,11,10,3)","q^(-9)" +"(2,12,10,3)","q^(-9)" +"(2,11,10,4)","-(t/q^8)" +"(2,12,10,4)","-(t/q^8)" +"(2,14,10,6)","t/q^11+t/q^10+t/q^9-t/q^8-t/q^7-t/q^6" +"(2,15,10,7)","-q^(-11)-q^(-10)-q^(-9)+q^(-8)+q^(-7)+q^(-6)+t/q^13+t/q^12+t/q^11-t/q^10-t/q^9-t/q^8" +"(2,15,10,8)","-(t/q^11)-t/q^10-t/q^9+t/q^8+t/q^7+t/q^6+t^2/q^13+t^2/q^12+t^2/q^11-t^2/q^10-t^2/q^9-t^2/q^8" +"(2,11,11,2)","-q^(-9)" +"(2,13,11,3)","1/(q^10*t)" +"(2,13,11,4)","-q^(-9)" +"(2,15,11,6)","q^(-12)+q^(-11)+q^(-10)-q^(-8)-q^(-7)-q^(-6)" +"(2,12,12,2)","-q^(-9)" +"(2,13,12,3)","-(1/(q^10*t))" +"(2,13,12,4)","q^(-9)" +"(2,15,12,6)","-q^(-12)+q^(-9)-t/q^13-t/q^12-t/q^11+t/q^10+t/q^9+t/q^8" +"(2,13,13,2)","-(1/(q^10*t))" +"(2,14,14,2)","t/q^11" +"(2,15,14,3)","q^(-12)-t/q^14" +"(2,15,14,4)","-(t/q^11)+t^2/q^13" +"(2,15,15,2)","q^(-12)" +"(3,0,0,3)","1" +"(3,1,0,5)","1" +"(3,2,0,6)","t/q^2" +"(3,3,0,7)","1" +"(3,4,0,7)","q^(-2)" +"(3,3,0,8)","t/q^2" +"(3,4,0,8)","t/q^2" +"(3,5,0,9)","1-q^(-2)" +"(3,6,0,10)","-(t/q^4)+t/q^2" +"(3,7,0,11)","1-q^(-2)" +"(3,8,0,11)","q^(-3)-q^(-1)" +"(3,7,0,12)","t/q^4-t/q^2" +"(3,8,0,12)","-(t/q^4)+t/q^2" +"(3,9,0,13)","1+q^(-3)-q^(-2)-q^(-1)" +"(3,10,0,14)","t/q^5-t/q^4-t/q^3+t/q^2" +"(3,11,0,15)","1+q^(-3)-q^(-2)-q^(-1)" +"(3,12,0,15)","1+q^(-3)-q^(-2)-q^(-1)" +"(3,1,1,3)","-(1/(q*t))" +"(3,3,1,6)","-q^(-3)" +"(3,4,1,6)","-q^(-2)" +"(3,5,1,7)","t^(-1)-1/(q^3*t)" +"(3,5,1,8)","-q^(-3)+q^(-2)" +"(3,7,1,10)","q^(-5)-q^(-3)" +"(3,8,1,10)","-q^(-3)+q^(-1)" +"(3,9,1,11)","-(1/(q^4*t))+1/(q^2*t)+1/(q*t)-q/t" +"(3,9,1,12)","q^(-5)-2/q^3+q^(-1)" +"(3,11,1,14)","-q^(-6)+q^(-5)+q^(-4)-q^(-3)" +"(3,12,1,14)","-1-q^(-3)+q^(-2)+q^(-1)" +"(3,13,1,15)","-t^(-1)-1/(q^4*t)+1/(q^3*t)+1/(q^2*t)-q/t+q^2/t" +"(3,2,2,3)","-(t/q^2)" +"(3,3,2,5)","-(t/q^2)" +"(3,4,2,5)","-(t/q^2)" +"(3,6,2,7)","t/q^4-t/q^2" +"(3,7,2,9)","-(t/q^4)+t/q^2" +"(3,8,2,9)","t/q^4-t/q^2" +"(3,10,2,11)","-(t/q^5)+t/q^4+t/q^3-t/q^2" +"(3,11,2,13)","-(t/q^5)+t/q^4+t/q^3-t/q^2" +"(3,12,2,13)","-(t/q^5)+t/q^4+t/q^3-t/q^2" +"(3,3,3,3)","q^(-3)" +"(3,5,3,5)","q^(-3)" +"(3,6,3,6)","-(t/q^4)" +"(3,7,3,7)","-q^(-5)+q^(-3)+q^(-2)" +"(3,8,3,7)","-q^(-4)" +"(3,7,3,8)","t/q^4" +"(3,8,3,8)","-(t/q^4)" +"(3,9,3,9)","-q^(-5)-q^(-4)+q^(-3)+q^(-2)" +"(3,10,3,10)","t/q^6+t/q^5-t/q^4-t/q^3" +"(3,11,3,11)","q^(-6)-q^(-5)-2/q^4+q^(-2)+q^(-1)" +"(3,12,3,11)","-q^(-5)-q^(-4)+q^(-3)+q^(-2)" +"(3,11,3,12)","t/q^6+t/q^5-t/q^4-t/q^3" +"(3,12,3,12)","t/q^6+t/q^5-t/q^4-t/q^3" +"(3,13,3,13)","q^(-6)-q^(-4)-q^(-3)+q^(-1)" +"(3,14,3,14)","-(t/q^7)+t/q^5+t/q^4-t/q^2" +"(3,15,3,15)","1+q^(-5)-q^(-3)-q^(-2)-t/q^7+t/q^5+t/q^4-t/q^2" +"(3,4,4,3)","q^(-3)" +"(3,5,4,5)","-q^(-3)" +"(3,6,4,6)","t/q^5" +"(3,7,4,7)","-q^(-3)" +"(3,8,4,7)","q^(-5)+q^(-4)-q^(-2)" +"(3,7,4,8)","-(t/q^5)" +"(3,8,4,8)","t/q^5" +"(3,9,4,9)","q^(-5)+q^(-4)-q^(-3)-q^(-2)" +"(3,10,4,10)","-(t/q^7)-t/q^6+t/q^5+t/q^4" +"(3,11,4,11)","q^(-5)+q^(-4)-q^(-3)-q^(-2)" +"(3,12,4,11)","q^(-6)+q^(-5)-2/q^3-q^(-2)+q^(-1)" +"(3,11,4,12)","-(t/q^7)-t/q^6+t/q^5+t/q^4" +"(3,12,4,12)","-(t/q^7)-t/q^6+t/q^5+t/q^4" +"(3,13,4,13)","-q^(-6)+q^(-4)+q^(-3)-q^(-1)" +"(3,14,4,14)","t/q^8-t/q^6-t/q^5+t/q^3" +"(3,15,4,15)","-q^(-6)+q^(-4)+q^(-3)-q^(-1)+t/q^8-t/q^6-t/q^5+t/q^3" +"(3,5,5,3)","-(1/(q^4*t))" +"(3,7,5,6)","-q^(-6)" +"(3,8,5,6)","q^(-4)" +"(3,9,5,7)","-(1/(q^6*t))-1/(q^5*t)+1/(q^3*t)+1/(q^2*t)" +"(3,9,5,8)","-q^(-6)+q^(-4)" +"(3,11,5,10)","q^(-8)+q^(-7)-q^(-6)-q^(-5)" +"(3,12,5,10)","q^(-5)+q^(-4)-q^(-3)-q^(-2)" +"(3,13,5,11)","-t^(-1)-1/(q^7*t)-1/(q^6*t)+2/(q^4*t)+2/(q^3*t)-1/(q*t)" +"(3,13,5,12)","q^(-8)+q^(-7)-q^(-6)-2/q^5-q^(-4)+q^(-3)+q^(-2)" +"(3,15,5,14)","-q^(-9)+q^(-7)+q^(-6)-q^(-4)+t/q^7-t/q^5-t/q^4+t/q^2" +"(3,6,6,3)","-(t/q^5)" +"(3,7,6,5)","t/q^5" +"(3,8,6,5)","-(t/q^5)" +"(3,10,6,7)","t/q^7+t/q^6-t/q^5-t/q^4" +"(3,11,6,9)","t/q^7+t/q^6-t/q^5-t/q^4" +"(3,12,6,9)","t/q^7+t/q^6-t/q^5-t/q^4" +"(3,14,6,11)","-(t/q^8)+t/q^6+t/q^5-t/q^3" +"(3,15,6,13)","t/q^8-t/q^6-t/q^5+t/q^3-t^2/q^10+t^2/q^8+t^2/q^7-t^2/q^5" +"(3,7,7,3)","q^(-6)" +"(3,9,7,5)","q^(-6)" +"(3,10,7,6)","t/q^6" +"(3,11,7,7)","-q^(-8)-q^(-7)+q^(-6)+q^(-5)+q^(-4)" +"(3,12,7,7)","q^(-6)" +"(3,11,7,8)","t/q^6" +"(3,12,7,8)","t/q^6" +"(3,13,7,9)","-q^(-8)-q^(-7)+q^(-5)+q^(-4)" +"(3,14,7,10)","-(t/q^8)-t/q^7+t/q^5+t/q^4" +"(3,15,7,11)","q^(-9)-q^(-7)-2/q^6-q^(-5)+q^(-4)+q^(-3)+q^(-2)+t/q^9+t/q^8-t/q^6-t/q^5" +"(3,15,7,12)","t/q^8+t/q^7-t/q^5-t/q^4-t^2/q^10-t^2/q^9+t^2/q^7+t^2/q^6" +"(3,8,8,3)","q^(-6)" +"(3,9,8,5)","q^(-6)" +"(3,10,8,6)","t/q^8" +"(3,11,8,7)","q^(-6)" +"(3,12,8,7)","q^(-8)+q^(-7)+q^(-6)-q^(-5)-q^(-4)" +"(3,11,8,8)","t/q^8" +"(3,12,8,8)","t/q^8" +"(3,13,8,9)","-q^(-8)-q^(-7)+q^(-5)+q^(-4)" +"(3,14,8,10)","-(t/q^10)-t/q^9+t/q^7+t/q^6" +"(3,15,8,11)","-q^(-8)-q^(-7)+q^(-5)+q^(-4)+t/q^11+t/q^10+t/q^9-t/q^8-(2*t)/q^7-t/q^6+t/q^4" +"(3,15,8,12)","t/q^10+t/q^9-t/q^7-t/q^6-t^2/q^12-t^2/q^11+t^2/q^9+t^2/q^8" +"(3,9,9,3)","-(1/(q^7*t))" +"(3,11,9,6)","-q^(-9)" +"(3,12,9,6)","-q^(-6)" +"(3,13,9,7)","-(1/(q^9*t))-1/(q^8*t)-1/(q^7*t)+1/(q^6*t)+1/(q^5*t)+1/(q^4*t)" +"(3,13,9,8)","-q^(-9)+q^(-6)" +"(3,15,9,10)","q^(-11)+q^(-10)-q^(-8)-q^(-7)-t/q^9-t/q^8+t/q^6+t/q^5" +"(3,10,10,3)","-(t/q^8)" +"(3,11,10,5)","-(t/q^8)" +"(3,12,10,5)","-(t/q^8)" +"(3,14,10,7)","t/q^10+t/q^9-t/q^7-t/q^6" +"(3,15,10,9)","-(t/q^10)-t/q^9+t/q^7+t/q^6+t^2/q^12+t^2/q^11-t^2/q^9-t^2/q^8" +"(3,11,11,3)","q^(-9)" +"(3,13,11,5)","q^(-9)" +"(3,14,11,6)","-(t/q^8)" +"(3,15,11,7)","-q^(-11)-q^(-10)+q^(-8)+q^(-7)+q^(-6)-t/q^10" +"(3,15,11,8)","t/q^8-t^2/q^10" +"(3,12,12,3)","q^(-9)" +"(3,13,12,5)","-q^(-9)" +"(3,14,12,6)","t/q^11" +"(3,15,12,7)","-q^(-9)+t/q^13+t/q^12+t/q^11-t/q^9-t/q^8" +"(3,15,12,8)","-(t/q^11)+t^2/q^13" +"(3,13,13,3)","-(1/(q^10*t))" +"(3,15,13,6)","-q^(-12)+t/q^10" +"(3,14,14,3)","-(t/q^11)" +"(3,15,14,5)","t/q^11-t^2/q^13" +"(3,15,15,3)","q^(-12)" +"(4,0,0,4)","1" +"(4,1,0,5)","1/(q*t)" +"(4,2,0,6)","1" +"(4,3,0,7)","1/(q*t)" +"(4,4,0,7)","t^(-1)" +"(4,3,0,8)","q^(-3)" +"(4,4,0,8)","1" +"(4,5,0,9)","-(1/(q^3*t))+1/(q*t)" +"(4,6,0,10)","1-q^(-2)" +"(4,7,0,11)","-(1/(q^3*t))+1/(q*t)" +"(4,8,0,11)","1/(q*t)-q/t" +"(4,7,0,12)","q^(-5)-q^(-3)" +"(4,8,0,12)","1-q^(-2)" +"(4,9,0,13)","1/(q^4*t)-1/(q^3*t)-1/(q^2*t)+1/(q*t)" +"(4,10,0,14)","1+q^(-3)-q^(-2)-q^(-1)" +"(4,11,0,15)","1/(q^4*t)-1/(q^3*t)-1/(q^2*t)+1/(q*t)" +"(4,12,0,15)","-t^(-1)+1/(q*t)-q/t+q^2/t" +"(4,1,1,4)","-(1/(q*t))" +"(4,3,1,6)","-(1/(q*t))" +"(4,4,1,6)","-t^(-1)" +"(4,5,1,8)","1/(q^3*t)-1/(q*t)" +"(4,7,1,10)","1/(q^3*t)-1/(q*t)" +"(4,8,1,10)","-(1/(q*t))+q/t" +"(4,9,1,12)","-(1/(q^4*t))+1/(q^3*t)+1/(q^2*t)-1/(q*t)" +"(4,11,1,14)","-(1/(q^4*t))+1/(q^3*t)+1/(q^2*t)-1/(q*t)" +"(4,12,1,14)","t^(-1)-1/(q*t)+q/t-q^2/t" +"(4,2,2,4)","-(t/q^2)" +"(4,3,2,5)","-q^(-3)" +"(4,4,2,5)","-q^(-3)" +"(4,6,2,7)","-q^(-3)+q^(-2)" +"(4,6,2,8)","-(t/q^5)+t/q^2" +"(4,7,2,9)","-q^(-5)+q^(-3)" +"(4,8,2,9)","q^(-5)-q^(-3)" +"(4,10,2,11)","q^(-5)-2/q^3+q^(-1)" +"(4,10,2,12)","-(t/q^7)+t/q^5+t/q^4-t/q^2" +"(4,11,2,13)","-q^(-6)+q^(-5)+q^(-4)-q^(-3)" +"(4,12,2,13)","-q^(-6)+q^(-5)+q^(-4)-q^(-3)" +"(4,14,2,15)","1-q^(-6)+q^(-5)+q^(-4)-q^(-2)-q^(-1)" +"(4,3,3,4)","q^(-3)" +"(4,5,3,5)","1/(q^4*t)" +"(4,6,3,6)","-q^(-2)" +"(4,7,3,7)","1/(q^4*t)" +"(4,8,3,7)","-(1/(q^2*t))" +"(4,7,3,8)","q^(-6)+q^(-5)-q^(-3)" +"(4,8,3,8)","-q^(-2)" +"(4,9,3,9)","-(1/(q^6*t))-1/(q^5*t)+1/(q^4*t)+1/(q^3*t)" +"(4,10,3,10)","q^(-4)+q^(-3)-q^(-2)-q^(-1)" +"(4,11,3,11)","-(1/(q^6*t))-1/(q^5*t)+1/(q^4*t)+1/(q^3*t)" +"(4,12,3,11)","t^(-1)-1/(q^3*t)-1/(q^2*t)+1/(q*t)" +"(4,11,3,12)","q^(-8)+q^(-7)-2/q^5-q^(-4)+q^(-3)" +"(4,12,3,12)","q^(-4)+q^(-3)-q^(-2)-q^(-1)" +"(4,13,3,13)","1/(q^7*t)-1/(q^5*t)-1/(q^4*t)+1/(q^2*t)" +"(4,14,3,14)","-1-q^(-5)+q^(-3)+q^(-2)" +"(4,15,3,15)","-1-q^(-5)+q^(-3)+q^(-2)+1/(q^7*t)-1/(q^5*t)-1/(q^4*t)+1/(q^2*t)" +"(4,4,4,4)","q^(-3)" +"(4,5,4,5)","-(1/(q^4*t))" +"(4,6,4,6)","q^(-3)" +"(4,7,4,7)","-(1/(q^4*t))" +"(4,8,4,7)","1/(q^2*t)" +"(4,7,4,8)","-q^(-6)" +"(4,8,4,8)","-q^(-5)+q^(-3)+q^(-2)" +"(4,9,4,9)","1/(q^6*t)+1/(q^5*t)-1/(q^4*t)-1/(q^3*t)" +"(4,10,4,10)","-q^(-5)-q^(-4)+q^(-3)+q^(-2)" +"(4,11,4,11)","1/(q^6*t)+1/(q^5*t)-1/(q^4*t)-1/(q^3*t)" +"(4,12,4,11)","-t^(-1)+1/(q^3*t)+1/(q^2*t)-1/(q*t)" +"(4,11,4,12)","-q^(-8)-q^(-7)+q^(-6)+q^(-5)" +"(4,12,4,12)","q^(-6)-q^(-5)-2/q^4+q^(-2)+q^(-1)" +"(4,13,4,13)","-(1/(q^7*t))+1/(q^5*t)+1/(q^4*t)-1/(q^2*t)" +"(4,14,4,14)","q^(-6)-q^(-4)-q^(-3)+q^(-1)" +"(4,15,4,15)","1+q^(-5)-q^(-3)-q^(-2)-1/(q^7*t)+1/(q^5*t)+1/(q^4*t)-1/(q^2*t)" +"(4,5,5,4)","-(1/(q^4*t))" +"(4,7,5,6)","-(1/(q^4*t))" +"(4,8,5,6)","1/(q^2*t)" +"(4,9,5,8)","1/(q^6*t)+1/(q^5*t)-1/(q^4*t)-1/(q^3*t)" +"(4,11,5,10)","1/(q^6*t)+1/(q^5*t)-1/(q^4*t)-1/(q^3*t)" +"(4,12,5,10)","-t^(-1)+1/(q^3*t)+1/(q^2*t)-1/(q*t)" +"(4,13,5,12)","-(1/(q^7*t))+1/(q^5*t)+1/(q^4*t)-1/(q^2*t)" +"(4,15,5,14)","1+q^(-5)-q^(-3)-q^(-2)-1/(q^7*t)+1/(q^5*t)+1/(q^4*t)-1/(q^2*t)" +"(4,6,6,4)","-(t/q^5)" +"(4,7,6,5)","q^(-6)" +"(4,8,6,5)","-q^(-6)" +"(4,10,6,7)","-q^(-6)+q^(-4)" +"(4,10,6,8)","-(t/q^8)-t/q^7+t/q^5+t/q^4" +"(4,11,6,9)","q^(-8)+q^(-7)-q^(-6)-q^(-5)" +"(4,12,6,9)","q^(-8)+q^(-7)-q^(-6)-q^(-5)" +"(4,14,6,11)","q^(-8)+q^(-7)-q^(-6)-2/q^5-q^(-4)+q^(-3)+q^(-2)" +"(4,14,6,12)","-(t/q^10)-t/q^9+(2*t)/q^7+(2*t)/q^6-t/q^4-t/q^3" +"(4,15,6,13)","q^(-9)-q^(-7)-q^(-6)+q^(-4)-t/q^11+t/q^9+t/q^8-t/q^6" +"(4,7,7,4)","q^(-6)" +"(4,9,7,5)","1/(q^7*t)" +"(4,10,7,6)","q^(-4)" +"(4,11,7,7)","1/(q^7*t)" +"(4,12,7,7)","1/(q^4*t)" +"(4,11,7,8)","q^(-9)+q^(-8)+q^(-7)-q^(-6)-q^(-5)" +"(4,12,7,8)","q^(-4)" +"(4,13,7,9)","-(1/(q^9*t))-1/(q^8*t)+1/(q^6*t)+1/(q^5*t)" +"(4,14,7,10)","-q^(-6)-q^(-5)+q^(-3)+q^(-2)" +"(4,15,7,11)","q^(-7)+q^(-6)-q^(-4)-q^(-3)-1/(q^9*t)-1/(q^8*t)+1/(q^6*t)+1/(q^5*t)" +"(4,15,7,12)","q^(-11)+q^(-10)+q^(-9)-q^(-8)-2/q^7-q^(-6)+q^(-4)-t/q^8-t/q^7+t/q^5+t/q^4" +"(4,8,8,4)","q^(-6)" +"(4,9,8,5)","1/(q^7*t)" +"(4,10,8,6)","q^(-6)" +"(4,11,8,7)","1/(q^7*t)" +"(4,12,8,7)","1/(q^4*t)" +"(4,11,8,8)","q^(-9)" +"(4,12,8,8)","-q^(-8)-q^(-7)+q^(-6)+q^(-5)+q^(-4)" +"(4,13,8,9)","-(1/(q^9*t))-1/(q^8*t)+1/(q^6*t)+1/(q^5*t)" +"(4,14,8,10)","-q^(-8)-q^(-7)+q^(-5)+q^(-4)" +"(4,15,8,11)","q^(-7)+q^(-6)-q^(-4)-q^(-3)-1/(q^9*t)-1/(q^8*t)+1/(q^6*t)+1/(q^5*t)" +"(4,15,8,12)","q^(-11)+q^(-10)-q^(-8)-q^(-7)+t/q^11-t/q^9-(2*t)/q^8-t/q^7+t/q^6+t/q^5+t/q^4" +"(4,9,9,4)","-(1/(q^7*t))" +"(4,11,9,6)","-(1/(q^7*t))" +"(4,12,9,6)","-(1/(q^4*t))" +"(4,13,9,8)","1/(q^9*t)+1/(q^8*t)-1/(q^6*t)-1/(q^5*t)" +"(4,15,9,10)","-q^(-7)-q^(-6)+q^(-4)+q^(-3)+1/(q^9*t)+1/(q^8*t)-1/(q^6*t)-1/(q^5*t)" +"(4,10,10,4)","-(t/q^8)" +"(4,11,10,5)","-q^(-9)" +"(4,12,10,5)","-q^(-9)" +"(4,14,10,7)","-q^(-9)+q^(-6)" +"(4,14,10,8)","-(t/q^11)-t/q^10-t/q^9+t/q^8+t/q^7+t/q^6" +"(4,15,10,9)","-q^(-11)-q^(-10)+q^(-8)+q^(-7)+t/q^13+t/q^12-t/q^10-t/q^9" +"(4,11,11,4)","q^(-9)" +"(4,13,11,5)","1/(q^10*t)" +"(4,14,11,6)","-q^(-6)" +"(4,15,11,7)","-q^(-8)+1/(q^10*t)" +"(4,15,11,8)","q^(-12)+q^(-11)+q^(-10)-q^(-8)-q^(-7)-t/q^8" +"(4,12,12,4)","q^(-9)" +"(4,13,12,5)","-(1/(q^10*t))" +"(4,14,12,6)","q^(-9)" +"(4,15,12,7)","q^(-8)-1/(q^10*t)" +"(4,15,12,8)","-q^(-12)-t/q^13-t/q^12+t/q^10+t/q^9+t/q^8" +"(4,13,13,4)","-(1/(q^10*t))" +"(4,15,13,6)","q^(-8)-1/(q^10*t)" +"(4,14,14,4)","-(t/q^11)" +"(4,15,14,5)","q^(-12)-t/q^14" +"(4,15,15,4)","q^(-12)" +"(5,0,0,5)","1" +"(5,2,0,7)","1" +"(5,2,0,8)","t/q^2" +"(5,4,0,9)","1-q^(-2)" +"(5,6,0,11)","1-q^(-2)" +"(5,6,0,12)","t/q^4-t/q^2" +"(5,8,0,13)","1+q^(-3)-q^(-2)-q^(-1)" +"(5,10,0,15)","1+q^(-3)-q^(-2)-q^(-1)" +"(5,1,1,5)","1/(q*t)" +"(5,3,1,7)","1/(q*t)" +"(5,4,1,7)","t^(-1)" +"(5,3,1,8)","q^(-3)" +"(5,4,1,8)","q^(-2)" +"(5,5,1,9)","-(1/(q^3*t))+1/(q*t)" +"(5,7,1,11)","-(1/(q^3*t))+1/(q*t)" +"(5,8,1,11)","1/(q*t)-q/t" +"(5,7,1,12)","q^(-5)-q^(-3)" +"(5,8,1,12)","-q^(-3)+q^(-1)" +"(5,9,1,13)","1/(q^4*t)-1/(q^3*t)-1/(q^2*t)+1/(q*t)" +"(5,11,1,15)","1/(q^4*t)-1/(q^3*t)-1/(q^2*t)+1/(q*t)" +"(5,12,1,15)","-t^(-1)+1/(q*t)-q/t+q^2/t" +"(5,2,2,5)","-(t/q^2)" +"(5,6,2,9)","-(t/q^4)+t/q^2" +"(5,10,2,13)","-(t/q^5)+t/q^4+t/q^3-t/q^2" +"(5,3,3,5)","-q^(-3)" +"(5,6,3,7)","q^(-2)" +"(5,6,3,8)","t/q^4" +"(5,7,3,9)","-q^(-5)+q^(-3)" +"(5,8,3,9)","-q^(-4)+q^(-2)" +"(5,10,3,11)","-q^(-4)-q^(-3)+q^(-2)+q^(-1)" +"(5,10,3,12)","t/q^6+t/q^5-t/q^4-t/q^3" +"(5,11,3,13)","-q^(-6)+q^(-5)+q^(-4)-q^(-3)" +"(5,12,3,13)","q^(-5)-2/q^3+q^(-1)" +"(5,14,3,15)","1+q^(-5)-q^(-3)-q^(-2)" +"(5,4,4,5)","-q^(-3)" +"(5,6,4,7)","-q^(-3)" +"(5,6,4,8)","-(t/q^5)" +"(5,8,4,9)","q^(-5)+q^(-4)-q^(-3)-q^(-2)" +"(5,10,4,11)","q^(-5)+q^(-4)-q^(-3)-q^(-2)" +"(5,10,4,12)","-(t/q^7)-t/q^6+t/q^5+t/q^4" +"(5,12,4,13)","-q^(-6)+q^(-4)+q^(-3)-q^(-1)" +"(5,14,4,15)","-q^(-6)+q^(-4)+q^(-3)-q^(-1)" +"(5,5,5,5)","-(1/(q^4*t))" +"(5,7,5,7)","-(1/(q^4*t))" +"(5,8,5,7)","1/(q^2*t)" +"(5,7,5,8)","-q^(-6)" +"(5,8,5,8)","q^(-4)" +"(5,9,5,9)","1/(q^6*t)+1/(q^5*t)-1/(q^4*t)-1/(q^3*t)" +"(5,11,5,11)","1/(q^6*t)+1/(q^5*t)-1/(q^4*t)-1/(q^3*t)" +"(5,12,5,11)","-t^(-1)+1/(q^3*t)+1/(q^2*t)-1/(q*t)" +"(5,11,5,12)","-q^(-8)-q^(-7)+q^(-6)+q^(-5)" +"(5,12,5,12)","-q^(-5)-q^(-4)+q^(-3)+q^(-2)" +"(5,13,5,13)","-(1/(q^7*t))+1/(q^5*t)+1/(q^4*t)-1/(q^2*t)" +"(5,15,5,15)","1+q^(-5)-q^(-3)-q^(-2)-1/(q^7*t)+1/(q^5*t)+1/(q^4*t)-1/(q^2*t)" +"(5,6,6,5)","t/q^5" +"(5,10,6,9)","t/q^7+t/q^6-t/q^5-t/q^4" +"(5,14,6,13)","t/q^8-t/q^6-t/q^5+t/q^3" +"(5,7,7,5)","q^(-6)" +"(5,10,7,7)","q^(-4)" +"(5,10,7,8)","t/q^6" +"(5,11,7,9)","q^(-8)+q^(-7)-q^(-6)-q^(-5)" +"(5,12,7,9)","-q^(-6)+q^(-4)" +"(5,14,7,11)","-q^(-6)-q^(-5)+q^(-3)+q^(-2)" +"(5,14,7,12)","t/q^8+t/q^7-t/q^5-t/q^4" +"(5,15,7,13)","q^(-9)-q^(-7)-q^(-6)+q^(-4)+t/q^9-t/q^7-t/q^6+t/q^4" +"(5,8,8,5)","q^(-6)" +"(5,10,8,7)","q^(-6)" +"(5,10,8,8)","t/q^8" +"(5,12,8,9)","-q^(-8)-q^(-7)+q^(-5)+q^(-4)" +"(5,14,8,11)","-q^(-8)-q^(-7)+q^(-5)+q^(-4)" +"(5,14,8,12)","t/q^10+t/q^9-t/q^7-t/q^6" +"(5,15,8,13)","t/q^11-t/q^8-t/q^7+t/q^4" +"(5,9,9,5)","1/(q^7*t)" +"(5,11,9,7)","1/(q^7*t)" +"(5,12,9,7)","1/(q^4*t)" +"(5,11,9,8)","q^(-9)" +"(5,12,9,8)","q^(-6)" +"(5,13,9,9)","-(1/(q^9*t))-1/(q^8*t)+1/(q^6*t)+1/(q^5*t)" +"(5,15,9,11)","q^(-7)+q^(-6)-q^(-4)-q^(-3)-1/(q^9*t)-1/(q^8*t)+1/(q^6*t)+1/(q^5*t)" +"(5,15,9,12)","q^(-11)+q^(-10)-q^(-8)-q^(-7)-t/q^9-t/q^8+t/q^6+t/q^5" +"(5,10,10,5)","-(t/q^8)" +"(5,14,10,9)","-(t/q^10)-t/q^9+t/q^7+t/q^6" +"(5,11,11,5)","-q^(-9)" +"(5,14,11,7)","q^(-6)" +"(5,14,11,8)","t/q^8" +"(5,15,11,9)","-q^(-11)-q^(-10)+q^(-8)+q^(-7)-t/q^10+t/q^8" +"(5,12,12,5)","-q^(-9)" +"(5,14,12,7)","-q^(-9)" +"(5,14,12,8)","-(t/q^11)" +"(5,15,12,9)","t/q^13+t/q^12-t/q^9-t/q^8" +"(5,13,13,5)","-(1/(q^10*t))" +"(5,15,13,7)","q^(-8)-1/(q^10*t)" +"(5,15,13,8)","-q^(-12)+t/q^10" +"(5,14,14,5)","t/q^11" +"(5,15,15,5)","q^(-12)" +"(6,0,0,6)","1" +"(6,1,0,7)","t^(-1)" +"(6,1,0,8)","1" +"(6,3,0,10)","1-q^(-2)" +"(6,5,0,11)","1/(q*t)-q/t" +"(6,5,0,12)","1-q^(-2)" +"(6,7,0,14)","1+q^(-3)-q^(-2)-q^(-1)" +"(6,9,0,15)","-t^(-1)+1/(q*t)-q/t+q^2/t" +"(6,1,1,6)","-t^(-1)" +"(6,5,1,10)","-(1/(q*t))+q/t" +"(6,9,1,14)","t^(-1)-1/(q*t)+q/t-q^2/t" +"(6,2,2,6)","t/q^2" +"(6,3,2,7)","q^(-2)" +"(6,4,2,7)","q^(-2)" +"(6,3,2,8)","t/q^2" +"(6,4,2,8)","t/q^2" +"(6,6,2,10)","-(t/q^4)+t/q^2" +"(6,7,2,11)","-q^(-3)+q^(-1)" +"(6,8,2,11)","q^(-3)-q^(-1)" +"(6,7,2,12)","t/q^4-t/q^2" +"(6,8,2,12)","-(t/q^4)+t/q^2" +"(6,10,2,14)","t/q^5-t/q^4-t/q^3+t/q^2" +"(6,11,2,15)","1+q^(-3)-q^(-2)-q^(-1)" +"(6,12,2,15)","1+q^(-3)-q^(-2)-q^(-1)" +"(6,3,3,6)","-q^(-2)" +"(6,5,3,7)","-(1/(q^2*t))" +"(6,5,3,8)","-q^(-2)" +"(6,7,3,10)","q^(-4)+q^(-3)-q^(-2)-q^(-1)" +"(6,9,3,11)","t^(-1)-1/(q^3*t)-1/(q^2*t)+1/(q*t)" +"(6,9,3,12)","q^(-4)+q^(-3)-q^(-2)-q^(-1)" +"(6,11,3,14)","-1-q^(-5)+q^(-3)+q^(-2)" +"(6,13,3,15)","t^(-1)-1/(q^3*t)+1/(q*t)-q^2/t" +"(6,4,4,6)","-q^(-2)" +"(6,5,4,7)","1/(q^2*t)" +"(6,5,4,8)","q^(-2)" +"(6,7,4,10)","-q^(-4)+q^(-2)" +"(6,8,4,10)","-q^(-3)+q^(-1)" +"(6,9,4,11)","-t^(-1)+1/(q^3*t)+1/(q^2*t)-1/(q*t)" +"(6,9,4,12)","-q^(-4)-q^(-3)+q^(-2)+q^(-1)" +"(6,11,4,14)","q^(-5)-2/q^3+q^(-1)" +"(6,12,4,14)","-1-q^(-3)+q^(-2)+q^(-1)" +"(6,13,4,15)","-t^(-1)+1/(q^3*t)-1/(q*t)+q^2/t" +"(6,5,5,6)","1/(q^2*t)" +"(6,9,5,10)","-t^(-1)+1/(q^3*t)+1/(q^2*t)-1/(q*t)" +"(6,13,5,14)","-t^(-1)+1/(q^3*t)-1/(q*t)+q^2/t" +"(6,6,6,6)","-(t/q^4)" +"(6,7,6,7)","q^(-4)" +"(6,8,6,7)","-q^(-4)" +"(6,7,6,8)","t/q^4" +"(6,8,6,8)","-(t/q^4)" +"(6,10,6,10)","t/q^6+t/q^5-t/q^4-t/q^3" +"(6,11,6,11)","-q^(-5)-q^(-4)+q^(-3)+q^(-2)" +"(6,12,6,11)","-q^(-5)-q^(-4)+q^(-3)+q^(-2)" +"(6,11,6,12)","t/q^6+t/q^5-t/q^4-t/q^3" +"(6,12,6,12)","t/q^6+t/q^5-t/q^4-t/q^3" +"(6,14,6,14)","-(t/q^7)+t/q^5+t/q^4-t/q^2" +"(6,15,6,15)","1+q^(-5)-q^(-3)-q^(-2)-t/q^7+t/q^5+t/q^4-t/q^2" +"(6,7,7,6)","q^(-4)" +"(6,9,7,7)","1/(q^4*t)" +"(6,9,7,8)","q^(-4)" +"(6,11,7,10)","-q^(-6)-q^(-5)+q^(-3)+q^(-2)" +"(6,13,7,11)","1/(q^5*t)+1/(q^4*t)-1/(q^2*t)-1/(q*t)" +"(6,13,7,12)","-q^(-6)-q^(-5)+q^(-3)+q^(-2)" +"(6,15,7,14)","1+q^(-7)-q^(-4)-q^(-3)" +"(6,8,8,6)","q^(-4)" +"(6,9,8,7)","1/(q^4*t)" +"(6,9,8,8)","q^(-4)" +"(6,11,8,10)","-q^(-6)+q^(-4)" +"(6,12,8,10)","q^(-5)+q^(-4)-q^(-3)-q^(-2)" +"(6,13,8,11)","1/(q^5*t)+1/(q^4*t)-1/(q^2*t)-1/(q*t)" +"(6,13,8,12)","-q^(-6)-q^(-5)+q^(-3)+q^(-2)" +"(6,15,8,14)","q^(-7)-q^(-5)-q^(-4)+q^(-2)+t/q^7-t/q^5-t/q^4+t/q^2" +"(6,9,9,6)","-(1/(q^4*t))" +"(6,13,9,10)","-(1/(q^5*t))-1/(q^4*t)+1/(q^2*t)+1/(q*t)" +"(6,10,10,6)","t/q^6" +"(6,11,10,7)","q^(-6)" +"(6,12,10,7)","q^(-6)" +"(6,11,10,8)","t/q^6" +"(6,12,10,8)","t/q^6" +"(6,14,10,10)","-(t/q^8)-t/q^7+t/q^5+t/q^4" +"(6,15,10,11)","-q^(-7)-q^(-6)+q^(-4)+q^(-3)+t/q^9+t/q^8-t/q^6-t/q^5" +"(6,15,10,12)","t/q^8+t/q^7-t/q^5-t/q^4-t^2/q^10-t^2/q^9+t^2/q^7+t^2/q^6" +"(6,11,11,6)","-q^(-6)" +"(6,13,11,7)","-(1/(q^6*t))" +"(6,13,11,8)","-q^(-6)" +"(6,15,11,10)","q^(-8)+q^(-7)-q^(-4)-q^(-3)" +"(6,12,12,6)","-q^(-6)" +"(6,13,12,7)","1/(q^6*t)" +"(6,13,12,8)","q^(-6)" +"(6,15,12,10)","-q^(-8)+q^(-6)-t/q^9-t/q^8+t/q^6+t/q^5" +"(6,13,13,6)","1/(q^6*t)" +"(6,14,14,6)","-(t/q^8)" +"(6,15,14,7)","q^(-8)-t/q^10" +"(6,15,14,8)","t/q^8-t^2/q^10" +"(6,15,15,6)","q^(-8)" +"(7,0,0,7)","1" +"(7,1,0,9)","1" +"(7,2,0,10)","-(t/q^2)" +"(7,3,0,11)","1" +"(7,4,0,11)","q^(-1)" +"(7,3,0,12)","-(t/q^2)" +"(7,4,0,12)","-(t/q^2)" +"(7,5,0,13)","1-q^(-1)" +"(7,6,0,14)","t/q^3-t/q^2" +"(7,7,0,15)","1-q^(-1)" +"(7,8,0,15)","-1+q^(-1)" +"(7,1,1,7)","t^(-1)" +"(7,3,1,10)","-q^(-2)" +"(7,4,1,10)","-q^(-1)" +"(7,5,1,11)","1/(q*t)-q/t" +"(7,5,1,12)","-q^(-2)+q^(-1)" +"(7,7,1,14)","q^(-3)-q^(-2)" +"(7,8,1,14)","1-q^(-1)" +"(7,9,1,15)","-t^(-1)+1/(q*t)-q/t+q^2/t" +"(7,2,2,7)","t/q^2" +"(7,3,2,9)","t/q^2" +"(7,4,2,9)","t/q^2" +"(7,6,2,11)","-(t/q^3)+t/q^2" +"(7,7,2,13)","t/q^3-t/q^2" +"(7,8,2,13)","-(t/q^3)+t/q^2" +"(7,3,3,7)","q^(-2)" +"(7,5,3,9)","q^(-2)" +"(7,6,3,10)","t/q^3" +"(7,7,3,11)","-q^(-3)+q^(-2)+q^(-1)" +"(7,8,3,11)","-q^(-2)" +"(7,7,3,12)","-(t/q^3)" +"(7,8,3,12)","t/q^3" +"(7,9,3,13)","-q^(-3)+q^(-1)" +"(7,10,3,14)","-(t/q^4)+t/q^2" +"(7,11,3,15)","1-q^(-2)" +"(7,12,3,15)","1-q^(-2)" +"(7,4,4,7)","q^(-2)" +"(7,5,4,9)","-q^(-2)" +"(7,6,4,10)","-(t/q^4)" +"(7,7,4,11)","-q^(-2)" +"(7,8,4,11)","q^(-3)+q^(-2)-q^(-1)" +"(7,7,4,12)","t/q^4" +"(7,8,4,12)","-(t/q^4)" +"(7,9,4,13)","q^(-3)-q^(-1)" +"(7,10,4,14)","t/q^5-t/q^3" +"(7,11,4,15)","q^(-3)-q^(-1)" +"(7,12,4,15)","q^(-3)-q^(-1)" +"(7,5,5,7)","1/(q^2*t)" +"(7,7,5,10)","-q^(-4)" +"(7,8,5,10)","q^(-2)" +"(7,9,5,11)","-t^(-1)+1/(q^3*t)+1/(q^2*t)-1/(q*t)" +"(7,9,5,12)","-q^(-4)+q^(-2)" +"(7,11,5,14)","q^(-5)-q^(-3)" +"(7,12,5,14)","-1+q^(-2)" +"(7,13,5,15)","-t^(-1)+1/(q^3*t)-1/(q*t)+q^2/t" +"(7,6,6,7)","t/q^4" +"(7,7,6,9)","-(t/q^4)" +"(7,8,6,9)","t/q^4" +"(7,10,6,11)","-(t/q^5)+t/q^3" +"(7,11,6,13)","-(t/q^5)+t/q^3" +"(7,12,6,13)","-(t/q^5)+t/q^3" +"(7,7,7,7)","q^(-4)" +"(7,9,7,9)","q^(-4)" +"(7,10,7,10)","-(t/q^4)" +"(7,11,7,11)","-q^(-5)+q^(-3)+q^(-2)" +"(7,12,7,11)","q^(-3)" +"(7,11,7,12)","-(t/q^4)" +"(7,12,7,12)","-(t/q^4)" +"(7,13,7,13)","-q^(-5)+q^(-2)" +"(7,14,7,14)","t/q^5-t/q^2" +"(7,15,7,15)","1-q^(-3)+t/q^5-t/q^2" +"(7,8,8,7)","q^(-4)" +"(7,9,8,9)","q^(-4)" +"(7,10,8,10)","-(t/q^6)" +"(7,11,8,11)","q^(-4)" +"(7,12,8,11)","q^(-5)+q^(-4)-q^(-2)" +"(7,11,8,12)","-(t/q^6)" +"(7,12,8,12)","-(t/q^6)" +"(7,13,8,13)","-q^(-5)+q^(-2)" +"(7,14,8,14)","t/q^7-t/q^4" +"(7,15,8,15)","-q^(-5)+q^(-2)+t/q^7-t/q^4" +"(7,9,9,7)","1/(q^4*t)" +"(7,11,9,10)","-q^(-6)" +"(7,12,9,10)","-q^(-3)" +"(7,13,9,11)","1/(q^5*t)+1/(q^4*t)-1/(q^2*t)-1/(q*t)" +"(7,13,9,12)","-q^(-6)+q^(-3)" +"(7,15,9,14)","q^(-7)-q^(-4)-t/q^5+t/q^2" +"(7,10,10,7)","t/q^6" +"(7,11,10,9)","t/q^6" +"(7,12,10,9)","t/q^6" +"(7,14,10,11)","-(t/q^7)+t/q^4" +"(7,15,10,13)","t/q^7-t/q^4-t^2/q^9+t^2/q^6" +"(7,11,11,7)","q^(-6)" +"(7,13,11,9)","q^(-6)" +"(7,14,11,10)","t/q^5" +"(7,15,11,11)","-q^(-7)+q^(-4)+q^(-3)-t/q^6" +"(7,15,11,12)","-(t/q^5)+t^2/q^7" +"(7,12,12,7)","q^(-6)" +"(7,13,12,9)","-q^(-6)" +"(7,14,12,10)","-(t/q^8)" +"(7,15,12,11)","-q^(-6)+t/q^9+t/q^8-t/q^5" +"(7,15,12,12)","t/q^8-t^2/q^10" +"(7,13,13,7)","1/(q^6*t)" +"(7,15,13,10)","-q^(-8)+t/q^6" +"(7,14,14,7)","t/q^8" +"(7,15,14,9)","-(t/q^8)+t^2/q^10" +"(7,15,15,7)","q^(-8)" +"(8,0,0,8)","1" +"(8,1,0,9)","-t^(-1)" +"(8,2,0,10)","1" +"(8,3,0,11)","-t^(-1)" +"(8,4,0,11)","-(q/t)" +"(8,3,0,12)","q^(-2)" +"(8,4,0,12)","1" +"(8,5,0,13)","-t^(-1)+1/(q*t)" +"(8,6,0,14)","1-q^(-1)" +"(8,7,0,15)","-t^(-1)+1/(q*t)" +"(8,8,0,15)","-(q/t)+q^2/t" +"(8,1,1,8)","t^(-1)" +"(8,3,1,10)","t^(-1)" +"(8,4,1,10)","q/t" +"(8,5,1,12)","t^(-1)-1/(q*t)" +"(8,7,1,14)","t^(-1)-1/(q*t)" +"(8,8,1,14)","q/t-q^2/t" +"(8,2,2,8)","t/q^2" +"(8,3,2,9)","-q^(-2)" +"(8,4,2,9)","-q^(-2)" +"(8,6,2,11)","-q^(-2)+q^(-1)" +"(8,6,2,12)","t/q^4-t/q^2" +"(8,7,2,13)","-q^(-3)+q^(-2)" +"(8,8,2,13)","q^(-3)-q^(-2)" +"(8,10,2,15)","1+q^(-3)-q^(-2)-q^(-1)" +"(8,3,3,8)","q^(-2)" +"(8,5,3,9)","-(1/(q^2*t))" +"(8,6,3,10)","-q^(-1)" +"(8,7,3,11)","-(1/(q^2*t))" +"(8,8,3,11)","t^(-1)" +"(8,7,3,12)","q^(-4)+q^(-3)-q^(-2)" +"(8,8,3,12)","-q^(-1)" +"(8,9,3,13)","1/(q^3*t)-1/(q*t)" +"(8,10,3,14)","-1+q^(-2)" +"(8,11,3,15)","1/(q^3*t)-1/(q*t)" +"(8,12,3,15)","t^(-1)-q^2/t" +"(8,4,4,8)","q^(-2)" +"(8,5,4,9)","1/(q^2*t)" +"(8,6,4,10)","q^(-2)" +"(8,7,4,11)","1/(q^2*t)" +"(8,8,4,11)","-t^(-1)" +"(8,7,4,12)","-q^(-4)" +"(8,8,4,12)","-q^(-3)+q^(-2)+q^(-1)" +"(8,9,4,13)","-(1/(q^3*t))+1/(q*t)" +"(8,10,4,14)","-q^(-3)+q^(-1)" +"(8,11,4,15)","-(1/(q^3*t))+1/(q*t)" +"(8,12,4,15)","-t^(-1)+q^2/t" +"(8,5,5,8)","1/(q^2*t)" +"(8,7,5,10)","1/(q^2*t)" +"(8,8,5,10)","-t^(-1)" +"(8,9,5,12)","-(1/(q^3*t))+1/(q*t)" +"(8,11,5,14)","-(1/(q^3*t))+1/(q*t)" +"(8,12,5,14)","-t^(-1)+q^2/t" +"(8,6,6,8)","t/q^4" +"(8,7,6,9)","q^(-4)" +"(8,8,6,9)","-q^(-4)" +"(8,10,6,11)","-q^(-4)+q^(-2)" +"(8,10,6,12)","t/q^6+t/q^5-t/q^4-t/q^3" +"(8,11,6,13)","q^(-5)-q^(-3)" +"(8,12,6,13)","q^(-5)-q^(-3)" +"(8,14,6,15)","1+q^(-5)-q^(-3)-q^(-2)" +"(8,7,7,8)","q^(-4)" +"(8,9,7,9)","-(1/(q^4*t))" +"(8,10,7,10)","q^(-2)" +"(8,11,7,11)","-(1/(q^4*t))" +"(8,12,7,11)","-(1/(q*t))" +"(8,11,7,12)","q^(-6)+q^(-5)-q^(-3)" +"(8,12,7,12)","q^(-2)" +"(8,13,7,13)","1/(q^5*t)-1/(q^2*t)" +"(8,14,7,14)","1-q^(-3)" +"(8,15,7,15)","1-q^(-3)+1/(q^5*t)-1/(q^2*t)" +"(8,8,8,8)","q^(-4)" +"(8,9,8,9)","-(1/(q^4*t))" +"(8,10,8,10)","q^(-4)" +"(8,11,8,11)","-(1/(q^4*t))" +"(8,12,8,11)","-(1/(q*t))" +"(8,11,8,12)","q^(-6)" +"(8,12,8,12)","-q^(-5)+q^(-3)+q^(-2)" +"(8,13,8,13)","1/(q^5*t)-1/(q^2*t)" +"(8,14,8,14)","-q^(-5)+q^(-2)" +"(8,15,8,15)","1-q^(-3)+1/(q^5*t)-1/(q^2*t)" +"(8,9,9,8)","1/(q^4*t)" +"(8,11,9,10)","1/(q^4*t)" +"(8,12,9,10)","1/(q*t)" +"(8,13,9,12)","-(1/(q^5*t))+1/(q^2*t)" +"(8,15,9,14)","-1+q^(-3)-1/(q^5*t)+1/(q^2*t)" +"(8,10,10,8)","t/q^6" +"(8,11,10,9)","-q^(-6)" +"(8,12,10,9)","-q^(-6)" +"(8,14,10,11)","-q^(-6)+q^(-3)" +"(8,14,10,12)","t/q^8+t/q^7-t/q^5-t/q^4" +"(8,15,10,13)","-q^(-7)+q^(-4)+t/q^9-t/q^6" +"(8,11,11,8)","q^(-6)" +"(8,13,11,9)","-(1/(q^6*t))" +"(8,14,11,10)","-q^(-3)" +"(8,15,11,11)","q^(-4)-1/(q^6*t)" +"(8,15,11,12)","q^(-8)+q^(-7)-q^(-4)-t/q^5" +"(8,12,12,8)","q^(-6)" +"(8,13,12,9)","1/(q^6*t)" +"(8,14,12,10)","q^(-6)" +"(8,15,12,11)","-q^(-4)+1/(q^6*t)" +"(8,15,12,12)","-q^(-8)-t/q^9+t/q^6+t/q^5" +"(8,13,13,8)","1/(q^6*t)" +"(8,15,13,10)","-q^(-4)+1/(q^6*t)" +"(8,14,14,8)","t/q^8" +"(8,15,14,9)","q^(-8)-t/q^10" +"(8,15,15,8)","q^(-8)" +"(9,0,0,9)","1" +"(9,2,0,11)","1" +"(9,2,0,12)","-(t/q^2)" +"(9,4,0,13)","1-q^(-1)" +"(9,6,0,15)","1-q^(-1)" +"(9,1,1,9)","-t^(-1)" +"(9,3,1,11)","-t^(-1)" +"(9,4,1,11)","-(q/t)" +"(9,3,1,12)","q^(-2)" +"(9,4,1,12)","q^(-1)" +"(9,5,1,13)","-t^(-1)+1/(q*t)" +"(9,7,1,15)","-t^(-1)+1/(q*t)" +"(9,8,1,15)","-(q/t)+q^2/t" +"(9,2,2,9)","t/q^2" +"(9,6,2,13)","t/q^3-t/q^2" +"(9,3,3,9)","-q^(-2)" +"(9,6,3,11)","q^(-1)" +"(9,6,3,12)","-(t/q^3)" +"(9,7,3,13)","-q^(-3)+q^(-2)" +"(9,8,3,13)","-q^(-2)+q^(-1)" +"(9,10,3,15)","1-q^(-2)" +"(9,4,4,9)","-q^(-2)" +"(9,6,4,11)","-q^(-2)" +"(9,6,4,12)","t/q^4" +"(9,8,4,13)","q^(-3)-q^(-1)" +"(9,10,4,15)","q^(-3)-q^(-1)" +"(9,5,5,9)","1/(q^2*t)" +"(9,7,5,11)","1/(q^2*t)" +"(9,8,5,11)","-t^(-1)" +"(9,7,5,12)","-q^(-4)" +"(9,8,5,12)","q^(-2)" +"(9,9,5,13)","-(1/(q^3*t))+1/(q*t)" +"(9,11,5,15)","-(1/(q^3*t))+1/(q*t)" +"(9,12,5,15)","-t^(-1)+q^2/t" +"(9,6,6,9)","-(t/q^4)" +"(9,10,6,13)","-(t/q^5)+t/q^3" +"(9,7,7,9)","q^(-4)" +"(9,10,7,11)","q^(-2)" +"(9,10,7,12)","-(t/q^4)" +"(9,11,7,13)","q^(-5)-q^(-3)" +"(9,12,7,13)","-q^(-3)+q^(-2)" +"(9,14,7,15)","1-q^(-3)" +"(9,8,8,9)","q^(-4)" +"(9,10,8,11)","q^(-4)" +"(9,10,8,12)","-(t/q^6)" +"(9,12,8,13)","-q^(-5)+q^(-2)" +"(9,14,8,15)","-q^(-5)+q^(-2)" +"(9,9,9,9)","-(1/(q^4*t))" +"(9,11,9,11)","-(1/(q^4*t))" +"(9,12,9,11)","-(1/(q*t))" +"(9,11,9,12)","q^(-6)" +"(9,12,9,12)","q^(-3)" +"(9,13,9,13)","1/(q^5*t)-1/(q^2*t)" +"(9,15,9,15)","1-q^(-3)+1/(q^5*t)-1/(q^2*t)" +"(9,10,10,9)","t/q^6" +"(9,14,10,13)","t/q^7-t/q^4" +"(9,11,11,9)","-q^(-6)" +"(9,14,11,11)","q^(-3)" +"(9,14,11,12)","-(t/q^5)" +"(9,15,11,13)","-q^(-7)+q^(-4)-t/q^6+t/q^5" +"(9,12,12,9)","-q^(-6)" +"(9,14,12,11)","-q^(-6)" +"(9,14,12,12)","t/q^8" +"(9,15,12,13)","t/q^9-t/q^5" +"(9,13,13,9)","1/(q^6*t)" +"(9,15,13,11)","-q^(-4)+1/(q^6*t)" +"(9,15,13,12)","-q^(-8)+t/q^6" +"(9,14,14,9)","-(t/q^8)" +"(9,15,15,9)","q^(-8)" +"(10,0,0,10)","1" +"(10,1,0,11)","-(q/t)" +"(10,1,0,12)","1" +"(10,3,0,14)","1-q^(-1)" +"(10,5,0,15)","-(q/t)+q^2/t" +"(10,1,1,10)","q/t" +"(10,5,1,14)","q/t-q^2/t" +"(10,2,2,10)","-(t/q^2)" +"(10,3,2,11)","q^(-1)" +"(10,4,2,11)","q^(-1)" +"(10,3,2,12)","-(t/q^2)" +"(10,4,2,12)","-(t/q^2)" +"(10,6,2,14)","t/q^3-t/q^2" +"(10,7,2,15)","1-q^(-1)" +"(10,8,2,15)","-1+q^(-1)" +"(10,3,3,10)","-q^(-1)" +"(10,5,3,11)","t^(-1)" +"(10,5,3,12)","-q^(-1)" +"(10,7,3,14)","-1+q^(-2)" +"(10,9,3,15)","t^(-1)-q^2/t" +"(10,4,4,10)","-q^(-1)" +"(10,5,4,11)","-t^(-1)" +"(10,5,4,12)","q^(-1)" +"(10,7,4,14)","-q^(-2)+q^(-1)" +"(10,8,4,14)","1-q^(-1)" +"(10,9,4,15)","-t^(-1)+q^2/t" +"(10,5,5,10)","-t^(-1)" +"(10,9,5,14)","-t^(-1)+q^2/t" +"(10,6,6,10)","t/q^3" +"(10,7,6,11)","q^(-2)" +"(10,8,6,11)","-q^(-2)" +"(10,7,6,12)","-(t/q^3)" +"(10,8,6,12)","t/q^3" +"(10,10,6,14)","-(t/q^4)+t/q^2" +"(10,11,6,15)","1-q^(-2)" +"(10,12,6,15)","1-q^(-2)" +"(10,7,7,10)","q^(-2)" +"(10,9,7,11)","-(1/(q*t))" +"(10,9,7,12)","q^(-2)" +"(10,11,7,14)","1-q^(-3)" +"(10,13,7,15)","-(1/(q*t))+q^2/t" +"(10,8,8,10)","q^(-2)" +"(10,9,8,11)","-(1/(q*t))" +"(10,9,8,12)","q^(-2)" +"(10,11,8,14)","-q^(-3)+q^(-2)" +"(10,12,8,14)","-1+q^(-2)" +"(10,13,8,15)","-(1/(q*t))+q^2/t" +"(10,9,9,10)","1/(q*t)" +"(10,13,9,14)","1/(q*t)-q^2/t" +"(10,10,10,10)","-(t/q^4)" +"(10,11,10,11)","q^(-3)" +"(10,12,10,11)","q^(-3)" +"(10,11,10,12)","-(t/q^4)" +"(10,12,10,12)","-(t/q^4)" +"(10,14,10,14)","t/q^5-t/q^2" +"(10,15,10,15)","1-q^(-3)+t/q^5-t/q^2" +"(10,11,11,10)","-q^(-3)" +"(10,13,11,11)","1/(q^2*t)" +"(10,13,11,12)","-q^(-3)" +"(10,15,11,14)","-1+q^(-4)" +"(10,12,12,10)","-q^(-3)" +"(10,13,12,11)","-(1/(q^2*t))" +"(10,13,12,12)","q^(-3)" +"(10,15,12,14)","-q^(-4)+q^(-3)-t/q^5+t/q^2" +"(10,13,13,10)","-(1/(q^2*t))" +"(10,14,14,10)","t/q^5" +"(10,15,14,11)","q^(-4)-t/q^6" +"(10,15,14,12)","-(t/q^5)+t^2/q^7" +"(10,15,15,10)","q^(-4)" +"(11,0,0,11)","1" +"(11,1,0,13)","1" +"(11,2,0,14)","t/q^2" +"(11,3,0,15)","1" +"(11,4,0,15)","1" +"(11,1,1,11)","-(q/t)" +"(11,3,1,14)","-q^(-1)" +"(11,4,1,14)","-1" +"(11,5,1,15)","-(q/t)+q^2/t" +"(11,2,2,11)","-(t/q^2)" +"(11,3,2,13)","-(t/q^2)" +"(11,4,2,13)","-(t/q^2)" +"(11,3,3,11)","q^(-1)" +"(11,5,3,13)","q^(-1)" +"(11,6,3,14)","-(t/q^2)" +"(11,7,3,15)","1" +"(11,8,3,15)","-1" +"(11,4,4,11)","q^(-1)" +"(11,5,4,13)","-q^(-1)" +"(11,6,4,14)","t/q^3" +"(11,7,4,15)","-q^(-1)" +"(11,8,4,15)","q^(-1)" +"(11,5,5,11)","-t^(-1)" +"(11,7,5,14)","-q^(-2)" +"(11,8,5,14)","1" +"(11,9,5,15)","-t^(-1)+q^2/t" +"(11,6,6,11)","-(t/q^3)" +"(11,7,6,13)","t/q^3" +"(11,8,6,13)","-(t/q^3)" +"(11,7,7,11)","q^(-2)" +"(11,9,7,13)","q^(-2)" +"(11,10,7,14)","t/q^2" +"(11,11,7,15)","1" +"(11,12,7,15)","1" +"(11,8,8,11)","q^(-2)" +"(11,9,8,13)","q^(-2)" +"(11,10,8,14)","t/q^4" +"(11,11,8,15)","q^(-2)" +"(11,12,8,15)","q^(-2)" +"(11,9,9,11)","-(1/(q*t))" +"(11,11,9,14)","-q^(-3)" +"(11,12,9,14)","-1" +"(11,13,9,15)","-(1/(q*t))+q^2/t" +"(11,10,10,11)","-(t/q^4)" +"(11,11,10,13)","-(t/q^4)" +"(11,12,10,13)","-(t/q^4)" +"(11,11,11,11)","q^(-3)" +"(11,13,11,13)","q^(-3)" +"(11,14,11,14)","-(t/q^2)" +"(11,15,11,15)","1-t/q^2" +"(11,12,12,11)","q^(-3)" +"(11,13,12,13)","-q^(-3)" +"(11,14,12,14)","t/q^5" +"(11,15,12,15)","-q^(-3)+t/q^5" +"(11,13,13,11)","-(1/(q^2*t))" +"(11,15,13,14)","-q^(-4)+t/q^2" +"(11,14,14,11)","-(t/q^5)" +"(11,15,14,13)","t/q^5-t^2/q^7" +"(11,15,15,11)","q^(-4)" +"(12,0,0,12)","1" +"(12,1,0,13)","q/t" +"(12,2,0,14)","1" +"(12,3,0,15)","q/t" +"(12,4,0,15)","q^2/t" +"(12,1,1,12)","-(q/t)" +"(12,3,1,14)","-(q/t)" +"(12,4,1,14)","-(q^2/t)" +"(12,2,2,12)","-(t/q^2)" +"(12,3,2,13)","-q^(-1)" +"(12,4,2,13)","-q^(-1)" +"(12,6,2,15)","1-q^(-1)" +"(12,3,3,12)","q^(-1)" +"(12,5,3,13)","t^(-1)" +"(12,6,3,14)","-1" +"(12,7,3,15)","t^(-1)" +"(12,8,3,15)","-(q^2/t)" +"(12,4,4,12)","q^(-1)" +"(12,5,4,13)","-t^(-1)" +"(12,6,4,14)","q^(-1)" +"(12,7,4,15)","-t^(-1)" +"(12,8,4,15)","q^2/t" +"(12,5,5,12)","-t^(-1)" +"(12,7,5,14)","-t^(-1)" +"(12,8,5,14)","q^2/t" +"(12,6,6,12)","-(t/q^3)" +"(12,7,6,13)","q^(-2)" +"(12,8,6,13)","-q^(-2)" +"(12,10,6,15)","1-q^(-2)" +"(12,7,7,12)","q^(-2)" +"(12,9,7,13)","1/(q*t)" +"(12,10,7,14)","1" +"(12,11,7,15)","1/(q*t)" +"(12,12,7,15)","q^2/t" +"(12,8,8,12)","q^(-2)" +"(12,9,8,13)","1/(q*t)" +"(12,10,8,14)","q^(-2)" +"(12,11,8,15)","1/(q*t)" +"(12,12,8,15)","q^2/t" +"(12,9,9,12)","-(1/(q*t))" +"(12,11,9,14)","-(1/(q*t))" +"(12,12,9,14)","-(q^2/t)" +"(12,10,10,12)","-(t/q^4)" +"(12,11,10,13)","-q^(-3)" +"(12,12,10,13)","-q^(-3)" +"(12,14,10,15)","1-q^(-3)" +"(12,11,11,12)","q^(-3)" +"(12,13,11,13)","1/(q^2*t)" +"(12,14,11,14)","-1" +"(12,15,11,15)","-1+1/(q^2*t)" +"(12,12,12,12)","q^(-3)" +"(12,13,12,13)","-(1/(q^2*t))" +"(12,14,12,14)","q^(-3)" +"(12,15,12,15)","1-1/(q^2*t)" +"(12,13,13,12)","-(1/(q^2*t))" +"(12,15,13,14)","1-1/(q^2*t)" +"(12,14,14,12)","-(t/q^5)" +"(12,15,14,13)","q^(-4)-t/q^6" +"(12,15,15,12)","q^(-4)" +"(13,0,0,13)","1" +"(13,2,0,15)","1" +"(13,1,1,13)","q/t" +"(13,3,1,15)","q/t" +"(13,4,1,15)","q^2/t" +"(13,2,2,13)","-(t/q^2)" +"(13,3,3,13)","-q^(-1)" +"(13,6,3,15)","1" +"(13,4,4,13)","-q^(-1)" +"(13,6,4,15)","-q^(-1)" +"(13,5,5,13)","-t^(-1)" +"(13,7,5,15)","-t^(-1)" +"(13,8,5,15)","q^2/t" +"(13,6,6,13)","t/q^3" +"(13,7,7,13)","q^(-2)" +"(13,10,7,15)","1" +"(13,8,8,13)","q^(-2)" +"(13,10,8,15)","q^(-2)" +"(13,9,9,13)","1/(q*t)" +"(13,11,9,15)","1/(q*t)" +"(13,12,9,15)","q^2/t" +"(13,10,10,13)","-(t/q^4)" +"(13,11,11,13)","-q^(-3)" +"(13,14,11,15)","1" +"(13,12,12,13)","-q^(-3)" +"(13,14,12,15)","-q^(-3)" +"(13,13,13,13)","-(1/(q^2*t))" +"(13,15,13,15)","1-1/(q^2*t)" +"(13,14,14,13)","t/q^5" +"(13,15,15,13)","q^(-4)" +"(14,0,0,14)","1" +"(14,1,0,15)","q^2/t" +"(14,1,1,14)","-(q^2/t)" +"(14,2,2,14)","t/q^2" +"(14,3,2,15)","1" +"(14,4,2,15)","1" +"(14,3,3,14)","-1" +"(14,5,3,15)","-(q^2/t)" +"(14,4,4,14)","-1" +"(14,5,4,15)","q^2/t" +"(14,5,5,14)","q^2/t" +"(14,6,6,14)","-(t/q^2)" +"(14,7,6,15)","1" +"(14,8,6,15)","-1" +"(14,7,7,14)","1" +"(14,9,7,15)","q^2/t" +"(14,8,8,14)","1" +"(14,9,8,15)","q^2/t" +"(14,9,9,14)","-(q^2/t)" +"(14,10,10,14)","t/q^2" +"(14,11,10,15)","1" +"(14,12,10,15)","1" +"(14,11,11,14)","-1" +"(14,13,11,15)","-(q^2/t)" +"(14,12,12,14)","-1" +"(14,13,12,15)","q^2/t" +"(14,13,13,14)","q^2/t" +"(14,14,14,14)","-(t/q^2)" +"(14,15,14,15)","1-t/q^2" +"(14,15,15,14)","1" +"(15,0,0,15)","1" +"(15,1,1,15)","q^2/t" +"(15,2,2,15)","t/q^2" +"(15,3,3,15)","1" +"(15,4,4,15)","1" +"(15,5,5,15)","q^2/t" +"(15,6,6,15)","t/q^2" +"(15,7,7,15)","1" +"(15,8,8,15)","1" +"(15,9,9,15)","q^2/t" +"(15,10,10,15)","t/q^2" +"(15,11,11,15)","1" +"(15,12,12,15)","1" +"(15,13,13,15)","q^2/t" +"(15,14,14,15)","t/q^2" +"(15,15,15,15)","1" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/hn.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/hn.csv new file mode 100644 index 0000000..1f0b31f --- /dev/null +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/hn.csv @@ -0,0 +1,17 @@ +"(16,16)","LaurentPolynomial" +"(0,0)","1" +"(1,1)","-1" +"(2,2)","-1" +"(3,3)","1" +"(4,4)","1" +"(5,5)","-1" +"(6,6)","-1" +"(7,7)","1" +"(8,8)","1" +"(9,9)","-1" +"(10,10)","-1" +"(11,11)","1" +"(12,12)","1" +"(13,13)","-1" +"(14,14)","-1" +"(15,15)","1" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/hp.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/hp.csv new file mode 100644 index 0000000..1f0b31f --- /dev/null +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/hp.csv @@ -0,0 +1,17 @@ +"(16,16)","LaurentPolynomial" +"(0,0)","1" +"(1,1)","-1" +"(2,2)","-1" +"(3,3)","1" +"(4,4)","1" +"(5,5)","-1" +"(6,6)","-1" +"(7,7)","1" +"(8,8)","1" +"(9,9)","-1" +"(10,10)","-1" +"(11,11)","1" +"(12,12)","1" +"(13,13)","-1" +"(14,14)","-1" +"(15,15)","1" diff --git a/spherogram_src/links/reshetikhin_turaev/__init__.py b/spherogram_src/links/reshetikhin_turaev/__init__.py new file mode 100644 index 0000000..1d0507b --- /dev/null +++ b/spherogram_src/links/reshetikhin_turaev/__init__.py @@ -0,0 +1,6 @@ +from .RT_network import RTNetwork +from .dict_laurent_polynomial import DictLaurentPolynomial +from .R_matrices import RMatrix, colored_links_gould_R_matrices +from .sparse_array import SparseArray, SparseTensor + +__all__ = ['RTNetwork', 'RMatrix', 'DictLaurentPolynomial', 'SparseArray', 'SparseTensor','colored_links_gould_R_matrices'] \ No newline at end of file diff --git a/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py new file mode 100644 index 0000000..ece9209 --- /dev/null +++ b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py @@ -0,0 +1,434 @@ +from ...sage_helper import _within_sage, sage_method + +if _within_sage: + from sage.all import LaurentPolynomialRing, ZZ + +@sage_method +def laurent_poly_from_dict(dict, vars, F = ZZ): + L = LaurentPolynomialRing(F, vars) + return L(dict) + +import re + +class LaurentVariable: + """ + A named variable with an optional denominator. + Exponent key k represents var^(k / denominator). + + Examples: + LaurentVariable('q') # q^k for integer k + LaurentVariable('q', 2) # q^(k/2), so key 1 means q^(1/2) + """ + __slots__ = ['name', 'denominator'] + + def __init__(self, name, denominator=1): + self.name = name + self.denominator = denominator + + def __eq__(self, other): + if isinstance(other, LaurentVariable): + return self.name == other.name and self.denominator == other.denominator + return NotImplemented + + def __hash__(self): + return hash((self.name, self.denominator)) + + def __repr__(self): + if self.denominator == 1: + return self.name + return f'{self.name}[1/{self.denominator}]' + + def fmt_exp(self, k): + """Format exponent k as a string for display.""" + if k == 0: + return None + num, den = k, self.denominator + g = _gcd(abs(num), den) + num, den = num // g, den // g + if den == 1: + if num == 1: return '' + if num > 1: return f'^{num}' + return f'^({num})' + return f'^({num}/{den})' + +def _gcd(a, b): + while b: + a, b = b, a % b + return a + +_vars_cache = {} + +def _intern_vars(vars_tuple): + return _vars_cache.setdefault(vars_tuple, vars_tuple) + +class DictLaurentPolynomial: + """ + A sparse Laurent polynomial in arbitrarily many variables. + + Represented as a dict mapping exponent tuples (of integers) to nonzero + coefficients. Each variable's denominator is encoded in its LaurentVariable, + so exponent key k for variable v means v^(k / v.denominator). + """ + __slots__ = ['vars', 'poly_dict'] + + def __init__(self, vars, poly_dict): + self.vars = _intern_vars(tuple( + v if isinstance(v, LaurentVariable) else LaurentVariable(*((v,) if isinstance(v, str) else v)) + for v in vars + )) + self.poly_dict = {k: v for k, v in poly_dict.items() if v != 0} + + @sage_method + def to_sage(self): + if all(var.denominator == 1 for var in self.vars): + return laurent_poly_from_dict(self.poly_dict, [var.name for var in self.vars]) + else: + raise NotImplementedError('Can only convert DictLaurentPolynomial with integer exponentials to LaurentPolynomial in Sage.') + + @classmethod + def _make(cls, vars, poly_dict): + """Construct without cleaning — caller guarantees no zero values.""" + obj = object.__new__(cls) + obj.vars = _intern_vars(vars if isinstance(vars, tuple) else tuple(vars)) + obj.poly_dict = poly_dict + return obj + + @classmethod + def generator(cls, vars, index=0): + """ + Return the unit monomial for the variable at `index`. + Exponent key 1 represents var^(1/denominator). + + Example: + q = LaurentVariable('q', 2) + gen = DictLaurentPolynomial.generator([q]) + # gen represents q^(1/2); gen**3 represents q^(3/2) + """ + exp = tuple(1 if i == index else 0 for i in range(len(vars))) + return cls._make(vars, {exp: 1}) + + def __bool__(self): + return bool(self.poly_dict) + + def __eq__(self, other): + if isinstance(other, DictLaurentPolynomial): + return self.poly_dict == other.poly_dict + if other == 0: + return not self.poly_dict + return NotImplemented + + __hash__ = None + + def __neg__(self): + return DictLaurentPolynomial._make( + self.vars, {k: -v for k, v in self.poly_dict.items()}) + + def __add__(self, other): + if isinstance(other, DictLaurentPolynomial): + result = dict(self.poly_dict) + for k, v in other.poly_dict.items(): + if k in result: + s = result[k] + v + if s: + result[k] = s + else: + del result[k] + else: + result[k] = v + return DictLaurentPolynomial._make(self.vars, result) + if other == 0: + return DictLaurentPolynomial._make(self.vars, dict(self.poly_dict)) + zero_key = (0,) * len(self.vars) + result = dict(self.poly_dict) + s = result.get(zero_key, 0) + other + if s: + result[zero_key] = s + else: + result.pop(zero_key, None) + return DictLaurentPolynomial._make(self.vars, result) + + def __radd__(self, other): + return self.__add__(other) + + def __sub__(self, other): + if isinstance(other, DictLaurentPolynomial): + result = dict(self.poly_dict) + for k, v in other.poly_dict.items(): + if k in result: + s = result[k] - v + if s: + result[k] = s + else: + del result[k] + else: + result[k] = -v + return DictLaurentPolynomial._make(self.vars, result) + return self.__add__(-other) + + def __rsub__(self, other): + return (-self).__add__(other) + + def __mul__(self, other): + if isinstance(other, DictLaurentPolynomial): + result = {} + for k1, v1 in self.poly_dict.items(): + for k2, v2 in other.poly_dict.items(): + k = tuple(a + b for a, b in zip(k1, k2)) + prod = v1 * v2 + if k in result: + s = result[k] + prod + if s: + result[k] = s + else: + del result[k] + else: + result[k] = prod + return DictLaurentPolynomial._make(self.vars, result) + if other == 0: + return DictLaurentPolynomial._make(self.vars, {}) + if other == 1: + return DictLaurentPolynomial._make(self.vars, dict(self.poly_dict)) + return DictLaurentPolynomial._make( + self.vars, {k: v * other for k, v in self.poly_dict.items()}) + + def __rmul__(self, other): + return self.__mul__(other) + + def change_vars(self, rules): + """ + Return a new DictLaurentPolynomial with variables substituted by rules. + + rules must have the same length as self.vars. Exponent keys are + rescaled when denominators change: exponent k (meaning var^(k/old_denom)) + rules: dict mapping LaurentVariable -> DictLaurentPolynomial, + or a list of DictLaurentPolynomials (one per variable, in order). + + Each variable is substituted by the corresponding polynomial. + Variables absent from a dict-style rules are kept as themselves + (identity substitution). + + Examples: + q = LaurentVariable('q', 2) + t = LaurentVariable('t', 1) + t_half = DictLaurentPolynomial.generator([t]) + p.change_vars({q: t_half}) # substitute q^(1/2) -> t + """ + if isinstance(rules, list): + rules = dict(zip(self.vars, rules)) + + # Build identity images for variables not in rules. + full_rules = {} + for i, var in enumerate(self.vars): + if var in rules: + full_rules[var] = rules[var] + else: + full_rules[var] = DictLaurentPolynomial.generator(self.vars, index=i) + + result = None + for key, coef in self.poly_dict.items(): + term = None + for var, k in zip(self.vars, key): + if k == 0: + continue + factor = full_rules[var] ** k + term = factor if term is None else term * factor + + if term is None: + zero_key = (0,) * len(next(iter(full_rules.values())).vars) + term = DictLaurentPolynomial._make( + next(iter(full_rules.values())).vars, {zero_key: coef}) + else: + term = term * coef + + result = term if result is None else result + term + + if result is None: + img = next(iter(full_rules.values())) + return DictLaurentPolynomial._make(img.vars, {}) + return result + + @staticmethod + def _preprocess_division(s, sym_names): + """Expand /var and /(var1*var2*...) into *var^(-1)*... so the main parser handles it.""" + sorted_syms = sorted(sym_names, key=len, reverse=True) + sym_alt = '|'.join(re.escape(sym) for sym in sorted_syms) + exp_pat = r'(?:\^(?:\([+-]?\d+(?:/\d+)?\)|[+-]?\d+))?' + factor_pat = r'(?:' + sym_alt + r')' + exp_pat + product_pat = factor_pat + r'(?:\*' + factor_pat + r')*' + denom_re = re.compile(r'/\((' + product_pat + r')\)|/(' + factor_pat + r')') + factor_re = re.compile(r'(' + sym_alt + r')(' + exp_pat + r')') + + def negate_exp(exp): + if not exp: + return '^(-1)' + inner = exp[1:] # strip '^' + if inner.startswith('(') and inner.endswith(')'): + inner = inner[1:-1] + if inner.startswith('-'): + inner = inner[1:] + return f'^({inner})' if '/' in inner else f'^{inner}' + return f'^(-{inner})' + + def replace_denom(m): + content = m.group(1) if m.group(1) is not None else m.group(2) + result = [] + for part in content.split('*'): + fm = factor_re.fullmatch(part) + if fm is None: + raise ValueError(f'Cannot parse denominator factor: {part!r}') + result.append(fm.group(1) + negate_exp(fm.group(2))) + return '*' + '*'.join(result) + + return denom_re.sub(replace_denom, s) + + @classmethod + def from_str(cls, s, vars): + """ + Parse a string into a DictLaurentPolynomial. + + vars: list of variable name strings, e.g. ['q'] or ['q', 't']. + The denominator for each LaurentVariable is the LCM of all + denominators appearing in its exponents in the string. + + Supported formats: + 'q^(1/2) + q^(-3/2) - 2' + '2*q^(1/2) - q^(-1) + 3*q^2' + 'q^2*t^(-1/3) + 1' + '1 - 1/(q*t)' + '(q - 1) / q' + """ + def lcm(a, b): + return a * b // _gcd(a, b) + def lcm_list(lst): + r = 1 + for x in lst: + r = lcm(r, x) + return r + + s = s.replace(' ', '') + s = cls._preprocess_division(s, vars) + + # Split at + or - that are not inside parentheses. + term_strs = [] + depth = 0 + start = 0 + for i, c in enumerate(s): + if c == '(': + depth += 1 + elif c == ')': + depth -= 1 + elif c in '+-' and depth == 0 and i > start: + term_strs.append(s[start:i]) + start = i + term_strs.append(s[start:]) + term_strs = [t for t in term_strs if t] + + # Pattern for each variable: sym optionally followed by ^(num/den) or ^num. + sym_pats = { + sym: re.compile( + re.escape(sym) + + r'(?:\^(?:\(([+-]?\d+)(?:/(\d+))?\)|([+-]?\d+)))?' + ) + for sym in vars + } + + parsed = [] # [(coef, {sym: (num, den)})] + all_denoms = {sym: {1} for sym in vars} + + for ts in term_strs: + # Extract leading coefficient (handles: 2*, -3*, -, +, 2, -2). + m = re.match(r'^([+-]?\d*)\*?', ts) + prefix = m.group(1) + rest = ts[m.end():] + coef = 1 if prefix in ('', '+') else (-1 if prefix == '-' else int(prefix)) + + var_exps = {} + for sym in vars: + pm = sym_pats[sym].search(rest) + if pm: + if pm.group(1) is not None: + num = int(pm.group(1)) + den = int(pm.group(2)) if pm.group(2) else 1 + elif pm.group(3) is not None: + num, den = int(pm.group(3)), 1 + else: + num, den = 1, 1 + var_exps[sym] = (num, den) + all_denoms[sym].add(den) + + parsed.append((coef, var_exps)) + + var_lcms = {sym: lcm_list(all_denoms[sym]) for sym in vars} + vars_list = [LaurentVariable(sym, var_lcms[sym]) for sym in vars] + + poly_dict = {} + for coef, var_exps in parsed: + key = tuple( + var_exps[sym][0] * (var_lcms[sym] // var_exps[sym][1]) + if sym in var_exps else 0 + for sym in vars + ) + if key in poly_dict: + new_v = poly_dict[key] + coef + if new_v: + poly_dict[key] = new_v + else: + del poly_dict[key] + else: + poly_dict[key] = coef + + return cls._make(vars_list, poly_dict) + + def __pow__(self, n): + if not isinstance(n, int): + raise ValueError(f'exponent must be an integer, got {n!r}') + if n == 0: + zero_key = (0,) * len(self.vars) + return DictLaurentPolynomial._make(self.vars, {zero_key: 1}) + if n < 0: + if len(self.poly_dict) != 1: + raise ValueError('negative powers only supported for monomials') + (key, coef), = self.poly_dict.items() + inv_key = tuple(k * n for k in key) + inv_coef = coef ** n # works when coef is ±1 or a symbolic type + return DictLaurentPolynomial._make(self.vars, {inv_key: inv_coef}) + result = self + base = self + n -= 1 + while n: + if n & 1: + result = result * base + base = base * base + n >>= 1 + return result + + def __repr__(self): + if not self.poly_dict: + return '0' + # Sort by total degree descending (exact rational arithmetic), then lex descending. + L = 1 + for var in self.vars: + L = L * var.denominator // _gcd(L, var.denominator) + scales = tuple(L // var.denominator for var in self.vars) + def _sort_key(item): + exp = item[0] + total = sum(k * s for k, s in zip(exp, scales)) + return (-total, tuple(-k for k in exp)) + terms = [] + for exp, coef in sorted(self.poly_dict.items(), key=_sort_key): + parts = [] + for var, k in zip(self.vars, exp): + fmt = var.fmt_exp(k) + if fmt is not None: + parts.append(var.name + fmt) + monomial = ''.join(parts) + if not monomial: + terms.append(str(coef)) + elif coef == 1: + terms.append(monomial) + elif coef == -1: + terms.append(f'-{monomial}') + else: + terms.append(f'{coef}*{monomial}') + return ' + '.join(terms).replace('+ -', '- ') + diff --git a/spherogram_src/links/reshetikhin_turaev/reshetikhin_turaev.py b/spherogram_src/links/reshetikhin_turaev/reshetikhin_turaev.py deleted file mode 100644 index 77e3b11..0000000 --- a/spherogram_src/links/reshetikhin_turaev/reshetikhin_turaev.py +++ /dev/null @@ -1,16 +0,0 @@ -from .sparse_array import SparseTensor - -import R_matrices - -# tangle to graph where nodes are SparseTensors and edges are paris of -# tuples (SparseTensor, index in tensor) -# also, create a dictionary of labels of arcs to the edges - -# For curls, do an honest implementation as SparseTensors, so that rot_num -# is only used when creating the tensor network. -# The said dictionary should then use honest lables of edges instead of labels of arcs... - -# The contraction sequence can then still be presented as a list of lists of labels - -# When doing contractions, need to update the info in adjacent edges accordingly...ff - diff --git a/spherogram_src/links/reshetikhin_turaev/sparse_array.py b/spherogram_src/links/reshetikhin_turaev/sparse_array.py index a5d202b..c41d57d 100644 --- a/spherogram_src/links/reshetikhin_turaev/sparse_array.py +++ b/spherogram_src/links/reshetikhin_turaev/sparse_array.py @@ -1,3 +1,5 @@ +from itertools import product as cartesian_product + class SparseArray: """ A sparse array supporting arbitrary-dimensional indexing via tuples. @@ -5,12 +7,14 @@ class SparseArray: Internally stores only non-default entries in a dict keyed by index tuples. Indices can be integers or tuples of integers of any length. """ - __slots__ = ('_data', '_default', '_rank') + __slots__ = ('_data', '_default', '_rank', '_shape') - def __init__(self, data= None, default=0, rank = None): + def __init__(self, shape, data=None, default=0): self._data = {} self._default = default - self._rank = rank + self._shape = tuple(shape) + self._rank = len(self._shape) + if data is not None: if isinstance(data, dict): data = data.items() @@ -30,11 +34,13 @@ def __setitem__(self, index, value): if value == self._default: self._data.pop(key, None) else: - if self._rank is None: - self._rank = len(key) - elif len(key) != self._rank: + if len(key) != self._rank: raise ValueError( f'key length {len(key)} does not match rank {self._rank}') + for i, (idx, dim) in enumerate(zip(key, self._shape)): + if not (0 <= idx < dim): + raise ValueError( + f'index {idx} at axis {i} is out of range [0, {dim})') self._data[key] = value def __delitem__(self, index): @@ -42,8 +48,6 @@ def __delitem__(self, index): if key not in self._data: raise KeyError(index) del self._data[key] - if not self._data: - self._rank = None def __contains__(self, index): return self._key(index) in self._data @@ -73,18 +77,19 @@ def get(self, index, default=None): def clear(self): self._data.clear() - self._rank = None def copy(self): - return SparseArray(data=self._data.copy(), - default=self._default, - rank = self.rank) + return SparseArray(self._shape, data=self._data.copy(), default=self._default) @property def rank(self): """Dimension of the indices.""" return self._rank + @property + def shape(self): + return self._shape + def nonzero_indices(self): """Return list of all indices with non-default values.""" return list(self._data.keys()) @@ -93,8 +98,8 @@ def to_dict(self): return dict(self._data) @classmethod - def from_dict(cls, d, default=0): - result = cls(default=default) + def from_dict(cls, shape, d, default=0): + result = cls(shape, default=default) for k, v in d.items(): result[k] = v return result @@ -113,31 +118,27 @@ def __repr__(self): return f'SparseTensor({self._data!r}, default={self._default!r})' def copy(self): - return SparseTensor(data=self._data.copy(), - default=self._default, - rank=self.rank) - - @property - def rank(self): - """Number of indices (tensor rank)""" - return self._rank + return SparseTensor(self._shape, data=self._data.copy(), default=self._default) def _set(self, key, value): """Write self[key] = value, dropping the entry if it equals default.""" - assert self.rank == len(key) - if value == self._default: self._data.pop(key, None) - if not self._data: - self._rank = None else: self._data[key] = value def _accumulate(self, key, value): """Add value into self[key], maintaining sparsity.""" - self._set(key, self._data.get(key, self._default) + value) + if key in self._data: + result = self._data[key] + value + if not result: + del self._data[key] + else: + self._data[key] = result + else: + self._data[key] = value - def contract(self, other: SparseTensor, pairs): + def contract(self, other: 'SparseTensor', pairs): """ Contract self with other over the specified index pairs, returning a new SparseTensor whose axes are the free axes of self followed by the @@ -157,14 +158,21 @@ def contract(self, other: SparseTensor, pairs): A.contract(B, [(1, 0), (2, 1)]) """ pairs = list(pairs) - if not self._data or not other._data: - return SparseTensor(default=self._default) + for ai, bj in pairs: + if self._shape[ai] != other._shape[bj]: + raise ValueError( + f'axis {ai} of self (size {self._shape[ai]}) is incompatible ' + f'with axis {bj} of other (size {other._shape[bj]})') self_contracted = {ai for ai, _ in pairs} other_contracted = {bj for _, bj in pairs} - self_free = [i for i in range(self.rank) if i not in self_contracted] other_free = [i for i in range(other.rank) if i not in other_contracted] + result_shape = [self._shape[i] for i in self_free] + \ + [other._shape[i] for i in other_free] + + if not self._data or not other._data: + return SparseTensor(result_shape, default=self._default) # Group self entries by their values at the contracted axes (in pairs order). # For each contraction key, we only need to visit other entries that match. @@ -174,19 +182,104 @@ def contract(self, other: SparseTensor, pairs): f_key = tuple(key[i] for i in self_free) self_groups.setdefault(c_key, []).append((f_key, val)) - result = SparseTensor(default=self._default, - rank = len(self_free) + len(other_free)) + result = SparseTensor(result_shape, default=self._default) for key_b, val_b in other.items(): c_key = tuple(key_b[bj] for _, bj in pairs) group = self_groups.get(c_key) if group is None: continue - assert len(group) > 0 - f_key_b = tuple(key_b[i] for i in other_free) for f_key_a, val_a in group: result._accumulate(f_key_a + f_key_b, val_a * val_b) return result + + def decorated_contract(self, other: 'SparseTensor', pairs): + """ + An enhanced version of contract, where: + + pairs: a dictionary whose keys are (self_axis, other_axis) to be summed over, + and values are tuples of the form (0/1, h[i,j]), where h[i,j] is a tensor of rank 2 + the 0 or 1 indicates whether i or j is contracted with self_axis. + + For example, let A[i,j] and B[k,l] be two tensors, then + A.contract(B, {(1,0): (0, h)}) gives C[i,l]:= sum_{j,k} A[i,j] * h[j,k] * B[k,l] + and + A.contract(B, {(1,0): (1, h)}) gives C[i,l]:= sum_{j,k} A[i,j] * h[k,j] * B[k,l] + """ + if self is other: + return self._decorated_trace_pairs(pairs) + + for (ai, bj), (side, h) in pairs.items(): + h_self, h_other = (0, 1) if side == 0 else (1, 0) + if self._shape[ai] != h._shape[h_self]: + raise ValueError( + f'self axis {ai} (size {self._shape[ai]}) is incompatible ' + f'with h axis {h_self} (size {h._shape[h_self]})') + if other._shape[bj] != h._shape[h_other]: + raise ValueError( + f'other axis {bj} (size {other._shape[bj]}) is incompatible ' + f'with h axis {h_other} (size {h._shape[h_other]})') + + pair_list = list(pairs.keys()) + self_contracted = {ai for ai, _ in pair_list} + other_contracted = {bj for _, bj in pair_list} + self_free = [i for i in range(self.rank) if i not in self_contracted] + other_free = [i for i in range(other.rank) if i not in other_contracted] + result_shape = [self._shape[i] for i in self_free] + \ + [other._shape[i] for i in other_free] + + if not self._data or not other._data: + return SparseTensor(result_shape, default=self._default) + + # Group self entries by their contracted-axis values (in pair_list order). + self_groups = {} + for key, val in self._data.items(): + c_key = tuple(key[ai] for ai, _ in pair_list) + f_key = tuple(key[i] for i in self_free) + self_groups.setdefault(c_key, []).append((f_key, val)) + + # For each pair m, precompute: given k (other's contracted value), + # which j values in self are reachable and with what h weight? + # h_lookup[m][k] = [(j, h_val), ...] + h_lookups = [] + for (ai, bj) in pair_list: + side, h = pairs[(ai, bj)] + lookup = {} + for hkey, hval in h._data.items(): + hi, hj = hkey + j, k = (hi, hj) if side == 0 else (hj, hi) + lookup.setdefault(k, []).append((j, hval)) + h_lookups.append(lookup) + + result = SparseTensor(result_shape, default=self._default) + + for key_b, val_b in other._data.items(): + f_key_b = tuple(key_b[i] for i in other_free) + + # For each pair m, collect reachable (j_m, h_val_m) from h_m. + per_pair = [] + for m, (ai, bj) in enumerate(pair_list): + k = key_b[bj] + js = h_lookups[m].get(k) + if not js: + break + per_pair.append(js) + else: + # Cartesian product: try every combination of j values across pairs. + for combo in cartesian_product(*per_pair): + c_key = tuple(j for j, _ in combo) + group = self_groups.get(c_key) + if group is None: + continue + h_weight = 1 + for _, hval in combo: + h_weight *= hval + if h_weight == self._default: + continue + for f_key_a, val_a in group: + result._accumulate(f_key_a + f_key_b, val_a * h_weight * val_b) + + return result def trace(self, i, j): """ @@ -195,21 +288,113 @@ def trace(self, i, j): Example: T[i,j,k].trace(0, 2) -> result[j] = sum_k T[k, j, k] """ - assert self.rank - 2 >= 0 - if not self._data: - return SparseTensor(default=self._default, - rank = self.rank - 2) - + assert self.rank >= 2 n = self.rank i, j = i % n, j % n if i == j: raise ValueError("trace indices must be distinct") - result = SparseTensor(default=self._default, - rank = self.rank - 2) + result_shape = [v for idx, v in enumerate(self._shape) if idx != i and idx != j] + if not self._data: + return SparseTensor(result_shape, default=self._default) + + result = SparseTensor(result_shape, default=self._default) for key, value in self.items(): if key[i] != key[j]: continue free_key = tuple(v for idx, v in enumerate(key) if idx != i and idx != j) result._accumulate(free_key, value) - return result \ No newline at end of file + return result + + def decorated_trace(self, i, j, decoration): + """ + Contract axes i and j of self with an edge tensor h inserted between + them, returning a SparseTensor of rank reduced by 2. + + decoration: (side, h) where h is a rank-2 SparseTensor. + side=0: result[free] = sum_{a,b} self[...,a at i,...,b at j,...] * h[a,b] + side=1: result[free] = sum_{a,b} self[...,a at i,...,b at j,...] * h[b,a] + + Example: A[i,j,k].decorated_trace(0, 2, (0, h)) + -> result[j] = sum_{i,k} A[i,j,k] * h[i,k] + """ + return self._decorated_trace_pairs({(i, j): decoration}) + + def _decorated_trace_pairs(self, pairs): + """ + Multi-pair decorated trace. All axis indices refer to self's original axes. + + pairs: dict {(i, j): (side, h)} — each entry contracts axes i and j of + self via the edge tensor h, simultaneously in a single pass. + """ + contracted = set() + for i, j in pairs: + n = self.rank + i, j = i % n, j % n + if i == j: + raise ValueError("trace indices must be distinct") + for ax in (i, j): + if ax in contracted: + raise ValueError(f"axis {ax} appears in more than one pair") + contracted.add(ax) + + for (i, j), (side, h) in pairs.items(): + h_i, h_j = (0, 1) if side == 0 else (1, 0) + if self._shape[i] != h._shape[h_i]: + raise ValueError( + f'axis {i} of self (size {self._shape[i]}) is incompatible ' + f'with h axis {h_i} (size {h._shape[h_i]})') + if self._shape[j] != h._shape[h_j]: + raise ValueError( + f'axis {j} of self (size {self._shape[j]}) is incompatible ' + f'with h axis {h_j} (size {h._shape[h_j]})') + + free = [idx for idx in range(self.rank) if idx not in contracted] + result_shape = [self._shape[idx] for idx in free] + result = SparseTensor(result_shape, default=self._default) + + for key, val in self._data.items(): + weight = 1 + for (i, j), (side, h) in pairs.items(): + a, b = key[i], key[j] + h_val = h[a, b] if side == 0 else h[b, a] + if h_val == h._default: + weight = 0 + break + weight *= h_val + if weight == 0: + continue + free_key = tuple(key[idx] for idx in free) + result._accumulate(free_key, val * weight) + + return result + + def fixate(self, i, value): + """ + Set index i to a fixed value, obtaining a new tensor with one less rank. + + T[i,j,k,l].fixate(i, 0) -> T[0,j,k,l] + """ + n = self.rank + i = i % n + result_shape = [v for idx, v in enumerate(self._shape) if idx != i] + result = SparseTensor(result_shape, default=self._default) + for key, val in self._data.items(): + if key[i] != value: + continue + free_key = tuple(v for idx, v in enumerate(key) if idx != i) + result._data[free_key] = val + return result + + def permute(self, indices): + """ + Permute self into the desired order. + + A[i,j,k,l].permute([2,1,0,3]) -> A[k,j,i,l] + """ + result_shape = [self._shape[i] for i in indices] + result = SparseTensor(result_shape, default=self._default) + for key, val in self._data.items(): + new_key = tuple(key[i] for i in indices) + result._data[new_key] = val + return result diff --git a/spherogram_src/links/tangles.py b/spherogram_src/links/tangles.py index a6be296..7bfd371 100644 --- a/spherogram_src/links/tangles.py +++ b/spherogram_src/links/tangles.py @@ -24,6 +24,7 @@ from collections import OrderedDict, Counter from .ordered_set import OrderedSet from .links import Crossing, Strand, Link, CrossingStrand, CrossingEntryPoint +from .reshetikhin_turaev import RTNetwork class CyclicList(list): def __init__(self, iterable): @@ -651,6 +652,9 @@ def entry_crossing(k): assert ans[s] == 0 return ans + + def apply_reshetikhin_turaev_functor(self, tensors): + return RTNetwork(tensors, T = self) def _component_starts_from_PD(self, code, labels, gluings, entry_dict): """ From 81cbeaa5ea168e16b3bd1c1ca6e72f6f3372bf7d Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Tue, 23 Jun 2026 20:25:16 -0500 Subject: [PATCH 15/53] Fix R matrices for V3 --- .../reshetikhin_turaev/R_matrices/V3/Rn.csv | 740 +++++++++--------- .../reshetikhin_turaev/R_matrices/V3/Rp.csv | 740 +++++++++--------- 2 files changed, 740 insertions(+), 740 deletions(-) diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/Rn.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/Rn.csv index f8ce0ab..0be0f5a 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/Rn.csv +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/Rn.csv @@ -14,572 +14,572 @@ "(1,0,0,1)","q^3*t" "(1,0,1,0)","1-q^3*t" "(1,1,1,1)","-(q^3*t)" -"(1,2,2,1)","q*t*Subscript[q,1,2]" +"(1,2,2,1)","q*t" "(1,2,3,0)","1" -"(1,3,3,1)","-(q*t*Subscript[q,1,2])" -"(1,2,4,0)","-(q*t*Subscript[q,1,2])" -"(1,4,4,1)","-(q*t*Subscript[q,1,2])" -"(1,3,5,0)","q*t*Subscript[q,1,2]" +"(1,3,3,1)","-(q*t)" +"(1,2,4,0)","-(q*t)" +"(1,4,4,1)","-(q*t)" +"(1,3,5,0)","q*t" "(1,4,5,0)","1" -"(1,5,5,1)","q*t*Subscript[q,1,2]" -"(1,6,6,1)","-((t*Subscript[q,1,2]^2)/q)" +"(1,5,5,1)","q*t" +"(1,6,6,1)","-(t/q)" "(1,6,7,0)","1" -"(1,7,7,1)","(t*Subscript[q,1,2]^2)/q" -"(1,6,8,0)","(t*Subscript[q,1,2]^2)/q" -"(1,8,8,1)","(t*Subscript[q,1,2]^2)/q" -"(1,7,9,0)","-((t*Subscript[q,1,2]^2)/q)" +"(1,7,7,1)","t/q" +"(1,6,8,0)","t/q" +"(1,8,8,1)","t/q" +"(1,7,9,0)","-(t/q)" "(1,8,9,0)","1" -"(1,9,9,1)","-((t*Subscript[q,1,2]^2)/q)" -"(1,10,10,1)","(t*Subscript[q,1,2]^3)/q^3" +"(1,9,9,1)","-(t/q)" +"(1,10,10,1)","t/q^3" "(1,10,11,0)","1" -"(1,11,11,1)","-((t*Subscript[q,1,2]^3)/q^3)" +"(1,11,11,1)","-(t/q^3)" "(2,0,0,2)","q^3/t" -"(2,1,1,2)","q^3/(t*Subscript[q,1,2])" +"(2,1,1,2)","q^3/t" "(2,0,2,0)","1-q^3/t" "(2,2,2,2)","-(q^3/t)" -"(2,1,3,0)","-(q^3/(t*Subscript[q,1,2]))" -"(2,3,3,2)","-(q^3/(t*Subscript[q,1,2]))" +"(2,1,3,0)","-(q^3/t)" +"(2,3,3,2)","-(q^3/t)" "(2,1,4,0)","1" -"(2,4,4,2)","-(q^3/(t*Subscript[q,1,2]))" -"(2,5,5,2)","-(q^3/(t*Subscript[q,1,2]^2))" +"(2,4,4,2)","-(q^3/t)" +"(2,5,5,2)","-(q^3/t)" "(2,3,6,0)","1" -"(2,4,6,0)","q^3/(t*Subscript[q,1,2])" -"(2,6,6,2)","q^3/(t*Subscript[q,1,2])" -"(2,5,7,0)","q^3/(t*Subscript[q,1,2]^2)" -"(2,7,7,2)","q^3/(t*Subscript[q,1,2]^2)" +"(2,4,6,0)","q^3/t" +"(2,6,6,2)","q^3/t" +"(2,5,7,0)","q^3/t" +"(2,7,7,2)","q^3/t" "(2,5,8,0)","1" -"(2,8,8,2)","q^3/(t*Subscript[q,1,2]^2)" -"(2,9,9,2)","q^3/(t*Subscript[q,1,2]^3)" +"(2,8,8,2)","q^3/t" +"(2,9,9,2)","q^3/t" "(2,7,10,0)","1" -"(2,8,10,0)","-(q^3/(t*Subscript[q,1,2]^2))" -"(2,10,10,2)","-(q^3/(t*Subscript[q,1,2]^2))" -"(2,9,11,0)","-(q^3/(t*Subscript[q,1,2]^3))" -"(2,11,11,2)","-(q^3/(t*Subscript[q,1,2]^3))" +"(2,8,10,0)","-(q^3/t)" +"(2,10,10,2)","-(q^3/t)" +"(2,9,11,0)","-(q^3/t)" +"(2,11,11,2)","-(q^3/t)" "(3,0,0,3)","q^6" "(3,0,1,2)","-q^6+q^3/t" -"(3,1,1,3)","-(q^6/Subscript[q,1,2])" -"(3,0,2,1)","-(q^4*Subscript[q,1,2])+q*t*Subscript[q,1,2]" -"(3,2,2,3)","-(q^4*Subscript[q,1,2])" +"(3,1,1,3)","-q^6" +"(3,0,2,1)","-q^4+q*t" +"(3,2,2,3)","-q^4" "(3,0,3,0)","1-q^3/t" "(3,1,3,1)","q^4" "(3,2,3,2)","-(q^3/t)" "(3,3,3,3)","q^4" -"(3,0,4,0)","q^4*Subscript[q,1,2]-q*t*Subscript[q,1,2]" -"(3,1,4,1)","-(q*t*Subscript[q,1,2])" -"(3,2,4,2)","q^4*Subscript[q,1,2]" +"(3,0,4,0)","q^4-q*t" +"(3,1,4,1)","-(q*t)" +"(3,2,4,2)","q^4" "(3,4,4,3)","q^4" "(3,1,5,0)","1-q^4" "(3,3,5,2)","-q^4" -"(3,4,5,2)","-(q^3/(t*Subscript[q,1,2]))" -"(3,5,5,3)","-(q^4/Subscript[q,1,2])" -"(3,3,6,1)","-((t*Subscript[q,1,2]^2)/q)" -"(3,4,6,1)","-(q^2*Subscript[q,1,2])" -"(3,6,6,3)","-(q^2*Subscript[q,1,2])" +"(3,4,5,2)","-(q^3/t)" +"(3,5,5,3)","-q^4" +"(3,3,6,1)","-(t/q)" +"(3,4,6,1)","-q^2" +"(3,6,6,3)","-q^2" "(3,3,7,0)","1" -"(3,4,7,0)","q^3/(t*Subscript[q,1,2])" +"(3,4,7,0)","q^3/t" "(3,5,7,1)","q^2" -"(3,6,7,2)","q^3/(t*Subscript[q,1,2])" +"(3,6,7,2)","q^3/t" "(3,7,7,3)","q^2" -"(3,3,8,0)","(t*Subscript[q,1,2]^2)/q" -"(3,4,8,0)","q^2*Subscript[q,1,2]" -"(3,5,8,1)","(t*Subscript[q,1,2]^2)/q" -"(3,6,8,2)","q^2*Subscript[q,1,2]" +"(3,3,8,0)","t/q" +"(3,4,8,0)","q^2" +"(3,5,8,1)","t/q" +"(3,6,8,2)","q^2" "(3,8,8,3)","q^2" "(3,5,9,0)","1-q^2" "(3,7,9,2)","-q^2" -"(3,8,9,2)","q^3/(t*Subscript[q,1,2]^2)" -"(3,9,9,3)","-(q^2/Subscript[q,1,2])" -"(3,7,10,1)","(t*Subscript[q,1,2]^3)/q^3" -"(3,8,10,1)","-Subscript[q,1,2]" -"(3,10,10,3)","-Subscript[q,1,2]" +"(3,8,9,2)","q^3/t" +"(3,9,9,3)","-q^2" +"(3,7,10,1)","t/q^3" +"(3,8,10,1)","-1" +"(3,10,10,3)","-1" "(3,7,11,0)","1" -"(3,8,11,0)","-(q^3/(t*Subscript[q,1,2]^2))" +"(3,8,11,0)","-(q^3/t)" "(3,9,11,1)","1" -"(3,10,11,2)","-(q^3/(t*Subscript[q,1,2]^2))" +"(3,10,11,2)","-(q^3/t)" "(3,11,11,3)","1" "(4,0,0,4)","q^6" -"(4,0,1,2)","-(q^6/Subscript[q,1,2])+q^3/(t*Subscript[q,1,2])" -"(4,1,1,4)","-(q^6/Subscript[q,1,2])" +"(4,0,1,2)","-q^6+q^3/t" +"(4,1,1,4)","-q^6" "(4,0,2,1)","-q^6+q^3*t" -"(4,2,2,4)","-(q^4*Subscript[q,1,2])" -"(4,0,3,0)","q^6/Subscript[q,1,2]-q^3/(t*Subscript[q,1,2])" -"(4,1,3,1)","q^6/Subscript[q,1,2]" -"(4,2,3,2)","-(q^3/(t*Subscript[q,1,2]))" +"(4,2,2,4)","-q^4" +"(4,0,3,0)","q^6-q^3/t" +"(4,1,3,1)","q^6" +"(4,2,3,2)","-(q^3/t)" "(4,3,3,4)","q^4" "(4,0,4,0)","1-q^3*t" "(4,1,4,1)","-(q^3*t)" "(4,2,4,2)","q^4" "(4,4,4,4)","q^4" -"(4,3,5,2)","-(q^4/Subscript[q,1,2])" -"(4,4,5,2)","-(q^3/(t*Subscript[q,1,2]^2))" -"(4,5,5,4)","-(q^4/Subscript[q,1,2])" +"(4,3,5,2)","-q^4" +"(4,4,5,2)","-(q^3/t)" +"(4,5,5,4)","-q^4" "(4,2,6,0)","1-q^4" -"(4,3,6,1)","-(q*t*Subscript[q,1,2])" +"(4,3,6,1)","-(q*t)" "(4,4,6,1)","-q^4" -"(4,6,6,4)","-(q^2*Subscript[q,1,2])" -"(4,3,7,0)","q^4/Subscript[q,1,2]" -"(4,4,7,0)","q^3/(t*Subscript[q,1,2]^2)" -"(4,5,7,1)","q^4/Subscript[q,1,2]" -"(4,6,7,2)","q^3/(t*Subscript[q,1,2]^2)" +"(4,6,6,4)","-q^2" +"(4,3,7,0)","q^4" +"(4,4,7,0)","q^3/t" +"(4,5,7,1)","q^4" +"(4,6,7,2)","q^3/t" "(4,7,7,4)","q^2" -"(4,3,8,0)","q*t*Subscript[q,1,2]" +"(4,3,8,0)","q*t" "(4,4,8,0)","1" -"(4,5,8,1)","q*t*Subscript[q,1,2]" +"(4,5,8,1)","q*t" "(4,6,8,2)","q^2" "(4,8,8,4)","q^2" -"(4,7,9,2)","-(q^2/Subscript[q,1,2])" -"(4,8,9,2)","q^3/(t*Subscript[q,1,2]^3)" -"(4,9,9,4)","-(q^2/Subscript[q,1,2])" +"(4,7,9,2)","-q^2" +"(4,8,9,2)","q^3/t" +"(4,9,9,4)","-q^2" "(4,6,10,0)","1-q^2" -"(4,7,10,1)","(t*Subscript[q,1,2]^2)/q" +"(4,7,10,1)","t/q" "(4,8,10,1)","-q^2" -"(4,10,10,4)","-Subscript[q,1,2]" -"(4,7,11,0)","q^2/Subscript[q,1,2]" -"(4,8,11,0)","-(q^3/(t*Subscript[q,1,2]^3))" -"(4,9,11,1)","q^2/Subscript[q,1,2]" -"(4,10,11,2)","-(q^3/(t*Subscript[q,1,2]^3))" +"(4,10,10,4)","-1" +"(4,7,11,0)","q^2" +"(4,8,11,0)","-(q^3/t)" +"(4,9,11,1)","q^2" +"(4,10,11,2)","-(q^3/t)" "(4,11,11,4)","1" "(5,0,0,5)","q^9*t" -"(5,0,1,3)","-(q^6/Subscript[q,1,2])+(q^9*t)/Subscript[q,1,2]" +"(5,0,1,3)","-q^6+q^9*t" "(5,0,1,4)","q^6-q^9*t" -"(5,1,1,5)","(q^9*t)/Subscript[q,1,2]" -"(5,2,2,5)","-(q^5*t*Subscript[q,1,2]^2)" +"(5,1,1,5)","q^9*t" +"(5,2,2,5)","-(q^5*t)" "(5,0,3,1)","q^4-q^6+q^3*t-q^7*t" "(5,2,3,3)","q^4" -"(5,2,3,4)","-(q^4*Subscript[q,1,2])" -"(5,3,3,5)","-(q^5*t*Subscript[q,1,2])" -"(5,0,4,1)","-(q*t*Subscript[q,1,2])+q^7*t*Subscript[q,1,2]" -"(5,2,4,3)","-(q^5*t*Subscript[q,1,2])" -"(5,2,4,4)","q^5*t*Subscript[q,1,2]^2" -"(5,4,4,5)","-(q^5*t*Subscript[q,1,2])" +"(5,2,3,4)","-q^4" +"(5,3,3,5)","-(q^5*t)" +"(5,0,4,1)","-(q*t)+q^7*t" +"(5,2,4,3)","-(q^5*t)" +"(5,2,4,4)","q^5*t" +"(5,4,4,5)","-(q^5*t)" "(5,0,5,0)","1-q^4-q^3*t+q^7*t" "(5,1,5,1)","-(q^3*t)+q^7*t" "(5,3,5,3)","-(q^5*t)" -"(5,4,5,3)","-(q^4/Subscript[q,1,2])" -"(5,3,5,4)","q^5*t*Subscript[q,1,2]" +"(5,4,5,3)","-q^4" +"(5,3,5,4)","q^5*t" "(5,4,5,4)","q^4" "(5,5,5,5)","-(q^5*t)" -"(5,2,6,1)","-((t*Subscript[q,1,2]^2)/q)+q^3*t*Subscript[q,1,2]^2" -"(5,6,6,5)","q*t*Subscript[q,1,2]^3" +"(5,2,6,1)","-(t/q)+q^3*t" +"(5,6,6,5)","q*t" "(5,2,7,0)","1-q^4" -"(5,3,7,1)","-(q*t*Subscript[q,1,2])+q^3*t*Subscript[q,1,2]" +"(5,3,7,1)","-(q*t)+q^3*t" "(5,4,7,1)","q^2-q^4" "(5,6,7,3)","q^2" -"(5,6,7,4)","-(q^2*Subscript[q,1,2])" -"(5,7,7,5)","q*t*Subscript[q,1,2]^2" -"(5,2,8,0)","(t*Subscript[q,1,2]^2)/q-q^3*t*Subscript[q,1,2]^2" -"(5,4,8,1)","(t*Subscript[q,1,2]^2)/q-q^3*t*Subscript[q,1,2]^2" -"(5,6,8,3)","q*t*Subscript[q,1,2]^2" -"(5,6,8,4)","-(q*t*Subscript[q,1,2]^3)" -"(5,8,8,5)","q*t*Subscript[q,1,2]^2" -"(5,3,9,0)","q*t*Subscript[q,1,2]-q^3*t*Subscript[q,1,2]" +"(5,6,7,4)","-q^2" +"(5,7,7,5)","q*t" +"(5,2,8,0)","t/q-q^3*t" +"(5,4,8,1)","t/q-q^3*t" +"(5,6,8,3)","q*t" +"(5,6,8,4)","-(q*t)" +"(5,8,8,5)","q*t" +"(5,3,9,0)","q*t-q^3*t" "(5,4,9,0)","1-q^2" -"(5,5,9,1)","q*t*Subscript[q,1,2]-q^3*t*Subscript[q,1,2]" -"(5,7,9,3)","q*t*Subscript[q,1,2]" -"(5,8,9,3)","-(q^2/Subscript[q,1,2])" -"(5,7,9,4)","-(q*t*Subscript[q,1,2]^2)" +"(5,5,9,1)","q*t-q^3*t" +"(5,7,9,3)","q*t" +"(5,8,9,3)","-q^2" +"(5,7,9,4)","-(q*t)" "(5,8,9,4)","q^2" -"(5,9,9,5)","q*t*Subscript[q,1,2]" -"(5,6,10,1)","(t*Subscript[q,1,2]^3)/q^3-(t*Subscript[q,1,2]^3)/q" -"(5,10,10,5)","-((t*Subscript[q,1,2]^4)/q^3)" +"(5,9,9,5)","q*t" +"(5,6,10,1)","t/q^3-t/q" +"(5,10,10,5)","-(t/q^3)" "(5,6,11,0)","1-q^2" "(5,8,11,1)","1-q^2" "(5,10,11,3)","1" -"(5,10,11,4)","-Subscript[q,1,2]" -"(5,11,11,5)","-((t*Subscript[q,1,2]^3)/q^3)" +"(5,10,11,4)","-1" +"(5,11,11,5)","-(t/q^3)" "(6,0,0,6)","q^9/t" -"(6,1,1,6)","-(q^9/(t*Subscript[q,1,2]^2))" +"(6,1,1,6)","-(q^9/t)" "(6,0,2,3)","q^6-q^9/t" -"(6,0,2,4)","-(q^4*Subscript[q,1,2])+(q^7*Subscript[q,1,2])/t" -"(6,2,2,6)","(q^7*Subscript[q,1,2])/t" -"(6,0,3,2)","-(q^3/(t*Subscript[q,1,2]))+q^9/(t*Subscript[q,1,2])" -"(6,1,3,3)","q^9/(t*Subscript[q,1,2]^2)" -"(6,1,3,4)","-(q^7/(t*Subscript[q,1,2]))" -"(6,3,3,6)","-(q^7/(t*Subscript[q,1,2]))" +"(6,0,2,4)","-q^4+q^7/t" +"(6,2,2,6)","q^7/t" +"(6,0,3,2)","-(q^3/t)+q^9/t" +"(6,1,3,3)","q^9/t" +"(6,1,3,4)","-(q^7/t)" +"(6,3,3,6)","-(q^7/t)" "(6,0,4,2)","q^4-q^6+q^3/t-q^7/t" -"(6,1,4,3)","-(q^6/Subscript[q,1,2])" +"(6,1,4,3)","-q^6" "(6,1,4,4)","q^4" -"(6,4,4,6)","-(q^7/(t*Subscript[q,1,2]))" -"(6,1,5,2)","-(q^3/(t*Subscript[q,1,2]^2))+q^7/(t*Subscript[q,1,2]^2)" -"(6,5,5,6)","q^7/(t*Subscript[q,1,2]^3)" +"(6,4,4,6)","-(q^7/t)" +"(6,1,5,2)","-(q^3/t)+q^7/t" +"(6,5,5,6)","q^7/t" "(6,0,6,0)","1-q^4-q^3/t+q^7/t" "(6,2,6,2)","-(q^3/t)+q^7/t" "(6,3,6,3)","q^4" -"(6,4,6,3)","q^7/(t*Subscript[q,1,2])" -"(6,3,6,4)","-(q^2*Subscript[q,1,2])" +"(6,4,6,3)","q^7/t" +"(6,3,6,4)","-q^2" "(6,4,6,4)","-(q^5/t)" "(6,6,6,6)","-(q^5/t)" -"(6,1,7,0)","q^3/(t*Subscript[q,1,2]^2)-q^7/(t*Subscript[q,1,2]^2)" -"(6,3,7,2)","q^3/(t*Subscript[q,1,2]^2)-q^7/(t*Subscript[q,1,2]^2)" -"(6,5,7,3)","-(q^7/(t*Subscript[q,1,2]^3))" -"(6,5,7,4)","q^5/(t*Subscript[q,1,2]^2)" -"(6,7,7,6)","q^5/(t*Subscript[q,1,2]^2)" +"(6,1,7,0)","q^3/t-q^7/t" +"(6,3,7,2)","q^3/t-q^7/t" +"(6,5,7,3)","-(q^7/t)" +"(6,5,7,4)","q^5/t" +"(6,7,7,6)","q^5/t" "(6,1,8,0)","1-q^4" "(6,3,8,2)","q^2-q^4" -"(6,4,8,2)","-(q^3/(t*Subscript[q,1,2]))+q^5/(t*Subscript[q,1,2])" -"(6,5,8,3)","-(q^4/Subscript[q,1,2])" +"(6,4,8,2)","-(q^3/t)+q^5/t" +"(6,5,8,3)","-q^4" "(6,5,8,4)","q^2" -"(6,8,8,6)","q^5/(t*Subscript[q,1,2]^2)" -"(6,5,9,2)","q^3/(t*Subscript[q,1,2]^3)-q^5/(t*Subscript[q,1,2]^3)" -"(6,9,9,6)","-(q^5/(t*Subscript[q,1,2]^4))" +"(6,8,8,6)","q^5/t" +"(6,5,9,2)","q^3/t-q^5/t" +"(6,9,9,6)","-(q^5/t)" "(6,3,10,0)","1-q^2" -"(6,4,10,0)","q^3/(t*Subscript[q,1,2])-q^5/(t*Subscript[q,1,2])" -"(6,6,10,2)","q^3/(t*Subscript[q,1,2])-q^5/(t*Subscript[q,1,2])" +"(6,4,10,0)","q^3/t-q^5/t" +"(6,6,10,2)","q^3/t-q^5/t" "(6,7,10,3)","q^2" -"(6,8,10,3)","-(q^5/(t*Subscript[q,1,2]^2))" -"(6,7,10,4)","-Subscript[q,1,2]" -"(6,8,10,4)","q^3/(t*Subscript[q,1,2])" -"(6,10,10,6)","q^3/(t*Subscript[q,1,2])" -"(6,5,11,0)","-(q^3/(t*Subscript[q,1,2]^3))+q^5/(t*Subscript[q,1,2]^3)" -"(6,7,11,2)","-(q^3/(t*Subscript[q,1,2]^3))+q^5/(t*Subscript[q,1,2]^3)" -"(6,9,11,3)","q^5/(t*Subscript[q,1,2]^4)" -"(6,9,11,4)","-(q^3/(t*Subscript[q,1,2]^3))" -"(6,11,11,6)","-(q^3/(t*Subscript[q,1,2]^3))" +"(6,8,10,3)","-(q^5/t)" +"(6,7,10,4)","-1" +"(6,8,10,4)","q^3/t" +"(6,10,10,6)","q^3/t" +"(6,5,11,0)","-(q^3/t)+q^5/t" +"(6,7,11,2)","-(q^3/t)+q^5/t" +"(6,9,11,3)","q^5/t" +"(6,9,11,4)","-(q^3/t)" +"(6,11,11,6)","-(q^3/t)" "(7,0,0,7)","q^12" "(7,0,1,6)","-q^12+q^9/t" -"(7,1,1,7)","q^12/Subscript[q,1,2]^2" -"(7,0,2,5)","q^8*Subscript[q,1,2]^2-q^5*t*Subscript[q,1,2]^2" -"(7,2,2,7)","q^8*Subscript[q,1,2]^2" +"(7,1,1,7)","q^12" +"(7,0,2,5)","q^8-q^5*t" +"(7,2,2,7)","q^8" "(7,0,3,3)","q^4+q^6-q^10-q^9/t" -"(7,0,3,4)","-(q^4*Subscript[q,1,2])+(q^7*Subscript[q,1,2])/t" +"(7,0,3,4)","-q^4+q^7/t" "(7,1,3,5)","q^8" -"(7,2,3,6)","(q^7*Subscript[q,1,2])/t" +"(7,2,3,6)","q^7/t" "(7,3,3,7)","q^8" -"(7,0,4,3)","-(q^4*Subscript[q,1,2])+q^8*Subscript[q,1,2]+q^10*Subscript[q,1,2]-q^5*t*Subscript[q,1,2]" -"(7,0,4,4)","-(q^8*Subscript[q,1,2]^2)+q^5*t*Subscript[q,1,2]^2" -"(7,1,4,5)","-(q^5*t*Subscript[q,1,2])" -"(7,2,4,6)","-(q^8*Subscript[q,1,2]^2)" +"(7,0,4,3)","-q^4+q^8+q^10-q^5*t" +"(7,0,4,4)","-q^8+q^5*t" +"(7,1,4,5)","-(q^5*t)" +"(7,2,4,6)","-q^8" "(7,4,4,7)","q^8" "(7,0,5,2)","-q^6+q^10+q^3/t-q^7/t" -"(7,1,5,3)","-(q^4/Subscript[q,1,2])-q^6/Subscript[q,1,2]+q^8/Subscript[q,1,2]+q^10/Subscript[q,1,2]" +"(7,1,5,3)","-q^4-q^6+q^8+q^10" "(7,1,5,4)","q^4-q^8" "(7,3,5,6)","-q^8" -"(7,4,5,6)","-(q^7/(t*Subscript[q,1,2]))" -"(7,5,5,7)","q^8/Subscript[q,1,2]^2" -"(7,0,6,1)","q^2*Subscript[q,1,2]^2-q^6*Subscript[q,1,2]^2-(t*Subscript[q,1,2]^2)/q+q^3*t*Subscript[q,1,2]^2" -"(7,2,6,3)","q^2*Subscript[q,1,2]^2-q^6*Subscript[q,1,2]^2" -"(7,3,6,5)","q*t*Subscript[q,1,2]^3" -"(7,4,6,5)","q^4*Subscript[q,1,2]^2" -"(7,6,6,7)","q^4*Subscript[q,1,2]^2" +"(7,4,5,6)","-(q^7/t)" +"(7,5,5,7)","q^8" +"(7,0,6,1)","q^2-q^6-t/q+q^3*t" +"(7,2,6,3)","q^2-q^6" +"(7,3,6,5)","q*t" +"(7,4,6,5)","q^4" +"(7,6,6,7)","q^4" "(7,0,7,0)","1-q^4-q^3/t+q^7/t" "(7,1,7,1)","q^2-q^6" "(7,2,7,2)","-(q^3/t)+q^7/t" "(7,3,7,3)","q^2+q^4-q^6" -"(7,4,7,3)","q^7/(t*Subscript[q,1,2])" -"(7,3,7,4)","-(q^2*Subscript[q,1,2])" +"(7,4,7,3)","q^7/t" +"(7,3,7,4)","-q^2" "(7,4,7,4)","-(q^5/t)" "(7,5,7,5)","q^4" "(7,6,7,6)","-(q^5/t)" "(7,7,7,7)","q^4" -"(7,0,8,0)","-(q^2*Subscript[q,1,2]^2)+q^6*Subscript[q,1,2]^2+(t*Subscript[q,1,2]^2)/q-q^3*t*Subscript[q,1,2]^2" -"(7,1,8,1)","(t*Subscript[q,1,2]^2)/q-q^3*t*Subscript[q,1,2]^2" -"(7,2,8,2)","-(q^2*Subscript[q,1,2]^2)+q^6*Subscript[q,1,2]^2" -"(7,3,8,3)","q*t*Subscript[q,1,2]^2" -"(7,4,8,3)","-(q^2*Subscript[q,1,2])+q^4*Subscript[q,1,2]+q^6*Subscript[q,1,2]" -"(7,3,8,4)","-(q*t*Subscript[q,1,2]^3)" -"(7,4,8,4)","-(q^4*Subscript[q,1,2]^2)" -"(7,5,8,5)","q*t*Subscript[q,1,2]^2" -"(7,6,8,6)","-(q^4*Subscript[q,1,2]^2)" +"(7,0,8,0)","-q^2+q^6+t/q-q^3*t" +"(7,1,8,1)","t/q-q^3*t" +"(7,2,8,2)","-q^2+q^6" +"(7,3,8,3)","q*t" +"(7,4,8,3)","-q^2+q^4+q^6" +"(7,3,8,4)","-(q*t)" +"(7,4,8,4)","-q^4" +"(7,5,8,5)","q*t" +"(7,6,8,6)","-q^4" "(7,8,8,7)","q^4" "(7,1,9,0)","1-q^2-q^4+q^6" "(7,3,9,2)","-q^4+q^6" -"(7,4,9,2)","-(q^3/(t*Subscript[q,1,2]))+q^5/(t*Subscript[q,1,2])" -"(7,5,9,3)","-(q^2/Subscript[q,1,2])+q^6/Subscript[q,1,2]" +"(7,4,9,2)","-(q^3/t)+q^5/t" +"(7,5,9,3)","-q^2+q^6" "(7,5,9,4)","q^2-q^4" "(7,7,9,6)","-q^4" -"(7,8,9,6)","q^5/(t*Subscript[q,1,2]^2)" -"(7,9,9,7)","q^4/Subscript[q,1,2]^2" -"(7,3,10,1)","(t*Subscript[q,1,2]^3)/q^3-(t*Subscript[q,1,2]^3)/q" -"(7,4,10,1)","Subscript[q,1,2]^2-q^2*Subscript[q,1,2]^2" -"(7,6,10,3)","Subscript[q,1,2]^2-q^2*Subscript[q,1,2]^2" -"(7,7,10,5)","-((t*Subscript[q,1,2]^4)/q^3)" -"(7,8,10,5)","Subscript[q,1,2]^2" -"(7,10,10,7)","Subscript[q,1,2]^2" +"(7,8,9,6)","q^5/t" +"(7,9,9,7)","q^4" +"(7,3,10,1)","t/q^3-t/q" +"(7,4,10,1)","1-q^2" +"(7,6,10,3)","1-q^2" +"(7,7,10,5)","-(t/q^3)" +"(7,8,10,5)","1" +"(7,10,10,7)","1" "(7,3,11,0)","1-q^2" -"(7,4,11,0)","q^3/(t*Subscript[q,1,2])-q^5/(t*Subscript[q,1,2])" +"(7,4,11,0)","q^3/t-q^5/t" "(7,5,11,1)","1-q^2" -"(7,6,11,2)","q^3/(t*Subscript[q,1,2])-q^5/(t*Subscript[q,1,2])" +"(7,6,11,2)","q^3/t-q^5/t" "(7,7,11,3)","1" -"(7,8,11,3)","-(q^5/(t*Subscript[q,1,2]^2))" -"(7,7,11,4)","-Subscript[q,1,2]" -"(7,8,11,4)","q^3/(t*Subscript[q,1,2])" +"(7,8,11,3)","-(q^5/t)" +"(7,7,11,4)","-1" +"(7,8,11,4)","q^3/t" "(7,9,11,5)","1" -"(7,10,11,6)","q^3/(t*Subscript[q,1,2])" +"(7,10,11,6)","q^3/t" "(7,11,11,7)","1" "(8,0,0,8)","q^12" -"(8,0,1,6)","q^12/Subscript[q,1,2]^2-q^9/(t*Subscript[q,1,2]^2)" -"(8,1,1,8)","q^12/Subscript[q,1,2]^2" +"(8,0,1,6)","q^12-q^9/t" +"(8,1,1,8)","q^12" "(8,0,2,5)","-q^12+q^9*t" -"(8,2,2,8)","q^8*Subscript[q,1,2]^2" -"(8,0,3,3)","-(q^12/Subscript[q,1,2]^2)+q^9/(t*Subscript[q,1,2]^2)" -"(8,0,3,4)","-(q^6/Subscript[q,1,2])+q^10/Subscript[q,1,2]+q^12/Subscript[q,1,2]-q^7/(t*Subscript[q,1,2])" -"(8,1,3,5)","-(q^12/Subscript[q,1,2]^2)" -"(8,2,3,6)","-(q^7/(t*Subscript[q,1,2]))" +"(8,2,2,8)","q^8" +"(8,0,3,3)","-q^12+q^9/t" +"(8,0,3,4)","-q^6+q^10+q^12-q^7/t" +"(8,1,3,5)","-q^12" +"(8,2,3,6)","-(q^7/t)" "(8,3,3,8)","q^8" -"(8,0,4,3)","-(q^6/Subscript[q,1,2])+(q^9*t)/Subscript[q,1,2]" +"(8,0,4,3)","-q^6+q^9*t" "(8,0,4,4)","q^4+q^6-q^10-q^9*t" -"(8,1,4,5)","(q^9*t)/Subscript[q,1,2]" +"(8,1,4,5)","q^9*t" "(8,2,4,6)","q^8" "(8,4,4,8)","q^8" -"(8,0,5,2)","q^6/Subscript[q,1,2]^2-q^10/Subscript[q,1,2]^2-q^3/(t*Subscript[q,1,2]^2)+q^7/(t*Subscript[q,1,2]^2)" -"(8,1,5,4)","q^6/Subscript[q,1,2]^2-q^10/Subscript[q,1,2]^2" -"(8,3,5,6)","q^8/Subscript[q,1,2]^2" -"(8,4,5,6)","q^7/(t*Subscript[q,1,2]^3)" -"(8,5,5,8)","q^8/Subscript[q,1,2]^2" +"(8,0,5,2)","q^6-q^10-q^3/t+q^7/t" +"(8,1,5,4)","q^6-q^10" +"(8,3,5,6)","q^8" +"(8,4,5,6)","q^7/t" +"(8,5,5,8)","q^8" "(8,0,6,1)","-q^6+q^10+q^3*t-q^7*t" "(8,2,6,3)","q^4-q^8" -"(8,2,6,4)","-(q^2*Subscript[q,1,2])-q^4*Subscript[q,1,2]+q^6*Subscript[q,1,2]+q^8*Subscript[q,1,2]" -"(8,3,6,5)","-(q^5*t*Subscript[q,1,2])" +"(8,2,6,4)","-q^2-q^4+q^6+q^8" +"(8,3,6,5)","-(q^5*t)" "(8,4,6,5)","-q^8" -"(8,6,6,8)","q^4*Subscript[q,1,2]^2" -"(8,0,7,0)","-(q^6/Subscript[q,1,2]^2)+q^10/Subscript[q,1,2]^2+q^3/(t*Subscript[q,1,2]^2)-q^7/(t*Subscript[q,1,2]^2)" -"(8,1,7,1)","-(q^6/Subscript[q,1,2]^2)+q^10/Subscript[q,1,2]^2" -"(8,2,7,2)","q^3/(t*Subscript[q,1,2]^2)-q^7/(t*Subscript[q,1,2]^2)" -"(8,3,7,3)","-(q^8/Subscript[q,1,2]^2)" -"(8,4,7,3)","-(q^7/(t*Subscript[q,1,2]^3))" -"(8,3,7,4)","-(q^4/Subscript[q,1,2])+q^6/Subscript[q,1,2]+q^8/Subscript[q,1,2]" -"(8,4,7,4)","q^5/(t*Subscript[q,1,2]^2)" -"(8,5,7,5)","-(q^8/Subscript[q,1,2]^2)" -"(8,6,7,6)","q^5/(t*Subscript[q,1,2]^2)" +"(8,6,6,8)","q^4" +"(8,0,7,0)","-q^6+q^10+q^3/t-q^7/t" +"(8,1,7,1)","-q^6+q^10" +"(8,2,7,2)","q^3/t-q^7/t" +"(8,3,7,3)","-q^8" +"(8,4,7,3)","-(q^7/t)" +"(8,3,7,4)","-q^4+q^6+q^8" +"(8,4,7,4)","q^5/t" +"(8,5,7,5)","-q^8" +"(8,6,7,6)","q^5/t" "(8,7,7,8)","q^4" "(8,0,8,0)","1-q^4-q^3*t+q^7*t" "(8,1,8,1)","-(q^3*t)+q^7*t" "(8,2,8,2)","q^2-q^6" "(8,3,8,3)","-(q^5*t)" -"(8,4,8,3)","-(q^4/Subscript[q,1,2])" -"(8,3,8,4)","q^5*t*Subscript[q,1,2]" +"(8,4,8,3)","-q^4" +"(8,3,8,4)","q^5*t" "(8,4,8,4)","q^2+q^4-q^6" "(8,5,8,5)","-(q^5*t)" "(8,6,8,6)","q^4" "(8,8,8,8)","q^4" -"(8,3,9,2)","q^4/Subscript[q,1,2]^2-q^6/Subscript[q,1,2]^2" -"(8,4,9,2)","q^3/(t*Subscript[q,1,2]^3)-q^5/(t*Subscript[q,1,2]^3)" -"(8,5,9,4)","q^4/Subscript[q,1,2]^2-q^6/Subscript[q,1,2]^2" -"(8,7,9,6)","q^4/Subscript[q,1,2]^2" -"(8,8,9,6)","-(q^5/(t*Subscript[q,1,2]^4))" -"(8,9,9,8)","q^4/Subscript[q,1,2]^2" +"(8,3,9,2)","q^4-q^6" +"(8,4,9,2)","q^3/t-q^5/t" +"(8,5,9,4)","q^4-q^6" +"(8,7,9,6)","q^4" +"(8,8,9,6)","-(q^5/t)" +"(8,9,9,8)","q^4" "(8,2,10,0)","1-q^2-q^4+q^6" -"(8,3,10,1)","-(q*t*Subscript[q,1,2])+q^3*t*Subscript[q,1,2]" +"(8,3,10,1)","-(q*t)+q^3*t" "(8,4,10,1)","-q^4+q^6" "(8,6,10,3)","q^2-q^4" -"(8,6,10,4)","-Subscript[q,1,2]+q^4*Subscript[q,1,2]" -"(8,7,10,5)","q*t*Subscript[q,1,2]^2" +"(8,6,10,4)","-1+q^4" +"(8,7,10,5)","q*t" "(8,8,10,5)","-q^4" -"(8,10,10,8)","Subscript[q,1,2]^2" -"(8,3,11,0)","-(q^4/Subscript[q,1,2]^2)+q^6/Subscript[q,1,2]^2" -"(8,4,11,0)","-(q^3/(t*Subscript[q,1,2]^3))+q^5/(t*Subscript[q,1,2]^3)" -"(8,5,11,1)","-(q^4/Subscript[q,1,2]^2)+q^6/Subscript[q,1,2]^2" -"(8,6,11,2)","-(q^3/(t*Subscript[q,1,2]^3))+q^5/(t*Subscript[q,1,2]^3)" -"(8,7,11,3)","-(q^4/Subscript[q,1,2]^2)" -"(8,8,11,3)","q^5/(t*Subscript[q,1,2]^4)" -"(8,7,11,4)","q^4/Subscript[q,1,2]" -"(8,8,11,4)","-(q^3/(t*Subscript[q,1,2]^3))" -"(8,9,11,5)","-(q^4/Subscript[q,1,2]^2)" -"(8,10,11,6)","-(q^3/(t*Subscript[q,1,2]^3))" +"(8,10,10,8)","1" +"(8,3,11,0)","-q^4+q^6" +"(8,4,11,0)","-(q^3/t)+q^5/t" +"(8,5,11,1)","-q^4+q^6" +"(8,6,11,2)","-(q^3/t)+q^5/t" +"(8,7,11,3)","-q^4" +"(8,8,11,3)","q^5/t" +"(8,7,11,4)","q^4" +"(8,8,11,4)","-(q^3/t)" +"(8,9,11,5)","-q^4" +"(8,10,11,6)","-(q^3/t)" "(8,11,11,8)","1" "(9,0,0,9)","q^15*t" -"(9,0,1,7)","q^12/Subscript[q,1,2]^2-(q^15*t)/Subscript[q,1,2]^2" +"(9,0,1,7)","q^12-q^15*t" "(9,0,1,8)","q^12-q^15*t" -"(9,1,1,9)","-((q^15*t)/Subscript[q,1,2]^2)" -"(9,2,2,9)","q^9*t*Subscript[q,1,2]^3" +"(9,1,1,9)","-(q^15*t)" +"(9,2,2,9)","q^9*t" "(9,0,3,5)","q^8-q^12+q^7*t+q^9*t-q^11*t-q^13*t" "(9,2,3,7)","q^8" -"(9,2,3,8)","q^8*Subscript[q,1,2]^2" -"(9,3,3,9)","-(q^9*t*Subscript[q,1,2])" -"(9,0,4,5)","-(q^5*t*Subscript[q,1,2])-q^7*t*Subscript[q,1,2]+q^11*t*Subscript[q,1,2]+q^13*t*Subscript[q,1,2]" -"(9,2,4,7)","-(q^9*t*Subscript[q,1,2])" -"(9,2,4,8)","-(q^9*t*Subscript[q,1,2]^3)" -"(9,4,4,9)","-(q^9*t*Subscript[q,1,2])" -"(9,0,5,3)","-(q^4/Subscript[q,1,2])-q^6/Subscript[q,1,2]+q^8/Subscript[q,1,2]+q^10/Subscript[q,1,2]+(q^7*t)/Subscript[q,1,2]+(q^9*t)/Subscript[q,1,2]-(q^11*t)/Subscript[q,1,2]-(q^13*t)/Subscript[q,1,2]" +"(9,2,3,8)","q^8" +"(9,3,3,9)","-(q^9*t)" +"(9,0,4,5)","-(q^5*t)-q^7*t+q^11*t+q^13*t" +"(9,2,4,7)","-(q^9*t)" +"(9,2,4,8)","-(q^9*t)" +"(9,4,4,9)","-(q^9*t)" +"(9,0,5,3)","-q^4-q^6+q^8+q^10+q^7*t+q^9*t-q^11*t-q^13*t" "(9,0,5,4)","q^4+q^6-q^8-q^10-q^7*t-q^9*t+q^11*t+q^13*t" -"(9,1,5,5)","(q^7*t)/Subscript[q,1,2]+(q^9*t)/Subscript[q,1,2]-(q^11*t)/Subscript[q,1,2]-(q^13*t)/Subscript[q,1,2]" -"(9,3,5,7)","(q^9*t)/Subscript[q,1,2]" -"(9,4,5,7)","q^8/Subscript[q,1,2]^2" -"(9,3,5,8)","q^9*t*Subscript[q,1,2]" +"(9,1,5,5)","q^7*t+q^9*t-q^11*t-q^13*t" +"(9,3,5,7)","q^9*t" +"(9,4,5,7)","q^8" +"(9,3,5,8)","q^9*t" "(9,4,5,8)","q^8" -"(9,5,5,9)","(q^9*t)/Subscript[q,1,2]" -"(9,2,6,5)","q*t*Subscript[q,1,2]^3+q^3*t*Subscript[q,1,2]^3-q^5*t*Subscript[q,1,2]^3-q^7*t*Subscript[q,1,2]^3" -"(9,6,6,9)","-(q^3*t*Subscript[q,1,2]^4)" +"(9,5,5,9)","q^9*t" +"(9,2,6,5)","q*t+q^3*t-q^5*t-q^7*t" +"(9,6,6,9)","-(q^3*t)" "(9,0,7,1)","q^2-2*q^6+q^10+q^3*t-q^5*t-q^7*t+q^9*t" "(9,2,7,3)","q^2+q^4-q^6-q^8" -"(9,2,7,4)","-(q^2*Subscript[q,1,2])-q^4*Subscript[q,1,2]+q^6*Subscript[q,1,2]+q^8*Subscript[q,1,2]" -"(9,3,7,5)","-(q^3*t*Subscript[q,1,2])+q^7*t*Subscript[q,1,2]" +"(9,2,7,4)","-q^2-q^4+q^6+q^8" +"(9,3,7,5)","-(q^3*t)+q^7*t" "(9,4,7,5)","q^4-q^8" "(9,6,7,7)","q^4" -"(9,6,7,8)","q^4*Subscript[q,1,2]^2" -"(9,7,7,9)","q^3*t*Subscript[q,1,2]^2" -"(9,0,8,1)","(t*Subscript[q,1,2]^2)/q-q^3*t*Subscript[q,1,2]^2-q^5*t*Subscript[q,1,2]^2+q^9*t*Subscript[q,1,2]^2" -"(9,2,8,3)","q*t*Subscript[q,1,2]^2+q^3*t*Subscript[q,1,2]^2-q^5*t*Subscript[q,1,2]^2-q^7*t*Subscript[q,1,2]^2" -"(9,2,8,4)","-(q*t*Subscript[q,1,2]^3)-q^3*t*Subscript[q,1,2]^3+q^5*t*Subscript[q,1,2]^3+q^7*t*Subscript[q,1,2]^3" -"(9,4,8,5)","q*t*Subscript[q,1,2]^2+q^3*t*Subscript[q,1,2]^2-q^5*t*Subscript[q,1,2]^2-q^7*t*Subscript[q,1,2]^2" -"(9,6,8,7)","q^3*t*Subscript[q,1,2]^2" -"(9,6,8,8)","q^3*t*Subscript[q,1,2]^4" -"(9,8,8,9)","q^3*t*Subscript[q,1,2]^2" +"(9,6,7,8)","q^4" +"(9,7,7,9)","q^3*t" +"(9,0,8,1)","t/q-q^3*t-q^5*t+q^9*t" +"(9,2,8,3)","q*t+q^3*t-q^5*t-q^7*t" +"(9,2,8,4)","-(q*t)-q^3*t+q^5*t+q^7*t" +"(9,4,8,5)","q*t+q^3*t-q^5*t-q^7*t" +"(9,6,8,7)","q^3*t" +"(9,6,8,8)","q^3*t" +"(9,8,8,9)","q^3*t" "(9,0,9,0)","1-q^2-q^4+q^6-q^3*t+q^5*t+q^7*t-q^9*t" "(9,1,9,1)","-(q^3*t)+q^5*t+q^7*t-q^9*t" "(9,3,9,3)","-(q^3*t)+q^7*t" -"(9,4,9,3)","-(q^2/Subscript[q,1,2])+q^6/Subscript[q,1,2]" -"(9,3,9,4)","q^3*t*Subscript[q,1,2]-q^7*t*Subscript[q,1,2]" +"(9,4,9,3)","-q^2+q^6" +"(9,3,9,4)","q^3*t-q^7*t" "(9,4,9,4)","q^2-q^6" "(9,5,9,5)","-(q^3*t)+q^7*t" "(9,7,9,7)","-(q^3*t)" -"(9,8,9,7)","q^4/Subscript[q,1,2]^2" -"(9,7,9,8)","-(q^3*t*Subscript[q,1,2]^2)" +"(9,8,9,7)","q^4" +"(9,7,9,8)","-(q^3*t)" "(9,8,9,8)","q^4" "(9,9,9,9)","-(q^3*t)" -"(9,2,10,1)","(t*Subscript[q,1,2]^3)/q^3-(t*Subscript[q,1,2]^3)/q-q*t*Subscript[q,1,2]^3+q^3*t*Subscript[q,1,2]^3" -"(9,6,10,5)","-((t*Subscript[q,1,2]^4)/q^3)+q*t*Subscript[q,1,2]^4" -"(9,10,10,9)","(t*Subscript[q,1,2]^5)/q^3" +"(9,2,10,1)","t/q^3-t/q-q*t+q^3*t" +"(9,6,10,5)","-(t/q^3)+q*t" +"(9,10,10,9)","t/q^3" "(9,2,11,0)","1-q^2-q^4+q^6" "(9,4,11,1)","1-q^2-q^4+q^6" "(9,6,11,3)","1-q^4" -"(9,6,11,4)","-Subscript[q,1,2]+q^4*Subscript[q,1,2]" +"(9,6,11,4)","-1+q^4" "(9,8,11,5)","1-q^4" "(9,10,11,7)","1" -"(9,10,11,8)","Subscript[q,1,2]^2" -"(9,11,11,9)","-((t*Subscript[q,1,2]^3)/q^3)" +"(9,10,11,8)","1" +"(9,11,11,9)","-(t/q^3)" "(10,0,0,10)","q^15/t" -"(10,1,1,10)","q^15/(t*Subscript[q,1,2]^3)" +"(10,1,1,10)","q^15/t" "(10,0,2,7)","q^12-q^15/t" -"(10,0,2,8)","q^8*Subscript[q,1,2]^2-(q^11*Subscript[q,1,2]^2)/t" -"(10,2,2,10)","-((q^11*Subscript[q,1,2]^2)/t)" -"(10,0,3,6)","-(q^7/(t*Subscript[q,1,2]))-q^9/(t*Subscript[q,1,2])+q^13/(t*Subscript[q,1,2])+q^15/(t*Subscript[q,1,2])" -"(10,1,3,7)","-(q^15/(t*Subscript[q,1,2]^3))" -"(10,1,3,8)","-(q^11/(t*Subscript[q,1,2]))" -"(10,3,3,10)","-(q^11/(t*Subscript[q,1,2]))" +"(10,0,2,8)","q^8-q^11/t" +"(10,2,2,10)","-(q^11/t)" +"(10,0,3,6)","-(q^7/t)-q^9/t+q^13/t+q^15/t" +"(10,1,3,7)","-(q^15/t)" +"(10,1,3,8)","-(q^11/t)" +"(10,3,3,10)","-(q^11/t)" "(10,0,4,6)","q^8-q^12+q^7/t+q^9/t-q^11/t-q^13/t" -"(10,1,4,7)","q^12/Subscript[q,1,2]^2" +"(10,1,4,7)","q^12" "(10,1,4,8)","q^8" -"(10,4,4,10)","-(q^11/(t*Subscript[q,1,2]))" -"(10,1,5,6)","q^7/(t*Subscript[q,1,2]^3)+q^9/(t*Subscript[q,1,2]^3)-q^11/(t*Subscript[q,1,2]^3)-q^13/(t*Subscript[q,1,2]^3)" -"(10,5,5,10)","-(q^11/(t*Subscript[q,1,2]^4))" +"(10,4,4,10)","-(q^11/t)" +"(10,1,5,6)","q^7/t+q^9/t-q^11/t-q^13/t" +"(10,5,5,10)","-(q^11/t)" "(10,0,6,3)","q^4+q^6-q^8-q^10-q^7/t-q^9/t+q^11/t+q^13/t" -"(10,0,6,4)","-(q^2*Subscript[q,1,2])-q^4*Subscript[q,1,2]+q^6*Subscript[q,1,2]+q^8*Subscript[q,1,2]+(q^5*Subscript[q,1,2])/t+(q^7*Subscript[q,1,2])/t-(q^9*Subscript[q,1,2])/t-(q^11*Subscript[q,1,2])/t" -"(10,2,6,6)","(q^5*Subscript[q,1,2])/t+(q^7*Subscript[q,1,2])/t-(q^9*Subscript[q,1,2])/t-(q^11*Subscript[q,1,2])/t" +"(10,0,6,4)","-q^2-q^4+q^6+q^8+q^5/t+q^7/t-q^9/t-q^11/t" +"(10,2,6,6)","q^5/t+q^7/t-q^9/t-q^11/t" "(10,3,6,7)","q^8" -"(10,4,6,7)","q^11/(t*Subscript[q,1,2])" -"(10,3,6,8)","q^4*Subscript[q,1,2]^2" -"(10,4,6,8)","(q^7*Subscript[q,1,2])/t" -"(10,6,6,10)","(q^7*Subscript[q,1,2])/t" -"(10,0,7,2)","q^3/(t*Subscript[q,1,2]^2)-q^7/(t*Subscript[q,1,2]^2)-q^9/(t*Subscript[q,1,2]^2)+q^13/(t*Subscript[q,1,2]^2)" -"(10,1,7,3)","-(q^7/(t*Subscript[q,1,2]^3))-q^9/(t*Subscript[q,1,2]^3)+q^11/(t*Subscript[q,1,2]^3)+q^13/(t*Subscript[q,1,2]^3)" -"(10,1,7,4)","q^5/(t*Subscript[q,1,2]^2)+q^7/(t*Subscript[q,1,2]^2)-q^9/(t*Subscript[q,1,2]^2)-q^11/(t*Subscript[q,1,2]^2)" -"(10,3,7,6)","q^5/(t*Subscript[q,1,2]^2)+q^7/(t*Subscript[q,1,2]^2)-q^9/(t*Subscript[q,1,2]^2)-q^11/(t*Subscript[q,1,2]^2)" -"(10,5,7,7)","q^11/(t*Subscript[q,1,2]^4)" -"(10,5,7,8)","q^7/(t*Subscript[q,1,2]^2)" -"(10,7,7,10)","q^7/(t*Subscript[q,1,2]^2)" +"(10,4,6,7)","q^11/t" +"(10,3,6,8)","q^4" +"(10,4,6,8)","q^7/t" +"(10,6,6,10)","q^7/t" +"(10,0,7,2)","q^3/t-q^7/t-q^9/t+q^13/t" +"(10,1,7,3)","-(q^7/t)-q^9/t+q^11/t+q^13/t" +"(10,1,7,4)","q^5/t+q^7/t-q^9/t-q^11/t" +"(10,3,7,6)","q^5/t+q^7/t-q^9/t-q^11/t" +"(10,5,7,7)","q^11/t" +"(10,5,7,8)","q^7/t" +"(10,7,7,10)","q^7/t" "(10,0,8,2)","q^2-2*q^6+q^10+q^3/t-q^5/t-q^7/t+q^9/t" -"(10,1,8,3)","-(q^4/Subscript[q,1,2])-q^6/Subscript[q,1,2]+q^8/Subscript[q,1,2]+q^10/Subscript[q,1,2]" +"(10,1,8,3)","-q^4-q^6+q^8+q^10" "(10,1,8,4)","q^2+q^4-q^6-q^8" "(10,3,8,6)","q^4-q^8" -"(10,4,8,6)","-(q^5/(t*Subscript[q,1,2]))+q^9/(t*Subscript[q,1,2])" -"(10,5,8,7)","q^8/Subscript[q,1,2]^2" +"(10,4,8,6)","-(q^5/t)+q^9/t" +"(10,5,8,7)","q^8" "(10,5,8,8)","q^4" -"(10,8,8,10)","q^7/(t*Subscript[q,1,2]^2)" -"(10,1,9,2)","q^3/(t*Subscript[q,1,2]^3)-q^5/(t*Subscript[q,1,2]^3)-q^7/(t*Subscript[q,1,2]^3)+q^9/(t*Subscript[q,1,2]^3)" -"(10,5,9,6)","-(q^5/(t*Subscript[q,1,2]^4))+q^9/(t*Subscript[q,1,2]^4)" -"(10,9,9,10)","q^7/(t*Subscript[q,1,2]^5)" +"(10,8,8,10)","q^7/t" +"(10,1,9,2)","q^3/t-q^5/t-q^7/t+q^9/t" +"(10,5,9,6)","-(q^5/t)+q^9/t" +"(10,9,9,10)","q^7/t" "(10,0,10,0)","1-q^2-q^4+q^6-q^3/t+q^5/t+q^7/t-q^9/t" "(10,2,10,2)","-(q^3/t)+q^5/t+q^7/t-q^9/t" "(10,3,10,3)","q^2-q^6" -"(10,4,10,3)","q^5/(t*Subscript[q,1,2])-q^9/(t*Subscript[q,1,2])" -"(10,3,10,4)","-Subscript[q,1,2]+q^4*Subscript[q,1,2]" +"(10,4,10,3)","q^5/t-q^9/t" +"(10,3,10,4)","-1+q^4" "(10,4,10,4)","-(q^3/t)+q^7/t" "(10,6,10,6)","-(q^3/t)+q^7/t" "(10,7,10,7)","q^4" -"(10,8,10,7)","-(q^7/(t*Subscript[q,1,2]^2))" -"(10,7,10,8)","Subscript[q,1,2]^2" +"(10,8,10,7)","-(q^7/t)" +"(10,7,10,8)","1" "(10,8,10,8)","-(q^3/t)" "(10,10,10,10)","-(q^3/t)" -"(10,1,11,0)","-(q^3/(t*Subscript[q,1,2]^3))+q^5/(t*Subscript[q,1,2]^3)+q^7/(t*Subscript[q,1,2]^3)-q^9/(t*Subscript[q,1,2]^3)" -"(10,3,11,2)","-(q^3/(t*Subscript[q,1,2]^3))+q^5/(t*Subscript[q,1,2]^3)+q^7/(t*Subscript[q,1,2]^3)-q^9/(t*Subscript[q,1,2]^3)" -"(10,5,11,3)","q^5/(t*Subscript[q,1,2]^4)-q^9/(t*Subscript[q,1,2]^4)" -"(10,5,11,4)","-(q^3/(t*Subscript[q,1,2]^3))+q^7/(t*Subscript[q,1,2]^3)" -"(10,7,11,6)","-(q^3/(t*Subscript[q,1,2]^3))+q^7/(t*Subscript[q,1,2]^3)" -"(10,9,11,7)","-(q^7/(t*Subscript[q,1,2]^5))" -"(10,9,11,8)","-(q^3/(t*Subscript[q,1,2]^3))" -"(10,11,11,10)","-(q^3/(t*Subscript[q,1,2]^3))" +"(10,1,11,0)","-(q^3/t)+q^5/t+q^7/t-q^9/t" +"(10,3,11,2)","-(q^3/t)+q^5/t+q^7/t-q^9/t" +"(10,5,11,3)","q^5/t-q^9/t" +"(10,5,11,4)","-(q^3/t)+q^7/t" +"(10,7,11,6)","-(q^3/t)+q^7/t" +"(10,9,11,7)","-(q^7/t)" +"(10,9,11,8)","-(q^3/t)" +"(10,11,11,10)","-(q^3/t)" "(11,0,0,11)","q^18" "(11,0,1,10)","-q^12-q^18+q^15/t+q^15*t" -"(11,1,1,11)","-(q^18/Subscript[q,1,2]^3)" -"(11,0,2,9)","-(q^12*Subscript[q,1,2]^3)+q^9*t*Subscript[q,1,2]^3+q^15*t*Subscript[q,1,2]^3-q^12*t^2*Subscript[q,1,2]^3" -"(11,2,2,11)","-(q^12*Subscript[q,1,2]^3)" +"(11,1,1,11)","-q^18" +"(11,0,2,9)","-q^12+q^9*t+q^15*t-q^12*t^2" +"(11,2,2,11)","-q^12" "(11,0,3,7)","q^8+q^10+2*q^12-q^14-q^16-q^15/t-q^15*t" -"(11,0,3,8)","2*q^8*Subscript[q,1,2]^2-(q^11*Subscript[q,1,2]^2)/t+q^7*t*Subscript[q,1,2]^2+q^9*t*Subscript[q,1,2]^2-q^11*t*Subscript[q,1,2]^2-q^13*t*Subscript[q,1,2]^2-q^15*t*Subscript[q,1,2]^2" +"(11,0,3,8)","2*q^8-q^11/t+q^7*t+q^9*t-q^11*t-q^13*t-q^15*t" "(11,1,3,9)","q^12-q^15*t" -"(11,2,3,10)","q^8*Subscript[q,1,2]^2-(q^11*Subscript[q,1,2]^2)/t" +"(11,2,3,10)","q^8-q^11/t" "(11,3,3,11)","q^12" -"(11,0,4,7)","-(q^8*Subscript[q,1,2])-q^10*Subscript[q,1,2]+q^12*Subscript[q,1,2]+q^14*Subscript[q,1,2]+q^16*Subscript[q,1,2]-2*q^9*t*Subscript[q,1,2]+q^12*t^2*Subscript[q,1,2]" -"(11,0,4,8)","q^12*Subscript[q,1,2]^3-q^5*t*Subscript[q,1,2]^3-q^7*t*Subscript[q,1,2]^3-2*q^9*t*Subscript[q,1,2]^3+q^11*t*Subscript[q,1,2]^3+q^13*t*Subscript[q,1,2]^3+q^12*t^2*Subscript[q,1,2]^3" -"(11,1,4,9)","-(q^9*t*Subscript[q,1,2])+q^12*t^2*Subscript[q,1,2]" -"(11,2,4,10)","q^12*Subscript[q,1,2]^3-q^9*t*Subscript[q,1,2]^3" +"(11,0,4,7)","-q^8-q^10+q^12+q^14+q^16-2*q^9*t+q^12*t^2" +"(11,0,4,8)","q^12-q^5*t-q^7*t-2*q^9*t+q^11*t+q^13*t+q^12*t^2" +"(11,1,4,9)","-(q^9*t)+q^12*t^2" +"(11,2,4,10)","q^12-q^9*t" "(11,4,4,11)","q^12" "(11,0,5,6)","-q^4-q^6+q^8-q^12+q^14+q^16+q^7/t+q^9/t-q^11/t-q^13/t+q^7*t+q^9*t-q^11*t-q^13*t" -"(11,1,5,7)","q^8/Subscript[q,1,2]^2+q^10/Subscript[q,1,2]^2-q^14/Subscript[q,1,2]^2-q^16/Subscript[q,1,2]^2" +"(11,1,5,7)","q^8+q^10-q^14-q^16" "(11,1,5,8)","q^8-q^12+q^7*t+q^9*t-q^11*t-q^13*t" "(11,3,5,10)","-q^12+q^9*t" -"(11,4,5,10)","q^8/Subscript[q,1,2]-q^11/(t*Subscript[q,1,2])" -"(11,5,5,11)","-(q^12/Subscript[q,1,2]^3)" -"(11,0,6,5)","-(q^4*Subscript[q,1,2]^3)-q^6*Subscript[q,1,2]^3+q^8*Subscript[q,1,2]^3+q^10*Subscript[q,1,2]^3+q*t*Subscript[q,1,2]^3+q^3*t*Subscript[q,1,2]^3-q^5*t*Subscript[q,1,2]^3+q^9*t*Subscript[q,1,2]^3-q^11*t*Subscript[q,1,2]^3-q^13*t*Subscript[q,1,2]^3-q^4*t^2*Subscript[q,1,2]^3-q^6*t^2*Subscript[q,1,2]^3+q^8*t^2*Subscript[q,1,2]^3+q^10*t^2*Subscript[q,1,2]^3" -"(11,2,6,7)","-(q^4*Subscript[q,1,2]^3)-q^6*Subscript[q,1,2]^3+q^8*Subscript[q,1,2]^3+q^10*Subscript[q,1,2]^3-q^5*t*Subscript[q,1,2]^3+q^9*t*Subscript[q,1,2]^3" -"(11,2,6,8)","-(q*t*Subscript[q,1,2]^5)-q^3*t*Subscript[q,1,2]^5+q^7*t*Subscript[q,1,2]^5+q^9*t*Subscript[q,1,2]^5" -"(11,3,6,9)","-(q^3*t*Subscript[q,1,2]^4)+q^6*t^2*Subscript[q,1,2]^4" -"(11,4,6,9)","-(q^6*Subscript[q,1,2]^3)+q^9*t*Subscript[q,1,2]^3" -"(11,6,6,11)","-(q^6*Subscript[q,1,2]^3)" +"(11,4,5,10)","q^8-q^11/t" +"(11,5,5,11)","-q^12" +"(11,0,6,5)","-q^4-q^6+q^8+q^10+q*t+q^3*t-q^5*t+q^9*t-q^11*t-q^13*t-q^4*t^2-q^6*t^2+q^8*t^2+q^10*t^2" +"(11,2,6,7)","-q^4-q^6+q^8+q^10-q^5*t+q^9*t" +"(11,2,6,8)","-(q*t)-q^3*t+q^7*t+q^9*t" +"(11,3,6,9)","-(q^3*t)+q^6*t^2" +"(11,4,6,9)","-q^6+q^9*t" +"(11,6,6,11)","-q^6" "(11,0,7,3)","q^2+2*q^4+q^6-3*q^8-2*q^10+q^12-q^7/t-q^9/t+q^11/t+q^13/t-q^7*t-q^9*t+q^11*t+q^13*t" -"(11,0,7,4)","-2*q^2*Subscript[q,1,2]-2*q^4*Subscript[q,1,2]+2*q^6*Subscript[q,1,2]+2*q^8*Subscript[q,1,2]+(q^5*Subscript[q,1,2])/t+(q^7*Subscript[q,1,2])/t-(q^9*Subscript[q,1,2])/t-(q^11*Subscript[q,1,2])/t-q^3*t*Subscript[q,1,2]+q^5*t*Subscript[q,1,2]+2*q^7*t*Subscript[q,1,2]-q^11*t*Subscript[q,1,2]-q^13*t*Subscript[q,1,2]" +"(11,0,7,4)","-2*q^2-2*q^4+2*q^6+2*q^8+q^5/t+q^7/t-q^9/t-q^11/t-q^3*t+q^5*t+2*q^7*t-q^11*t-q^13*t" "(11,1,7,5)","q^4+q^6-q^8-q^10-q^7*t-q^9*t+q^11*t+q^13*t" -"(11,2,7,6)","-(q^2*Subscript[q,1,2])-q^4*Subscript[q,1,2]+q^6*Subscript[q,1,2]+q^8*Subscript[q,1,2]+(q^5*Subscript[q,1,2])/t+(q^7*Subscript[q,1,2])/t-(q^9*Subscript[q,1,2])/t-(q^11*Subscript[q,1,2])/t" +"(11,2,7,6)","-q^2-q^4+q^6+q^8+q^5/t+q^7/t-q^9/t-q^11/t" "(11,3,7,7)","q^4+q^6-q^10-q^9*t" -"(11,4,7,7)","-(q^8/Subscript[q,1,2])+q^11/(t*Subscript[q,1,2])" -"(11,3,7,8)","q^4*Subscript[q,1,2]^2+q^3*t*Subscript[q,1,2]^2-q^7*t*Subscript[q,1,2]^2-q^9*t*Subscript[q,1,2]^2" -"(11,4,7,8)","-(q^4*Subscript[q,1,2])+(q^7*Subscript[q,1,2])/t" +"(11,4,7,7)","-q^8+q^11/t" +"(11,3,7,8)","q^4+q^3*t-q^7*t-q^9*t" +"(11,4,7,8)","-q^4+q^7/t" "(11,5,7,9)","q^6-q^9*t" -"(11,6,7,10)","-(q^4*Subscript[q,1,2])+(q^7*Subscript[q,1,2])/t" +"(11,6,7,10)","-q^4+q^7/t" "(11,7,7,11)","q^6" -"(11,0,8,3)","q^2*Subscript[q,1,2]^2-q^4*Subscript[q,1,2]^2-2*q^6*Subscript[q,1,2]^2+q^10*Subscript[q,1,2]^2+q^12*Subscript[q,1,2]^2+2*q*t*Subscript[q,1,2]^2+2*q^3*t*Subscript[q,1,2]^2-2*q^5*t*Subscript[q,1,2]^2-2*q^7*t*Subscript[q,1,2]^2-q^4*t^2*Subscript[q,1,2]^2-q^6*t^2*Subscript[q,1,2]^2+q^8*t^2*Subscript[q,1,2]^2+q^10*t^2*Subscript[q,1,2]^2" -"(11,0,8,4)","q^4*Subscript[q,1,2]^3+q^6*Subscript[q,1,2]^3-q^8*Subscript[q,1,2]^3-q^10*Subscript[q,1,2]^3-(t*Subscript[q,1,2]^3)/q-2*q*t*Subscript[q,1,2]^3-q^3*t*Subscript[q,1,2]^3+3*q^5*t*Subscript[q,1,2]^3+2*q^7*t*Subscript[q,1,2]^3-q^9*t*Subscript[q,1,2]^3+q^4*t^2*Subscript[q,1,2]^3+q^6*t^2*Subscript[q,1,2]^3-q^8*t^2*Subscript[q,1,2]^3-q^10*t^2*Subscript[q,1,2]^3" -"(11,1,8,5)","q*t*Subscript[q,1,2]^2+q^3*t*Subscript[q,1,2]^2-q^5*t*Subscript[q,1,2]^2-q^7*t*Subscript[q,1,2]^2-q^4*t^2*Subscript[q,1,2]^2-q^6*t^2*Subscript[q,1,2]^2+q^8*t^2*Subscript[q,1,2]^2+q^10*t^2*Subscript[q,1,2]^2" -"(11,2,8,6)","q^4*Subscript[q,1,2]^3+q^6*Subscript[q,1,2]^3-q^8*Subscript[q,1,2]^3-q^10*Subscript[q,1,2]^3-q*t*Subscript[q,1,2]^3-q^3*t*Subscript[q,1,2]^3+q^5*t*Subscript[q,1,2]^3+q^7*t*Subscript[q,1,2]^3" -"(11,3,8,7)","q^3*t*Subscript[q,1,2]^2-q^6*t^2*Subscript[q,1,2]^2" -"(11,4,8,7)","-(q^4*Subscript[q,1,2])+q^8*Subscript[q,1,2]+q^10*Subscript[q,1,2]-q^5*t*Subscript[q,1,2]" -"(11,3,8,8)","q^3*t*Subscript[q,1,2]^4-q^6*t^2*Subscript[q,1,2]^4" -"(11,4,8,8)","q^6*Subscript[q,1,2]^3-q*t*Subscript[q,1,2]^3-q^3*t*Subscript[q,1,2]^3+q^7*t*Subscript[q,1,2]^3" -"(11,5,8,9)","q^3*t*Subscript[q,1,2]^2-q^6*t^2*Subscript[q,1,2]^2" -"(11,6,8,10)","q^6*Subscript[q,1,2]^3-q^3*t*Subscript[q,1,2]^3" +"(11,0,8,3)","q^2-q^4-2*q^6+q^10+q^12+2*q*t+2*q^3*t-2*q^5*t-2*q^7*t-q^4*t^2-q^6*t^2+q^8*t^2+q^10*t^2" +"(11,0,8,4)","q^4+q^6-q^8-q^10-t/q-2*q*t-q^3*t+3*q^5*t+2*q^7*t-q^9*t+q^4*t^2+q^6*t^2-q^8*t^2-q^10*t^2" +"(11,1,8,5)","q*t+q^3*t-q^5*t-q^7*t-q^4*t^2-q^6*t^2+q^8*t^2+q^10*t^2" +"(11,2,8,6)","q^4+q^6-q^8-q^10-q*t-q^3*t+q^5*t+q^7*t" +"(11,3,8,7)","q^3*t-q^6*t^2" +"(11,4,8,7)","-q^4+q^8+q^10-q^5*t" +"(11,3,8,8)","q^3*t-q^6*t^2" +"(11,4,8,8)","q^6-q*t-q^3*t+q^7*t" +"(11,5,8,9)","q^3*t-q^6*t^2" +"(11,6,8,10)","q^6-q^3*t" "(11,8,8,11)","q^6" "(11,0,9,2)","-1+q^2+q^4-2*q^6+q^8+q^10-q^12+q^3/t-q^5/t-q^7/t+q^9/t+q^3*t-q^5*t-q^7*t+q^9*t" -"(11,1,9,3)","-(q^2/Subscript[q,1,2])+q^6/Subscript[q,1,2]+q^8/Subscript[q,1,2]-q^12/Subscript[q,1,2]" +"(11,1,9,3)","-q^2+q^6+q^8-q^12" "(11,1,9,4)","q^2-2*q^6+q^10+q^3*t-q^5*t-q^7*t+q^9*t" "(11,3,9,6)","-q^6+q^10+q^3*t-q^7*t" -"(11,4,9,6)","q^2/Subscript[q,1,2]-q^6/Subscript[q,1,2]-q^5/(t*Subscript[q,1,2])+q^9/(t*Subscript[q,1,2])" -"(11,5,9,7)","q^4/Subscript[q,1,2]^2-q^10/Subscript[q,1,2]^2" +"(11,4,9,6)","q^2-q^6-q^5/t+q^9/t" +"(11,5,9,7)","q^4-q^10" "(11,5,9,8)","q^4-q^6+q^3*t-q^7*t" "(11,7,9,10)","-q^6+q^3*t" -"(11,8,9,10)","-(q^4/Subscript[q,1,2]^2)+q^7/(t*Subscript[q,1,2]^2)" -"(11,9,9,11)","-(q^6/Subscript[q,1,2]^3)" -"(11,0,10,1)","-Subscript[q,1,2]^3+q^2*Subscript[q,1,2]^3+q^4*Subscript[q,1,2]^3-q^6*Subscript[q,1,2]^3+(t*Subscript[q,1,2]^3)/q^3-(t*Subscript[q,1,2]^3)/q-q*t*Subscript[q,1,2]^3+2*q^3*t*Subscript[q,1,2]^3-q^5*t*Subscript[q,1,2]^3-q^7*t*Subscript[q,1,2]^3+q^9*t*Subscript[q,1,2]^3-t^2*Subscript[q,1,2]^3+q^2*t^2*Subscript[q,1,2]^3+q^4*t^2*Subscript[q,1,2]^3-q^6*t^2*Subscript[q,1,2]^3" -"(11,2,10,3)","-Subscript[q,1,2]^3+q^2*Subscript[q,1,2]^3+q^4*Subscript[q,1,2]^3-q^6*Subscript[q,1,2]^3-(t*Subscript[q,1,2]^3)/q+2*q^3*t*Subscript[q,1,2]^3-q^7*t*Subscript[q,1,2]^3" -"(11,2,10,4)","(t*Subscript[q,1,2]^4)/q^3-q*t*Subscript[q,1,2]^4-q^3*t*Subscript[q,1,2]^4+q^7*t*Subscript[q,1,2]^4" -"(11,3,10,5)","-((t*Subscript[q,1,2]^4)/q^3)+q*t*Subscript[q,1,2]^4+t^2*Subscript[q,1,2]^4-q^4*t^2*Subscript[q,1,2]^4" -"(11,4,10,5)","-Subscript[q,1,2]^3+q^4*Subscript[q,1,2]^3+q^3*t*Subscript[q,1,2]^3-q^7*t*Subscript[q,1,2]^3" -"(11,6,10,7)","-Subscript[q,1,2]^3+q^4*Subscript[q,1,2]^3-q*t*Subscript[q,1,2]^3+q^3*t*Subscript[q,1,2]^3" -"(11,6,10,8)","-((t*Subscript[q,1,2]^5)/q^3)+q^3*t*Subscript[q,1,2]^5" -"(11,7,10,9)","(t*Subscript[q,1,2]^5)/q^3-t^2*Subscript[q,1,2]^5" -"(11,8,10,9)","-Subscript[q,1,2]^3+q^3*t*Subscript[q,1,2]^3" -"(11,10,10,11)","-Subscript[q,1,2]^3" +"(11,8,9,10)","-q^4+q^7/t" +"(11,9,9,11)","-q^6" +"(11,0,10,1)","-1+q^2+q^4-q^6+t/q^3-t/q-q*t+2*q^3*t-q^5*t-q^7*t+q^9*t-t^2+q^2*t^2+q^4*t^2-q^6*t^2" +"(11,2,10,3)","-1+q^2+q^4-q^6-t/q+2*q^3*t-q^7*t" +"(11,2,10,4)","t/q^3-q*t-q^3*t+q^7*t" +"(11,3,10,5)","-(t/q^3)+q*t+t^2-q^4*t^2" +"(11,4,10,5)","-1+q^4+q^3*t-q^7*t" +"(11,6,10,7)","-1+q^4-q*t+q^3*t" +"(11,6,10,8)","-(t/q^3)+q^3*t" +"(11,7,10,9)","t/q^3-t^2" +"(11,8,10,9)","-1+q^3*t" +"(11,10,10,11)","-1" "(11,0,11,0)","2-2*q^2-2*q^4+2*q^6-q^3/t+q^5/t+q^7/t-q^9/t-q^3*t+q^5*t+q^7*t-q^9*t" "(11,1,11,1)","1-q^2-q^4+q^6-q^3*t+q^5*t+q^7*t-q^9*t" "(11,2,11,2)","1-q^2-q^4+q^6-q^3/t+q^5/t+q^7/t-q^9/t" "(11,3,11,3)","1-q^4-q^3*t+q^7*t" -"(11,4,11,3)","-(q^2/Subscript[q,1,2])+q^6/Subscript[q,1,2]+q^5/(t*Subscript[q,1,2])-q^9/(t*Subscript[q,1,2])" -"(11,3,11,4)","-Subscript[q,1,2]+q^4*Subscript[q,1,2]+q^3*t*Subscript[q,1,2]-q^7*t*Subscript[q,1,2]" +"(11,4,11,3)","-q^2+q^6+q^5/t-q^9/t" +"(11,3,11,4)","-1+q^4+q^3*t-q^7*t" "(11,4,11,4)","1-q^4-q^3/t+q^7/t" "(11,5,11,5)","1-q^4-q^3*t+q^7*t" "(11,6,11,6)","1-q^4-q^3/t+q^7/t" "(11,7,11,7)","1-q^3*t" -"(11,8,11,7)","q^4/Subscript[q,1,2]^2-q^7/(t*Subscript[q,1,2]^2)" -"(11,7,11,8)","Subscript[q,1,2]^2-q^3*t*Subscript[q,1,2]^2" +"(11,8,11,7)","q^4-q^7/t" +"(11,7,11,8)","1-q^3*t" "(11,8,11,8)","1-q^3/t" "(11,9,11,9)","1-q^3*t" "(11,10,11,10)","1-q^3/t" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/Rp.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/Rp.csv index 672d8c0..525ef17 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/Rp.csv +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/Rp.csv @@ -3,584 +3,584 @@ "(0,1,0,1)","1-1/(q^3*t)" "(0,2,0,2)","1-t/q^3" "(0,3,0,3)","1-1/(q^3*t)" -"(0,4,0,3)","1/(q^4*Subscript[q,1,2])-1/(q*t*Subscript[q,1,2])" -"(0,3,0,4)","Subscript[q,1,2]/q^6-(t*Subscript[q,1,2])/q^3" +"(0,4,0,3)","q^(-4)-1/(q*t)" +"(0,3,0,4)","q^(-6)-t/q^3" "(0,4,0,4)","1-t/q^3" "(0,5,0,5)","1-q^(-4)+1/(q^7*t)-1/(q^3*t)" "(0,6,0,6)","1-q^(-4)+t/q^7-t/q^3" "(0,7,0,7)","1-q^(-4)+1/(q^7*t)-1/(q^3*t)" -"(0,8,0,7)","1/(q^6*Subscript[q,1,2]^2)-1/(q^2*Subscript[q,1,2]^2)-1/(q^3*t*Subscript[q,1,2]^2)+q/(t*Subscript[q,1,2]^2)" -"(0,7,0,8)","Subscript[q,1,2]^2/q^10-Subscript[q,1,2]^2/q^6-(t*Subscript[q,1,2]^2)/q^7+(t*Subscript[q,1,2]^2)/q^3" +"(0,8,0,7)","q^(-6)-q^(-2)-1/(q^3*t)+q/t" +"(0,7,0,8)","q^(-10)-q^(-6)-t/q^7+t/q^3" "(0,8,0,8)","1-q^(-4)+t/q^7-t/q^3" "(0,9,0,9)","1+q^(-6)-q^(-4)-q^(-2)-1/(q^9*t)+1/(q^7*t)+1/(q^5*t)-1/(q^3*t)" "(0,10,0,10)","1+q^(-6)-q^(-4)-q^(-2)-t/q^9+t/q^7+t/q^5-t/q^3" "(0,11,0,11)","2+2/q^6-2/q^4-2/q^2-1/(q^9*t)+1/(q^7*t)+1/(q^5*t)-1/(q^3*t)-t/q^9+t/q^7+t/q^5-t/q^3" "(0,1,1,0)","1/(q^3*t)" "(0,3,1,2)","-q^(-6)+1/(q^3*t)" -"(0,4,1,2)","-(1/(q^4*Subscript[q,1,2]))+1/(q*t*Subscript[q,1,2])" -"(0,5,1,3)","1/(q^7*t*Subscript[q,1,2])-1/(q*t*Subscript[q,1,2])" +"(0,4,1,2)","-q^(-4)+1/(q*t)" +"(0,5,1,3)","1/(q^7*t)-1/(q*t)" "(0,5,1,4)","-q^(-6)+q^(-4)-1/(q^7*t)+1/(q^3*t)" "(0,7,1,6)","q^(-10)-q^(-6)-1/(q^7*t)+1/(q^3*t)" -"(0,8,1,6)","-(1/(q^6*Subscript[q,1,2]^2))+1/(q^2*Subscript[q,1,2]^2)+1/(q^3*t*Subscript[q,1,2]^2)-q/(t*Subscript[q,1,2]^2)" -"(0,9,1,7)","1/(q^9*t*Subscript[q,1,2]^2)-1/(q^5*t*Subscript[q,1,2]^2)-1/(q^3*t*Subscript[q,1,2]^2)+q/(t*Subscript[q,1,2]^2)" +"(0,8,1,6)","-q^(-6)+q^(-2)+1/(q^3*t)-q/t" +"(0,9,1,7)","1/(q^9*t)-1/(q^5*t)-1/(q^3*t)+q/t" "(0,9,1,8)","q^(-10)-2/q^6+q^(-2)+1/(q^9*t)-1/(q^7*t)-1/(q^5*t)+1/(q^3*t)" "(0,11,1,10)","-1-q^(-12)+q^(-10)+q^(-8)-2/q^6+q^(-4)+q^(-2)+1/(q^9*t)-1/(q^7*t)-1/(q^5*t)+1/(q^3*t)+t/q^9-t/q^7-t/q^5+t/q^3" "(0,2,2,0)","t/q^3" -"(0,3,2,1)","-(Subscript[q,1,2]/q^6)+(t*Subscript[q,1,2])/q^3" +"(0,3,2,1)","-q^(-6)+t/q^3" "(0,4,2,1)","-q^(-6)+t/q^3" "(0,6,2,3)","-q^(-6)+q^(-4)-t/q^7+t/q^3" -"(0,6,2,4)","(t*Subscript[q,1,2])/q^9-(t*Subscript[q,1,2])/q^3" -"(0,7,2,5)","-(Subscript[q,1,2]^2/q^10)+Subscript[q,1,2]^2/q^6+(t*Subscript[q,1,2]^2)/q^7-(t*Subscript[q,1,2]^2)/q^3" +"(0,6,2,4)","t/q^9-t/q^3" +"(0,7,2,5)","-q^(-10)+q^(-6)+t/q^7-t/q^3" "(0,8,2,5)","q^(-10)-q^(-6)-t/q^7+t/q^3" "(0,10,2,7)","q^(-10)-2/q^6+q^(-2)+t/q^9-t/q^7-t/q^5+t/q^3" -"(0,10,2,8)","(t*Subscript[q,1,2]^2)/q^13-(t*Subscript[q,1,2]^2)/q^9-(t*Subscript[q,1,2]^2)/q^7+(t*Subscript[q,1,2]^2)/q^3" -"(0,11,2,9)","-(Subscript[q,1,2]^3/q^12)+Subscript[q,1,2]^3/q^10+Subscript[q,1,2]^3/q^8-Subscript[q,1,2]^3/q^6+(t*Subscript[q,1,2]^3)/q^15-(t*Subscript[q,1,2]^3)/q^13-(t*Subscript[q,1,2]^3)/q^11+(2*t*Subscript[q,1,2]^3)/q^9-(t*Subscript[q,1,2]^3)/q^7-(t*Subscript[q,1,2]^3)/q^5+(t*Subscript[q,1,2]^3)/q^3-(t^2*Subscript[q,1,2]^3)/q^12+(t^2*Subscript[q,1,2]^3)/q^10+(t^2*Subscript[q,1,2]^3)/q^8-(t^2*Subscript[q,1,2]^3)/q^6" +"(0,10,2,8)","t/q^13-t/q^9-t/q^7+t/q^3" +"(0,11,2,9)","-q^(-12)+q^(-10)+q^(-8)-q^(-6)+t/q^15-t/q^13-t/q^11+(2*t)/q^9-t/q^7-t/q^5+t/q^3-t^2/q^12+t^2/q^10+t^2/q^8-t^2/q^6" "(0,3,3,0)","q^(-6)" "(0,5,3,1)","q^(-6)-1/(q^9*t)" -"(0,6,3,2)","-(1/(q^4*Subscript[q,1,2]))+t/(q^7*Subscript[q,1,2])" +"(0,6,3,2)","-q^(-4)+t/q^7" "(0,7,3,3)","-q^(-10)+q^(-6)+q^(-4)-1/(q^9*t)" -"(0,8,3,3)","-(1/(q^8*Subscript[q,1,2]^2))+1/(q^5*t*Subscript[q,1,2]^2)" -"(0,7,3,4)","Subscript[q,1,2]/q^12+Subscript[q,1,2]/q^10-Subscript[q,1,2]/q^6-(t*Subscript[q,1,2])/q^7" -"(0,8,3,4)","-(1/(q^4*Subscript[q,1,2]))+t/(q^7*Subscript[q,1,2])" +"(0,8,3,3)","-q^(-8)+1/(q^5*t)" +"(0,7,3,4)","q^(-12)+q^(-10)-q^(-6)-t/q^7" +"(0,8,3,4)","-q^(-4)+t/q^7" "(0,9,3,5)","-q^(-10)-q^(-8)+q^(-6)+q^(-4)+1/(q^13*t)+1/(q^11*t)-1/(q^9*t)-1/(q^7*t)" -"(0,10,3,6)","1/(q^8*Subscript[q,1,2])+1/(q^6*Subscript[q,1,2])-1/(q^4*Subscript[q,1,2])-1/(q^2*Subscript[q,1,2])-t/(q^11*Subscript[q,1,2])-t/(q^9*Subscript[q,1,2])+t/(q^7*Subscript[q,1,2])+t/(q^5*Subscript[q,1,2])" +"(0,10,3,6)","q^(-8)+q^(-6)-q^(-4)-q^(-2)-t/q^11-t/q^9+t/q^7+t/q^5" "(0,11,3,7)","q^(-12)-2/q^10-3/q^8+q^(-6)+2/q^4+q^(-2)+1/(q^13*t)+1/(q^11*t)-1/(q^9*t)-1/(q^7*t)+t/q^13+t/q^11-t/q^9-t/q^7" -"(0,11,3,8)","Subscript[q,1,2]^2/q^16+Subscript[q,1,2]^2/q^14-(2*Subscript[q,1,2]^2)/q^10-Subscript[q,1,2]^2/q^8+Subscript[q,1,2]^2/q^6-(2*t*Subscript[q,1,2]^2)/q^11-(2*t*Subscript[q,1,2]^2)/q^9+(2*t*Subscript[q,1,2]^2)/q^7+(2*t*Subscript[q,1,2]^2)/q^5+(t^2*Subscript[q,1,2]^2)/q^14+(t^2*Subscript[q,1,2]^2)/q^12-(t^2*Subscript[q,1,2]^2)/q^10-(t^2*Subscript[q,1,2]^2)/q^8" +"(0,11,3,8)","q^(-16)+q^(-14)-2/q^10-q^(-8)+q^(-6)-(2*t)/q^11-(2*t)/q^9+(2*t)/q^7+(2*t)/q^5+t^2/q^14+t^2/q^12-t^2/q^10-t^2/q^8" "(0,4,4,0)","q^(-6)" -"(0,5,4,1)","-(Subscript[q,1,2]/q^6)+Subscript[q,1,2]/(q^9*t)" +"(0,5,4,1)","-q^(-6)+1/(q^9*t)" "(0,6,4,2)","q^(-6)-t/q^9" -"(0,7,4,3)","-(Subscript[q,1,2]/q^6)+Subscript[q,1,2]/(q^9*t)" -"(0,8,4,3)","1/(q^10*Subscript[q,1,2])+1/(q^8*Subscript[q,1,2])-1/(q^4*Subscript[q,1,2])-1/(q^5*t*Subscript[q,1,2])" -"(0,7,4,4)","-(Subscript[q,1,2]^2/q^12)+(t*Subscript[q,1,2]^2)/q^9" +"(0,7,4,3)","-q^(-6)+1/(q^9*t)" +"(0,8,4,3)","q^(-10)+q^(-8)-q^(-4)-1/(q^5*t)" +"(0,7,4,4)","-q^(-12)+t/q^9" "(0,8,4,4)","-q^(-10)+q^(-6)+q^(-4)-t/q^9" -"(0,9,4,5)","Subscript[q,1,2]/q^10+Subscript[q,1,2]/q^8-Subscript[q,1,2]/q^6-Subscript[q,1,2]/q^4-Subscript[q,1,2]/(q^13*t)-Subscript[q,1,2]/(q^11*t)+Subscript[q,1,2]/(q^9*t)+Subscript[q,1,2]/(q^7*t)" +"(0,9,4,5)","q^(-10)+q^(-8)-q^(-6)-q^(-4)-1/(q^13*t)-1/(q^11*t)+1/(q^9*t)+1/(q^7*t)" "(0,10,4,6)","-q^(-10)-q^(-8)+q^(-6)+q^(-4)+t/q^13+t/q^11-t/q^9-t/q^7" -"(0,11,4,7)","(2*Subscript[q,1,2])/q^10+(2*Subscript[q,1,2])/q^8-(2*Subscript[q,1,2])/q^6-(2*Subscript[q,1,2])/q^4-Subscript[q,1,2]/(q^13*t)-Subscript[q,1,2]/(q^11*t)+Subscript[q,1,2]/(q^9*t)+Subscript[q,1,2]/(q^7*t)-(t*Subscript[q,1,2])/q^15-(t*Subscript[q,1,2])/q^13+(2*t*Subscript[q,1,2])/q^9+(t*Subscript[q,1,2])/q^7-(t*Subscript[q,1,2])/q^5" -"(0,11,4,8)","-(Subscript[q,1,2]^3/q^16)-Subscript[q,1,2]^3/q^14+Subscript[q,1,2]^3/q^12+Subscript[q,1,2]^3/q^10-(t*Subscript[q,1,2]^3)/q^15+(2*t*Subscript[q,1,2]^3)/q^13+(3*t*Subscript[q,1,2]^3)/q^11-(t*Subscript[q,1,2]^3)/q^9-(2*t*Subscript[q,1,2]^3)/q^7-(t*Subscript[q,1,2]^3)/q^5-(t^2*Subscript[q,1,2]^3)/q^16-(t^2*Subscript[q,1,2]^3)/q^14+(t^2*Subscript[q,1,2]^3)/q^12+(t^2*Subscript[q,1,2]^3)/q^10" +"(0,11,4,7)","2/q^10+2/q^8-2/q^6-2/q^4-1/(q^13*t)-1/(q^11*t)+1/(q^9*t)+1/(q^7*t)-t/q^15-t/q^13+(2*t)/q^9+t/q^7-t/q^5" +"(0,11,4,8)","-q^(-16)-q^(-14)+q^(-12)+q^(-10)-t/q^15+(2*t)/q^13+(3*t)/q^11-t/q^9-(2*t)/q^7-t/q^5-t^2/q^16-t^2/q^14+t^2/q^12+t^2/q^10" "(0,5,5,0)","1/(q^9*t)" "(0,7,5,2)","-q^(-12)+1/(q^9*t)" -"(0,8,5,2)","1/(q^8*Subscript[q,1,2]^2)-1/(q^5*t*Subscript[q,1,2]^2)" -"(0,9,5,3)","1/(q^13*t*Subscript[q,1,2])+1/(q^11*t*Subscript[q,1,2])-1/(q^7*t*Subscript[q,1,2])-1/(q^5*t*Subscript[q,1,2])" +"(0,8,5,2)","q^(-8)-1/(q^5*t)" +"(0,9,5,3)","1/(q^13*t)+1/(q^11*t)-1/(q^7*t)-1/(q^5*t)" "(0,9,5,4)","-q^(-12)+q^(-8)-1/(q^13*t)-1/(q^11*t)+1/(q^9*t)+1/(q^7*t)" "(0,11,5,6)","q^(-16)+q^(-14)-q^(-12)+q^(-8)-q^(-6)-q^(-4)-1/(q^13*t)-1/(q^11*t)+1/(q^9*t)+1/(q^7*t)-t/q^13-t/q^11+t/q^9+t/q^7" "(0,6,6,0)","t/q^9" -"(0,7,6,1)","Subscript[q,1,2]^2/q^12-(t*Subscript[q,1,2]^2)/q^9" +"(0,7,6,1)","q^(-12)-t/q^9" "(0,8,6,1)","-q^(-12)+t/q^9" "(0,10,6,3)","-q^(-12)+q^(-8)-t/q^13-t/q^11+t/q^9+t/q^7" -"(0,10,6,4)","(t*Subscript[q,1,2])/q^15+(t*Subscript[q,1,2])/q^13-(t*Subscript[q,1,2])/q^9-(t*Subscript[q,1,2])/q^7" -"(0,11,6,5)","Subscript[q,1,2]^3/q^16+Subscript[q,1,2]^3/q^14-Subscript[q,1,2]^3/q^12-Subscript[q,1,2]^3/q^10-(t*Subscript[q,1,2]^3)/q^19-(t*Subscript[q,1,2]^3)/q^17+(t*Subscript[q,1,2]^3)/q^15-(t*Subscript[q,1,2]^3)/q^11+(t*Subscript[q,1,2]^3)/q^9+(t*Subscript[q,1,2]^3)/q^7+(t^2*Subscript[q,1,2]^3)/q^16+(t^2*Subscript[q,1,2]^3)/q^14-(t^2*Subscript[q,1,2]^3)/q^12-(t^2*Subscript[q,1,2]^3)/q^10" +"(0,10,6,4)","t/q^15+t/q^13-t/q^9-t/q^7" +"(0,11,6,5)","q^(-16)+q^(-14)-q^(-12)-q^(-10)-t/q^19-t/q^17+t/q^15-t/q^11+t/q^9+t/q^7+t^2/q^16+t^2/q^14-t^2/q^12-t^2/q^10" "(0,7,7,0)","q^(-12)" "(0,9,7,1)","q^(-12)-1/(q^15*t)" -"(0,10,7,2)","1/(q^8*Subscript[q,1,2]^2)-t/(q^11*Subscript[q,1,2]^2)" +"(0,10,7,2)","q^(-8)-t/q^11" "(0,11,7,3)","-q^(-16)-q^(-14)+2/q^12+q^(-10)+q^(-8)-1/(q^15*t)-t/q^15" -"(0,11,7,4)","Subscript[q,1,2]/q^18+Subscript[q,1,2]/q^16+Subscript[q,1,2]/q^14-Subscript[q,1,2]/q^12-Subscript[q,1,2]/q^10-(2*t*Subscript[q,1,2])/q^11+(t^2*Subscript[q,1,2])/q^14" +"(0,11,7,4)","q^(-18)+q^(-16)+q^(-14)-q^(-12)-q^(-10)-(2*t)/q^11+t^2/q^14" "(0,8,8,0)","q^(-12)" -"(0,9,8,1)","Subscript[q,1,2]^2/q^12-Subscript[q,1,2]^2/(q^15*t)" +"(0,9,8,1)","q^(-12)-1/(q^15*t)" "(0,10,8,2)","q^(-12)-t/q^15" -"(0,11,8,3)","(2*Subscript[q,1,2]^2)/q^12-Subscript[q,1,2]^2/(q^15*t)-(t*Subscript[q,1,2]^2)/q^19-(t*Subscript[q,1,2]^2)/q^17-(t*Subscript[q,1,2]^2)/q^15+(t*Subscript[q,1,2]^2)/q^13+(t*Subscript[q,1,2]^2)/q^11" -"(0,11,8,4)","Subscript[q,1,2]^3/q^18+(t*Subscript[q,1,2]^3)/q^19+(t*Subscript[q,1,2]^3)/q^17-(2*t*Subscript[q,1,2]^3)/q^15-(t*Subscript[q,1,2]^3)/q^13-(t*Subscript[q,1,2]^3)/q^11+(t^2*Subscript[q,1,2]^3)/q^18" +"(0,11,8,3)","2/q^12-1/(q^15*t)-t/q^19-t/q^17-t/q^15+t/q^13+t/q^11" +"(0,11,8,4)","q^(-18)+t/q^19+t/q^17-(2*t)/q^15-t/q^13-t/q^11+t^2/q^18" "(0,9,9,0)","1/(q^15*t)" "(0,11,9,2)","-q^(-18)-q^(-12)+1/(q^15*t)+t/q^15" "(0,10,10,0)","t/q^15" -"(0,11,10,1)","-(Subscript[q,1,2]^3/q^18)+(t*Subscript[q,1,2]^3)/q^21+(t*Subscript[q,1,2]^3)/q^15-(t^2*Subscript[q,1,2]^3)/q^18" +"(0,11,10,1)","-q^(-18)+t/q^21+t/q^15-t^2/q^18" "(0,11,11,0)","q^(-18)" "(1,0,0,1)","1" "(1,2,0,3)","1" -"(1,2,0,4)","-((t*Subscript[q,1,2])/q^3)" +"(1,2,0,4)","-(t/q^3)" "(1,4,0,5)","1-q^(-4)" "(1,6,0,7)","1-q^(-4)" -"(1,6,0,8)","-((t*Subscript[q,1,2]^2)/q^7)+(t*Subscript[q,1,2]^2)/q^3" +"(1,6,0,8)","-(t/q^7)+t/q^3" "(1,8,0,9)","1+q^(-6)-q^(-4)-q^(-2)" "(1,10,0,11)","1+q^(-6)-q^(-4)-q^(-2)" "(1,1,1,1)","-(1/(q^3*t))" "(1,3,1,3)","-(1/(q^3*t))" -"(1,4,1,3)","-(1/(q*t*Subscript[q,1,2]))" -"(1,3,1,4)","Subscript[q,1,2]/q^6" +"(1,4,1,3)","-(1/(q*t))" +"(1,3,1,4)","q^(-6)" "(1,4,1,4)","q^(-4)" "(1,5,1,5)","1/(q^7*t)-1/(q^3*t)" "(1,7,1,7)","1/(q^7*t)-1/(q^3*t)" -"(1,8,1,7)","-(1/(q^3*t*Subscript[q,1,2]^2))+q/(t*Subscript[q,1,2]^2)" -"(1,7,1,8)","Subscript[q,1,2]^2/q^10-Subscript[q,1,2]^2/q^6" +"(1,8,1,7)","-(1/(q^3*t))+q/t" +"(1,7,1,8)","q^(-10)-q^(-6)" "(1,8,1,8)","-q^(-6)+q^(-2)" "(1,9,1,9)","-(1/(q^9*t))+1/(q^7*t)+1/(q^5*t)-1/(q^3*t)" "(1,11,1,11)","1+q^(-6)-q^(-4)-q^(-2)-1/(q^9*t)+1/(q^7*t)+1/(q^5*t)-1/(q^3*t)" -"(1,2,2,1)","(t*Subscript[q,1,2])/q^3" -"(1,6,2,5)","(t*Subscript[q,1,2]^2)/q^7-(t*Subscript[q,1,2]^2)/q^3" -"(1,10,2,9)","(t*Subscript[q,1,2]^3)/q^9-(t*Subscript[q,1,2]^3)/q^7-(t*Subscript[q,1,2]^3)/q^5+(t*Subscript[q,1,2]^3)/q^3" -"(1,3,3,1)","-(Subscript[q,1,2]/q^6)" +"(1,2,2,1)","t/q^3" +"(1,6,2,5)","t/q^7-t/q^3" +"(1,10,2,9)","t/q^9-t/q^7-t/q^5+t/q^3" +"(1,3,3,1)","-q^(-6)" "(1,6,3,3)","q^(-4)" -"(1,6,3,4)","-((t*Subscript[q,1,2])/q^7)" -"(1,7,3,5)","-(Subscript[q,1,2]^2/q^10)+Subscript[q,1,2]^2/q^6" +"(1,6,3,4)","-(t/q^7)" +"(1,7,3,5)","-q^(-10)+q^(-6)" "(1,8,3,5)","-q^(-8)+q^(-4)" "(1,10,3,7)","-q^(-8)-q^(-6)+q^(-4)+q^(-2)" -"(1,10,3,8)","-((t*Subscript[q,1,2]^2)/q^11)-(t*Subscript[q,1,2]^2)/q^9+(t*Subscript[q,1,2]^2)/q^7+(t*Subscript[q,1,2]^2)/q^5" -"(1,11,3,9)","-(Subscript[q,1,2]^3/q^12)+Subscript[q,1,2]^3/q^10+Subscript[q,1,2]^3/q^8-Subscript[q,1,2]^3/q^6-(t*Subscript[q,1,2]^3)/q^13+(2*t*Subscript[q,1,2]^3)/q^9-(t*Subscript[q,1,2]^3)/q^5" -"(1,4,4,1)","-(Subscript[q,1,2]/q^6)" -"(1,6,4,3)","-(Subscript[q,1,2]/q^6)" -"(1,6,4,4)","(t*Subscript[q,1,2]^2)/q^9" -"(1,8,4,5)","Subscript[q,1,2]/q^10+Subscript[q,1,2]/q^8-Subscript[q,1,2]/q^6-Subscript[q,1,2]/q^4" -"(1,10,4,7)","Subscript[q,1,2]/q^10+Subscript[q,1,2]/q^8-Subscript[q,1,2]/q^6-Subscript[q,1,2]/q^4" -"(1,10,4,8)","(t*Subscript[q,1,2]^3)/q^13+(t*Subscript[q,1,2]^3)/q^11-(t*Subscript[q,1,2]^3)/q^9-(t*Subscript[q,1,2]^3)/q^7" -"(1,11,4,9)","(t*Subscript[q,1,2]^4)/q^15-(t*Subscript[q,1,2]^4)/q^11-(t*Subscript[q,1,2]^4)/q^9+(t*Subscript[q,1,2]^4)/q^5" -"(1,5,5,1)","Subscript[q,1,2]/(q^9*t)" -"(1,7,5,3)","Subscript[q,1,2]/(q^9*t)" -"(1,8,5,3)","-(1/(q^5*t*Subscript[q,1,2]))" -"(1,7,5,4)","-(Subscript[q,1,2]^2/q^12)" +"(1,10,3,8)","-(t/q^11)-t/q^9+t/q^7+t/q^5" +"(1,11,3,9)","-q^(-12)+q^(-10)+q^(-8)-q^(-6)-t/q^13+(2*t)/q^9-t/q^5" +"(1,4,4,1)","-q^(-6)" +"(1,6,4,3)","-q^(-6)" +"(1,6,4,4)","t/q^9" +"(1,8,4,5)","q^(-10)+q^(-8)-q^(-6)-q^(-4)" +"(1,10,4,7)","q^(-10)+q^(-8)-q^(-6)-q^(-4)" +"(1,10,4,8)","t/q^13+t/q^11-t/q^9-t/q^7" +"(1,11,4,9)","t/q^15-t/q^11-t/q^9+t/q^5" +"(1,5,5,1)","1/(q^9*t)" +"(1,7,5,3)","1/(q^9*t)" +"(1,8,5,3)","-(1/(q^5*t))" +"(1,7,5,4)","-q^(-12)" "(1,8,5,4)","q^(-8)" -"(1,9,5,5)","-(Subscript[q,1,2]/(q^13*t))-Subscript[q,1,2]/(q^11*t)+Subscript[q,1,2]/(q^9*t)+Subscript[q,1,2]/(q^7*t)" -"(1,11,5,7)","Subscript[q,1,2]/q^10+Subscript[q,1,2]/q^8-Subscript[q,1,2]/q^6-Subscript[q,1,2]/q^4-Subscript[q,1,2]/(q^13*t)-Subscript[q,1,2]/(q^11*t)+Subscript[q,1,2]/(q^9*t)+Subscript[q,1,2]/(q^7*t)" -"(1,11,5,8)","-(Subscript[q,1,2]^3/q^16)-Subscript[q,1,2]^3/q^14+Subscript[q,1,2]^3/q^12+Subscript[q,1,2]^3/q^10+(t*Subscript[q,1,2]^3)/q^13+(t*Subscript[q,1,2]^3)/q^11-(t*Subscript[q,1,2]^3)/q^9-(t*Subscript[q,1,2]^3)/q^7" -"(1,6,6,1)","-((t*Subscript[q,1,2]^2)/q^9)" -"(1,10,6,5)","-((t*Subscript[q,1,2]^3)/q^13)-(t*Subscript[q,1,2]^3)/q^11+(t*Subscript[q,1,2]^3)/q^9+(t*Subscript[q,1,2]^3)/q^7" -"(1,7,7,1)","Subscript[q,1,2]^2/q^12" +"(1,9,5,5)","-(1/(q^13*t))-1/(q^11*t)+1/(q^9*t)+1/(q^7*t)" +"(1,11,5,7)","q^(-10)+q^(-8)-q^(-6)-q^(-4)-1/(q^13*t)-1/(q^11*t)+1/(q^9*t)+1/(q^7*t)" +"(1,11,5,8)","-q^(-16)-q^(-14)+q^(-12)+q^(-10)+t/q^13+t/q^11-t/q^9-t/q^7" +"(1,6,6,1)","-(t/q^9)" +"(1,10,6,5)","-(t/q^13)-t/q^11+t/q^9+t/q^7" +"(1,7,7,1)","q^(-12)" "(1,10,7,3)","q^(-8)" -"(1,10,7,4)","-((t*Subscript[q,1,2])/q^11)" -"(1,11,7,5)","Subscript[q,1,2]^3/q^16+Subscript[q,1,2]^3/q^14-Subscript[q,1,2]^3/q^12-Subscript[q,1,2]^3/q^10+(t*Subscript[q,1,2]^3)/q^15-(t*Subscript[q,1,2]^3)/q^11" -"(1,8,8,1)","Subscript[q,1,2]^2/q^12" -"(1,10,8,3)","Subscript[q,1,2]^2/q^12" -"(1,10,8,4)","-((t*Subscript[q,1,2]^3)/q^15)" -"(1,11,8,5)","(t*Subscript[q,1,2]^5)/q^19+(t*Subscript[q,1,2]^5)/q^17-(t*Subscript[q,1,2]^5)/q^13-(t*Subscript[q,1,2]^5)/q^11" -"(1,9,9,1)","-(Subscript[q,1,2]^2/(q^15*t))" -"(1,11,9,3)","Subscript[q,1,2]^2/q^12-Subscript[q,1,2]^2/(q^15*t)" -"(1,11,9,4)","Subscript[q,1,2]^3/q^18-(t*Subscript[q,1,2]^3)/q^15" -"(1,10,10,1)","(t*Subscript[q,1,2]^3)/q^15" -"(1,11,11,1)","-(Subscript[q,1,2]^3/q^18)" +"(1,10,7,4)","-(t/q^11)" +"(1,11,7,5)","q^(-16)+q^(-14)-q^(-12)-q^(-10)+t/q^15-t/q^11" +"(1,8,8,1)","q^(-12)" +"(1,10,8,3)","q^(-12)" +"(1,10,8,4)","-(t/q^15)" +"(1,11,8,5)","t/q^19+t/q^17-t/q^13-t/q^11" +"(1,9,9,1)","-(1/(q^15*t))" +"(1,11,9,3)","q^(-12)-1/(q^15*t)" +"(1,11,9,4)","q^(-18)-t/q^15" +"(1,10,10,1)","t/q^15" +"(1,11,11,1)","-q^(-18)" "(2,0,0,2)","1" -"(2,1,0,3)","-(1/(q*t*Subscript[q,1,2]))" +"(2,1,0,3)","-(1/(q*t))" "(2,1,0,4)","1" "(2,3,0,6)","1-q^(-4)" -"(2,5,0,7)","-(1/(q^3*t*Subscript[q,1,2]^2))+q/(t*Subscript[q,1,2]^2)" +"(2,5,0,7)","-(1/(q^3*t))+q/t" "(2,5,0,8)","1-q^(-4)" "(2,7,0,10)","1+q^(-6)-q^(-4)-q^(-2)" -"(2,9,0,11)","-(1/(q^3*t*Subscript[q,1,2]^3))+1/(q*t*Subscript[q,1,2]^3)+q/(t*Subscript[q,1,2]^3)-q^3/(t*Subscript[q,1,2]^3)" -"(2,1,1,2)","1/(q*t*Subscript[q,1,2])" -"(2,5,1,6)","1/(q^3*t*Subscript[q,1,2]^2)-q/(t*Subscript[q,1,2]^2)" -"(2,9,1,10)","1/(q^3*t*Subscript[q,1,2]^3)-1/(q*t*Subscript[q,1,2]^3)-q/(t*Subscript[q,1,2]^3)+q^3/(t*Subscript[q,1,2]^3)" +"(2,9,0,11)","-(1/(q^3*t))+1/(q*t)+q/t-q^3/t" +"(2,1,1,2)","1/(q*t)" +"(2,5,1,6)","1/(q^3*t)-q/t" +"(2,9,1,10)","1/(q^3*t)-1/(q*t)-q/t+q^3/t" "(2,2,2,2)","-(t/q^3)" "(2,3,2,3)","q^(-4)" -"(2,4,2,3)","1/(q^4*Subscript[q,1,2])" -"(2,3,2,4)","-((t*Subscript[q,1,2])/q^3)" +"(2,4,2,3)","q^(-4)" +"(2,3,2,4)","-(t/q^3)" "(2,4,2,4)","-(t/q^3)" "(2,6,2,6)","t/q^7-t/q^3" "(2,7,2,7)","-q^(-6)+q^(-2)" -"(2,8,2,7)","1/(q^6*Subscript[q,1,2]^2)-1/(q^2*Subscript[q,1,2]^2)" -"(2,7,2,8)","-((t*Subscript[q,1,2]^2)/q^7)+(t*Subscript[q,1,2]^2)/q^3" +"(2,8,2,7)","q^(-6)-q^(-2)" +"(2,7,2,8)","-(t/q^7)+t/q^3" "(2,8,2,8)","t/q^7-t/q^3" "(2,10,2,10)","-(t/q^9)+t/q^7+t/q^5-t/q^3" "(2,11,2,11)","1+q^(-6)-q^(-4)-q^(-2)-t/q^9+t/q^7+t/q^5-t/q^3" -"(2,3,3,2)","-(1/(q^4*Subscript[q,1,2]))" -"(2,5,3,3)","1/(q^5*t*Subscript[q,1,2]^2)" -"(2,5,3,4)","-(1/(q^4*Subscript[q,1,2]))" -"(2,7,3,6)","1/(q^8*Subscript[q,1,2])+1/(q^6*Subscript[q,1,2])-1/(q^4*Subscript[q,1,2])-1/(q^2*Subscript[q,1,2])" -"(2,9,3,7)","1/(q^7*t*Subscript[q,1,2]^3)+1/(q^5*t*Subscript[q,1,2]^3)-1/(q^3*t*Subscript[q,1,2]^3)-1/(q*t*Subscript[q,1,2]^3)" -"(2,9,3,8)","1/(q^8*Subscript[q,1,2])+1/(q^6*Subscript[q,1,2])-1/(q^4*Subscript[q,1,2])-1/(q^2*Subscript[q,1,2])" -"(2,11,3,10)","-Subscript[q,1,2]^(-1)-1/(q^10*Subscript[q,1,2])+1/(q^6*Subscript[q,1,2])+1/(q^4*Subscript[q,1,2])" -"(2,4,4,2)","-(1/(q^4*Subscript[q,1,2]))" -"(2,5,4,3)","-(1/(q^5*t*Subscript[q,1,2]))" +"(2,3,3,2)","-q^(-4)" +"(2,5,3,3)","1/(q^5*t)" +"(2,5,3,4)","-q^(-4)" +"(2,7,3,6)","q^(-8)+q^(-6)-q^(-4)-q^(-2)" +"(2,9,3,7)","1/(q^7*t)+1/(q^5*t)-1/(q^3*t)-1/(q*t)" +"(2,9,3,8)","q^(-8)+q^(-6)-q^(-4)-q^(-2)" +"(2,11,3,10)","-1-q^(-10)+q^(-6)+q^(-4)" +"(2,4,4,2)","-q^(-4)" +"(2,5,4,3)","-(1/(q^5*t))" "(2,5,4,4)","q^(-4)" "(2,7,4,6)","-q^(-8)+q^(-4)" -"(2,8,4,6)","-(1/(q^6*Subscript[q,1,2]^2))+1/(q^2*Subscript[q,1,2]^2)" -"(2,9,4,7)","-(1/(q^7*t*Subscript[q,1,2]^2))-1/(q^5*t*Subscript[q,1,2]^2)+1/(q^3*t*Subscript[q,1,2]^2)+1/(q*t*Subscript[q,1,2]^2)" +"(2,8,4,6)","-q^(-6)+q^(-2)" +"(2,9,4,7)","-(1/(q^7*t))-1/(q^5*t)+1/(q^3*t)+1/(q*t)" "(2,9,4,8)","-q^(-8)-q^(-6)+q^(-4)+q^(-2)" "(2,11,4,10)","q^(-10)-2/q^6+q^(-2)+t/q^9-t/q^7-t/q^5+t/q^3" -"(2,5,5,2)","-(1/(q^5*t*Subscript[q,1,2]^2))" -"(2,9,5,6)","-(1/(q^7*t*Subscript[q,1,2]^3))-1/(q^5*t*Subscript[q,1,2]^3)+1/(q^3*t*Subscript[q,1,2]^3)+1/(q*t*Subscript[q,1,2]^3)" -"(2,6,6,2)","t/(q^7*Subscript[q,1,2])" +"(2,5,5,2)","-(1/(q^5*t))" +"(2,9,5,6)","-(1/(q^7*t))-1/(q^5*t)+1/(q^3*t)+1/(q*t)" +"(2,6,6,2)","t/q^7" "(2,7,6,3)","q^(-8)" -"(2,8,6,3)","-(1/(q^8*Subscript[q,1,2]^2))" -"(2,7,6,4)","-((t*Subscript[q,1,2])/q^7)" -"(2,8,6,4)","t/(q^7*Subscript[q,1,2])" -"(2,10,6,6)","-(t/(q^11*Subscript[q,1,2]))-t/(q^9*Subscript[q,1,2])+t/(q^7*Subscript[q,1,2])+t/(q^5*Subscript[q,1,2])" +"(2,8,6,3)","-q^(-8)" +"(2,7,6,4)","-(t/q^7)" +"(2,8,6,4)","t/q^7" +"(2,10,6,6)","-(t/q^11)-t/q^9+t/q^7+t/q^5" "(2,11,6,7)","-q^(-10)-q^(-8)+q^(-6)+q^(-4)+t/q^13+t/q^11-t/q^9-t/q^7" -"(2,11,6,8)","-((t*Subscript[q,1,2]^2)/q^11)-(t*Subscript[q,1,2]^2)/q^9+(t*Subscript[q,1,2]^2)/q^7+(t*Subscript[q,1,2]^2)/q^5+(t^2*Subscript[q,1,2]^2)/q^14+(t^2*Subscript[q,1,2]^2)/q^12-(t^2*Subscript[q,1,2]^2)/q^10-(t^2*Subscript[q,1,2]^2)/q^8" -"(2,7,7,2)","1/(q^8*Subscript[q,1,2]^2)" -"(2,9,7,3)","-(1/(q^9*t*Subscript[q,1,2]^3))" -"(2,9,7,4)","1/(q^8*Subscript[q,1,2]^2)" -"(2,11,7,6)","-(1/(q^12*Subscript[q,1,2]^2))-1/(q^10*Subscript[q,1,2]^2)+1/(q^6*Subscript[q,1,2]^2)+1/(q^4*Subscript[q,1,2]^2)" -"(2,8,8,2)","1/(q^8*Subscript[q,1,2]^2)" -"(2,9,8,3)","-(1/(q^9*t*Subscript[q,1,2]))" +"(2,11,6,8)","-(t/q^11)-t/q^9+t/q^7+t/q^5+t^2/q^14+t^2/q^12-t^2/q^10-t^2/q^8" +"(2,7,7,2)","q^(-8)" +"(2,9,7,3)","-(1/(q^9*t))" +"(2,9,7,4)","q^(-8)" +"(2,11,7,6)","-q^(-12)-q^(-10)+q^(-6)+q^(-4)" +"(2,8,8,2)","q^(-8)" +"(2,9,8,3)","-(1/(q^9*t))" "(2,9,8,4)","q^(-8)" "(2,11,8,6)","-q^(-12)+q^(-8)-t/q^13-t/q^11+t/q^9+t/q^7" -"(2,9,9,2)","1/(q^9*t*Subscript[q,1,2]^3)" -"(2,10,10,2)","-(t/(q^11*Subscript[q,1,2]^2))" +"(2,9,9,2)","1/(q^9*t)" +"(2,10,10,2)","-(t/q^11)" "(2,11,10,3)","q^(-12)-t/q^15" -"(2,11,10,4)","-((t*Subscript[q,1,2])/q^11)+(t^2*Subscript[q,1,2])/q^14" -"(2,11,11,2)","-(1/(q^12*Subscript[q,1,2]^3))" +"(2,11,10,4)","-(t/q^11)+t^2/q^14" +"(2,11,11,2)","-q^(-12)" "(3,0,0,3)","1" "(3,1,0,5)","1" -"(3,2,0,6)","(t*Subscript[q,1,2])/q^3" +"(3,2,0,6)","t/q^3" "(3,3,0,7)","1" -"(3,4,0,7)","1/(q^2*Subscript[q,1,2])" -"(3,3,0,8)","(t*Subscript[q,1,2]^2)/q^3" -"(3,4,0,8)","(t*Subscript[q,1,2])/q^3" +"(3,4,0,7)","q^(-2)" +"(3,3,0,8)","t/q^3" +"(3,4,0,8)","t/q^3" "(3,5,0,9)","1-q^(-2)" -"(3,6,0,10)","-((t*Subscript[q,1,2])/q^5)+(t*Subscript[q,1,2])/q^3" +"(3,6,0,10)","-(t/q^5)+t/q^3" "(3,7,0,11)","1-q^(-2)" -"(3,8,0,11)","-Subscript[q,1,2]^(-2)+1/(q^2*Subscript[q,1,2]^2)" -"(3,1,1,3)","-(1/(q*t*Subscript[q,1,2]))" +"(3,8,0,11)","-1+q^(-2)" +"(3,1,1,3)","-(1/(q*t))" "(3,3,1,6)","-q^(-4)" -"(3,4,1,6)","-(1/(q^2*Subscript[q,1,2]))" -"(3,5,1,7)","-(1/(q^3*t*Subscript[q,1,2]^2))+q/(t*Subscript[q,1,2]^2)" +"(3,4,1,6)","-q^(-2)" +"(3,5,1,7)","-(1/(q^3*t))+q/t" "(3,5,1,8)","-q^(-4)+q^(-2)" "(3,7,1,10)","q^(-6)-q^(-4)" -"(3,8,1,10)","Subscript[q,1,2]^(-2)-1/(q^2*Subscript[q,1,2]^2)" -"(3,9,1,11)","-(1/(q^3*t*Subscript[q,1,2]^3))+1/(q*t*Subscript[q,1,2]^3)+q/(t*Subscript[q,1,2]^3)-q^3/(t*Subscript[q,1,2]^3)" -"(3,2,2,3)","-((t*Subscript[q,1,2])/q^3)" -"(3,3,2,5)","-((t*Subscript[q,1,2]^2)/q^3)" -"(3,4,2,5)","-((t*Subscript[q,1,2])/q^3)" -"(3,6,2,7)","(t*Subscript[q,1,2])/q^5-(t*Subscript[q,1,2])/q^3" -"(3,7,2,9)","-((t*Subscript[q,1,2]^3)/q^5)+(t*Subscript[q,1,2]^3)/q^3" -"(3,8,2,9)","(t*Subscript[q,1,2])/q^5-(t*Subscript[q,1,2])/q^3" +"(3,8,1,10)","1-q^(-2)" +"(3,9,1,11)","-(1/(q^3*t))+1/(q*t)+q/t-q^3/t" +"(3,2,2,3)","-(t/q^3)" +"(3,3,2,5)","-(t/q^3)" +"(3,4,2,5)","-(t/q^3)" +"(3,6,2,7)","t/q^5-t/q^3" +"(3,7,2,9)","-(t/q^5)+t/q^3" +"(3,8,2,9)","t/q^5-t/q^3" "(3,3,3,3)","q^(-4)" "(3,5,3,5)","q^(-4)" "(3,6,3,6)","-(t/q^5)" "(3,7,3,7)","-q^(-6)+q^(-4)+q^(-2)" -"(3,8,3,7)","-(1/(q^4*Subscript[q,1,2]^2))" -"(3,7,3,8)","(t*Subscript[q,1,2]^2)/q^5" +"(3,8,3,7)","-q^(-4)" +"(3,7,3,8)","t/q^5" "(3,8,3,8)","-(t/q^5)" "(3,9,3,9)","-q^(-6)+q^(-2)" "(3,10,3,10)","t/q^7-t/q^3" "(3,11,3,11)","1-q^(-4)+t/q^7-t/q^3" "(3,4,4,3)","q^(-4)" -"(3,5,4,5)","-(Subscript[q,1,2]/q^4)" -"(3,6,4,6)","(t*Subscript[q,1,2])/q^7" -"(3,7,4,7)","-(Subscript[q,1,2]/q^4)" -"(3,8,4,7)","1/(q^6*Subscript[q,1,2])+1/(q^4*Subscript[q,1,2])-1/(q^2*Subscript[q,1,2])" -"(3,7,4,8)","-((t*Subscript[q,1,2]^3)/q^7)" -"(3,8,4,8)","(t*Subscript[q,1,2])/q^7" -"(3,9,4,9)","Subscript[q,1,2]/q^6-Subscript[q,1,2]/q^2" -"(3,10,4,10)","-((t*Subscript[q,1,2])/q^9)+(t*Subscript[q,1,2])/q^5" -"(3,11,4,11)","Subscript[q,1,2]/q^6-Subscript[q,1,2]/q^2-(t*Subscript[q,1,2])/q^9+(t*Subscript[q,1,2])/q^5" -"(3,5,5,3)","-(1/(q^5*t*Subscript[q,1,2]))" +"(3,5,4,5)","-q^(-4)" +"(3,6,4,6)","t/q^7" +"(3,7,4,7)","-q^(-4)" +"(3,8,4,7)","q^(-6)+q^(-4)-q^(-2)" +"(3,7,4,8)","-(t/q^7)" +"(3,8,4,8)","t/q^7" +"(3,9,4,9)","q^(-6)-q^(-2)" +"(3,10,4,10)","-(t/q^9)+t/q^5" +"(3,11,4,11)","q^(-6)-q^(-2)-t/q^9+t/q^5" +"(3,5,5,3)","-(1/(q^5*t))" "(3,7,5,6)","-q^(-8)" -"(3,8,5,6)","1/(q^4*Subscript[q,1,2]^2)" -"(3,9,5,7)","-(1/(q^7*t*Subscript[q,1,2]^2))-1/(q^5*t*Subscript[q,1,2]^2)+1/(q^3*t*Subscript[q,1,2]^2)+1/(q*t*Subscript[q,1,2]^2)" +"(3,8,5,6)","q^(-4)" +"(3,9,5,7)","-(1/(q^7*t))-1/(q^5*t)+1/(q^3*t)+1/(q*t)" "(3,9,5,8)","-q^(-8)+q^(-4)" "(3,11,5,10)","q^(-10)-q^(-6)-t/q^7+t/q^3" -"(3,6,6,3)","-((t*Subscript[q,1,2])/q^7)" -"(3,7,6,5)","(t*Subscript[q,1,2]^3)/q^7" -"(3,8,6,5)","-((t*Subscript[q,1,2])/q^7)" -"(3,10,6,7)","(t*Subscript[q,1,2])/q^9-(t*Subscript[q,1,2])/q^5" -"(3,11,6,9)","(t*Subscript[q,1,2]^4)/q^9-(t*Subscript[q,1,2]^4)/q^5-(t^2*Subscript[q,1,2]^4)/q^12+(t^2*Subscript[q,1,2]^4)/q^8" +"(3,6,6,3)","-(t/q^7)" +"(3,7,6,5)","t/q^7" +"(3,8,6,5)","-(t/q^7)" +"(3,10,6,7)","t/q^9-t/q^5" +"(3,11,6,9)","t/q^9-t/q^5-t^2/q^12+t^2/q^8" "(3,7,7,3)","q^(-8)" "(3,9,7,5)","q^(-8)" -"(3,10,7,6)","t/(q^7*Subscript[q,1,2])" +"(3,10,7,6)","t/q^7" "(3,11,7,7)","-q^(-10)+q^(-6)+q^(-4)-t/q^9" -"(3,11,7,8)","(t*Subscript[q,1,2]^2)/q^7-(t^2*Subscript[q,1,2]^2)/q^10" +"(3,11,7,8)","t/q^7-t^2/q^10" "(3,8,8,3)","q^(-8)" -"(3,9,8,5)","Subscript[q,1,2]^2/q^8" -"(3,10,8,6)","(t*Subscript[q,1,2])/q^11" -"(3,11,8,7)","Subscript[q,1,2]^2/q^8-(t*Subscript[q,1,2]^2)/q^13-(t*Subscript[q,1,2]^2)/q^11+(t*Subscript[q,1,2]^2)/q^7" -"(3,11,8,8)","(t*Subscript[q,1,2]^4)/q^11-(t^2*Subscript[q,1,2]^4)/q^14" -"(3,9,9,3)","-(1/(q^9*t*Subscript[q,1,2]))" +"(3,9,8,5)","q^(-8)" +"(3,10,8,6)","t/q^11" +"(3,11,8,7)","q^(-8)-t/q^13-t/q^11+t/q^7" +"(3,11,8,8)","t/q^11-t^2/q^14" +"(3,9,9,3)","-(1/(q^9*t))" "(3,11,9,6)","-q^(-12)+t/q^9" -"(3,10,10,3)","-((t*Subscript[q,1,2])/q^11)" -"(3,11,10,5)","-((t*Subscript[q,1,2]^4)/q^11)+(t^2*Subscript[q,1,2]^4)/q^14" +"(3,10,10,3)","-(t/q^11)" +"(3,11,10,5)","-(t/q^11)+t^2/q^14" "(3,11,11,3)","q^(-12)" "(4,0,0,4)","1" -"(4,1,0,5)","1/(q*t*Subscript[q,1,2])" +"(4,1,0,5)","1/(q*t)" "(4,2,0,6)","1" -"(4,3,0,7)","1/(q*t*Subscript[q,1,2])" -"(4,4,0,7)","q/(t*Subscript[q,1,2]^2)" -"(4,3,0,8)","Subscript[q,1,2]/q^4" +"(4,3,0,7)","1/(q*t)" +"(4,4,0,7)","q/t" +"(4,3,0,8)","q^(-4)" "(4,4,0,8)","1" -"(4,5,0,9)","-(1/(q^3*t*Subscript[q,1,2]))+1/(q*t*Subscript[q,1,2])" +"(4,5,0,9)","-(1/(q^3*t))+1/(q*t)" "(4,6,0,10)","1-q^(-2)" -"(4,7,0,11)","-(1/(q^3*t*Subscript[q,1,2]))+1/(q*t*Subscript[q,1,2])" -"(4,8,0,11)","q/(t*Subscript[q,1,2]^3)-q^3/(t*Subscript[q,1,2]^3)" -"(4,1,1,4)","-(1/(q*t*Subscript[q,1,2]))" -"(4,3,1,6)","-(1/(q*t*Subscript[q,1,2]))" -"(4,4,1,6)","-(q/(t*Subscript[q,1,2]^2))" -"(4,5,1,8)","1/(q^3*t*Subscript[q,1,2])-1/(q*t*Subscript[q,1,2])" -"(4,7,1,10)","1/(q^3*t*Subscript[q,1,2])-1/(q*t*Subscript[q,1,2])" -"(4,8,1,10)","-(q/(t*Subscript[q,1,2]^3))+q^3/(t*Subscript[q,1,2]^3)" -"(4,2,2,4)","-((t*Subscript[q,1,2])/q^3)" -"(4,3,2,5)","-(Subscript[q,1,2]/q^4)" +"(4,7,0,11)","-(1/(q^3*t))+1/(q*t)" +"(4,8,0,11)","q/t-q^3/t" +"(4,1,1,4)","-(1/(q*t))" +"(4,3,1,6)","-(1/(q*t))" +"(4,4,1,6)","-(q/t)" +"(4,5,1,8)","1/(q^3*t)-1/(q*t)" +"(4,7,1,10)","1/(q^3*t)-1/(q*t)" +"(4,8,1,10)","-(q/t)+q^3/t" +"(4,2,2,4)","-(t/q^3)" +"(4,3,2,5)","-q^(-4)" "(4,4,2,5)","-q^(-4)" "(4,6,2,7)","-q^(-4)+q^(-2)" -"(4,6,2,8)","-((t*Subscript[q,1,2]^2)/q^7)+(t*Subscript[q,1,2]^2)/q^3" -"(4,7,2,9)","-(Subscript[q,1,2]^2/q^6)+Subscript[q,1,2]^2/q^4" +"(4,6,2,8)","-(t/q^7)+t/q^3" +"(4,7,2,9)","-q^(-6)+q^(-4)" "(4,8,2,9)","q^(-6)-q^(-4)" "(4,10,2,11)","1+q^(-6)-q^(-4)-q^(-2)" "(4,3,3,4)","q^(-4)" -"(4,5,3,5)","1/(q^5*t*Subscript[q,1,2])" -"(4,6,3,6)","-(1/(q^2*Subscript[q,1,2]))" -"(4,7,3,7)","1/(q^5*t*Subscript[q,1,2])" -"(4,8,3,7)","-(1/(q*t*Subscript[q,1,2]^3))" -"(4,7,3,8)","Subscript[q,1,2]/q^8+Subscript[q,1,2]/q^6-Subscript[q,1,2]/q^4" -"(4,8,3,8)","-(1/(q^2*Subscript[q,1,2]))" -"(4,9,3,9)","-(1/(q^7*t*Subscript[q,1,2]))+1/(q^3*t*Subscript[q,1,2])" -"(4,10,3,10)","-Subscript[q,1,2]^(-1)+1/(q^4*Subscript[q,1,2])" -"(4,11,3,11)","-Subscript[q,1,2]^(-1)+1/(q^4*Subscript[q,1,2])-1/(q^7*t*Subscript[q,1,2])+1/(q^3*t*Subscript[q,1,2])" +"(4,5,3,5)","1/(q^5*t)" +"(4,6,3,6)","-q^(-2)" +"(4,7,3,7)","1/(q^5*t)" +"(4,8,3,7)","-(1/(q*t))" +"(4,7,3,8)","q^(-8)+q^(-6)-q^(-4)" +"(4,8,3,8)","-q^(-2)" +"(4,9,3,9)","-(1/(q^7*t))+1/(q^3*t)" +"(4,10,3,10)","-1+q^(-4)" +"(4,11,3,11)","-1+q^(-4)-1/(q^7*t)+1/(q^3*t)" "(4,4,4,4)","q^(-4)" "(4,5,4,5)","-(1/(q^5*t))" "(4,6,4,6)","q^(-4)" "(4,7,4,7)","-(1/(q^5*t))" -"(4,8,4,7)","1/(q*t*Subscript[q,1,2]^2)" -"(4,7,4,8)","-(Subscript[q,1,2]^2/q^8)" +"(4,8,4,7)","1/(q*t)" +"(4,7,4,8)","-q^(-8)" "(4,8,4,8)","-q^(-6)+q^(-4)+q^(-2)" "(4,9,4,9)","1/(q^7*t)-1/(q^3*t)" "(4,10,4,10)","-q^(-6)+q^(-2)" "(4,11,4,11)","1-q^(-4)+1/(q^7*t)-1/(q^3*t)" -"(4,5,5,4)","-(1/(q^5*t*Subscript[q,1,2]))" -"(4,7,5,6)","-(1/(q^5*t*Subscript[q,1,2]))" -"(4,8,5,6)","1/(q*t*Subscript[q,1,2]^3)" -"(4,9,5,8)","1/(q^7*t*Subscript[q,1,2])-1/(q^3*t*Subscript[q,1,2])" -"(4,11,5,10)","Subscript[q,1,2]^(-1)-1/(q^4*Subscript[q,1,2])+1/(q^7*t*Subscript[q,1,2])-1/(q^3*t*Subscript[q,1,2])" -"(4,6,6,4)","-((t*Subscript[q,1,2])/q^7)" -"(4,7,6,5)","Subscript[q,1,2]^2/q^8" +"(4,5,5,4)","-(1/(q^5*t))" +"(4,7,5,6)","-(1/(q^5*t))" +"(4,8,5,6)","1/(q*t)" +"(4,9,5,8)","1/(q^7*t)-1/(q^3*t)" +"(4,11,5,10)","1-q^(-4)+1/(q^7*t)-1/(q^3*t)" +"(4,6,6,4)","-(t/q^7)" +"(4,7,6,5)","q^(-8)" "(4,8,6,5)","-q^(-8)" "(4,10,6,7)","-q^(-8)+q^(-4)" -"(4,10,6,8)","-((t*Subscript[q,1,2]^2)/q^11)-(t*Subscript[q,1,2]^2)/q^9+(t*Subscript[q,1,2]^2)/q^7+(t*Subscript[q,1,2]^2)/q^5" -"(4,11,6,9)","Subscript[q,1,2]^3/q^10-Subscript[q,1,2]^3/q^6-(t*Subscript[q,1,2]^3)/q^13+(t*Subscript[q,1,2]^3)/q^9" +"(4,10,6,8)","-(t/q^11)-t/q^9+t/q^7+t/q^5" +"(4,11,6,9)","q^(-10)-q^(-6)-t/q^13+t/q^9" "(4,7,7,4)","q^(-8)" -"(4,9,7,5)","1/(q^9*t*Subscript[q,1,2])" -"(4,10,7,6)","1/(q^4*Subscript[q,1,2]^2)" -"(4,11,7,7)","-(1/(q^6*Subscript[q,1,2]))+1/(q^9*t*Subscript[q,1,2])" -"(4,11,7,8)","Subscript[q,1,2]/q^12+Subscript[q,1,2]/q^10-Subscript[q,1,2]/q^6-(t*Subscript[q,1,2])/q^7" +"(4,9,7,5)","1/(q^9*t)" +"(4,10,7,6)","q^(-4)" +"(4,11,7,7)","-q^(-6)+1/(q^9*t)" +"(4,11,7,8)","q^(-12)+q^(-10)-q^(-6)-t/q^7" "(4,8,8,4)","q^(-8)" -"(4,9,8,5)","Subscript[q,1,2]/(q^9*t)" +"(4,9,8,5)","1/(q^9*t)" "(4,10,8,6)","q^(-8)" -"(4,11,8,7)","-(Subscript[q,1,2]/q^6)+Subscript[q,1,2]/(q^9*t)" -"(4,11,8,8)","Subscript[q,1,2]^3/q^12+(t*Subscript[q,1,2]^3)/q^13-(t*Subscript[q,1,2]^3)/q^9-(t*Subscript[q,1,2]^3)/q^7" -"(4,9,9,4)","-(1/(q^9*t*Subscript[q,1,2]))" -"(4,11,9,6)","1/(q^6*Subscript[q,1,2])-1/(q^9*t*Subscript[q,1,2])" -"(4,10,10,4)","-((t*Subscript[q,1,2])/q^11)" -"(4,11,10,5)","-(Subscript[q,1,2]^3/q^12)+(t*Subscript[q,1,2]^3)/q^15" +"(4,11,8,7)","-q^(-6)+1/(q^9*t)" +"(4,11,8,8)","q^(-12)+t/q^13-t/q^9-t/q^7" +"(4,9,9,4)","-(1/(q^9*t))" +"(4,11,9,6)","q^(-6)-1/(q^9*t)" +"(4,10,10,4)","-(t/q^11)" +"(4,11,10,5)","-q^(-12)+t/q^15" "(4,11,11,4)","q^(-12)" "(5,0,0,5)","1" "(5,2,0,7)","1" -"(5,2,0,8)","(t*Subscript[q,1,2]^2)/q^3" +"(5,2,0,8)","t/q^3" "(5,4,0,9)","1-q^(-2)" "(5,6,0,11)","1-q^(-2)" -"(5,1,1,5)","1/(q*t*Subscript[q,1,2])" -"(5,3,1,7)","1/(q*t*Subscript[q,1,2])" -"(5,4,1,7)","q/(t*Subscript[q,1,2]^2)" -"(5,3,1,8)","Subscript[q,1,2]/q^4" +"(5,1,1,5)","1/(q*t)" +"(5,3,1,7)","1/(q*t)" +"(5,4,1,7)","q/t" +"(5,3,1,8)","q^(-4)" "(5,4,1,8)","q^(-2)" -"(5,5,1,9)","-(1/(q^3*t*Subscript[q,1,2]))+1/(q*t*Subscript[q,1,2])" -"(5,7,1,11)","-(1/(q^3*t*Subscript[q,1,2]))+1/(q*t*Subscript[q,1,2])" -"(5,8,1,11)","q/(t*Subscript[q,1,2]^3)-q^3/(t*Subscript[q,1,2]^3)" -"(5,2,2,5)","-((t*Subscript[q,1,2]^2)/q^3)" -"(5,6,2,9)","-((t*Subscript[q,1,2]^3)/q^5)+(t*Subscript[q,1,2]^3)/q^3" -"(5,3,3,5)","-(Subscript[q,1,2]/q^4)" +"(5,5,1,9)","-(1/(q^3*t))+1/(q*t)" +"(5,7,1,11)","-(1/(q^3*t))+1/(q*t)" +"(5,8,1,11)","q/t-q^3/t" +"(5,2,2,5)","-(t/q^3)" +"(5,6,2,9)","-(t/q^5)+t/q^3" +"(5,3,3,5)","-q^(-4)" "(5,6,3,7)","q^(-2)" -"(5,6,3,8)","(t*Subscript[q,1,2]^2)/q^5" -"(5,7,3,9)","-(Subscript[q,1,2]^2/q^6)+Subscript[q,1,2]^2/q^4" +"(5,6,3,8)","t/q^5" +"(5,7,3,9)","-q^(-6)+q^(-4)" "(5,8,3,9)","-q^(-4)+q^(-2)" "(5,10,3,11)","1-q^(-4)" -"(5,4,4,5)","-(Subscript[q,1,2]/q^4)" -"(5,6,4,7)","-(Subscript[q,1,2]/q^4)" -"(5,6,4,8)","-((t*Subscript[q,1,2]^3)/q^7)" -"(5,8,4,9)","Subscript[q,1,2]/q^6-Subscript[q,1,2]/q^2" -"(5,10,4,11)","Subscript[q,1,2]/q^6-Subscript[q,1,2]/q^2" +"(5,4,4,5)","-q^(-4)" +"(5,6,4,7)","-q^(-4)" +"(5,6,4,8)","-(t/q^7)" +"(5,8,4,9)","q^(-6)-q^(-2)" +"(5,10,4,11)","q^(-6)-q^(-2)" "(5,5,5,5)","-(1/(q^5*t))" "(5,7,5,7)","-(1/(q^5*t))" -"(5,8,5,7)","1/(q*t*Subscript[q,1,2]^2)" -"(5,7,5,8)","-(Subscript[q,1,2]^2/q^8)" +"(5,8,5,7)","1/(q*t)" +"(5,7,5,8)","-q^(-8)" "(5,8,5,8)","q^(-4)" "(5,9,5,9)","1/(q^7*t)-1/(q^3*t)" "(5,11,5,11)","1-q^(-4)+1/(q^7*t)-1/(q^3*t)" -"(5,6,6,5)","(t*Subscript[q,1,2]^3)/q^7" -"(5,10,6,9)","(t*Subscript[q,1,2]^4)/q^9-(t*Subscript[q,1,2]^4)/q^5" -"(5,7,7,5)","Subscript[q,1,2]^2/q^8" +"(5,6,6,5)","t/q^7" +"(5,10,6,9)","t/q^9-t/q^5" +"(5,7,7,5)","q^(-8)" "(5,10,7,7)","q^(-4)" -"(5,10,7,8)","(t*Subscript[q,1,2]^2)/q^7" -"(5,11,7,9)","Subscript[q,1,2]^3/q^10-Subscript[q,1,2]^3/q^6+(t*Subscript[q,1,2]^3)/q^9-(t*Subscript[q,1,2]^3)/q^7" -"(5,8,8,5)","Subscript[q,1,2]^2/q^8" -"(5,10,8,7)","Subscript[q,1,2]^2/q^8" -"(5,10,8,8)","(t*Subscript[q,1,2]^4)/q^11" -"(5,11,8,9)","(t*Subscript[q,1,2]^5)/q^13-(t*Subscript[q,1,2]^5)/q^7" -"(5,9,9,5)","Subscript[q,1,2]/(q^9*t)" -"(5,11,9,7)","-(Subscript[q,1,2]/q^6)+Subscript[q,1,2]/(q^9*t)" -"(5,11,9,8)","Subscript[q,1,2]^3/q^12-(t*Subscript[q,1,2]^3)/q^9" -"(5,10,10,5)","-((t*Subscript[q,1,2]^4)/q^11)" -"(5,11,11,5)","-(Subscript[q,1,2]^3/q^12)" +"(5,10,7,8)","t/q^7" +"(5,11,7,9)","q^(-10)-q^(-6)+t/q^9-t/q^7" +"(5,8,8,5)","q^(-8)" +"(5,10,8,7)","q^(-8)" +"(5,10,8,8)","t/q^11" +"(5,11,8,9)","t/q^13-t/q^7" +"(5,9,9,5)","1/(q^9*t)" +"(5,11,9,7)","-q^(-6)+1/(q^9*t)" +"(5,11,9,8)","q^(-12)-t/q^9" +"(5,10,10,5)","-(t/q^11)" +"(5,11,11,5)","-q^(-12)" "(6,0,0,6)","1" -"(6,1,0,7)","q/(t*Subscript[q,1,2]^2)" +"(6,1,0,7)","q/t" "(6,1,0,8)","1" "(6,3,0,10)","1-q^(-2)" -"(6,5,0,11)","q/(t*Subscript[q,1,2]^3)-q^3/(t*Subscript[q,1,2]^3)" -"(6,1,1,6)","-(q/(t*Subscript[q,1,2]^2))" -"(6,5,1,10)","-(q/(t*Subscript[q,1,2]^3))+q^3/(t*Subscript[q,1,2]^3)" -"(6,2,2,6)","(t*Subscript[q,1,2])/q^3" +"(6,5,0,11)","q/t-q^3/t" +"(6,1,1,6)","-(q/t)" +"(6,5,1,10)","-(q/t)+q^3/t" +"(6,2,2,6)","t/q^3" "(6,3,2,7)","q^(-2)" -"(6,4,2,7)","1/(q^2*Subscript[q,1,2])" -"(6,3,2,8)","(t*Subscript[q,1,2]^2)/q^3" -"(6,4,2,8)","(t*Subscript[q,1,2])/q^3" -"(6,6,2,10)","-((t*Subscript[q,1,2])/q^5)+(t*Subscript[q,1,2])/q^3" +"(6,4,2,7)","q^(-2)" +"(6,3,2,8)","t/q^3" +"(6,4,2,8)","t/q^3" +"(6,6,2,10)","-(t/q^5)+t/q^3" "(6,7,2,11)","1-q^(-2)" -"(6,8,2,11)","-Subscript[q,1,2]^(-2)+1/(q^2*Subscript[q,1,2]^2)" -"(6,3,3,6)","-(1/(q^2*Subscript[q,1,2]))" -"(6,5,3,7)","-(1/(q*t*Subscript[q,1,2]^3))" -"(6,5,3,8)","-(1/(q^2*Subscript[q,1,2]))" -"(6,7,3,10)","-Subscript[q,1,2]^(-1)+1/(q^4*Subscript[q,1,2])" -"(6,9,3,11)","-(1/(q*t*Subscript[q,1,2]^4))+q^3/(t*Subscript[q,1,2]^4)" -"(6,4,4,6)","-(1/(q^2*Subscript[q,1,2]))" -"(6,5,4,7)","1/(q*t*Subscript[q,1,2]^2)" +"(6,8,2,11)","-1+q^(-2)" +"(6,3,3,6)","-q^(-2)" +"(6,5,3,7)","-(1/(q*t))" +"(6,5,3,8)","-q^(-2)" +"(6,7,3,10)","-1+q^(-4)" +"(6,9,3,11)","-(1/(q*t))+q^3/t" +"(6,4,4,6)","-q^(-2)" +"(6,5,4,7)","1/(q*t)" "(6,5,4,8)","q^(-2)" "(6,7,4,10)","-q^(-4)+q^(-2)" -"(6,8,4,10)","Subscript[q,1,2]^(-2)-1/(q^2*Subscript[q,1,2]^2)" -"(6,9,4,11)","1/(q*t*Subscript[q,1,2]^3)-q^3/(t*Subscript[q,1,2]^3)" -"(6,5,5,6)","1/(q*t*Subscript[q,1,2]^3)" -"(6,9,5,10)","1/(q*t*Subscript[q,1,2]^4)-q^3/(t*Subscript[q,1,2]^4)" +"(6,8,4,10)","1-q^(-2)" +"(6,9,4,11)","1/(q*t)-q^3/t" +"(6,5,5,6)","1/(q*t)" +"(6,9,5,10)","1/(q*t)-q^3/t" "(6,6,6,6)","-(t/q^5)" "(6,7,6,7)","q^(-4)" -"(6,8,6,7)","-(1/(q^4*Subscript[q,1,2]^2))" -"(6,7,6,8)","(t*Subscript[q,1,2]^2)/q^5" +"(6,8,6,7)","-q^(-4)" +"(6,7,6,8)","t/q^5" "(6,8,6,8)","-(t/q^5)" "(6,10,6,10)","t/q^7-t/q^3" "(6,11,6,11)","1-q^(-4)+t/q^7-t/q^3" -"(6,7,7,6)","1/(q^4*Subscript[q,1,2]^2)" -"(6,9,7,7)","1/(q^3*t*Subscript[q,1,2]^4)" -"(6,9,7,8)","1/(q^4*Subscript[q,1,2]^2)" -"(6,11,7,10)","Subscript[q,1,2]^(-2)-1/(q^6*Subscript[q,1,2]^2)" -"(6,8,8,6)","1/(q^4*Subscript[q,1,2]^2)" -"(6,9,8,7)","1/(q^3*t*Subscript[q,1,2]^2)" +"(6,7,7,6)","q^(-4)" +"(6,9,7,7)","1/(q^3*t)" +"(6,9,7,8)","q^(-4)" +"(6,11,7,10)","1-q^(-6)" +"(6,8,8,6)","q^(-4)" +"(6,9,8,7)","1/(q^3*t)" "(6,9,8,8)","q^(-4)" "(6,11,8,10)","-q^(-6)+q^(-4)-t/q^7+t/q^3" -"(6,9,9,6)","-(1/(q^3*t*Subscript[q,1,2]^4))" -"(6,10,10,6)","t/(q^7*Subscript[q,1,2])" +"(6,9,9,6)","-(1/(q^3*t))" +"(6,10,10,6)","t/q^7" "(6,11,10,7)","q^(-6)-t/q^9" -"(6,11,10,8)","(t*Subscript[q,1,2]^2)/q^7-(t^2*Subscript[q,1,2]^2)/q^10" -"(6,11,11,6)","-(1/(q^6*Subscript[q,1,2]^3))" +"(6,11,10,8)","t/q^7-t^2/q^10" +"(6,11,11,6)","-q^(-6)" "(7,0,0,7)","1" "(7,1,0,9)","1" -"(7,2,0,10)","-((t*Subscript[q,1,2]^2)/q^3)" +"(7,2,0,10)","-(t/q^3)" "(7,3,0,11)","1" -"(7,4,0,11)","Subscript[q,1,2]^(-1)" -"(7,1,1,7)","q/(t*Subscript[q,1,2]^2)" +"(7,4,0,11)","1" +"(7,1,1,7)","q/t" "(7,3,1,10)","-q^(-2)" -"(7,4,1,10)","-Subscript[q,1,2]^(-1)" -"(7,5,1,11)","q/(t*Subscript[q,1,2]^3)-q^3/(t*Subscript[q,1,2]^3)" -"(7,2,2,7)","(t*Subscript[q,1,2]^2)/q^3" -"(7,3,2,9)","(t*Subscript[q,1,2]^3)/q^3" -"(7,4,2,9)","(t*Subscript[q,1,2]^2)/q^3" +"(7,4,1,10)","-1" +"(7,5,1,11)","q/t-q^3/t" +"(7,2,2,7)","t/q^3" +"(7,3,2,9)","t/q^3" +"(7,4,2,9)","t/q^3" "(7,3,3,7)","q^(-2)" "(7,5,3,9)","q^(-2)" -"(7,6,3,10)","(t*Subscript[q,1,2])/q^3" +"(7,6,3,10)","t/q^3" "(7,7,3,11)","1" -"(7,8,3,11)","-Subscript[q,1,2]^(-2)" +"(7,8,3,11)","-1" "(7,4,4,7)","q^(-2)" -"(7,5,4,9)","-(Subscript[q,1,2]/q^2)" -"(7,6,4,10)","-((t*Subscript[q,1,2]^2)/q^5)" -"(7,7,4,11)","-(Subscript[q,1,2]/q^2)" -"(7,8,4,11)","1/(q^2*Subscript[q,1,2])" -"(7,5,5,7)","1/(q*t*Subscript[q,1,2]^2)" +"(7,5,4,9)","-q^(-2)" +"(7,6,4,10)","-(t/q^5)" +"(7,7,4,11)","-q^(-2)" +"(7,8,4,11)","q^(-2)" +"(7,5,5,7)","1/(q*t)" "(7,7,5,10)","-q^(-4)" -"(7,8,5,10)","Subscript[q,1,2]^(-2)" -"(7,9,5,11)","1/(q*t*Subscript[q,1,2]^3)-q^3/(t*Subscript[q,1,2]^3)" -"(7,6,6,7)","(t*Subscript[q,1,2]^2)/q^5" -"(7,7,6,9)","-((t*Subscript[q,1,2]^4)/q^5)" -"(7,8,6,9)","(t*Subscript[q,1,2]^2)/q^5" +"(7,8,5,10)","1" +"(7,9,5,11)","1/(q*t)-q^3/t" +"(7,6,6,7)","t/q^5" +"(7,7,6,9)","-(t/q^5)" +"(7,8,6,9)","t/q^5" "(7,7,7,7)","q^(-4)" "(7,9,7,9)","q^(-4)" "(7,10,7,10)","-(t/q^3)" "(7,11,7,11)","1-t/q^3" "(7,8,8,7)","q^(-4)" -"(7,9,8,9)","Subscript[q,1,2]^2/q^4" -"(7,10,8,10)","-((t*Subscript[q,1,2]^2)/q^7)" -"(7,11,8,11)","Subscript[q,1,2]^2/q^4-(t*Subscript[q,1,2]^2)/q^7" -"(7,9,9,7)","1/(q^3*t*Subscript[q,1,2]^2)" +"(7,9,8,9)","q^(-4)" +"(7,10,8,10)","-(t/q^7)" +"(7,11,8,11)","q^(-4)-t/q^7" +"(7,9,9,7)","1/(q^3*t)" "(7,11,9,10)","-q^(-6)+t/q^3" -"(7,10,10,7)","(t*Subscript[q,1,2]^2)/q^7" -"(7,11,10,9)","(t*Subscript[q,1,2]^5)/q^7-(t^2*Subscript[q,1,2]^5)/q^10" +"(7,10,10,7)","t/q^7" +"(7,11,10,9)","t/q^7-t^2/q^10" "(7,11,11,7)","q^(-6)" "(8,0,0,8)","1" -"(8,1,0,9)","-(q/(t*Subscript[q,1,2]^2))" +"(8,1,0,9)","-(q/t)" "(8,2,0,10)","1" -"(8,3,0,11)","-(q/(t*Subscript[q,1,2]^2))" -"(8,4,0,11)","-(q^3/(t*Subscript[q,1,2]^3))" -"(8,1,1,8)","q/(t*Subscript[q,1,2]^2)" -"(8,3,1,10)","q/(t*Subscript[q,1,2]^2)" -"(8,4,1,10)","q^3/(t*Subscript[q,1,2]^3)" -"(8,2,2,8)","(t*Subscript[q,1,2]^2)/q^3" -"(8,3,2,9)","-(Subscript[q,1,2]/q^2)" +"(8,3,0,11)","-(q/t)" +"(8,4,0,11)","-(q^3/t)" +"(8,1,1,8)","q/t" +"(8,3,1,10)","q/t" +"(8,4,1,10)","q^3/t" +"(8,2,2,8)","t/q^3" +"(8,3,2,9)","-q^(-2)" "(8,4,2,9)","-q^(-2)" "(8,6,2,11)","1-q^(-2)" "(8,3,3,8)","q^(-2)" -"(8,5,3,9)","-(1/(q*t*Subscript[q,1,2]^2))" -"(8,6,3,10)","-Subscript[q,1,2]^(-1)" -"(8,7,3,11)","-(1/(q*t*Subscript[q,1,2]^2))" -"(8,8,3,11)","q^3/(t*Subscript[q,1,2]^4)" +"(8,5,3,9)","-(1/(q*t))" +"(8,6,3,10)","-1" +"(8,7,3,11)","-(1/(q*t))" +"(8,8,3,11)","q^3/t" "(8,4,4,8)","q^(-2)" -"(8,5,4,9)","1/(q*t*Subscript[q,1,2])" +"(8,5,4,9)","1/(q*t)" "(8,6,4,10)","q^(-2)" -"(8,7,4,11)","1/(q*t*Subscript[q,1,2])" -"(8,8,4,11)","-(q^3/(t*Subscript[q,1,2]^3))" -"(8,5,5,8)","1/(q*t*Subscript[q,1,2]^2)" -"(8,7,5,10)","1/(q*t*Subscript[q,1,2]^2)" -"(8,8,5,10)","-(q^3/(t*Subscript[q,1,2]^4))" -"(8,6,6,8)","(t*Subscript[q,1,2]^2)/q^5" -"(8,7,6,9)","Subscript[q,1,2]^2/q^4" +"(8,7,4,11)","1/(q*t)" +"(8,8,4,11)","-(q^3/t)" +"(8,5,5,8)","1/(q*t)" +"(8,7,5,10)","1/(q*t)" +"(8,8,5,10)","-(q^3/t)" +"(8,6,6,8)","t/q^5" +"(8,7,6,9)","q^(-4)" "(8,8,6,9)","-q^(-4)" "(8,10,6,11)","1-q^(-4)" "(8,7,7,8)","q^(-4)" -"(8,9,7,9)","-(1/(q^3*t*Subscript[q,1,2]^2))" -"(8,10,7,10)","Subscript[q,1,2]^(-2)" -"(8,11,7,11)","Subscript[q,1,2]^(-2)-1/(q^3*t*Subscript[q,1,2]^2)" +"(8,9,7,9)","-(1/(q^3*t))" +"(8,10,7,10)","1" +"(8,11,7,11)","1-1/(q^3*t)" "(8,8,8,8)","q^(-4)" "(8,9,8,9)","-(1/(q^3*t))" "(8,10,8,10)","q^(-4)" "(8,11,8,11)","1-1/(q^3*t)" -"(8,9,9,8)","1/(q^3*t*Subscript[q,1,2]^2)" -"(8,11,9,10)","-Subscript[q,1,2]^(-2)+1/(q^3*t*Subscript[q,1,2]^2)" -"(8,10,10,8)","(t*Subscript[q,1,2]^2)/q^7" -"(8,11,10,9)","-(Subscript[q,1,2]^3/q^6)+(t*Subscript[q,1,2]^3)/q^9" +"(8,9,9,8)","1/(q^3*t)" +"(8,11,9,10)","-1+1/(q^3*t)" +"(8,10,10,8)","t/q^7" +"(8,11,10,9)","-q^(-6)+t/q^9" "(8,11,11,8)","q^(-6)" "(9,0,0,9)","1" "(9,2,0,11)","1" -"(9,1,1,9)","-(q/(t*Subscript[q,1,2]^2))" -"(9,3,1,11)","-(q/(t*Subscript[q,1,2]^2))" -"(9,4,1,11)","-(q^3/(t*Subscript[q,1,2]^3))" -"(9,2,2,9)","(t*Subscript[q,1,2]^3)/q^3" -"(9,3,3,9)","-(Subscript[q,1,2]/q^2)" +"(9,1,1,9)","-(q/t)" +"(9,3,1,11)","-(q/t)" +"(9,4,1,11)","-(q^3/t)" +"(9,2,2,9)","t/q^3" +"(9,3,3,9)","-q^(-2)" "(9,6,3,11)","1" -"(9,4,4,9)","-(Subscript[q,1,2]/q^2)" -"(9,6,4,11)","-(Subscript[q,1,2]/q^2)" -"(9,5,5,9)","1/(q*t*Subscript[q,1,2])" -"(9,7,5,11)","1/(q*t*Subscript[q,1,2])" -"(9,8,5,11)","-(q^3/(t*Subscript[q,1,2]^3))" -"(9,6,6,9)","-((t*Subscript[q,1,2]^4)/q^5)" -"(9,7,7,9)","Subscript[q,1,2]^2/q^4" +"(9,4,4,9)","-q^(-2)" +"(9,6,4,11)","-q^(-2)" +"(9,5,5,9)","1/(q*t)" +"(9,7,5,11)","1/(q*t)" +"(9,8,5,11)","-(q^3/t)" +"(9,6,6,9)","-(t/q^5)" +"(9,7,7,9)","q^(-4)" "(9,10,7,11)","1" -"(9,8,8,9)","Subscript[q,1,2]^2/q^4" -"(9,10,8,11)","Subscript[q,1,2]^2/q^4" +"(9,8,8,9)","q^(-4)" +"(9,10,8,11)","q^(-4)" "(9,9,9,9)","-(1/(q^3*t))" "(9,11,9,11)","1-1/(q^3*t)" -"(9,10,10,9)","(t*Subscript[q,1,2]^5)/q^7" -"(9,11,11,9)","-(Subscript[q,1,2]^3/q^6)" +"(9,10,10,9)","t/q^7" +"(9,11,11,9)","-q^(-6)" "(10,0,0,10)","1" -"(10,1,0,11)","-(q^3/(t*Subscript[q,1,2]^3))" -"(10,1,1,10)","q^3/(t*Subscript[q,1,2]^3)" -"(10,2,2,10)","-((t*Subscript[q,1,2]^2)/q^3)" +"(10,1,0,11)","-(q^3/t)" +"(10,1,1,10)","q^3/t" +"(10,2,2,10)","-(t/q^3)" "(10,3,2,11)","1" -"(10,4,2,11)","Subscript[q,1,2]^(-1)" -"(10,3,3,10)","-Subscript[q,1,2]^(-1)" -"(10,5,3,11)","q^3/(t*Subscript[q,1,2]^4)" -"(10,4,4,10)","-Subscript[q,1,2]^(-1)" -"(10,5,4,11)","-(q^3/(t*Subscript[q,1,2]^3))" -"(10,5,5,10)","-(q^3/(t*Subscript[q,1,2]^4))" -"(10,6,6,10)","(t*Subscript[q,1,2])/q^3" +"(10,4,2,11)","1" +"(10,3,3,10)","-1" +"(10,5,3,11)","q^3/t" +"(10,4,4,10)","-1" +"(10,5,4,11)","-(q^3/t)" +"(10,5,5,10)","-(q^3/t)" +"(10,6,6,10)","t/q^3" "(10,7,6,11)","1" -"(10,8,6,11)","-Subscript[q,1,2]^(-2)" -"(10,7,7,10)","Subscript[q,1,2]^(-2)" -"(10,9,7,11)","-(q^3/(t*Subscript[q,1,2]^5))" -"(10,8,8,10)","Subscript[q,1,2]^(-2)" -"(10,9,8,11)","-(q^3/(t*Subscript[q,1,2]^3))" -"(10,9,9,10)","q^3/(t*Subscript[q,1,2]^5)" +"(10,8,6,11)","-1" +"(10,7,7,10)","1" +"(10,9,7,11)","-(q^3/t)" +"(10,8,8,10)","1" +"(10,9,8,11)","-(q^3/t)" +"(10,9,9,10)","q^3/t" "(10,10,10,10)","-(t/q^3)" "(10,11,10,11)","1-t/q^3" -"(10,11,11,10)","-Subscript[q,1,2]^(-3)" +"(10,11,11,10)","-1" "(11,0,0,11)","1" -"(11,1,1,11)","-(q^3/(t*Subscript[q,1,2]^3))" -"(11,2,2,11)","-((t*Subscript[q,1,2]^3)/q^3)" +"(11,1,1,11)","-(q^3/t)" +"(11,2,2,11)","-(t/q^3)" "(11,3,3,11)","1" "(11,4,4,11)","1" -"(11,5,5,11)","-(q^3/(t*Subscript[q,1,2]^3))" -"(11,6,6,11)","-((t*Subscript[q,1,2]^3)/q^3)" +"(11,5,5,11)","-(q^3/t)" +"(11,6,6,11)","-(t/q^3)" "(11,7,7,11)","1" "(11,8,8,11)","1" -"(11,9,9,11)","-(q^3/(t*Subscript[q,1,2]^3))" -"(11,10,10,11)","-((t*Subscript[q,1,2]^3)/q^3)" +"(11,9,9,11)","-(q^3/t)" +"(11,10,10,11)","-(t/q^3)" "(11,11,11,11)","1" From 444957b392af50646f3e112e48f7c5c440622bf4 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Tue, 23 Jun 2026 20:53:15 -0500 Subject: [PATCH 16/53] Fix bug in DictLaurentPolyhnomial.from_str --- .../dict_laurent_polynomial.py | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py index ece9209..48cd879 100644 --- a/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py +++ b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py @@ -47,8 +47,7 @@ def fmt_exp(self, k): num, den = num // g, den // g if den == 1: if num == 1: return '' - if num > 1: return f'^{num}' - return f'^({num})' + return f'^{num}' return f'^({num}/{den})' def _gcd(a, b): @@ -279,6 +278,26 @@ def replace_denom(m): result.append(fm.group(1) + negate_exp(fm.group(2))) return '*' + '*'.join(result) + # Strip outer parens from pure-product numerators: (a*b)/c → a*b/c. + # A "pure product" means no + or - at the top level inside the parens. + expanded = [] + i, n = 0, len(s) + while i < n: + if s[i] != '(': + expanded.append(s[i]); i += 1; continue + depth, j, pure = 1, i + 1, True + while j < n and depth > 0: + if s[j] == '(': depth += 1 + elif s[j] == ')': depth -= 1 + elif s[j] in '+-' and depth == 1: pure = False + j += 1 + if pure and j < n and s[j] == '/': + expanded.append(s[i+1:j-1]) # content without outer parens + else: + expanded.append(s[i:j]) + i = j + s = ''.join(expanded) + return denom_re.sub(replace_denom, s) @classmethod @@ -317,7 +336,7 @@ def lcm_list(lst): depth += 1 elif c == ')': depth -= 1 - elif c in '+-' and depth == 0 and i > start: + elif c in '+-' and depth == 0 and i > start and s[i-1] != '^': term_strs.append(s[start:i]) start = i term_strs.append(s[start:]) From 1950096971e2bb596d66aa68563353d5b745e1c3 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Tue, 23 Jun 2026 23:44:41 -0500 Subject: [PATCH 17/53] Add various optimizations and a flag to switch between DictLaurentPolynomial and sage's LaurentPolynomial --- spherogram_src/links/invariants.py | 4 +- spherogram_src/links/links_base.py | 25 +++- .../links/reshetikhin_turaev/RT_network.py | 137 ++++++++++++++---- .../links/reshetikhin_turaev/R_matrices.py | 33 +++-- .../dict_laurent_polynomial.py | 108 ++++++++++---- .../links/reshetikhin_turaev/sparse_array.py | 7 +- spherogram_src/links/tangles.py | 3 + 7 files changed, 247 insertions(+), 70 deletions(-) diff --git a/spherogram_src/links/invariants.py b/spherogram_src/links/invariants.py index 4779c1d..aabcdd1 100644 --- a/spherogram_src/links/invariants.py +++ b/spherogram_src/links/invariants.py @@ -331,10 +331,10 @@ def alexander_polynomial(self, multivar=True, v='no', method='default', return p.factor() return p - def colored_links_gould_polynomial(self, n): + def colored_links_gould_polynomial(self, n, sage_polynomials = False): from .reshetikhin_turaev import colored_links_gould_R_matrices - return self.long_diagram().apply_reshetikhin_turaev_functor(colored_links_gould_R_matrices(n)).evaluate() + return self.min_long_diagram().apply_reshetikhin_turaev_functor(colored_links_gould_R_matrices(n, sage_polynomials=sage_polynomials)).evaluate() def knot_floer_homology(self, prime=2, complex=False): """ diff --git a/spherogram_src/links/links_base.py b/spherogram_src/links/links_base.py index 670843c..4b5b37a 100644 --- a/spherogram_src/links/links_base.py +++ b/spherogram_src/links/links_base.py @@ -1370,7 +1370,7 @@ def long_diagram(self, cut_at = None): """ from .tangles import Tangle L = self.copy() - + if cut_at is None: strand = L.crossings[0].crossing_strands()[0] else: @@ -1382,7 +1382,28 @@ def long_diagram(self, cut_at = None): for c in L.crossings: c._clear() - return Tangle((1,1), L.crossings, open_strands) + return Tangle((1,1), L.crossings, open_strands) + + def min_long_diagram(self): + """ + Return the long diagram of self with the minimal contraction width + """ + + if not self.crossings: + return self.long_diagram() + + min_width = None + + for i in range(len(self.crossings)): + entry_indices = [3, 0] if self.crossings[i].sign == 1 else [0, 1] + for j in entry_indices: + diagram = self.long_diagram(cut_at=(i,j)) + width = diagram.contraction_width() + if min_width is None or width < min_width: + ans = diagram + min_width = width + + return ans def __len__(self): return len(self.crossings) diff --git a/spherogram_src/links/reshetikhin_turaev/RT_network.py b/spherogram_src/links/reshetikhin_turaev/RT_network.py index 6c69892..c5d49b7 100644 --- a/spherogram_src/links/reshetikhin_turaev/RT_network.py +++ b/spherogram_src/links/reshetikhin_turaev/RT_network.py @@ -66,14 +66,21 @@ def __init__(self, tensors, T = None, network = None, rot_num = None, boundary = edge[labels[3]], edge[labels[2]]) - network.append((tensors.R(c.sign), key)) + if tensors is not None: + network.append((tensors.R(c.sign), key)) + else: + network.append((None, key)) self.idle_labels = set(self.boundary_labels) for arc in self.idle_labels: if arc not in edge.keys(): edge[arc] = DirectedEdge(arc) - network.append((tensors.h(0), (~edge[arc], edge[arc]))) + + if tensors is not None: + network.append((tensors.h(0), (~edge[arc], edge[arc]))) + else: + network.append((None, (~edge[arc], edge[arc]))) else: assert all(item is not None for item in (network, rot_num, boundary, boundary_labels)) self.network = network @@ -104,11 +111,84 @@ def optimal_contraction_sequence(self): except: raise ValueError(f'key {key[0].index} not found in {idle}') else: - oe_network.append(np.empty(tensor.shape)) + if tensor is not None: + oe_network.append(np.empty(tensor.shape)) + else: + oe_network.append(np.empty([4 for _ in key])) oe_network.append([edge.index for edge in key]) return oe.contract_path(*oe_network, idle)[0] + @staticmethod + def local_contraction_width(abstract_network, indices): + idx1, idx2 = indices + ans = list(abstract_network) + key1 = abstract_network[idx1] + key2 = abstract_network[idx2] + + contracted_indices = set() + + pairs = [] + for pos_i, ei in enumerate(key1): + for pos_j, ej in enumerate(key2): + if ei.index == ej.index and ei.sign * ej.sign == -1: + pairs.append((pos_i, pos_j)) + contracted_indices.add(ei.index) + + contracted1 = {pos_i for pos_i, _ in pairs} + contracted2 = {pos_j for _, pos_j in pairs} + + if idx1 == idx2: + contracted_all = contracted1 | contracted2 + new_key = tuple(e for pos, e in enumerate(key1) if pos not in contracted_all) + ans.pop(idx1) + else: + new_key = (tuple(e for pos, e in enumerate(key1) if pos not in contracted1) + + tuple(e for pos, e in enumerate(key2) if pos not in contracted2)) + hi, lo = max(idx1, idx2), min(idx1, idx2) + ans.pop(hi) + ans.pop(lo) + + ans.append(new_key) + + return len(new_key) + len(contracted_indices), ans + + def seq_contraction_width(self, seq): + abstract_network = [key for _, key in self.network] + + width = 0 + + for indices in seq: + local_width, abstract_network = RTNetwork.local_contraction_width(abstract_network, indices) + + if local_width > width: + width = local_width + + return width, abstract_network + + def contraction_width(self, omit_idle_arcs = True): + abstract_network = [] + for _, key in self.network: + if omit_idle_arcs: + non_idle_key = tuple(e for e in key if e.index not in self.idle_labels) + abstract_network.append((None, non_idle_key)) + else: + abstract_network.append((None, key)) + + abstract_copy = RTNetwork(None, + network = abstract_network, + rot_num = self.rot_num, + boundary = (0,0) if omit_idle_arcs else self.boundary, + boundary_labels = [] if omit_idle_arcs else self.boundary_labels) + + width = 0 + if abstract_copy._resolve_self_loops(): + width = 3 + + seq_width, ans = abstract_copy.seq_contraction_width(abstract_copy.optimal_contraction_sequence()) + + return max(width, seq_width), ans + def contract_nodes(self, indices): idx1, idx2 = indices tensor1, key1 = self.network[idx1] @@ -121,7 +201,10 @@ def contract_nodes(self, indices): side = 0 if ei.sign == 1 else 1 pairs[(pos_i, pos_j)] = (side, self.tensors.h(self.rot_num[ei.index])) - result_tensor = tensor1.decorated_contract(tensor2, pairs) + if tensor1 is not None: + result_tensor = tensor1.decorated_contract(tensor2, pairs) + else: + result_tensor = None contracted1 = {pos_i for pos_i, _ in pairs} contracted2 = {pos_j for _, pos_j in pairs} @@ -138,35 +221,35 @@ def contract_nodes(self, indices): self.network.pop(lo) del tensor1, tensor2 - + self.network.append((result_tensor, new_key)) - self._resolve_self_loops(len(self.network) - 1) - - def _resolve_self_loops(self, idx): - while True: - _, key = self.network[idx] - pairs = {} - seen = {} - for pos, e in enumerate(key): - if e.index in seen: - other_pos, other_e = seen[e.index] - if e.sign * other_e.sign == -1: - side = 0 if other_e.sign == 1 else 1 - pairs[(other_pos, pos)] = (side, self.tensors.h(self.rot_num[e.index])) - else: - seen[e.index] = (pos, e) - if not pairs: - break - self.contract_nodes((idx, idx)) - idx = len(self.network) - 1 - + + def _resolve_self_loop_at(self, idx): + _, key = self.network[idx] + pairs = [] + seen = {} + for pos, e in enumerate(key): + if e.index in seen: + other_pos, other_e = seen[e.index] + if e.sign * other_e.sign == -1: + pairs.append((other_pos, pos)) + break + else: + seen[e.index] = (pos, e) + if not pairs: + return False + self.contract_nodes((idx, idx)) + return True + + def _resolve_self_loops(self): + return any(self._resolve_self_loop_at(i) for i in range(len(self.network))) + def contract_sequence(self, seq): for indices in seq: self.contract_nodes(indices) def contract_all(self): - for i in range(len(self.network)): - self._resolve_self_loops(i) + self._resolve_self_loops() self.contract_sequence(self.optimal_contraction_sequence()) def evaluate(self): diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices.py b/spherogram_src/links/reshetikhin_turaev/R_matrices.py index b750443..5ee0ad7 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices.py +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices.py @@ -8,7 +8,7 @@ _cache = dict() -def laurent_sparse_tensor_from_file(file, vars = ['t', 'q']): +def laurent_sparse_tensor_from_file(file, vars = ['t', 'q'], sage_polynomials = False): reader = csv.reader(file) header = next(reader) shape = ast.literal_eval(header[0]) @@ -21,18 +21,25 @@ def laurent_sparse_tensor_from_file(file, vars = ['t', 'q']): key = tuple(ast.literal_eval(key)) assert key not in data.keys(), f'{key} appeared multiple times in {file.name}' value = DictLaurentPolynomial.from_str(value, vars = vars) - data[key] = value + if not sage_polynomials: + data[key] = value + else: + data[key] = value.to_sage() + + default = DictLaurentPolynomial.from_str('0', vars = vars) + if sage_polynomials: + default = default.to_sage() - return SparseTensor(shape = shape, data = data) + return SparseTensor(shape = shape, data = data, default = default) -def laurent_sparse_tensor_from_path(path, vars = ['t', 'q'], compressed = False): +def laurent_sparse_tensor_from_path(path, vars = ['t', 'q'], compressed = False, sage_polynomials = False): if compressed: import bz2 with bz2.open(path, 'rt') as f: - return laurent_sparse_tensor_from_file(f, vars = vars) + return laurent_sparse_tensor_from_file(f, vars = vars, sage_polynomials = sage_polynomials) else: with open(path, 'r') as f: - return laurent_sparse_tensor_from_file(f, vars = vars) + return laurent_sparse_tensor_from_file(f, vars = vars, sage_polynomials = sage_polynomials) class RMatrix: __slots__ = ['_R', '_h', '_id'] @@ -41,7 +48,7 @@ def __init__(self, Rp, Rm, hp, hm): self._R = (Rp, Rm) self._h = (hp, hm) - self._id = SparseTensor(hp.shape, data = {(i,i): 1 for i in range(hp.shape[0])}) + self._id = SparseTensor(hp.shape, data = {(i,i): 1 for i in range(hp.shape[0])}, default = hp.default) def R(self, sign): if sign == 1: @@ -60,24 +67,26 @@ def h(self, sign): return self._id @staticmethod - def laurent_R_from_directory(dir_path, vars = ['t', 'q'], compressed = False): + def laurent_R_from_directory(dir_path, vars = ['t', 'q'], compressed = False, sage_polynomials = False): names = [name + '.csv' + ('.bz2' if compressed else '') for name in ['Rp', 'Rn', 'hp', 'hn']] tensors = [laurent_sparse_tensor_from_path(os.path.join(dir_path, name), vars = vars, - compressed = compressed) + compressed = compressed, + sage_polynomials = sage_polynomials) for name in names] return RMatrix(*tensors) -def colored_links_gould_R_matrices(n): +def colored_links_gould_R_matrices(n, sage_polynomials = False): if 0 < n <= 4: - key = f'V{n}' + key = (f'V{n}', sage_polynomials) if key in _cache.keys(): return _cache[key] else: - _cache[key] = RMatrix.laurent_R_from_directory(dir_path = os.path.join(dir_path, f'R_matrices/V{n}/')) + _cache[key] = RMatrix.laurent_R_from_directory(dir_path = os.path.join(dir_path, f'R_matrices/V{n}/'), + sage_polynomials = sage_polynomials) return _cache[key] else: raise NotImplementedError \ No newline at end of file diff --git a/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py index 48cd879..6eea833 100644 --- a/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py +++ b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py @@ -85,10 +85,11 @@ def to_sage(self): raise NotImplementedError('Can only convert DictLaurentPolynomial with integer exponentials to LaurentPolynomial in Sage.') @classmethod - def _make(cls, vars, poly_dict): - """Construct without cleaning — caller guarantees no zero values.""" + def _make(cls, vars, poly_dict, _interned=False): + """Construct without cleaning — caller guarantees no zero values. + Pass _interned=True when vars is already a canonical interned tuple.""" obj = object.__new__(cls) - obj.vars = _intern_vars(vars if isinstance(vars, tuple) else tuple(vars)) + obj.vars = vars if _interned else _intern_vars(vars if isinstance(vars, tuple) else tuple(vars)) obj.poly_dict = poly_dict return obj @@ -120,7 +121,7 @@ def __eq__(self, other): def __neg__(self): return DictLaurentPolynomial._make( - self.vars, {k: -v for k, v in self.poly_dict.items()}) + self.vars, {k: -v for k, v in self.poly_dict.items()}, _interned=True) def __add__(self, other): if isinstance(other, DictLaurentPolynomial): @@ -134,9 +135,9 @@ def __add__(self, other): del result[k] else: result[k] = v - return DictLaurentPolynomial._make(self.vars, result) + return DictLaurentPolynomial._make(self.vars, result, _interned=True) if other == 0: - return DictLaurentPolynomial._make(self.vars, dict(self.poly_dict)) + return DictLaurentPolynomial._make(self.vars, dict(self.poly_dict), _interned=True) zero_key = (0,) * len(self.vars) result = dict(self.poly_dict) s = result.get(zero_key, 0) + other @@ -144,7 +145,7 @@ def __add__(self, other): result[zero_key] = s else: result.pop(zero_key, None) - return DictLaurentPolynomial._make(self.vars, result) + return DictLaurentPolynomial._make(self.vars, result, _interned=True) def __radd__(self, other): return self.__add__(other) @@ -161,7 +162,7 @@ def __sub__(self, other): del result[k] else: result[k] = -v - return DictLaurentPolynomial._make(self.vars, result) + return DictLaurentPolynomial._make(self.vars, result, _interned=True) return self.__add__(-other) def __rsub__(self, other): @@ -170,25 +171,80 @@ def __rsub__(self, other): def __mul__(self, other): if isinstance(other, DictLaurentPolynomial): result = {} - for k1, v1 in self.poly_dict.items(): - for k2, v2 in other.poly_dict.items(): - k = tuple(a + b for a, b in zip(k1, k2)) - prod = v1 * v2 - if k in result: - s = result[k] + prod - if s: - result[k] = s - else: - del result[k] - else: - result[k] = prod - return DictLaurentPolynomial._make(self.vars, result) + if len(self.poly_dict) == 1: + # monomial * polynomial: no cancellation possible, assign directly + (k1, v1), = self.poly_dict.items() + n = len(k1) + if n == 1: + for k2, v2 in other.poly_dict.items(): + result[(k1[0] + k2[0],)] = v1 * v2 + elif n == 2: + for k2, v2 in other.poly_dict.items(): + result[(k1[0] + k2[0], k1[1] + k2[1])] = v1 * v2 + else: + for k2, v2 in other.poly_dict.items(): + result[tuple(a + b for a, b in zip(k1, k2))] = v1 * v2 + elif len(other.poly_dict) == 1: + # polynomial * monomial: no cancellation possible, assign directly + (k2, v2), = other.poly_dict.items() + n = len(k2) + if n == 1: + for k1, v1 in self.poly_dict.items(): + result[(k1[0] + k2[0],)] = v1 * v2 + elif n == 2: + for k1, v1 in self.poly_dict.items(): + result[(k1[0] + k2[0], k1[1] + k2[1])] = v1 * v2 + else: + for k1, v1 in self.poly_dict.items(): + result[tuple(a + b for a, b in zip(k1, k2))] = v1 * v2 + else: + n = len(self.vars) + if n == 1: + for k1, v1 in self.poly_dict.items(): + for k2, v2 in other.poly_dict.items(): + k = (k1[0] + k2[0],) + prod = v1 * v2 + if k in result: + s = result[k] + prod + if s: + result[k] = s + else: + del result[k] + else: + result[k] = prod + elif n == 2: + for k1, v1 in self.poly_dict.items(): + for k2, v2 in other.poly_dict.items(): + k = (k1[0] + k2[0], k1[1] + k2[1]) + prod = v1 * v2 + if k in result: + s = result[k] + prod + if s: + result[k] = s + else: + del result[k] + else: + result[k] = prod + else: + for k1, v1 in self.poly_dict.items(): + for k2, v2 in other.poly_dict.items(): + k = tuple(a + b for a, b in zip(k1, k2)) + prod = v1 * v2 + if k in result: + s = result[k] + prod + if s: + result[k] = s + else: + del result[k] + else: + result[k] = prod + return DictLaurentPolynomial._make(self.vars, result, _interned=True) if other == 0: - return DictLaurentPolynomial._make(self.vars, {}) + return DictLaurentPolynomial._make(self.vars, {}, _interned=True) if other == 1: - return DictLaurentPolynomial._make(self.vars, dict(self.poly_dict)) + return DictLaurentPolynomial._make(self.vars, dict(self.poly_dict), _interned=True) return DictLaurentPolynomial._make( - self.vars, {k: v * other for k, v in self.poly_dict.items()}) + self.vars, {k: v * other for k, v in self.poly_dict.items()}, _interned=True) def __rmul__(self, other): return self.__mul__(other) @@ -403,14 +459,14 @@ def __pow__(self, n): raise ValueError(f'exponent must be an integer, got {n!r}') if n == 0: zero_key = (0,) * len(self.vars) - return DictLaurentPolynomial._make(self.vars, {zero_key: 1}) + return DictLaurentPolynomial._make(self.vars, {zero_key: 1}, _interned=True) if n < 0: if len(self.poly_dict) != 1: raise ValueError('negative powers only supported for monomials') (key, coef), = self.poly_dict.items() inv_key = tuple(k * n for k in key) inv_coef = coef ** n # works when coef is ±1 or a symbolic type - return DictLaurentPolynomial._make(self.vars, {inv_key: inv_coef}) + return DictLaurentPolynomial._make(self.vars, {inv_key: inv_coef}, _interned=True) result = self base = self n -= 1 diff --git a/spherogram_src/links/reshetikhin_turaev/sparse_array.py b/spherogram_src/links/reshetikhin_turaev/sparse_array.py index c41d57d..a38aa84 100644 --- a/spherogram_src/links/reshetikhin_turaev/sparse_array.py +++ b/spherogram_src/links/reshetikhin_turaev/sparse_array.py @@ -89,6 +89,10 @@ def rank(self): @property def shape(self): return self._shape + + @property + def default(self): + return self._default def nonzero_indices(self): """Return list of all indices with non-default values.""" @@ -276,8 +280,9 @@ def decorated_contract(self, other: 'SparseTensor', pairs): h_weight *= hval if h_weight == self._default: continue + hval_b = h_weight * val_b for f_key_a, val_a in group: - result._accumulate(f_key_a + f_key_b, val_a * h_weight * val_b) + result._accumulate(f_key_a + f_key_b, val_a * hval_b) return result diff --git a/spherogram_src/links/tangles.py b/spherogram_src/links/tangles.py index 7bfd371..5632349 100644 --- a/spherogram_src/links/tangles.py +++ b/spherogram_src/links/tangles.py @@ -655,6 +655,9 @@ def entry_crossing(k): def apply_reshetikhin_turaev_functor(self, tensors): return RTNetwork(tensors, T = self) + + def contraction_width(self): + return RTNetwork(None, T = self).contraction_width() def _component_starts_from_PD(self, code, labels, gluings, entry_dict): """ From c6e293bcf1ef0342f77481475508c7d4fbcee89a Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Wed, 24 Jun 2026 02:09:26 -0500 Subject: [PATCH 18/53] Colored Jones polynomial implemented --- spherogram_src/links/invariants.py | 20 +++- .../links/reshetikhin_turaev/RT_network.py | 21 +++- .../links/reshetikhin_turaev/R_matrices.py | 109 ++++++++++++++++-- .../links/reshetikhin_turaev/__init__.py | 4 +- .../dict_laurent_polynomial.py | 69 ++++++++++- .../links/reshetikhin_turaev/sparse_array.py | 2 - 6 files changed, 199 insertions(+), 26 deletions(-) diff --git a/spherogram_src/links/invariants.py b/spherogram_src/links/invariants.py index aabcdd1..e9ce5a0 100644 --- a/spherogram_src/links/invariants.py +++ b/spherogram_src/links/invariants.py @@ -331,10 +331,26 @@ def alexander_polynomial(self, multivar=True, v='no', method='default', return p.factor() return p - def colored_links_gould_polynomial(self, n, sage_polynomials = False): + def colored_links_gould_polynomial(self, n, sage_polynomials = False, timed = False): from .reshetikhin_turaev import colored_links_gould_R_matrices - return self.min_long_diagram().apply_reshetikhin_turaev_functor(colored_links_gould_R_matrices(n, sage_polynomials=sage_polynomials)).evaluate() + ans = self.min_long_diagram().apply_reshetikhin_turaev_functor(colored_links_gould_R_matrices(n, sage_polynomials=sage_polynomials)).evaluate(timed = timed) + + if timed: + return ans + else: + return ans[0] + + def colored_jones_polynomial(self, n, sage_polynomials = False, timed = False): + from .reshetikhin_turaev import colored_jones_R_matrices, prefactor_colored_jones + + ans = self.min_long_diagram().apply_reshetikhin_turaev_functor(colored_jones_R_matrices(n, sage_polynomials=sage_polynomials)).evaluate(timed = timed) + ans = (ans[0] * prefactor_colored_jones(n, self.writhe(), sage_polynomial=sage_polynomials), ans[1]) + + if timed: + return ans + else: + return ans[0] def knot_floer_homology(self, prime=2, complex=False): """ diff --git a/spherogram_src/links/reshetikhin_turaev/RT_network.py b/spherogram_src/links/reshetikhin_turaev/RT_network.py index c5d49b7..2b4d03b 100644 --- a/spherogram_src/links/reshetikhin_turaev/RT_network.py +++ b/spherogram_src/links/reshetikhin_turaev/RT_network.py @@ -244,15 +244,24 @@ def _resolve_self_loop_at(self, idx): def _resolve_self_loops(self): return any(self._resolve_self_loop_at(i) for i in range(len(self.network))) - def contract_sequence(self, seq): + def contract_sequence(self, seq, timed = False): + if timed: + import time + start_time = time.time() + for indices in seq: self.contract_nodes(indices) + + if timed: + time_cost = time.time() - start_time + + return time_cost - def contract_all(self): + def contract_all(self, timed = False): self._resolve_self_loops() - self.contract_sequence(self.optimal_contraction_sequence()) + return self.contract_sequence(self.optimal_contraction_sequence(), timed = timed) - def evaluate(self): + def evaluate(self, timed = False): """ Fixate all idle labels at value 0, obtaining a new RTNework with (0,0) boundary, contract all and return the product of all values of the resulting tensors. @@ -283,9 +292,9 @@ def evaluate(self): boundary=(0, 0), boundary_labels=[] ) - reduced.contract_all() + time = reduced.contract_all(timed = timed) result = prefactor for tensor, _ in reduced.network: result *= tensor[()] - return result + return (result, time) diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices.py b/spherogram_src/links/reshetikhin_turaev/R_matrices.py index 5ee0ad7..284c0ca 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices.py +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices.py @@ -1,4 +1,4 @@ -from .dict_laurent_polynomial import DictLaurentPolynomial +from .dict_laurent_polynomial import DictLaurentPolynomial, LaurentVariable from .sparse_array import SparseTensor @@ -26,11 +26,7 @@ def laurent_sparse_tensor_from_file(file, vars = ['t', 'q'], sage_polynomials = else: data[key] = value.to_sage() - default = DictLaurentPolynomial.from_str('0', vars = vars) - if sage_polynomials: - default = default.to_sage() - - return SparseTensor(shape = shape, data = data, default = default) + return SparseTensor(shape = shape, data = data) def laurent_sparse_tensor_from_path(path, vars = ['t', 'q'], compressed = False, sage_polynomials = False): if compressed: @@ -48,7 +44,7 @@ def __init__(self, Rp, Rm, hp, hm): self._R = (Rp, Rm) self._h = (hp, hm) - self._id = SparseTensor(hp.shape, data = {(i,i): 1 for i in range(hp.shape[0])}, default = hp.default) + self._id = SparseTensor(hp.shape, data = {(i,i): 1 for i in range(hp.shape[0])}) def R(self, sign): if sign == 1: @@ -89,4 +85,101 @@ def colored_links_gould_R_matrices(n, sage_polynomials = False): sage_polynomials = sage_polynomials) return _cache[key] else: - raise NotImplementedError \ No newline at end of file + raise NotImplementedError + +def _q_binomial(n, k, q): + if k < 0 or k > n: + return 0 + # table[i][j] = q_binomial(i, j, q) + table = [[0] * (k + 1) for _ in range(n + 1)] + for i in range(n + 1): + table[i][0] = 1 + for i in range(1, n + 1): + for j in range(1, min(i, k) + 1): + table[i][j] = table[i-1][j-1] + q**j * table[i-1][j] + return table[n][k] + +def _q_pochhammer(a, q, n): + result = 1 + for k in range(n): + result = result * (1 - a * q**k) + return result + +def _q_pow(e4): + """DictLaurentPolynomial representing q^(e4/4). e4 must be an integer.""" + return DictLaurentPolynomial._make((LaurentVariable('q', 4),), {(e4,): 1}) + +def colored_jones_R_matrices(n, sage_polynomials=False): + """ + The R matrices for the n-colored Jones polynomial. + In particular, n = 1 gives the Jones polynomial + """ + n = n + 1 + + q_actual = _q_pow(4) # q^1 + q_inv = _q_pow(-4) # q^(-1) + + shape = (n, n, n, n) + data_p = {} + data_n = {} + + for i in range(n): + for j in range(n): + for k in range(n): + l = i + j - k + if l < 0 or l >= n: + continue + + # JRRp[i,j,k,l] = JRp[i, j, m_p, n] with m_p = j - k + # = q^(-(n-1)^2/4) * q^(-(i+j-k)*k) * q^((n-1)*(i+k)/2) + # * qbin[j, m_p] * qp[q^(n-1-i), q^-1, m_p] + m_p = j - k + e4_p = -(n-1)**2 - 4*(i+j-k)*k + 2*(n-1)*(i+k) + mono = _q_pow(e4_p) + qb = _q_binomial(j, m_p, q_actual) # 0 when m_p < 0 or m_p > j + qp = _q_pochhammer(_q_pow(4*(n-1-i)), q_inv, m_p) + val = mono * qb * qp + if val: + if not sage_polynomials: + data_p[(i, j, k, l)] = val + else: + data_p[(i, j, k, l)] = val.to_sage() + + # JRRn[i,j,k,l] = JRn[i, j, m_n, n] with m_n = k - j + # = q^((n-1)^2/4) * (-1)^m_n * q^(i*j + m_n*(m_n-1)/2) * q^(-(n-1)*(i+k)/2) + # * qbin[i, m_n] * qp[q^(n-1-j), q^-1, m_n] + m_n = k - j + # m_n*(m_n-1) is always even (product of consecutive integers) + e4_n = (n-1)**2 + 4*(i*j + m_n*(m_n-1)//2) - 2*(n-1)*(i+k) + sign = (-1)**m_n + mono = _q_pow(e4_n) * sign + qb = _q_binomial(i, m_n, q_actual) # 0 when m_n < 0 or m_n > i + qp = _q_pochhammer(_q_pow(4*(n-1-j)), q_inv, m_n) + val = mono * qb * qp + if val: + if not sage_polynomials: + data_n[(i, j, k, l)] = val + else: + data_n[(i, j, k, l)] = val.to_sage() + + Rp = SparseTensor(shape, data=data_p) + Rn = SparseTensor(shape, data=data_n) + + # hp[i,i] = q^(i + (1-n)/2) = q^(i - (n-1)/2), key e4 = 4*i - 2*(n-1) + # hn[i,i] = 1 / hp[i,i] , key e4 = 2*(n-1) - 4*i + if not sage_polynomials: + hp = SparseTensor((n, n), data={(i, i): _q_pow(4*i - 2*(n-1)) for i in range(n)}) + hn = SparseTensor((n, n), data={(i, i): _q_pow(2*(n-1) - 4*i) for i in range(n)}) + else: + hp = SparseTensor((n, n), data={(i, i): _q_pow(4*i - 2*(n-1)).to_sage() for i in range(n)}) + hn = SparseTensor((n, n), data={(i, i): _q_pow(2*(n-1) - 4*i).to_sage() for i in range(n)}) + + return RMatrix(Rp, Rn, hp, hn) + +def prefactor_colored_jones(n, writhe, sage_polynomial = False): + n = n + 1 + + if not sage_polynomial: + return _q_pow(writhe * ((n**2) -1)) + else: + return _q_pow(writhe * ((n**2) -1)).to_sage() diff --git a/spherogram_src/links/reshetikhin_turaev/__init__.py b/spherogram_src/links/reshetikhin_turaev/__init__.py index 1d0507b..48c677a 100644 --- a/spherogram_src/links/reshetikhin_turaev/__init__.py +++ b/spherogram_src/links/reshetikhin_turaev/__init__.py @@ -1,6 +1,6 @@ from .RT_network import RTNetwork from .dict_laurent_polynomial import DictLaurentPolynomial -from .R_matrices import RMatrix, colored_links_gould_R_matrices +from .R_matrices import RMatrix, colored_links_gould_R_matrices, colored_jones_R_matrices, prefactor_colored_jones from .sparse_array import SparseArray, SparseTensor -__all__ = ['RTNetwork', 'RMatrix', 'DictLaurentPolynomial', 'SparseArray', 'SparseTensor','colored_links_gould_R_matrices'] \ No newline at end of file +__all__ = ['RTNetwork', 'RMatrix', 'DictLaurentPolynomial', 'SparseArray', 'SparseTensor','colored_links_gould_R_matrices', 'colored_jones_R_matrices', 'prefactor_colored_jones'] \ No newline at end of file diff --git a/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py index 6eea833..0505cab 100644 --- a/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py +++ b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py @@ -1,13 +1,28 @@ from ...sage_helper import _within_sage, sage_method if _within_sage: - from sage.all import LaurentPolynomialRing, ZZ + from sage.all import PuiseuxSeriesRing, LaurentPolynomialRing, ZZ @sage_method def laurent_poly_from_dict(dict, vars, F = ZZ): L = LaurentPolynomialRing(F, vars) return L(dict) +@sage_method +def puiseux_series_from_dict(poly_dict, var, F=ZZ): + """ + Build a Sage Puiseux series from a poly_dict and a single LaurentVariable. + Key k represents var^(k / var.denominator). + """ + P = PuiseuxSeriesRing(F, var.name) + t = P.gen() + result = P.zero() + den = ZZ(var.denominator) + for key, coef in poly_dict.items(): + (k,) = key + result += coef * t ** (ZZ(k) / den) + return result + import re class LaurentVariable: @@ -81,8 +96,10 @@ def __init__(self, vars, poly_dict): def to_sage(self): if all(var.denominator == 1 for var in self.vars): return laurent_poly_from_dict(self.poly_dict, [var.name for var in self.vars]) + elif len(self.vars) == 1: + return puiseux_series_from_dict(self.poly_dict, self.vars[0]) else: - raise NotImplementedError('Can only convert DictLaurentPolynomial with integer exponentials to LaurentPolynomial in Sage.') + raise NotImplementedError('Multi-variable Puiseux conversion to Sage is not supported.') @classmethod def _make(cls, vars, poly_dict, _interned=False): @@ -112,9 +129,12 @@ def __bool__(self): def __eq__(self, other): if isinstance(other, DictLaurentPolynomial): - return self.poly_dict == other.poly_dict + return self.poly_dict == other.poly_dict and self.vars == other.vars if other == 0: return not self.poly_dict + if other == 1: + return len(self.poly_dict) == 1 and \ + self.poly_dict.get((0,) * len(self.vars), 0) == 1 return NotImplemented __hash__ = None @@ -125,8 +145,13 @@ def __neg__(self): def __add__(self, other): if isinstance(other, DictLaurentPolynomial): - result = dict(self.poly_dict) - for k, v in other.poly_dict.items(): + sp, op = self.poly_dict, other.poly_dict + # Copy the larger dict; iterate over the smaller to minimise lookups. + if len(sp) >= len(op): + result, iterate = dict(sp), op + else: + result, iterate = dict(op), sp + for k, v in iterate.items(): if k in result: s = result[k] + v if s: @@ -136,8 +161,10 @@ def __add__(self, other): else: result[k] = v return DictLaurentPolynomial._make(self.vars, result, _interned=True) + if other == 0: return DictLaurentPolynomial._make(self.vars, dict(self.poly_dict), _interned=True) + zero_key = (0,) * len(self.vars) result = dict(self.poly_dict) s = result.get(zero_key, 0) + other @@ -239,16 +266,46 @@ def __mul__(self, other): else: result[k] = prod return DictLaurentPolynomial._make(self.vars, result, _interned=True) - if other == 0: + + if self == 0 or other == 0: return DictLaurentPolynomial._make(self.vars, {}, _interned=True) + if other == 1: return DictLaurentPolynomial._make(self.vars, dict(self.poly_dict), _interned=True) + return DictLaurentPolynomial._make( self.vars, {k: v * other for k, v in self.poly_dict.items()}, _interned=True) def __rmul__(self, other): return self.__mul__(other) + def simplify_denominator(self): + """ + Return a simplified copy where each variable's denominator is divided + by the GCD it shares with all exponents for that variable. + + Example: LaurentVariable('q', 4) with all exponents divisible by 2 + becomes LaurentVariable('q', 2) with exponents halved. + """ + reductions = [] + for i, var in enumerate(self.vars): + g = 0 + for key in self.poly_dict: + g = _gcd(abs(key[i]), g) + if g == 1: + break + reductions.append(_gcd(g, var.denominator)) + + new_vars = tuple( + LaurentVariable(var.name, var.denominator // r) + for var, r in zip(self.vars, reductions) + ) + new_poly_dict = { + tuple(k // r for k, r in zip(key, reductions)): coef + for key, coef in self.poly_dict.items() + } + return DictLaurentPolynomial._make(new_vars, new_poly_dict) + def change_vars(self, rules): """ Return a new DictLaurentPolynomial with variables substituted by rules. diff --git a/spherogram_src/links/reshetikhin_turaev/sparse_array.py b/spherogram_src/links/reshetikhin_turaev/sparse_array.py index a38aa84..4139494 100644 --- a/spherogram_src/links/reshetikhin_turaev/sparse_array.py +++ b/spherogram_src/links/reshetikhin_turaev/sparse_array.py @@ -278,8 +278,6 @@ def decorated_contract(self, other: 'SparseTensor', pairs): h_weight = 1 for _, hval in combo: h_weight *= hval - if h_weight == self._default: - continue hval_b = h_weight * val_b for f_key_a, val_a in group: result._accumulate(f_key_a + f_key_b, val_a * hval_b) From 9c4465e637300f54d9e2f8784d36e6ff95e639ec Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Wed, 24 Jun 2026 02:42:37 -0500 Subject: [PATCH 19/53] Before changing the algorithm of from_str. Sanity checks added. --- .../dict_laurent_polynomial.py | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py index 0505cab..ffdd0c3 100644 --- a/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py +++ b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py @@ -437,6 +437,9 @@ def lcm_list(lst): r = lcm(r, x) return r + if len(vars) != len(set(vars)): + raise ValueError(f'from_str: duplicate variable names in {vars!r}') + s = s.replace(' ', '') s = cls._preprocess_division(s, vars) @@ -455,6 +458,9 @@ def lcm_list(lst): term_strs.append(s[start:]) term_strs = [t for t in term_strs if t] + if depth != 0: + raise ValueError(f'from_str: unmatched parentheses in {s!r}') + # Pattern for each variable: sym optionally followed by ^(num/den) or ^num. sym_pats = { sym: re.compile( @@ -474,8 +480,31 @@ def lcm_list(lst): rest = ts[m.end():] coef = 1 if prefix in ('', '+') else (-1 if prefix == '-' else int(prefix)) + # Strip outer parens from rest when the entire rest is wrapped and + # the content contains no unparenthesized + or - (i.e. a pure product). + # This handles terms like -(1*q^(-1)*t^(-1)) from _preprocess_division. + if rest.startswith('(') and rest.endswith(')'): + inner = rest[1:-1] + d, pure = 0, True + for ch in inner: + if ch == '(': d += 1 + elif ch == ')': d -= 1 + elif ch in '+-' and d == 0: pure = False; break + if d == 0 and pure: + rest = inner + + # Absorb a leading integer multiplier into coef (e.g. '2*q^(-1)' → coef*=2). + m2 = re.match(r'^(\d+)\*', rest) + if m2: + coef *= int(m2.group(1)) + rest = rest[m2.end():] + var_exps = {} for sym in vars: + if sum(1 for _ in sym_pats[sym].finditer(rest)) > 1: + raise ValueError( + f'from_str: variable {sym!r} appears more than once ' + f'in term {ts!r}') pm = sym_pats[sym].search(rest) if pm: if pm.group(1) is not None: @@ -485,9 +514,27 @@ def lcm_list(lst): num, den = int(pm.group(3)), 1 else: num, den = 1, 1 + if den == 0: + raise ValueError( + f'from_str: zero denominator in exponent in term {ts!r}') var_exps[sym] = (num, den) all_denoms[sym].add(den) + # After stripping all known variable patterns, only '*' separators + # should remain — any leftover letters, digits, or '.' indicate a + # mis-parsed coefficient (e.g. '1.5*q') or unknown content. + remainder = rest + for sym2 in vars: + remainder = sym_pats[sym2].sub('', remainder) + if re.search(r'[a-zA-Z]', remainder): + raise ValueError( + f'from_str: unrecognized content in term {ts!r} ' + f'(vars={vars!r}); leftover: {remainder!r}') + if re.search(r'[\d.]', remainder): + raise ValueError( + f'from_str: unexpected numeric content in term {ts!r}; ' + f'leftover after variables: {remainder!r}') + parsed.append((coef, var_exps)) var_lcms = {sym: lcm_list(all_denoms[sym]) for sym in vars} From ca538984d72e7cdf5f63083fdfe8346d2f787dbb Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Wed, 24 Jun 2026 03:18:42 -0500 Subject: [PATCH 20/53] DictLaurentPolynomial polished. --- .../links/reshetikhin_turaev/R_matrices.py | 22 +- .../dict_laurent_polynomial.py | 577 ++++++++++++------ 2 files changed, 406 insertions(+), 193 deletions(-) diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices.py b/spherogram_src/links/reshetikhin_turaev/R_matrices.py index 284c0ca..31728df 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices.py +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices.py @@ -2,7 +2,7 @@ from .sparse_array import SparseTensor -import csv, ast, pathlib, os +import csv, ast, pathlib, os, math dir_path = pathlib.Path(__file__).resolve().parent @@ -20,11 +20,21 @@ def laurent_sparse_tensor_from_file(file, vars = ['t', 'q'], sage_polynomials = key, value = line key = tuple(ast.literal_eval(key)) assert key not in data.keys(), f'{key} appeared multiple times in {file.name}' - value = DictLaurentPolynomial.from_str(value, vars = vars) - if not sage_polynomials: - data[key] = value - else: - data[key] = value.to_sage() + data[key] = DictLaurentPolynomial.from_str(value, vars = vars) + + # Unify variable denominators: compute LCM across all loaded polynomials so + # every value shares the same vars tuple (enabling interning and consistent arithmetic). + if data: + common_denoms = [1] * len(vars) + for poly in data.values(): + for i, var in enumerate(poly.vars): + d = common_denoms[i] + common_denoms[i] = d * var.denominator // math.gcd(d, var.denominator) + common_vars = tuple(LaurentVariable(v, d) for v, d in zip(vars, common_denoms)) + data = {key: poly.refactor_variables(common_vars) for key, poly in data.items()} + + if sage_polynomials: + data = {key: poly.to_sage() for key, poly in data.items()} return SparseTensor(shape = shape, data = data) diff --git a/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py index ffdd0c3..d44e08c 100644 --- a/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py +++ b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py @@ -23,8 +23,6 @@ def puiseux_series_from_dict(poly_dict, var, F=ZZ): result += coef * t ** (ZZ(k) / den) return result -import re - class LaurentVariable: """ A named variable with an optional denominator. @@ -78,10 +76,23 @@ def _intern_vars(vars_tuple): class DictLaurentPolynomial: """ A sparse Laurent polynomial in arbitrarily many variables. - + Represented as a dict mapping exponent tuples (of integers) to nonzero coefficients. Each variable's denominator is encoded in its LaurentVariable, so exponent key k for variable v means v^(k / v.denominator). + + Warning: arithmetic operations (+, -, *, /, **) do NOT check that the two + operands have compatible variables. It is the caller's responsibility to + ensure both polynomials share the same vars tuple (same names, same order, + same denominators) before combining them. The denoiminators can be normalized + via refactor_variables(new_vars) method. + + >>> p = DictLaurentPolynomial.from_str('q^2 - q^-1 + 3', ['q']) + >>> p + q^2 + 3 - q^-1 + >>> r = DictLaurentPolynomial.from_str('q^2*t - 1', ['q', 't']) + >>> r + q^2*t - 1 """ __slots__ = ['vars', 'poly_dict'] @@ -116,20 +127,55 @@ def generator(cls, vars, index=0): Return the unit monomial for the variable at `index`. Exponent key 1 represents var^(1/denominator). - Example: - q = LaurentVariable('q', 2) - gen = DictLaurentPolynomial.generator([q]) - # gen represents q^(1/2); gen**3 represents q^(3/2) + >>> q = LaurentVariable('q', 2) + >>> g = DictLaurentPolynomial.generator([q]) + >>> g + q^(1/2) + >>> g ** 3 + q^(3/2) """ exp = tuple(1 if i == index else 0 for i in range(len(vars))) return cls._make(vars, {exp: 1}) def __bool__(self): + """ + >>> bool(DictLaurentPolynomial.from_str('q + 1', ['q'])) + True + >>> bool(DictLaurentPolynomial.from_str('q - q', ['q'])) + False + """ return bool(self.poly_dict) def __eq__(self, other): + """ + >>> p = DictLaurentPolynomial.from_str('q + 1', ['q']) + >>> p == DictLaurentPolynomial.from_str('1 + q', ['q']) + True + >>> p == 0 + False + >>> DictLaurentPolynomial.from_str('q - q', ['q']) == 0 + True + >>> DictLaurentPolynomial.from_str('1', ['q']) == 1 + True + >>> v4 = LaurentVariable('q', 4) + >>> p4 = DictLaurentPolynomial._make((v4,), {(4,): 1}) # q stored with denom 4 + >>> p4 == DictLaurentPolynomial.from_str('q', ['q']) # same value, different denom + True + """ if isinstance(other, DictLaurentPolynomial): - return self.poly_dict == other.poly_dict and self.vars == other.vars + if self.vars is other.vars: + return self.poly_dict == other.poly_dict + if len(self.vars) != len(other.vars): + return False + if any(v1.name != v2.name for v1, v2 in zip(self.vars, other.vars)): + return False + def lcm(a, b): return a * b // _gcd(a, b) + common_vars = tuple( + LaurentVariable(v1.name, lcm(v1.denominator, v2.denominator)) + for v1, v2 in zip(self.vars, other.vars) + ) + return (self.refactor_variables(common_vars).poly_dict == + other.refactor_variables(common_vars).poly_dict) if other == 0: return not self.poly_dict if other == 1: @@ -140,10 +186,23 @@ def __eq__(self, other): __hash__ = None def __neg__(self): + """ + >>> p = DictLaurentPolynomial.from_str('q + 2', ['q']) + >>> -p + -q - 2 + """ return DictLaurentPolynomial._make( self.vars, {k: -v for k, v in self.poly_dict.items()}, _interned=True) def __add__(self, other): + """ + >>> p = DictLaurentPolynomial.from_str('q + 1', ['q']) + >>> r = DictLaurentPolynomial.from_str('q^-1 - 1', ['q']) + >>> p + r + q + q^-1 + >>> p + 3 + q + 4 + """ if isinstance(other, DictLaurentPolynomial): sp, op = self.poly_dict, other.poly_dict # Copy the larger dict; iterate over the smaller to minimise lookups. @@ -178,6 +237,14 @@ def __radd__(self, other): return self.__add__(other) def __sub__(self, other): + """ + >>> p = DictLaurentPolynomial.from_str('q^2 + q', ['q']) + >>> r = DictLaurentPolynomial.from_str('q', ['q']) + >>> p - r + q^2 + >>> p - 1 + q^2 + q - 1 + """ if isinstance(other, DictLaurentPolynomial): result = dict(self.poly_dict) for k, v in other.poly_dict.items(): @@ -196,6 +263,14 @@ def __rsub__(self, other): return (-self).__add__(other) def __mul__(self, other): + """ + >>> p = DictLaurentPolynomial.from_str('q + 1', ['q']) + >>> r = DictLaurentPolynomial.from_str('q - 1', ['q']) + >>> p * r + q^2 - 1 + >>> p * 3 + 3*q + 3 + """ if isinstance(other, DictLaurentPolynomial): result = {} if len(self.poly_dict) == 1: @@ -279,13 +354,55 @@ def __mul__(self, other): def __rmul__(self, other): return self.__mul__(other) + def __truediv__(self, other): + """ + Divide by a monomial DictLaurentPolynomial, or by a scalar that evenly + divides all coefficients. Raises ValueError otherwise. + + >>> p = DictLaurentPolynomial.from_str('q^2 - 1', ['q']) + >>> q = DictLaurentPolynomial.from_str('q', ['q']) + >>> p / q + q - q^-1 + >>> r = DictLaurentPolynomial.from_str('2*q^2 + 4*q', ['q']) + >>> r / 2 + q^2 + 2*q + >>> r / 3 + Traceback (most recent call last): + ... + ValueError: DictLaurentPolynomial division: scalar 3 does not divide all coefficients + >>> p / DictLaurentPolynomial.from_str('q + 1', ['q']) + Traceback (most recent call last): + ... + ValueError: DictLaurentPolynomial division: divisor must be a monomial + """ + if isinstance(other, DictLaurentPolynomial): + if len(other.poly_dict) != 1: + raise ValueError('DictLaurentPolynomial division: divisor must be a monomial') + return self * other ** -1 + if other == 0: + raise ZeroDivisionError('DictLaurentPolynomial division by zero') + if not all(c % other == 0 for c in self.poly_dict.values()): + raise ValueError( + f'DictLaurentPolynomial division: scalar {other!r} does not ' + f'divide all coefficients') + return DictLaurentPolynomial._make( + self.vars, {k: c // other for k, c in self.poly_dict.items()}, + _interned=True) + def simplify_denominator(self): """ Return a simplified copy where each variable's denominator is divided by the GCD it shares with all exponents for that variable. - Example: LaurentVariable('q', 4) with all exponents divisible by 2 - becomes LaurentVariable('q', 2) with exponents halved. + >>> v = LaurentVariable('q', 4) + >>> p = DictLaurentPolynomial._make((v,), {(2,): 1, (-2,): -1}) + >>> p.vars[0].denominator + 4 + >>> s = p.simplify_denominator() + >>> s.vars[0].denominator + 2 + >>> p == s + True """ reductions = [] for i, var in enumerate(self.vars): @@ -306,24 +423,64 @@ def simplify_denominator(self): } return DictLaurentPolynomial._make(new_vars, new_poly_dict) + def refactor_variables(self, new_vars): + """ + Return a copy rescaled to use new_vars. + + new_vars must list the same variable names in the same order, and each + new denominator must be a multiple of the current denominator. Exponent + keys are multiplied by the ratio new_denom / old_denom so the rational + exponents (key / denom) remain unchanged. + + >>> v1 = LaurentVariable('q', 1) + >>> p = DictLaurentPolynomial._make((v1,), {(1,): 1, (-1,): -1}) + >>> p + q - q^-1 + >>> v4 = LaurentVariable('q', 4) + >>> p4 = p.refactor_variables([v4]) + >>> p4.vars[0].denominator + 4 + >>> p4 + q - q^-1 + """ + new_vars = tuple( + v if isinstance(v, LaurentVariable) else LaurentVariable(v) + for v in new_vars + ) + scales = [] + for old_var, new_var in zip(self.vars, new_vars): + if old_var.name != new_var.name: + raise ValueError( + f'refactor_variables: variable name mismatch: ' + f'{old_var.name!r} vs {new_var.name!r}') + if new_var.denominator % old_var.denominator != 0: + raise ValueError( + f'refactor_variables: new denominator {new_var.denominator} ' + f'is not a multiple of old denominator {old_var.denominator} ' + f'for variable {old_var.name!r}') + scales.append(new_var.denominator // old_var.denominator) + new_poly_dict = { + tuple(k * s for k, s in zip(key, scales)): coef + for key, coef in self.poly_dict.items() + } + return DictLaurentPolynomial._make(new_vars, new_poly_dict) + def change_vars(self, rules): """ Return a new DictLaurentPolynomial with variables substituted by rules. - rules must have the same length as self.vars. Exponent keys are - rescaled when denominators change: exponent k (meaning var^(k/old_denom)) - rules: dict mapping LaurentVariable -> DictLaurentPolynomial, - or a list of DictLaurentPolynomials (one per variable, in order). - - Each variable is substituted by the corresponding polynomial. - Variables absent from a dict-style rules are kept as themselves - (identity substitution). - - Examples: - q = LaurentVariable('q', 2) - t = LaurentVariable('t', 1) - t_half = DictLaurentPolynomial.generator([t]) - p.change_vars({q: t_half}) # substitute q^(1/2) -> t + rules: dict mapping LaurentVariable -> DictLaurentPolynomial, or a list + of DictLaurentPolynomials (one per variable, in order). Variables absent + from a dict-style rules are kept as themselves (identity substitution). + + >>> q_var = LaurentVariable('q') + >>> t_var = LaurentVariable('t') + >>> p = DictLaurentPolynomial.from_str('q^2 + q', ['q']) + >>> t = DictLaurentPolynomial.generator([t_var]) + >>> p.change_vars({q_var: t}) + t^2 + t + >>> p.change_vars([DictLaurentPolynomial.from_str('t^-1', ['t'])]) + t^-1 + t^-2 """ if isinstance(rules, list): rules = dict(zip(self.vars, rules)) @@ -359,60 +516,6 @@ def change_vars(self, rules): return DictLaurentPolynomial._make(img.vars, {}) return result - @staticmethod - def _preprocess_division(s, sym_names): - """Expand /var and /(var1*var2*...) into *var^(-1)*... so the main parser handles it.""" - sorted_syms = sorted(sym_names, key=len, reverse=True) - sym_alt = '|'.join(re.escape(sym) for sym in sorted_syms) - exp_pat = r'(?:\^(?:\([+-]?\d+(?:/\d+)?\)|[+-]?\d+))?' - factor_pat = r'(?:' + sym_alt + r')' + exp_pat - product_pat = factor_pat + r'(?:\*' + factor_pat + r')*' - denom_re = re.compile(r'/\((' + product_pat + r')\)|/(' + factor_pat + r')') - factor_re = re.compile(r'(' + sym_alt + r')(' + exp_pat + r')') - - def negate_exp(exp): - if not exp: - return '^(-1)' - inner = exp[1:] # strip '^' - if inner.startswith('(') and inner.endswith(')'): - inner = inner[1:-1] - if inner.startswith('-'): - inner = inner[1:] - return f'^({inner})' if '/' in inner else f'^{inner}' - return f'^(-{inner})' - - def replace_denom(m): - content = m.group(1) if m.group(1) is not None else m.group(2) - result = [] - for part in content.split('*'): - fm = factor_re.fullmatch(part) - if fm is None: - raise ValueError(f'Cannot parse denominator factor: {part!r}') - result.append(fm.group(1) + negate_exp(fm.group(2))) - return '*' + '*'.join(result) - - # Strip outer parens from pure-product numerators: (a*b)/c → a*b/c. - # A "pure product" means no + or - at the top level inside the parens. - expanded = [] - i, n = 0, len(s) - while i < n: - if s[i] != '(': - expanded.append(s[i]); i += 1; continue - depth, j, pure = 1, i + 1, True - while j < n and depth > 0: - if s[j] == '(': depth += 1 - elif s[j] == ')': depth -= 1 - elif s[j] in '+-' and depth == 1: pure = False - j += 1 - if pure and j < n and s[j] == '/': - expanded.append(s[i+1:j-1]) # content without outer parens - else: - expanded.append(s[i:j]) - i = j - s = ''.join(expanded) - - return denom_re.sub(replace_denom, s) - @classmethod def from_str(cls, s, vars): """ @@ -422,130 +525,221 @@ def from_str(cls, s, vars): The denominator for each LaurentVariable is the LCM of all denominators appearing in its exponents in the string. - Supported formats: - 'q^(1/2) + q^(-3/2) - 2' - '2*q^(1/2) - q^(-1) + 3*q^2' - 'q^2*t^(-1/3) + 1' - '1 - 1/(q*t)' - '(q - 1) / q' + >>> DictLaurentPolynomial.from_str('q^2 - q^-1 + 3', ['q']) + q^2 + 3 - q^-1 + >>> DictLaurentPolynomial.from_str('q^(1/2) + q^(-1/2)', ['q']) + q^(1/2) + q^(-1/2) + >>> DictLaurentPolynomial.from_str('1 - 1/(q*t)', ['q', 't']) + 1 - q^-1*t^-1 + >>> DictLaurentPolynomial.from_str('(q^2 - 1) / q', ['q']) + q - q^-1 """ - def lcm(a, b): - return a * b // _gcd(a, b) - def lcm_list(lst): - r = 1 - for x in lst: - r = lcm(r, x) - return r - if len(vars) != len(set(vars)): raise ValueError(f'from_str: duplicate variable names in {vars!r}') s = s.replace(' ', '') - s = cls._preprocess_division(s, vars) - # Split at + or - that are not inside parentheses. - term_strs = [] - depth = 0 - start = 0 - for i, c in enumerate(s): - if c == '(': - depth += 1 - elif c == ')': - depth -= 1 - elif c in '+-' and depth == 0 and i > start and s[i-1] != '^': - term_strs.append(s[start:i]) - start = i - term_strs.append(s[start:]) - term_strs = [t for t in term_strs if t] - - if depth != 0: - raise ValueError(f'from_str: unmatched parentheses in {s!r}') - - # Pattern for each variable: sym optionally followed by ^(num/den) or ^num. - sym_pats = { - sym: re.compile( - re.escape(sym) + - r'(?:\^(?:\(([+-]?\d+)(?:/(\d+))?\)|([+-]?\d+)))?' - ) - for sym in vars - } + # --- tokeniser state (mutable via single-element list) --- + pos = [0] - parsed = [] # [(coef, {sym: (num, den)})] - all_denoms = {sym: {1} for sym in vars} - - for ts in term_strs: - # Extract leading coefficient (handles: 2*, -3*, -, +, 2, -2). - m = re.match(r'^([+-]?\d*)\*?', ts) - prefix = m.group(1) - rest = ts[m.end():] - coef = 1 if prefix in ('', '+') else (-1 if prefix == '-' else int(prefix)) - - # Strip outer parens from rest when the entire rest is wrapped and - # the content contains no unparenthesized + or - (i.e. a pure product). - # This handles terms like -(1*q^(-1)*t^(-1)) from _preprocess_division. - if rest.startswith('(') and rest.endswith(')'): - inner = rest[1:-1] - d, pure = 0, True - for ch in inner: - if ch == '(': d += 1 - elif ch == ')': d -= 1 - elif ch in '+-' and d == 0: pure = False; break - if d == 0 and pure: - rest = inner - - # Absorb a leading integer multiplier into coef (e.g. '2*q^(-1)' → coef*=2). - m2 = re.match(r'^(\d+)\*', rest) - if m2: - coef *= int(m2.group(1)) - rest = rest[m2.end():] - - var_exps = {} - for sym in vars: - if sum(1 for _ in sym_pats[sym].finditer(rest)) > 1: - raise ValueError( - f'from_str: variable {sym!r} appears more than once ' - f'in term {ts!r}') - pm = sym_pats[sym].search(rest) - if pm: - if pm.group(1) is not None: - num = int(pm.group(1)) - den = int(pm.group(2)) if pm.group(2) else 1 - elif pm.group(3) is not None: - num, den = int(pm.group(3)), 1 - else: - num, den = 1, 1 + def expect(ch): + if pos[0] >= len(s) or s[pos[0]] != ch: + got = repr(s[pos[0]]) if pos[0] < len(s) else 'end of string' + raise ValueError( + f'from_str: expected {ch!r}, got {got} ' + f'at position {pos[0]} in {s!r}') + pos[0] += 1 + + def parse_pos_int(): + start = pos[0] + while pos[0] < len(s) and s[pos[0]].isdigit(): + pos[0] += 1 + if pos[0] == start: + got = repr(s[pos[0]]) if pos[0] < len(s) else 'end of string' + raise ValueError( + f'from_str: expected integer at position {pos[0]} ' + f'in {s!r}, got {got}') + return int(s[start:pos[0]]) + + def parse_signed_int(): + sign = 1 + if pos[0] < len(s) and s[pos[0]] in '+-': + if s[pos[0]] == '-': + sign = -1 + pos[0] += 1 + return sign * parse_pos_int() + + def parse_exponent(): + # Called after '^' has been consumed. + if pos[0] < len(s) and s[pos[0]] == '(': + pos[0] += 1 + num = parse_signed_int() + den = 1 + if pos[0] < len(s) and s[pos[0]] == '/': + pos[0] += 1 + den = parse_pos_int() if den == 0: raise ValueError( - f'from_str: zero denominator in exponent in term {ts!r}') - var_exps[sym] = (num, den) - all_denoms[sym].add(den) - - # After stripping all known variable patterns, only '*' separators - # should remain — any leftover letters, digits, or '.' indicate a - # mis-parsed coefficient (e.g. '1.5*q') or unknown content. - remainder = rest - for sym2 in vars: - remainder = sym_pats[sym2].sub('', remainder) - if re.search(r'[a-zA-Z]', remainder): + f'from_str: zero denominator in exponent in {s!r}') + expect(')') + return num, den + return parse_signed_int(), 1 + + # --- polynomial representation --- + # Each poly is a list of (coef: int, exps: dict[var_name → (num, den)]). + # Exponents use exact rational arithmetic; variable names are strings. + + sorted_vars = sorted(vars, key=len, reverse=True) + + def _rat_add(n1, d1, n2, d2): + n = n1 * d2 + n2 * d1 + d = d1 * d2 + if n == 0: + return 0, 1 + g = _gcd(abs(n), d) + return n // g, d // g + + def _mono_key(exps): + return tuple(sorted(exps.items())) + + def _combine(terms): + acc = {} + for c, e in terms: + k = _mono_key(e) + if k in acc: + acc[k] = (acc[k][0] + c, acc[k][1]) + else: + acc[k] = (c, e) + return [(c, e) for c, e in acc.values() if c != 0] + + def poly_neg(p): + return [(-c, e) for c, e in p] + + def poly_add(p1, p2): + return _combine(p1 + p2) + + def poly_mul(p1, p2): + result = [] + for c1, e1 in p1: + for c2, e2 in p2: + c = c1 * c2 + e = dict(e1) + for v, (n2, d2) in e2.items(): + if v in e: + n1, d1 = e[v] + rn, rd = _rat_add(n1, d1, n2, d2) + if rn == 0: + del e[v] + else: + e[v] = (rn, rd) + else: + e[v] = (n2, d2) + result.append((c, e)) + return _combine(result) + + def poly_inv(p): + if len(p) != 1: + raise ValueError( + f'from_str: can only divide by a monomial in {s!r}') + c, e = p[0] + if c == 0: + raise ValueError(f'from_str: division by zero in {s!r}') + if abs(c) != 1: raise ValueError( - f'from_str: unrecognized content in term {ts!r} ' - f'(vars={vars!r}); leftover: {remainder!r}') - if re.search(r'[\d.]', remainder): + f'from_str: division by non-unit coefficient {c} ' + f'in {s!r}; write the coefficient in the numerator') + return [(c, {v: (-n, d) for v, (n, d) in e.items()})] + + # --- grammar --- + + def parse_expr(): + result = parse_term() + while pos[0] < len(s) and s[pos[0]] in '+-': + op = s[pos[0]]; pos[0] += 1 + right = parse_term() + result = poly_add(result, poly_neg(right) if op == '-' else right) + return result + + def parse_term(): + result = parse_factor() + while pos[0] < len(s) and s[pos[0]] in '*/': + op = s[pos[0]]; pos[0] += 1 + right = parse_factor() + result = poly_mul(result, poly_inv(right) if op == '/' else right) + return result + + def parse_factor(): + sign = 1 + while pos[0] < len(s) and s[pos[0]] in '+-': + if s[pos[0]] == '-': + sign = -sign + pos[0] += 1 + result = parse_atom() + return poly_neg(result) if sign == -1 else result + + def parse_atom(): + if pos[0] >= len(s): raise ValueError( - f'from_str: unexpected numeric content in term {ts!r}; ' - f'leftover after variables: {remainder!r}') + f'from_str: unexpected end of expression in {s!r}') + c = s[pos[0]] + + if c == '(': + pos[0] += 1 + result = parse_expr() + expect(')') + return result + + # Match a variable name (longest first to handle ambiguous prefixes). + for var in sorted_vars: + end = pos[0] + len(var) + if s[pos[0]:end] == var: + # Require a non-identifier character to follow (avoid prefix match). + if end < len(s) and (s[end].isalnum() or s[end] == '_'): + continue + pos[0] = end + num, den = 1, 1 + if pos[0] < len(s) and s[pos[0]] == '^': + pos[0] += 1 + num, den = parse_exponent() + return [(1, {var: (num, den)} if num != 0 else {})] + + # Must be an integer coefficient. + if c.isdigit(): + n = parse_pos_int() + if pos[0] < len(s) and s[pos[0]] == '.': + raise ValueError( + f'from_str: decimal numbers not supported ' + f'at position {pos[0]} in {s!r}') + return [(n, {})] + + raise ValueError( + f'from_str: unexpected character {c!r} ' + f'at position {pos[0]} in {s!r}; known variables: {vars!r}') - parsed.append((coef, var_exps)) + # --- parse and convert --- - var_lcms = {sym: lcm_list(all_denoms[sym]) for sym in vars} - vars_list = [LaurentVariable(sym, var_lcms[sym]) for sym in vars] + poly = parse_expr() + + if pos[0] != len(s): + raise ValueError( + f'from_str: unexpected content {s[pos[0]:]!r} ' + f'at position {pos[0]} in {s!r}') + + def lcm(a, b): + return a * b // _gcd(a, b) + + var_lcms = {v: 1 for v in vars} + for _, exps in poly: + for v, (n, d) in exps.items(): + var_lcms[v] = lcm(var_lcms[v], d) + + vars_list = [LaurentVariable(v, var_lcms[v]) for v in vars] poly_dict = {} - for coef, var_exps in parsed: + for coef, exps in poly: key = tuple( - var_exps[sym][0] * (var_lcms[sym] // var_exps[sym][1]) - if sym in var_exps else 0 - for sym in vars + exps[v][0] * (var_lcms[v] // exps[v][1]) if v in exps else 0 + for v in vars ) if key in poly_dict: new_v = poly_dict[key] + coef @@ -559,6 +753,15 @@ def lcm_list(lst): return cls._make(vars_list, poly_dict) def __pow__(self, n): + """ + >>> p = DictLaurentPolynomial.from_str('q', ['q']) + >>> p ** 3 + q^3 + >>> p ** -2 + q^-2 + >>> p ** 0 + 1 + """ if not isinstance(n, int): raise ValueError(f'exponent must be an integer, got {n!r}') if n == 0: @@ -600,7 +803,7 @@ def _sort_key(item): fmt = var.fmt_exp(k) if fmt is not None: parts.append(var.name + fmt) - monomial = ''.join(parts) + monomial = '*'.join(parts) if not monomial: terms.append(str(coef)) elif coef == 1: From a575264e6a2355ca7069e40b2c6463124b706910 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Wed, 24 Jun 2026 07:55:06 -0500 Subject: [PATCH 21/53] Various updates --- spherogram_src/links/invariants.py | 34 +++- spherogram_src/links/links_base.py | 2 +- .../links/reshetikhin_turaev/RT_network.py | 23 ++- .../links/reshetikhin_turaev/R_matrices.py | 15 +- .../dict_laurent_polynomial.py | 165 +++++++++++++++++- 5 files changed, 212 insertions(+), 27 deletions(-) diff --git a/spherogram_src/links/invariants.py b/spherogram_src/links/invariants.py index e9ce5a0..6f0e263 100644 --- a/spherogram_src/links/invariants.py +++ b/spherogram_src/links/invariants.py @@ -331,22 +331,46 @@ def alexander_polynomial(self, multivar=True, v='no', method='default', return p.factor() return p - def colored_links_gould_polynomial(self, n, sage_polynomials = False, timed = False): - from .reshetikhin_turaev import colored_links_gould_R_matrices + def colored_links_gould_polynomial(self, n, sage_output = _within_sage, sage_polynomials = False, timed = False): + """ + Colored Links--Gould polynomials are bivariate, hence we default to + using DictLaurentPolynomial to reduce RAM consumption. + + The output, by default, follows whether in sage or not. + """ + from .reshetikhin_turaev import colored_links_gould_R_matrices, DictLaurentPolynomial ans = self.min_long_diagram().apply_reshetikhin_turaev_functor(colored_links_gould_R_matrices(n, sage_polynomials=sage_polynomials)).evaluate(timed = timed) + if sage_output: + if not sage_polynomials: + ans = (ans[0].to_sage(), ans[1]) + else: + if sage_polynomials: + ans = (DictLaurentPolynomial.from_sage(ans[0]), ans[1]) + if timed: return ans else: return ans[0] - def colored_jones_polynomial(self, n, sage_polynomials = False, timed = False): - from .reshetikhin_turaev import colored_jones_R_matrices, prefactor_colored_jones + def colored_jones_polynomial(self, n, sage_output = _within_sage, sage_polynomials = _within_sage, timed = False): + """ + Colored Jones polynomials are univariate, for whom sage's PuiseuxSeries + has highly optimized multiplications, hence we default to use sage whenever possible. + """ + from .reshetikhin_turaev import colored_jones_R_matrices, prefactor_colored_jones, DictLaurentPolynomial ans = self.min_long_diagram().apply_reshetikhin_turaev_functor(colored_jones_R_matrices(n, sage_polynomials=sage_polynomials)).evaluate(timed = timed) ans = (ans[0] * prefactor_colored_jones(n, self.writhe(), sage_polynomial=sage_polynomials), ans[1]) - + + if sage_output: + if not sage_polynomials: + ans = (ans[0].to_sage(), ans[1]) + else: + if sage_polynomials: + ans = (DictLaurentPolynomial.from_sage(ans[0]), ans[1]) + if timed: return ans else: diff --git a/spherogram_src/links/links_base.py b/spherogram_src/links/links_base.py index 4b5b37a..1f32d11 100644 --- a/spherogram_src/links/links_base.py +++ b/spherogram_src/links/links_base.py @@ -1398,7 +1398,7 @@ def min_long_diagram(self): entry_indices = [3, 0] if self.crossings[i].sign == 1 else [0, 1] for j in entry_indices: diagram = self.long_diagram(cut_at=(i,j)) - width = diagram.contraction_width() + width = diagram.contraction_width()[0] if min_width is None or width < min_width: ans = diagram min_width = width diff --git a/spherogram_src/links/reshetikhin_turaev/RT_network.py b/spherogram_src/links/reshetikhin_turaev/RT_network.py index 2b4d03b..012e134 100644 --- a/spherogram_src/links/reshetikhin_turaev/RT_network.py +++ b/spherogram_src/links/reshetikhin_turaev/RT_network.py @@ -34,6 +34,8 @@ def __init__(self, tensors, T = None, network = None, rot_num = None, boundary = given RMatrix tensors to the tangle T. The network is represented as a list of pairs (tensor, legs) + + Requires numpy and opt_einsum modules for finding out the optimal contraction sequences """ self.tensors = tensors @@ -156,15 +158,19 @@ def local_contraction_width(abstract_network, indices): def seq_contraction_width(self, seq): abstract_network = [key for _, key in self.network] - width = 0 - + w = 0 + m = 0 + for indices in seq: local_width, abstract_network = RTNetwork.local_contraction_width(abstract_network, indices) - if local_width > width: - width = local_width + if local_width > w: + w = local_width + m = 1 + elif local_width == w: + m += 1 - return width, abstract_network + return (w, m), abstract_network def contraction_width(self, omit_idle_arcs = True): abstract_network = [] @@ -181,9 +187,8 @@ def contraction_width(self, omit_idle_arcs = True): boundary = (0,0) if omit_idle_arcs else self.boundary, boundary_labels = [] if omit_idle_arcs else self.boundary_labels) - width = 0 - if abstract_copy._resolve_self_loops(): - width = 3 + m = abstract_copy._resolve_self_loops() + width = (3 if m else 0, m) seq_width, ans = abstract_copy.seq_contraction_width(abstract_copy.optimal_contraction_sequence()) @@ -242,7 +247,7 @@ def _resolve_self_loop_at(self, idx): return True def _resolve_self_loops(self): - return any(self._resolve_self_loop_at(i) for i in range(len(self.network))) + return sum(int(self._resolve_self_loop_at(i)) for i in range(len(self.network))) def contract_sequence(self, seq, timed = False): if timed: diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices.py b/spherogram_src/links/reshetikhin_turaev/R_matrices.py index 31728df..b53004b 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices.py +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices.py @@ -92,6 +92,8 @@ def colored_links_gould_R_matrices(n, sage_polynomials = False): return _cache[key] else: _cache[key] = RMatrix.laurent_R_from_directory(dir_path = os.path.join(dir_path, f'R_matrices/V{n}/'), + vars = ['t', 'q'], + compressed = False, sage_polynomials = sage_polynomials) return _cache[key] else: @@ -115,15 +117,20 @@ def _q_pochhammer(a, q, n): result = result * (1 - a * q**k) return result -def _q_pow(e4): - """DictLaurentPolynomial representing q^(e4/4). e4 must be an integer.""" - return DictLaurentPolynomial._make((LaurentVariable('q', 4),), {(e4,): 1}) +def _q_pow(e): + """ + DictLaurentPolynomial representing q^(e/2). e must be an integer. + """ + return DictLaurentPolynomial._make((LaurentVariable('q', 4),), {(e,): 1}) def colored_jones_R_matrices(n, sage_polynomials=False): """ The R matrices for the n-colored Jones polynomial. - In particular, n = 1 gives the Jones polynomial + In particular, n = 1 gives the Jones polynomial. """ + if n < 0: + raise NotImplementedError + n = n + 1 q_actual = _q_pow(4) # q^1 diff --git a/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py index d44e08c..9d8b028 100644 --- a/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py +++ b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py @@ -105,13 +105,58 @@ def __init__(self, vars, poly_dict): @sage_method def to_sage(self): - if all(var.denominator == 1 for var in self.vars): - return laurent_poly_from_dict(self.poly_dict, [var.name for var in self.vars]) - elif len(self.vars) == 1: + if len(self.vars) == 1: return puiseux_series_from_dict(self.poly_dict, self.vars[0]) + elif all(var.denominator == 1 for var in self.vars): + return laurent_poly_from_dict(self.poly_dict, [var.name for var in self.vars]) else: raise NotImplementedError('Multi-variable Puiseux conversion to Sage is not supported.') + @classmethod + @sage_method + def from_sage(cls, p, var_names=None): + """ + Convert a Sage PuiseuxSeries or LaurentPolynomial to a DictLaurentPolynomial. + + var_names: optional list of variable name strings; defaults to the + variable names from p's parent ring. + + For PuiseuxSeries the conversion relies on the internal _l (Laurent + series) and _e (ramification index) attributes of Sage's implementation. + + >>> p = DictLaurentPolynomial.from_str('q^2 + 3 - q^-1', ['q']) + >>> DictLaurentPolynomial.from_sage(p.to_sage()) == p + True + >>> r = DictLaurentPolynomial.from_str('q^2*t - 1', ['q', 't']) + >>> DictLaurentPolynomial.from_sage(r.to_sage()) == r + True + >>> s = DictLaurentPolynomial.from_str('q^(1/2) + q^(-1/4)', ['q']) + >>> DictLaurentPolynomial.from_sage(s.to_sage()) == s + True + """ + from sage.rings.puiseux_series_ring_element import PuiseuxSeries + + if isinstance(p, PuiseuxSeries): + e = int(p.ramification_index()) + name = var_names[0] if var_names else str(p.variable()) + var = LaurentVariable(name, e) + l = p.laurent_part() + poly_dict = {(int(k),): int(v) for k, v in zip(l.exponents(), l.coefficients()) if v != 0} + return cls._make((var,), poly_dict) + + # LaurentPolynomial (univariate or multivariate, all denominators 1). + parent = p.parent() + names = var_names or [str(v) for v in parent.gens()] + vars_tuple = tuple(LaurentVariable(name) for name in names) + nvars = len(names) + poly_dict = {} + for exp, v in p.dict().items(): + if v == 0: + continue + key = (int(exp),) if nvars == 1 else tuple(int(k) for k in exp) + poly_dict[key] = int(v) + return cls._make(vars_tuple, poly_dict) + @classmethod def _make(cls, vars, poly_dict, _interned=False): """Construct without cleaning — caller guarantees no zero values. @@ -465,14 +510,24 @@ def refactor_variables(self, new_vars): } return DictLaurentPolynomial._make(new_vars, new_poly_dict) - def change_vars(self, rules): + def change_vars(self, rules, new_var_names = None): """ Return a new DictLaurentPolynomial with variables substituted by rules. - rules: dict mapping LaurentVariable -> DictLaurentPolynomial, or a list - of DictLaurentPolynomials (one per variable, in order). Variables absent + rules: dict mapping LaurentVariable (or variable name string) -> + DictLaurentPolynomial (or expression string), or a list of + DictLaurentPolynomials (one per variable, in order). Variables absent from a dict-style rules are kept as themselves (identity substitution). + String values are parsed using the source variable names as the ring, + or new_var_names if provided. A string value describes the image of + the full variable (e.g. q^1); for source variables with denominator + d > 1 the image must be a monomial. + + new_var_names: optional list of variable name strings for the target + ring. Source variables absent from rules are auto-mapped to themselves + (by name); if their name is not already in new_var_names it is appended. + >>> q_var = LaurentVariable('q') >>> t_var = LaurentVariable('t') >>> p = DictLaurentPolynomial.from_str('q^2 + q', ['q']) @@ -481,10 +536,74 @@ def change_vars(self, rules): t^2 + t >>> p.change_vars([DictLaurentPolynomial.from_str('t^-1', ['t'])]) t^-1 + t^-2 + >>> p.change_vars({'q': 'q^-1'}) + q^-1 + q^-2 + >>> r = DictLaurentPolynomial.from_str('q + t', ['q', 't']) + >>> r.change_vars({'q': 'q*t^2', 't': 't^-1'}) + q*t^2 + t^-1 + >>> s = DictLaurentPolynomial.from_str('q^(1/2)*t^(1/3)', ['q', 't']) + >>> s.change_vars({'q': 'q*t^2', 't': 't^-1'}) + q^(1/2)*t^(2/3) + >>> p.change_vars({'q': 'a^2'}, new_var_names=['a']) + a^4 + a^2 + >>> r.change_vars({'q': 'a^2'}, new_var_names=['a']) + a^2 + t + >>> r.change_vars({'q': 'a^2', 't': 'b^-1'}, new_var_names=['a', 'b']) + a^2 + b^-1 + >>> half_q = DictLaurentPolynomial.from_str('q^(1/2)', ['q']) + >>> half_q.change_vars({'q': 'a^4'}, new_var_names=['a']) + a^2 """ if isinstance(rules, list): rules = dict(zip(self.vars, rules)) + # Normalize string keys to LaurentVariable via name lookup. + if any(isinstance(k, str) for k in rules): + name_to_var = {v.name: v for v in self.vars} + rules = { + (name_to_var[k] if isinstance(k, str) else k): v + for k, v in rules.items() + } + + # When new_var_names is provided, auto-fill unspecified source vars + # with string identity rules, extending new_var_names as needed. + if new_var_names is not None: + rules = dict(rules) + new_var_names = list(new_var_names) + for var in self.vars: + if var not in rules: + if var.name not in new_var_names: + new_var_names.append(var.name) + rules[var] = var.name + + # Parse string values. + var_names = new_var_names if new_var_names is not None else [v.name for v in self.vars] + has_str_values = any(isinstance(v, str) for v in rules.values()) + if has_str_values: + parsed_rules = {} + for src_var, img in rules.items(): + if isinstance(img, str): + parsed = DictLaurentPolynomial.from_str(img, var_names) + d = src_var.denominator + if d > 1: + # String describes image of the full variable (src_var^1). + # We need the image of the unit generator (src_var^(1/d)). + # Only valid when image is a monomial (can be raised to 1/d power). + if len(parsed.poly_dict) > 1: + raise ValueError( + f'change_vars: image of {src_var!r} has denominator ' + f'{d} > 1 but the image is not a monomial') + # Scale image variable denominators by d; keys unchanged. + new_img_vars = tuple( + LaurentVariable(v.name, v.denominator * d) + for v in parsed.vars + ) + parsed = DictLaurentPolynomial._make(new_img_vars, parsed.poly_dict) + parsed_rules[src_var] = parsed + else: + parsed_rules[src_var] = img + rules = parsed_rules + # Build identity images for variables not in rules. full_rules = {} for i, var in enumerate(self.vars): @@ -493,6 +612,23 @@ def change_vars(self, rules): else: full_rules[var] = DictLaurentPolynomial.generator(self.vars, index=i) + # Unify all image DLPs to a common vars tuple when string values were used. + if has_str_values and full_rules: + all_imgs = list(full_rules.values()) + ref_vars = all_imgs[0].vars + def _lcm2(a, b): return a * b // _gcd(a, b) + common_denoms = [v.denominator for v in ref_vars] + for img in all_imgs[1:]: + for i, v in enumerate(img.vars): + common_denoms[i] = _lcm2(common_denoms[i], v.denominator) + common_vars = tuple( + LaurentVariable(v.name, d) for v, d in zip(ref_vars, common_denoms) + ) + full_rules = { + src_var: img.refactor_variables(common_vars) + for src_var, img in full_rules.items() + } + result = None for key, coef in self.poly_dict.items(): term = None @@ -533,6 +669,14 @@ def from_str(cls, s, vars): 1 - q^-1*t^-1 >>> DictLaurentPolynomial.from_str('(q^2 - 1) / q', ['q']) q - q^-1 + >>> DictLaurentPolynomial.from_str('t1^2 + t2^-1', ['t1', 't2']) + t1^2 + t2^-1 + >>> DictLaurentPolynomial.from_str('t^2 + t1', ['t', 't1']) + t^2 + t1 + >>> DictLaurentPolynomial.from_str('t1^2*t10 + t1 - t10^-1', ['t1', 't10']) + t1^2*t10 + t1 - t10^-1 + >>> DictLaurentPolynomial.from_str('(t1^2 - 1) / t1', ['t1', 't2']) + t1 - t1^-1 """ if len(vars) != len(set(vars)): raise ValueError(f'from_str: duplicate variable names in {vars!r}') @@ -762,7 +906,9 @@ def __pow__(self, n): >>> p ** 0 1 """ - if not isinstance(n, int): + try: + n = n.__index__() + except (AttributeError, TypeError): raise ValueError(f'exponent must be an integer, got {n!r}') if n == 0: zero_key = (0,) * len(self.vars) @@ -771,8 +917,11 @@ def __pow__(self, n): if len(self.poly_dict) != 1: raise ValueError('negative powers only supported for monomials') (key, coef), = self.poly_dict.items() + if coef != 1 and coef != -1: + raise ValueError( + f'negative powers require a ±1 leading coefficient, got {coef!r}') inv_key = tuple(k * n for k in key) - inv_coef = coef ** n # works when coef is ±1 or a symbolic type + inv_coef = coef ** (-n) # -n > 0, so int**int stays int; (±1)^k = (±1)^{-k} return DictLaurentPolynomial._make(self.vars, {inv_key: inv_coef}, _interned=True) result = self base = self From 21b78b44a166bb95bb0e075c8cdceb074290ed63 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Wed, 24 Jun 2026 21:21:47 -0500 Subject: [PATCH 22/53] Change sort for representation of DictLaurentPolynomial to match sage's behaviour. Update doctests --- .../dict_laurent_polynomial.py | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py index 9d8b028..5c8f687 100644 --- a/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py +++ b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py @@ -89,7 +89,7 @@ class DictLaurentPolynomial: >>> p = DictLaurentPolynomial.from_str('q^2 - q^-1 + 3', ['q']) >>> p - q^2 + 3 - q^-1 + -q^-1 + 3 + q^2 >>> r = DictLaurentPolynomial.from_str('q^2*t - 1', ['q', 't']) >>> r q^2*t - 1 @@ -234,7 +234,7 @@ def __neg__(self): """ >>> p = DictLaurentPolynomial.from_str('q + 2', ['q']) >>> -p - -q - 2 + -2 - q """ return DictLaurentPolynomial._make( self.vars, {k: -v for k, v in self.poly_dict.items()}, _interned=True) @@ -244,9 +244,9 @@ def __add__(self, other): >>> p = DictLaurentPolynomial.from_str('q + 1', ['q']) >>> r = DictLaurentPolynomial.from_str('q^-1 - 1', ['q']) >>> p + r - q + q^-1 + q^-1 + q >>> p + 3 - q + 4 + 4 + q """ if isinstance(other, DictLaurentPolynomial): sp, op = self.poly_dict, other.poly_dict @@ -288,7 +288,7 @@ def __sub__(self, other): >>> p - r q^2 >>> p - 1 - q^2 + q - 1 + -1 + q + q^2 """ if isinstance(other, DictLaurentPolynomial): result = dict(self.poly_dict) @@ -312,9 +312,9 @@ def __mul__(self, other): >>> p = DictLaurentPolynomial.from_str('q + 1', ['q']) >>> r = DictLaurentPolynomial.from_str('q - 1', ['q']) >>> p * r - q^2 - 1 + -1 + q^2 >>> p * 3 - 3*q + 3 + 3 + 3*q """ if isinstance(other, DictLaurentPolynomial): result = {} @@ -407,10 +407,10 @@ def __truediv__(self, other): >>> p = DictLaurentPolynomial.from_str('q^2 - 1', ['q']) >>> q = DictLaurentPolynomial.from_str('q', ['q']) >>> p / q - q - q^-1 + -q^-1 + q >>> r = DictLaurentPolynomial.from_str('2*q^2 + 4*q', ['q']) >>> r / 2 - q^2 + 2*q + 2*q + q^2 >>> r / 3 Traceback (most recent call last): ... @@ -480,13 +480,13 @@ def refactor_variables(self, new_vars): >>> v1 = LaurentVariable('q', 1) >>> p = DictLaurentPolynomial._make((v1,), {(1,): 1, (-1,): -1}) >>> p - q - q^-1 + -q^-1 + q >>> v4 = LaurentVariable('q', 4) >>> p4 = p.refactor_variables([v4]) >>> p4.vars[0].denominator 4 >>> p4 - q - q^-1 + -q^-1 + q """ new_vars = tuple( v if isinstance(v, LaurentVariable) else LaurentVariable(v) @@ -533,11 +533,11 @@ def change_vars(self, rules, new_var_names = None): >>> p = DictLaurentPolynomial.from_str('q^2 + q', ['q']) >>> t = DictLaurentPolynomial.generator([t_var]) >>> p.change_vars({q_var: t}) - t^2 + t + t + t^2 >>> p.change_vars([DictLaurentPolynomial.from_str('t^-1', ['t'])]) - t^-1 + t^-2 + t^-2 + t^-1 >>> p.change_vars({'q': 'q^-1'}) - q^-1 + q^-2 + q^-2 + q^-1 >>> r = DictLaurentPolynomial.from_str('q + t', ['q', 't']) >>> r.change_vars({'q': 'q*t^2', 't': 't^-1'}) q*t^2 + t^-1 @@ -545,7 +545,7 @@ def change_vars(self, rules, new_var_names = None): >>> s.change_vars({'q': 'q*t^2', 't': 't^-1'}) q^(1/2)*t^(2/3) >>> p.change_vars({'q': 'a^2'}, new_var_names=['a']) - a^4 + a^2 + a^2 + a^4 >>> r.change_vars({'q': 'a^2'}, new_var_names=['a']) a^2 + t >>> r.change_vars({'q': 'a^2', 't': 'b^-1'}, new_var_names=['a', 'b']) @@ -662,13 +662,13 @@ def from_str(cls, s, vars): denominators appearing in its exponents in the string. >>> DictLaurentPolynomial.from_str('q^2 - q^-1 + 3', ['q']) - q^2 + 3 - q^-1 + -q^-1 + 3 + q^2 >>> DictLaurentPolynomial.from_str('q^(1/2) + q^(-1/2)', ['q']) - q^(1/2) + q^(-1/2) + q^(-1/2) + q^(1/2) >>> DictLaurentPolynomial.from_str('1 - 1/(q*t)', ['q', 't']) 1 - q^-1*t^-1 >>> DictLaurentPolynomial.from_str('(q^2 - 1) / q', ['q']) - q - q^-1 + -q^-1 + q >>> DictLaurentPolynomial.from_str('t1^2 + t2^-1', ['t1', 't2']) t1^2 + t2^-1 >>> DictLaurentPolynomial.from_str('t^2 + t1', ['t', 't1']) @@ -944,7 +944,9 @@ def __repr__(self): def _sort_key(item): exp = item[0] total = sum(k * s for k, s in zip(exp, scales)) - return (-total, tuple(-k for k in exp)) + if len(self.vars) == 1: + return total # ascending for univariate (matches PuiseuxSeries) + return (-total, tuple(-k for k in exp)) # descending for multivariate terms = [] for exp, coef in sorted(self.poly_dict.items(), key=_sort_key): parts = [] From c9ca05ea78092d0f9ed84f299fb5b512805569f4 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Wed, 24 Jun 2026 21:41:35 -0500 Subject: [PATCH 23/53] Add doctest for reverse_orientation and remove unnecessary &= --- spherogram_src/links/links_base.py | 14 ++++++++++++++ spherogram_src/links/simplify.py | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/spherogram_src/links/links_base.py b/spherogram_src/links/links_base.py index c14acb7..b9fc437 100644 --- a/spherogram_src/links/links_base.py +++ b/spherogram_src/links/links_base.py @@ -895,6 +895,20 @@ def reverse_orientation(self, component_index): component_index: either a single index of component or a list of indices of components + >>> L = snappy.Link([(4, 0, 5, 3), (0, 6, 1, 5), (6, 2, 7, 1), (2, 4, 3, 7)]) + >>> L + + >>> L.linking_number() + 2.0 + >>> L.reverse_orientation(0) + >>> L.linking_number() + -2.0 + >>> L.reverse_orientation(1) + >>> L.linking_number() + 2.0 + >>> L.reverse_orientation([0,1]) + >>> L.PD_code() + [(4, 0, 5, 3), (0, 6, 1, 5), (6, 2, 7, 1), (2, 4, 3, 7)] """ if not isinstance(component_index, (set, list, tuple)): component_index = [component_index] diff --git a/spherogram_src/links/simplify.py b/spherogram_src/links/simplify.py index bb2c122..2d543be 100644 --- a/spherogram_src/links/simplify.py +++ b/spherogram_src/links/simplify.py @@ -143,7 +143,7 @@ def reidemeister_I_and_II(link, A): break - changed &= {x for x in changed if isinstance(x, Crossing)} + changed = {x for x in changed if isinstance(x, Crossing)} return eliminated, changed From d00851ce46440bdc2858ac5bb57de8d08abda07e Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Wed, 24 Jun 2026 21:44:25 -0500 Subject: [PATCH 24/53] Fix typo in doctest --- spherogram_src/links/links_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spherogram_src/links/links_base.py b/spherogram_src/links/links_base.py index b9fc437..ffbe296 100644 --- a/spherogram_src/links/links_base.py +++ b/spherogram_src/links/links_base.py @@ -895,7 +895,7 @@ def reverse_orientation(self, component_index): component_index: either a single index of component or a list of indices of components - >>> L = snappy.Link([(4, 0, 5, 3), (0, 6, 1, 5), (6, 2, 7, 1), (2, 4, 3, 7)]) + >>> L = Link([(4, 0, 5, 3), (0, 6, 1, 5), (6, 2, 7, 1), (2, 4, 3, 7)]) >>> L >>> L.linking_number() From 194e030a663f456f6c33d21801b0316402bf1a01 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Thu, 25 Jun 2026 13:30:33 -0500 Subject: [PATCH 25/53] Let linking_number() return int instead of float --- spherogram_src/links/links_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spherogram_src/links/links_base.py b/spherogram_src/links/links_base.py index ffbe296..e441998 100644 --- a/spherogram_src/links/links_base.py +++ b/spherogram_src/links/links_base.py @@ -1537,7 +1537,7 @@ def linking_number(self): for i, m in enumerate(tally): if m == 1: n += (self.crossings)[i].sign - n = n / 4 + n = n // 4 return n def _pieces(self): From 698f114144debd912c2b206ea47e0b5bc7a4f5ea Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Thu, 25 Jun 2026 15:15:58 -0500 Subject: [PATCH 26/53] Fix doctests --- spherogram_src/links/links_base.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spherogram_src/links/links_base.py b/spherogram_src/links/links_base.py index e441998..51e559a 100644 --- a/spherogram_src/links/links_base.py +++ b/spherogram_src/links/links_base.py @@ -899,13 +899,13 @@ def reverse_orientation(self, component_index): >>> L >>> L.linking_number() - 2.0 + 2 >>> L.reverse_orientation(0) >>> L.linking_number() - -2.0 + -2 >>> L.reverse_orientation(1) >>> L.linking_number() - 2.0 + 2 >>> L.reverse_orientation([0,1]) >>> L.PD_code() [(4, 0, 5, 3), (0, 6, 1, 5), (6, 2, 7, 1), (2, 4, 3, 7)] From b0c020bb64bf1552c8beb727fe77eb0c0f171a5c Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Thu, 25 Jun 2026 18:34:58 -0500 Subject: [PATCH 27/53] Add variable checks for DictLaurentPolynomial --- spherogram_src/__init__.py | 4 +- spherogram_src/links/invariants.py | 4 + .../links/reshetikhin_turaev/RT_network.py | 16 +- .../links/reshetikhin_turaev/R_matrices.py | 6 +- .../dict_laurent_polynomial.py | 285 +++++++++++------- 5 files changed, 187 insertions(+), 128 deletions(-) diff --git a/spherogram_src/__init__.py b/spherogram_src/__init__.py index fc98bef..b75a395 100644 --- a/spherogram_src/__init__.py +++ b/spherogram_src/__init__.py @@ -1,6 +1,7 @@ from .presentations import * from .links import * from .codecs import * +from .links.reshetikhin_turaev import DictLaurentPolynomial # Make the module version number easily accessible. from . import version as _version @@ -23,4 +24,5 @@ def version(): # from spherogram.links.tangles: 'Tangle', 'CapTangle', 'CupTangle', 'RationalTangle', 'ZeroTangle', 'InfinityTangle', 'MinusOneTangle', 'OneTangle', 'IntegerTangle', - 'IdentityBraid', 'BraidTangle', 'ComponentTangle', 'join_strands'] + 'IdentityBraid', 'BraidTangle', 'ComponentTangle', 'join_strands', + 'DictLaurentPolynomial'] diff --git a/spherogram_src/links/invariants.py b/spherogram_src/links/invariants.py index 6f0e263..0794c91 100644 --- a/spherogram_src/links/invariants.py +++ b/spherogram_src/links/invariants.py @@ -348,6 +348,8 @@ def colored_links_gould_polynomial(self, n, sage_output = _within_sage, sage_pol else: if sage_polynomials: ans = (DictLaurentPolynomial.from_sage(ans[0]), ans[1]) + else: + ans = (ans[0].to_checked(), ans[1]) if timed: return ans @@ -370,6 +372,8 @@ def colored_jones_polynomial(self, n, sage_output = _within_sage, sage_polynomia else: if sage_polynomials: ans = (DictLaurentPolynomial.from_sage(ans[0]), ans[1]) + else: + ans = (ans[0].to_checked(), ans[1]) if timed: return ans diff --git a/spherogram_src/links/reshetikhin_turaev/RT_network.py b/spherogram_src/links/reshetikhin_turaev/RT_network.py index 012e134..4041976 100644 --- a/spherogram_src/links/reshetikhin_turaev/RT_network.py +++ b/spherogram_src/links/reshetikhin_turaev/RT_network.py @@ -99,11 +99,15 @@ def __init__(self, tensors, T = None, network = None, rot_num = None, boundary = def optimal_contraction_sequence(self): try: - import numpy as np import opt_einsum as oe except ImportError: - raise ModuleNotFoundError('Modules numpy and opt_einsum is required for computing the optimal contraction sequences') - + raise ModuleNotFoundError('Module opt_einsum is required for computing the optimal contraction sequences') + + class _ShapeOnly: + __slots__ = ['shape'] + def __init__(self, shape): + self.shape = shape + oe_network = [] idle = set(self.idle_labels) for tensor, key in self.network: @@ -113,10 +117,8 @@ def optimal_contraction_sequence(self): except: raise ValueError(f'key {key[0].index} not found in {idle}') else: - if tensor is not None: - oe_network.append(np.empty(tensor.shape)) - else: - oe_network.append(np.empty([4 for _ in key])) + shape = tensor.shape if tensor is not None else tuple(4 for _ in key) + oe_network.append(_ShapeOnly(shape)) oe_network.append([edge.index for edge in key]) return oe.contract_path(*oe_network, idle)[0] diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices.py b/spherogram_src/links/reshetikhin_turaev/R_matrices.py index b53004b..ddf6fea 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices.py +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices.py @@ -1,4 +1,4 @@ -from .dict_laurent_polynomial import DictLaurentPolynomial, LaurentVariable +from .dict_laurent_polynomial import FastDictLaurentPolynomial, LaurentVariable from .sparse_array import SparseTensor @@ -20,7 +20,7 @@ def laurent_sparse_tensor_from_file(file, vars = ['t', 'q'], sage_polynomials = key, value = line key = tuple(ast.literal_eval(key)) assert key not in data.keys(), f'{key} appeared multiple times in {file.name}' - data[key] = DictLaurentPolynomial.from_str(value, vars = vars) + data[key] = FastDictLaurentPolynomial.from_str(value, vars = vars) # Unify variable denominators: compute LCM across all loaded polynomials so # every value shares the same vars tuple (enabling interning and consistent arithmetic). @@ -121,7 +121,7 @@ def _q_pow(e): """ DictLaurentPolynomial representing q^(e/2). e must be an integer. """ - return DictLaurentPolynomial._make((LaurentVariable('q', 4),), {(e,): 1}) + return FastDictLaurentPolynomial._make((LaurentVariable('q', 4),), {(e,): 1}) def colored_jones_R_matrices(n, sage_polynomials=False): """ diff --git a/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py index 5c8f687..1c601b7 100644 --- a/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py +++ b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py @@ -73,7 +73,7 @@ def _gcd(a, b): def _intern_vars(vars_tuple): return _vars_cache.setdefault(vars_tuple, vars_tuple) -class DictLaurentPolynomial: +class FastDictLaurentPolynomial: """ A sparse Laurent polynomial in arbitrarily many variables. @@ -103,6 +103,21 @@ def __init__(self, vars, poly_dict): )) self.poly_dict = {k: v for k, v in poly_dict.items() if v != 0} + def to_checked(self): + """ + Return a DictLaurentPolynomial with the same variables and coefficients. + + >>> p = FastDictLaurentPolynomial.from_str('q^2 - 1', ['q']) + >>> type(p).__name__ + 'FastDictLaurentPolynomial' + >>> c = p.to_checked() + >>> type(c).__name__ + 'DictLaurentPolynomial' + >>> c == p + True + """ + return DictLaurentPolynomial._make(self.vars, self.poly_dict, _interned=True) + @sage_method def to_sage(self): if len(self.vars) == 1: @@ -207,7 +222,7 @@ def __eq__(self, other): >>> p4 == DictLaurentPolynomial.from_str('q', ['q']) # same value, different denom True """ - if isinstance(other, DictLaurentPolynomial): + if isinstance(other, FastDictLaurentPolynomial): if self.vars is other.vars: return self.poly_dict == other.poly_dict if len(self.vars) != len(other.vars): @@ -236,7 +251,7 @@ def __neg__(self): >>> -p -2 - q """ - return DictLaurentPolynomial._make( + return FastDictLaurentPolynomial._make( self.vars, {k: -v for k, v in self.poly_dict.items()}, _interned=True) def __add__(self, other): @@ -248,7 +263,7 @@ def __add__(self, other): >>> p + 3 4 + q """ - if isinstance(other, DictLaurentPolynomial): + if isinstance(other, FastDictLaurentPolynomial): sp, op = self.poly_dict, other.poly_dict # Copy the larger dict; iterate over the smaller to minimise lookups. if len(sp) >= len(op): @@ -264,10 +279,10 @@ def __add__(self, other): del result[k] else: result[k] = v - return DictLaurentPolynomial._make(self.vars, result, _interned=True) + return FastDictLaurentPolynomial._make(self.vars, result, _interned=True) if other == 0: - return DictLaurentPolynomial._make(self.vars, dict(self.poly_dict), _interned=True) + return FastDictLaurentPolynomial._make(self.vars, dict(self.poly_dict), _interned=True) zero_key = (0,) * len(self.vars) result = dict(self.poly_dict) @@ -276,7 +291,7 @@ def __add__(self, other): result[zero_key] = s else: result.pop(zero_key, None) - return DictLaurentPolynomial._make(self.vars, result, _interned=True) + return FastDictLaurentPolynomial._make(self.vars, result, _interned=True) def __radd__(self, other): return self.__add__(other) @@ -290,7 +305,7 @@ def __sub__(self, other): >>> p - 1 -1 + q + q^2 """ - if isinstance(other, DictLaurentPolynomial): + if isinstance(other, FastDictLaurentPolynomial): result = dict(self.poly_dict) for k, v in other.poly_dict.items(): if k in result: @@ -301,7 +316,7 @@ def __sub__(self, other): del result[k] else: result[k] = -v - return DictLaurentPolynomial._make(self.vars, result, _interned=True) + return FastDictLaurentPolynomial._make(self.vars, result, _interned=True) return self.__add__(-other) def __rsub__(self, other): @@ -316,7 +331,7 @@ def __mul__(self, other): >>> p * 3 3 + 3*q """ - if isinstance(other, DictLaurentPolynomial): + if isinstance(other, FastDictLaurentPolynomial): result = {} if len(self.poly_dict) == 1: # monomial * polynomial: no cancellation possible, assign directly @@ -385,15 +400,15 @@ def __mul__(self, other): del result[k] else: result[k] = prod - return DictLaurentPolynomial._make(self.vars, result, _interned=True) + return FastDictLaurentPolynomial._make(self.vars, result, _interned=True) if self == 0 or other == 0: - return DictLaurentPolynomial._make(self.vars, {}, _interned=True) + return FastDictLaurentPolynomial._make(self.vars, {}, _interned=True) if other == 1: - return DictLaurentPolynomial._make(self.vars, dict(self.poly_dict), _interned=True) + return FastDictLaurentPolynomial._make(self.vars, dict(self.poly_dict), _interned=True) - return DictLaurentPolynomial._make( + return FastDictLaurentPolynomial._make( self.vars, {k: v * other for k, v in self.poly_dict.items()}, _interned=True) def __rmul__(self, other): @@ -420,7 +435,7 @@ def __truediv__(self, other): ... ValueError: DictLaurentPolynomial division: divisor must be a monomial """ - if isinstance(other, DictLaurentPolynomial): + if isinstance(other, FastDictLaurentPolynomial): if len(other.poly_dict) != 1: raise ValueError('DictLaurentPolynomial division: divisor must be a monomial') return self * other ** -1 @@ -430,7 +445,7 @@ def __truediv__(self, other): raise ValueError( f'DictLaurentPolynomial division: scalar {other!r} does not ' f'divide all coefficients') - return DictLaurentPolynomial._make( + return FastDictLaurentPolynomial._make( self.vars, {k: c // other for k, c in self.poly_dict.items()}, _interned=True) @@ -466,7 +481,7 @@ def simplify_denominator(self): tuple(k // r for k, r in zip(key, reductions)): coef for key, coef in self.poly_dict.items() } - return DictLaurentPolynomial._make(new_vars, new_poly_dict) + return FastDictLaurentPolynomial._make(new_vars, new_poly_dict) def refactor_variables(self, new_vars): """ @@ -508,7 +523,7 @@ def refactor_variables(self, new_vars): tuple(k * s for k, s in zip(key, scales)): coef for key, coef in self.poly_dict.items() } - return DictLaurentPolynomial._make(new_vars, new_poly_dict) + return FastDictLaurentPolynomial._make(new_vars, new_poly_dict) def change_vars(self, rules, new_var_names = None): """ @@ -583,7 +598,7 @@ def change_vars(self, rules, new_var_names = None): parsed_rules = {} for src_var, img in rules.items(): if isinstance(img, str): - parsed = DictLaurentPolynomial.from_str(img, var_names) + parsed = FastDictLaurentPolynomial.from_str(img, var_names) d = src_var.denominator if d > 1: # String describes image of the full variable (src_var^1). @@ -598,7 +613,7 @@ def change_vars(self, rules, new_var_names = None): LaurentVariable(v.name, v.denominator * d) for v in parsed.vars ) - parsed = DictLaurentPolynomial._make(new_img_vars, parsed.poly_dict) + parsed = FastDictLaurentPolynomial._make(new_img_vars, parsed.poly_dict) parsed_rules[src_var] = parsed else: parsed_rules[src_var] = img @@ -610,7 +625,7 @@ def change_vars(self, rules, new_var_names = None): if var in rules: full_rules[var] = rules[var] else: - full_rules[var] = DictLaurentPolynomial.generator(self.vars, index=i) + full_rules[var] = FastDictLaurentPolynomial.generator(self.vars, index=i) # Unify all image DLPs to a common vars tuple when string values were used. if has_str_values and full_rules: @@ -640,7 +655,7 @@ def _lcm2(a, b): return a * b // _gcd(a, b) if term is None: zero_key = (0,) * len(next(iter(full_rules.values())).vars) - term = DictLaurentPolynomial._make( + term = FastDictLaurentPolynomial._make( next(iter(full_rules.values())).vars, {zero_key: coef}) else: term = term * coef @@ -649,7 +664,7 @@ def _lcm2(a, b): return a * b // _gcd(a, b) if result is None: img = next(iter(full_rules.values())) - return DictLaurentPolynomial._make(img.vars, {}) + return FastDictLaurentPolynomial._make(img.vars, {}) return result @classmethod @@ -729,87 +744,26 @@ def parse_exponent(): return num, den return parse_signed_int(), 1 - # --- polynomial representation --- - # Each poly is a list of (coef: int, exps: dict[var_name → (num, den)]). - # Exponents use exact rational arithmetic; variable names are strings. + # --- grammar (builds AST as nested tuples) --- + # Nodes: ('v', name, num, den) variable^(num/den) + # ('c', n) integer constant + # ('u-', expr) unary negation + # (op, left, right) op in '+', '-', '*', '/' sorted_vars = sorted(vars, key=len, reverse=True) - def _rat_add(n1, d1, n2, d2): - n = n1 * d2 + n2 * d1 - d = d1 * d2 - if n == 0: - return 0, 1 - g = _gcd(abs(n), d) - return n // g, d // g - - def _mono_key(exps): - return tuple(sorted(exps.items())) - - def _combine(terms): - acc = {} - for c, e in terms: - k = _mono_key(e) - if k in acc: - acc[k] = (acc[k][0] + c, acc[k][1]) - else: - acc[k] = (c, e) - return [(c, e) for c, e in acc.values() if c != 0] - - def poly_neg(p): - return [(-c, e) for c, e in p] - - def poly_add(p1, p2): - return _combine(p1 + p2) - - def poly_mul(p1, p2): - result = [] - for c1, e1 in p1: - for c2, e2 in p2: - c = c1 * c2 - e = dict(e1) - for v, (n2, d2) in e2.items(): - if v in e: - n1, d1 = e[v] - rn, rd = _rat_add(n1, d1, n2, d2) - if rn == 0: - del e[v] - else: - e[v] = (rn, rd) - else: - e[v] = (n2, d2) - result.append((c, e)) - return _combine(result) - - def poly_inv(p): - if len(p) != 1: - raise ValueError( - f'from_str: can only divide by a monomial in {s!r}') - c, e = p[0] - if c == 0: - raise ValueError(f'from_str: division by zero in {s!r}') - if abs(c) != 1: - raise ValueError( - f'from_str: division by non-unit coefficient {c} ' - f'in {s!r}; write the coefficient in the numerator') - return [(c, {v: (-n, d) for v, (n, d) in e.items()})] - - # --- grammar --- - def parse_expr(): result = parse_term() while pos[0] < len(s) and s[pos[0]] in '+-': op = s[pos[0]]; pos[0] += 1 - right = parse_term() - result = poly_add(result, poly_neg(right) if op == '-' else right) + result = (op, result, parse_term()) return result def parse_term(): result = parse_factor() while pos[0] < len(s) and s[pos[0]] in '*/': op = s[pos[0]]; pos[0] += 1 - right = parse_factor() - result = poly_mul(result, poly_inv(right) if op == '/' else right) + result = (op, result, parse_factor()) return result def parse_factor(): @@ -819,7 +773,7 @@ def parse_factor(): sign = -sign pos[0] += 1 result = parse_atom() - return poly_neg(result) if sign == -1 else result + return ('u-', result) if sign == -1 else result def parse_atom(): if pos[0] >= len(s): @@ -845,7 +799,7 @@ def parse_atom(): if pos[0] < len(s) and s[pos[0]] == '^': pos[0] += 1 num, den = parse_exponent() - return [(1, {var: (num, den)} if num != 0 else {})] + return ('v', var, num, den) # Must be an integer coefficient. if c.isdigit(): @@ -854,47 +808,89 @@ def parse_atom(): raise ValueError( f'from_str: decimal numbers not supported ' f'at position {pos[0]} in {s!r}') - return [(n, {})] + return ('c', n) raise ValueError( f'from_str: unexpected character {c!r} ' f'at position {pos[0]} in {s!r}; known variables: {vars!r}') - # --- parse and convert --- + # --- parse --- - poly = parse_expr() + ast = parse_expr() if pos[0] != len(s): raise ValueError( f'from_str: unexpected content {s[pos[0]:]!r} ' f'at position {pos[0]} in {s!r}') + # --- scan AST for per-variable denominator LCMs --- + def lcm(a, b): return a * b // _gcd(a, b) var_lcms = {v: 1 for v in vars} - for _, exps in poly: - for v, (n, d) in exps.items(): - var_lcms[v] = lcm(var_lcms[v], d) - vars_list = [LaurentVariable(v, var_lcms[v]) for v in vars] + def collect_denoms(node): + tag = node[0] + if tag == 'v': + _, name, num, den = node + if num != 0: + var_lcms[name] = lcm(var_lcms[name], den) + elif tag == 'u-': + collect_denoms(node[1]) + elif tag != 'c': + collect_denoms(node[1]) + collect_denoms(node[2]) + + collect_denoms(ast) + + # --- build generators in the joint variable space --- + + vars_list = tuple(LaurentVariable(v, var_lcms[v]) for v in vars) + n = len(vars) + generators = { + v: cls._make(vars_list, {tuple(1 if j == i else 0 for j in range(n)): 1}) + for i, v in enumerate(vars) + } - poly_dict = {} - for coef, exps in poly: - key = tuple( - exps[v][0] * (var_lcms[v] // exps[v][1]) if v in exps else 0 - for v in vars - ) - if key in poly_dict: - new_v = poly_dict[key] + coef - if new_v: - poly_dict[key] = new_v - else: - del poly_dict[key] - else: - poly_dict[key] = coef + # --- evaluate AST using DictLaurentPolynomial arithmetic --- + + def evaluate(node): + tag = node[0] + if tag == 'c': + return node[1] + if tag == 'v': + _, name, num, den = node + if num == 0: + return 1 + return generators[name] ** (num * var_lcms[name] // den) + if tag == 'u-': + return -evaluate(node[1]) + if tag == '+': + return evaluate(node[1]) + evaluate(node[2]) + if tag == '-': + return evaluate(node[1]) - evaluate(node[2]) + if tag == '*': + return evaluate(node[1]) * evaluate(node[2]) + # tag == '/' + right_val = evaluate(node[2]) + if isinstance(right_val, int): + if abs(right_val) != 1: + raise ValueError( + f'from_str: division by non-unit coefficient {right_val} ' + f'in {s!r}; write the coefficient in the numerator') + return evaluate(node[1]) * right_val + if len(right_val.poly_dict) != 1: + raise ValueError( + f'from_str: can only divide by a monomial in {s!r}') + return evaluate(node[1]) * (right_val ** -1) - return cls._make(vars_list, poly_dict) + result = evaluate(ast) + + if isinstance(result, int): + zero_key = (0,) * n + return cls._make(vars_list, {zero_key: result} if result != 0 else {}) + return result def __pow__(self, n): """ @@ -912,7 +908,7 @@ def __pow__(self, n): raise ValueError(f'exponent must be an integer, got {n!r}') if n == 0: zero_key = (0,) * len(self.vars) - return DictLaurentPolynomial._make(self.vars, {zero_key: 1}, _interned=True) + return FastDictLaurentPolynomial._make(self.vars, {zero_key: 1}, _interned=True) if n < 0: if len(self.poly_dict) != 1: raise ValueError('negative powers only supported for monomials') @@ -922,7 +918,7 @@ def __pow__(self, n): f'negative powers require a ±1 leading coefficient, got {coef!r}') inv_key = tuple(k * n for k in key) inv_coef = coef ** (-n) # -n > 0, so int**int stays int; (±1)^k = (±1)^{-k} - return DictLaurentPolynomial._make(self.vars, {inv_key: inv_coef}, _interned=True) + return FastDictLaurentPolynomial._make(self.vars, {inv_key: inv_coef}, _interned=True) result = self base = self n -= 1 @@ -965,3 +961,58 @@ def _sort_key(item): terms.append(f'{coef}*{monomial}') return ' + '.join(terms).replace('+ -', '- ') +class DictLaurentPolynomial(FastDictLaurentPolynomial): + """ + A checked variant of FastDictLaurentPolynomial that verifies variable name + compatibility and normalises denominators to their LCM before each binary + arithmetic operation. + + >>> a = DictLaurentPolynomial.from_str('q^2', ['q']) + >>> b = DictLaurentPolynomial.from_str('q^(1/2)', ['q']) + >>> a + b + q^(1/2) + q^2 + >>> t = DictLaurentPolynomial.from_str('t', ['t']) + >>> a + t + Traceback (most recent call last): + ... + ValueError: incompatible variables: ['q'] vs ['t'] + """ + + def _match(self, other): + """Return (self, other) refactored to share a common vars tuple. + + Raises ValueError if variable names or counts differ. + """ + if self.vars is other.vars: + return self, other + if len(self.vars) != len(other.vars): + raise ValueError( + f'incompatible variables: ' + f'{[v.name for v in self.vars]!r} vs ' + f'{[v.name for v in other.vars]!r}') + if any(v1.name != v2.name for v1, v2 in zip(self.vars, other.vars)): + raise ValueError( + f'incompatible variables: ' + f'{[v.name for v in self.vars]!r} vs ' + f'{[v.name for v in other.vars]!r}') + def lcm(a, b): return a * b // _gcd(a, b) + common_vars = tuple( + LaurentVariable(v1.name, lcm(v1.denominator, v2.denominator)) + for v1, v2 in zip(self.vars, other.vars) + ) + return self.refactor_variables(common_vars), other.refactor_variables(common_vars) + + def __add__(self, other): + lhs, rhs = self._match(other) if isinstance(other, FastDictLaurentPolynomial) else (self, other) + result = FastDictLaurentPolynomial.__add__(lhs, rhs) + return DictLaurentPolynomial._make(result.vars, result.poly_dict, _interned=True) + + def __sub__(self, other): + lhs, rhs = self._match(other) if isinstance(other, FastDictLaurentPolynomial) else (self, other) + result = FastDictLaurentPolynomial.__sub__(lhs, rhs) + return DictLaurentPolynomial._make(result.vars, result.poly_dict, _interned=True) + + def __mul__(self, other): + lhs, rhs = self._match(other) if isinstance(other, FastDictLaurentPolynomial) else (self, other) + result = FastDictLaurentPolynomial.__mul__(lhs, rhs) + return DictLaurentPolynomial._make(result.vars, result.poly_dict, _interned=True) \ No newline at end of file From 193678a8dfe7ee43dadf03366e869177c113549c Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Fri, 26 Jun 2026 16:29:55 -0500 Subject: [PATCH 28/53] Add opt_einsum into dependency & minor adjustment. --- setup.py | 3 ++- spherogram_src/links/reshetikhin_turaev/RT_network.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index f232a8d..5e842fd 100644 --- a/setup.py +++ b/setup.py @@ -205,7 +205,8 @@ def run(self): 'networkx', 'packaging', 'snappy_manifolds>=1.4', - 'knot_floer_homology>=1.2.2'] + 'knot_floer_homology>=1.2.2', + 'opt_einsum>=3.4.0'] setup( name = 'spherogram', version = version, diff --git a/spherogram_src/links/reshetikhin_turaev/RT_network.py b/spherogram_src/links/reshetikhin_turaev/RT_network.py index 4041976..4352282 100644 --- a/spherogram_src/links/reshetikhin_turaev/RT_network.py +++ b/spherogram_src/links/reshetikhin_turaev/RT_network.py @@ -117,7 +117,7 @@ def __init__(self, shape): except: raise ValueError(f'key {key[0].index} not found in {idle}') else: - shape = tensor.shape if tensor is not None else tuple(4 for _ in key) + shape = tensor.shape if tensor is not None else tuple(8 for _ in key) oe_network.append(_ShapeOnly(shape)) oe_network.append([edge.index for edge in key]) From dac34aeaa9ca801f0739cbed99b1fcfc36d15adf Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Fri, 26 Jun 2026 17:49:11 -0500 Subject: [PATCH 29/53] Make DictLaurentPolynomial accept operations with arbitrary variables from other --- .../dict_laurent_polynomial.py | 92 ++++++++++++++----- 1 file changed, 69 insertions(+), 23 deletions(-) diff --git a/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py index 1c601b7..10b4bb6 100644 --- a/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py +++ b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py @@ -445,7 +445,7 @@ def __truediv__(self, other): raise ValueError( f'DictLaurentPolynomial division: scalar {other!r} does not ' f'divide all coefficients') - return FastDictLaurentPolynomial._make( + return type(self)._make( self.vars, {k: c // other for k, c in self.poly_dict.items()}, _interned=True) @@ -692,6 +692,8 @@ def from_str(cls, s, vars): t1^2*t10 + t1 - t10^-1 >>> DictLaurentPolynomial.from_str('(t1^2 - 1) / t1', ['t1', 't2']) t1 - t1^-1 + >>> DictLaurentPolynomial.from_str('3', []) + 3 """ if len(vars) != len(set(vars)): raise ValueError(f'from_str: duplicate variable names in {vars!r}') @@ -963,8 +965,8 @@ def _sort_key(item): class DictLaurentPolynomial(FastDictLaurentPolynomial): """ - A checked variant of FastDictLaurentPolynomial that verifies variable name - compatibility and normalises denominators to their LCM before each binary + A checked variant of FastDictLaurentPolynomial that normalises denominators + to their LCM and expands to the union of variable sets before each binary arithmetic operation. >>> a = DictLaurentPolynomial.from_str('q^2', ['q']) @@ -973,34 +975,52 @@ class DictLaurentPolynomial(FastDictLaurentPolynomial): q^(1/2) + q^2 >>> t = DictLaurentPolynomial.from_str('t', ['t']) >>> a + t - Traceback (most recent call last): - ... - ValueError: incompatible variables: ['q'] vs ['t'] + q^2 + t """ def _match(self, other): """Return (self, other) refactored to share a common vars tuple. - Raises ValueError if variable names or counts differ. + If the variable sets differ, both are expanded to their union (self's + variable order first, then other's exclusive variables appended). + Denominators for shared variables are normalised to their LCM. """ if self.vars is other.vars: return self, other - if len(self.vars) != len(other.vars): - raise ValueError( - f'incompatible variables: ' - f'{[v.name for v in self.vars]!r} vs ' - f'{[v.name for v in other.vars]!r}') - if any(v1.name != v2.name for v1, v2 in zip(self.vars, other.vars)): - raise ValueError( - f'incompatible variables: ' - f'{[v.name for v in self.vars]!r} vs ' - f'{[v.name for v in other.vars]!r}') + def lcm(a, b): return a * b // _gcd(a, b) - common_vars = tuple( - LaurentVariable(v1.name, lcm(v1.denominator, v2.denominator)) - for v1, v2 in zip(self.vars, other.vars) - ) - return self.refactor_variables(common_vars), other.refactor_variables(common_vars) + + self_by_name = {v.name: v for v in self.vars} + other_by_name = {v.name: v for v in other.vars} + + union_list = [] + for v in self.vars: + d = lcm(v.denominator, other_by_name[v.name].denominator) if v.name in other_by_name else v.denominator + union_list.append(LaurentVariable(v.name, d)) + for v in other.vars: + if v.name not in self_by_name: + union_list.append(LaurentVariable(v.name, v.denominator)) + union_vars = _intern_vars(tuple(union_list)) + + def expand(poly_obj): + old_vars = poly_obj.vars + name_to_old = {v.name: i for i, v in enumerate(old_vars)} + slots = [] + for uv in union_vars: + oi = name_to_old.get(uv.name) + if oi is not None: + slots.append((oi, uv.denominator // old_vars[oi].denominator)) + else: + slots.append(None) + new_dict = {} + for old_key, coeff in poly_obj.poly_dict.items(): + new_key = [] + for slot in slots: + new_key.append(old_key[slot[0]] * slot[1] if slot is not None else 0) + new_dict[tuple(new_key)] = coeff + return FastDictLaurentPolynomial._make(union_vars, new_dict, _interned=True) + + return expand(self), expand(other) def __add__(self, other): lhs, rhs = self._match(other) if isinstance(other, FastDictLaurentPolynomial) else (self, other) @@ -1015,4 +1035,30 @@ def __sub__(self, other): def __mul__(self, other): lhs, rhs = self._match(other) if isinstance(other, FastDictLaurentPolynomial) else (self, other) result = FastDictLaurentPolynomial.__mul__(lhs, rhs) - return DictLaurentPolynomial._make(result.vars, result.poly_dict, _interned=True) \ No newline at end of file + return DictLaurentPolynomial._make(result.vars, result.poly_dict, _interned=True) + + def simplify_variables(self): + """ + Return a copy with any variable whose exponent is 0 in every term removed. + + >>> p = DictLaurentPolynomial.from_str('q^2 + 1', ['q', 't']) + >>> p.simplify_variables() + 1 + q^2 + >>> p.simplify_variables().vars + (q,) + >>> DictLaurentPolynomial.from_str('3', ['q', 't']).simplify_variables() + 3 + >>> DictLaurentPolynomial.from_str('q*t + q', ['q', 't']).simplify_variables() + q*t + q + """ + if not self.poly_dict or not self.vars: + return self + keep = [any(key[i] != 0 for key in self.poly_dict) for i in range(len(self.vars))] + if all(keep): + return self + new_vars = _intern_vars(tuple(v for v, k in zip(self.vars, keep) if k)) + new_dict = { + tuple(exp for exp, k in zip(key, keep) if k): coeff + for key, coeff in self.poly_dict.items() + } + return DictLaurentPolynomial._make(new_vars, new_dict, _interned=True) \ No newline at end of file From e3bbba9c5ec687dc34b2e96057b0ca78dafa059d Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Tue, 30 Jun 2026 17:47:32 -0500 Subject: [PATCH 30/53] Update R-matrices and add contraction_sequence method --- Vn_sql.py | 214 ++++++++++++++++++ .../links/reshetikhin_turaev/RT_network.py | 98 +++++++- .../links/reshetikhin_turaev/R_matrices.py | 3 +- .../reshetikhin_turaev/R_matrices/V1/Rn.csv | 2 +- .../reshetikhin_turaev/R_matrices/V1/Rp.csv | 2 +- .../reshetikhin_turaev/R_matrices/V1/hn.csv | 2 +- .../reshetikhin_turaev/R_matrices/V1/hp.csv | 2 +- .../reshetikhin_turaev/R_matrices/V2/Rn.csv | 2 +- .../reshetikhin_turaev/R_matrices/V2/Rp.csv | 2 +- .../reshetikhin_turaev/R_matrices/V2/hn.csv | 2 +- .../reshetikhin_turaev/R_matrices/V2/hp.csv | 2 +- .../reshetikhin_turaev/R_matrices/V3/Rn.csv | 2 +- .../reshetikhin_turaev/R_matrices/V3/Rp.csv | 2 +- .../reshetikhin_turaev/R_matrices/V3/hn.csv | 2 +- .../reshetikhin_turaev/R_matrices/V3/hp.csv | 2 +- .../reshetikhin_turaev/R_matrices/V4/Rn.csv | 2 +- .../reshetikhin_turaev/R_matrices/V4/Rp.csv | 2 +- .../reshetikhin_turaev/R_matrices/V4/hn.csv | 2 +- .../reshetikhin_turaev/R_matrices/V4/hp.csv | 2 +- spherogram_src/links/tangles.py | 7 +- spherogram_src/version.py | 2 +- 21 files changed, 326 insertions(+), 30 deletions(-) create mode 100644 Vn_sql.py diff --git a/Vn_sql.py b/Vn_sql.py new file mode 100644 index 0000000..2254598 --- /dev/null +++ b/Vn_sql.py @@ -0,0 +1,214 @@ + + +# This file was *autogenerated* from the file /Users/shana/Documents/Projects/read_databases/sage/Vn_sql.sage +from sage.all_cmdline import * # import sage library + +_sage_const_1 = Integer(1); _sage_const_5 = Integer(5); _sage_const_0 = Integer(0); _sage_const_8 = Integer(8); _sage_const_19 = Integer(19); _sage_const_9 = Integer(9); _sage_const_42 = Integer(42); _sage_const_10 = Integer(10); _sage_const_124 = Integer(124) +import sqlite3 +import re, ast + +# Edit path accordingly +sql_path = "/Users/shana/data-V/" + +# Names of databases and tables in them you want to access. +# Files should all be stored under the directory sql_path points to + + +schema = { + 'V-database_3-16c.db' : [f'V{i}' for i in range(_sage_const_1 ,_sage_const_5 )], + 'V-database_17c-loose.db' : ['V1', 'V2'], + 'V-database_18c-loose.db' : ['V1', 'V2'], + 'V1-database_17a.db' : ['V1'], + 'V1-database_18a.db' : ['V1'] +} + +""" +schema_16c = { + 'V-database_3-15c.db' : [f'V{i}' for i in range(1 ,5)], + 'V1-database_16c.db' : ['V1'], + 'V-database_V2-equiv_15-16c.db' : ['V3', 'V4'], +} | { + f'V2-database_16c_part-{i}.db' : ['V2'] for i in range(1,3) +} + +schema_loose = { + 'V-database_17c-loose.db' : ['V1', 'V2'], + 'V-database_18c-loose.db' : ['V1', 'V2'] +} + +schema_alt = { + 'V1-database_17a.db' : ['V1'] +} | { + f'V1-database_18a_part-{i}.db' : ['V1'] for i in range(1,5) +} + +schema = schema_16c | schema_alt +""" + +# The rest is automatic and you have nothing to worry about + +assert schema + +table_dict = dict() +alias_dict = dict() + +V_conn = sqlite3.connect(':memory:') +cursor = V_conn.cursor() + +for i, sql_name in enumerate(schema.keys(), start = _sage_const_1 ): + alias = f'db{i}' + + alias_dict.update({sql_name : alias}) + + for table_name in schema[sql_name]: + if table_name not in table_dict.keys(): + table_dict.update({table_name : [alias]}) + else: + table_dict.update({table_name : table_dict[table_name] + [alias]}) + + cursor.execute('ATTACH DATABASE ? AS ?', (sql_path + sql_name, alias)) + + +for table_name in table_dict.keys(): + select_statements = [] + for alias in table_dict[table_name]: + select_statements.append(f"SELECT * FROM {alias}.{table_name}") + + union_query = ' UNION ALL '.join(select_statements) + + view_name = f'all_{table_name}' + + create_view_sql = f"CREATE TEMPORARY VIEW {view_name} AS {union_query}" + + cursor.execute(create_view_sql) + +cursor.close() + +def laurent_poly_from_str(string, vars = ['t','q']): + F = PolynomialRing(ZZ, vars).fraction_field() + L = LaurentPolynomialRing(ZZ, vars) + + return L(F(string)) + +def laurent_poly_from_dict(dict, vars): + L = LaurentPolynomialRing(ZZ, vars) + return L(dict) + +def degree(laurent_poly, vars, var): + L = LaurentPolynomialRing(ZZ, vars) + t = L.gens()[vars.index(var)] + + return laurent_poly.degree(t) - laurent_poly.valuation(t) + +def t_degree(v_poly): + return degree(v_poly, ['t','q'], 't') + +def q_degree(v_poly): + return degree(v_poly, ['t','q'], 'q') + +def knot_info(n, snappy_name, conn = V_conn): + """ + Selects knot data from a specific table based on its SnapPy Name. + + Args: + conn: An active sqlite3 connection object. + n (int): The integer used to specify the table (Vn) and polynomial column. + snappy_name (str): The SnapPy name of the knot to find. + + Returns: + list: A list of tuples, where each tuple represents a row from the database. + Returns an empty list if no match is found. + """ + # Create a cursor object to execute SQL commands + cursor = conn.cursor() + + table_name = f"V{n}" + poly_column_name = f"V{n} Polynomial" + + header = ['KnotTheory Name', 'PD Code', 'Genus', 'Degree of Alexander Polynomial', 'Tightness', poly_column_name] + + query = f""" + SELECT + "KnotTheory Name", + "PD Code", + Genus, + "Degree of Alexander Polynomial", + Tightness, + "{poly_column_name}" + FROM + "all_{table_name}" + WHERE + "SnapPy Name" = ? + """ + + try: + cursor.execute(query, (snappy_name,)) + + results = cursor.fetchall() + return dict(zip(header, results[_sage_const_0 ])) + except sqlite3.Error as e: + print(f"An error occurred with sqlite3: {e}") + return dict() + except IndexError as e: + print(f"Knot name unknown") + return dict() + +def knot_table(n, cross_num, sort = True, conn = V_conn): + cursor = conn.cursor() + table_name = f"V{n}" + + query = f''' + SELECT "SnapPy Name" + FROM "all_{table_name}" + WHERE CAST( + SUBSTR("SnapPy Name", 1, LENGTH("SnapPy Name") - LENGTH(LTRIM("SnapPy Name", '0123456789'))) + AS INTEGER) = ? + ''' + params = [int(cross_num)] + + try: + cursor.execute(query, params) + filtered_knots = [row[_sage_const_0 ] for row in cursor.fetchall()] + except sqlite3.Error as e: + print(f"An error occurred: {e}") + return [] + + if sort: + key_func = lambda name: [re.sub(r'[0-9]', '', name)] + [int(part) for part in re.split(r'[a-zA-Z]*_|[an]', name)] + ans = sorted(filtered_knots, key=key_func) + else: + ans = filtered_knots + + return ans + +def genus(snappy_name, conn = V_conn): + return int(knot_info(_sage_const_1 , snappy_name, conn)['Genus']) +def alex_degree(snappy_name, conn = V_conn): + return int(knot_info(_sage_const_1 , snappy_name, conn)['Degree of Alexander Polynomial']) + +def is_tight(snappy_name, conn = V_conn): + return int(knot_info(_sage_const_1 , snappy_name, conn)['Tightness']) + +def PD(snappy_name, conn = V_conn): + return ast.literal_eval(knot_info(_sage_const_1 , snappy_name, conn)['PD Code']) + +def V(n, snappy_name, conn = V_conn): + return laurent_poly_from_str(knot_info(n, snappy_name, conn)[f"V{n} Polynomial"]) + +def V_str(n, snappy_name, conn = V_conn): + return knot_info(n, snappy_name, conn)[f"V{n} Polynomial"] + +def is_nonalt(snappy_name): + if len(snappy_name.split('n')) > _sage_const_1 : + return True + elif len(snappy_name.split('a')) > _sage_const_1 : + return False + else: + [cross, i] = snappy_name.split('_') + cross = int(cross) + i = int(i) + if (cross == _sage_const_8 and i >= _sage_const_19 ) or (cross == _sage_const_9 and i >= _sage_const_42 ) or (cross == _sage_const_10 and i >= _sage_const_124 ): + return True + else: + return False + diff --git a/spherogram_src/links/reshetikhin_turaev/RT_network.py b/spherogram_src/links/reshetikhin_turaev/RT_network.py index 4352282..f43f3a7 100644 --- a/spherogram_src/links/reshetikhin_turaev/RT_network.py +++ b/spherogram_src/links/reshetikhin_turaev/RT_network.py @@ -189,13 +189,87 @@ def contraction_width(self, omit_idle_arcs = True): boundary = (0,0) if omit_idle_arcs else self.boundary, boundary_labels = [] if omit_idle_arcs else self.boundary_labels) - m = abstract_copy._resolve_self_loops() - width = (3 if m else 0, m) + loops = abstract_copy._resolve_self_loops() + if loops: + if all(len(loop) > 1 for loop in loops): + width = (2, len(loops)) + else: + width = (3, len([loop for loop in loops if len(loop) == 1])) + else: + width = (0, 0) + seq_width, ans = abstract_copy.seq_contraction_width(abstract_copy.optimal_contraction_sequence()) return max(width, seq_width), ans + @staticmethod + def local_contraction_seq(abstract_network, indices): + idx1, idx2 = indices + ans = list(abstract_network) + key1 = abstract_network[idx1] + key2 = abstract_network[idx2] + + contracted_indices = set() + + pairs = [] + for pos_i, ei in enumerate(key1): + for pos_j, ej in enumerate(key2): + if ei.index == ej.index and ei.sign * ej.sign == -1: + pairs.append((pos_i, pos_j)) + contracted_indices.add(ei.index) + + contracted1 = {pos_i for pos_i, _ in pairs} + contracted2 = {pos_j for _, pos_j in pairs} + + if idx1 == idx2: + contracted_all = contracted1 | contracted2 + new_key = tuple(e for pos, e in enumerate(key1) if pos not in contracted_all) + ans.pop(idx1) + else: + new_key = (tuple(e for pos, e in enumerate(key1) if pos not in contracted1) + + tuple(e for pos, e in enumerate(key2) if pos not in contracted2)) + hi, lo = max(idx1, idx2), min(idx1, idx2) + ans.pop(hi) + ans.pop(lo) + + ans.append(new_key) + + return contracted_indices, ans + + def seq_contraction_seq(self, seq): + abstract_network = [key for _, key in self.network] + + ans = [] + + for indices in seq: + contracted_indices, abstract_network = RTNetwork.local_contraction_seq(abstract_network, indices) + + ans.append(contracted_indices) + + return ans, abstract_network + + def contraction_sequence(self, omit_idle_arcs = True): + abstract_network = [] + for _, key in self.network: + if omit_idle_arcs: + non_idle_key = tuple(e for e in key if e.index not in self.idle_labels) + abstract_network.append((None, non_idle_key)) + else: + abstract_network.append((None, key)) + + abstract_copy = RTNetwork(None, + network = abstract_network, + rot_num = self.rot_num, + boundary = (0,0) if omit_idle_arcs else self.boundary, + boundary_labels = [] if omit_idle_arcs else self.boundary_labels) + + loops = abstract_copy._resolve_self_loops() + + contraction_seq, ans = abstract_copy.seq_contraction_seq(abstract_copy.optimal_contraction_sequence()) + + return loops + contraction_seq, ans + def contract_nodes(self, indices): idx1, idx2 = indices tensor1, key1 = self.network[idx1] @@ -233,23 +307,27 @@ def contract_nodes(self, indices): def _resolve_self_loop_at(self, idx): _, key = self.network[idx] - pairs = [] + edge_indices = [] seen = {} for pos, e in enumerate(key): if e.index in seen: - other_pos, other_e = seen[e.index] + _, other_e = seen[e.index] if e.sign * other_e.sign == -1: - pairs.append((other_pos, pos)) + edge_indices.append(e.index) break else: seen[e.index] = (pos, e) - if not pairs: - return False - self.contract_nodes((idx, idx)) - return True + if edge_indices: + self.contract_nodes((idx, idx)) + return edge_indices def _resolve_self_loops(self): - return sum(int(self._resolve_self_loop_at(i)) for i in range(len(self.network))) + ans = [] + for i in range(len(self.network)): + loop = self._resolve_self_loop_at(i) + if loop: + ans.append(loop) + return ans def contract_sequence(self, seq, timed = False): if timed: diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices.py b/spherogram_src/links/reshetikhin_turaev/R_matrices.py index ddf6fea..869593a 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices.py +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices.py @@ -13,7 +13,8 @@ def laurent_sparse_tensor_from_file(file, vars = ['t', 'q'], sage_polynomials = header = next(reader) shape = ast.literal_eval(header[0]) - assert header[1] == 'LaurentPolynomial', f'Expected type LaurentPolynomial, got {header[1]}' + if header[1] != 'ZZ': + raise NotImplementedError data = dict() for line in reader: diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/Rn.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/Rn.csv index d74d959..56349e6 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/Rn.csv +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/Rn.csv @@ -1,4 +1,4 @@ -"(4,4,4,4)","LaurentPolynomial" +"(4,4,4,4)","ZZ" "(0,0,0,0)","1" "(0,1,1,0)","1" "(0,2,2,0)","1" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/Rp.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/Rp.csv index b48550e..238ccc2 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/Rp.csv +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/Rp.csv @@ -1,4 +1,4 @@ -"(4,4,4,4)","LaurentPolynomial" +"(4,4,4,4)","ZZ" "(0,0,0,0)","1" "(0,1,0,1)","1-1/(q*t)" "(0,2,0,2)","1-t/q" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/hn.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/hn.csv index fd6c3b1..f848bbb 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/hn.csv +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/hn.csv @@ -1,4 +1,4 @@ -"(4,4)","LaurentPolynomial" +"(4,4)","ZZ" "(0,0)","1" "(1,1)","-1" "(2,2)","-1" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/hp.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/hp.csv index fd6c3b1..f848bbb 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/hp.csv +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V1/hp.csv @@ -1,4 +1,4 @@ -"(4,4)","LaurentPolynomial" +"(4,4)","ZZ" "(0,0)","1" "(1,1)","-1" "(2,2)","-1" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/Rn.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/Rn.csv index 5673d80..29bded3 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/Rn.csv +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/Rn.csv @@ -1,4 +1,4 @@ -"(8,8,8,8)","LaurentPolynomial" +"(8,8,8,8)","ZZ" "(0,0,0,0)","1" "(0,1,1,0)","1" "(0,2,2,0)","1" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/Rp.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/Rp.csv index 865785f..53f1303 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/Rp.csv +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/Rp.csv @@ -1,4 +1,4 @@ -"(8,8,8,8)","LaurentPolynomial" +"(8,8,8,8)","ZZ" "(0,0,0,0)","1" "(0,1,0,1)","1-1/(q*t)" "(0,2,0,2)","1-t/q" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/hn.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/hn.csv index 8c4464a..8394cdc 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/hn.csv +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/hn.csv @@ -1,4 +1,4 @@ -"(8,8)","LaurentPolynomial" +"(8,8)","ZZ" "(0,0)","1" "(1,1)","-1" "(2,2)","-1" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/hp.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/hp.csv index 8c4464a..8394cdc 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/hp.csv +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V2/hp.csv @@ -1,4 +1,4 @@ -"(8,8)","LaurentPolynomial" +"(8,8)","ZZ" "(0,0)","1" "(1,1)","-1" "(2,2)","-1" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/Rn.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/Rn.csv index 0be0f5a..bc4bb3a 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/Rn.csv +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/Rn.csv @@ -1,4 +1,4 @@ -"(12,12,12,12)","LaurentPolynomial" +"(12,12,12,12)","ZZ" "(0,0,0,0)","1" "(0,1,1,0)","1" "(0,2,2,0)","1" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/Rp.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/Rp.csv index 525ef17..85992d5 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/Rp.csv +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/Rp.csv @@ -1,4 +1,4 @@ -"(12,12,12,12)","LaurentPolynomial" +"(12,12,12,12)","ZZ" "(0,0,0,0)","1" "(0,1,0,1)","1-1/(q^3*t)" "(0,2,0,2)","1-t/q^3" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/hn.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/hn.csv index 7b97704..1e73983 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/hn.csv +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/hn.csv @@ -1,4 +1,4 @@ -"(12,12)","LaurentPolynomial" +"(12,12)","ZZ" "(0,0)","1" "(1,1)","-1" "(2,2)","-1" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/hp.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/hp.csv index 7b97704..1e73983 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/hp.csv +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V3/hp.csv @@ -1,4 +1,4 @@ -"(12,12)","LaurentPolynomial" +"(12,12)","ZZ" "(0,0)","1" "(1,1)","-1" "(2,2)","-1" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/Rn.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/Rn.csv index b7f72b9..c18266d 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/Rn.csv +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/Rn.csv @@ -1,4 +1,4 @@ -"(16,16,16,16)","LaurentPolynomial" +"(16,16,16,16)","ZZ" "(0,0,0,0)","1" "(0,1,1,0)","1" "(0,2,2,0)","1" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/Rp.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/Rp.csv index e50f0d4..b361ed3 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/Rp.csv +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/Rp.csv @@ -1,4 +1,4 @@ -"(16,16,16,16)","LaurentPolynomial" +"(16,16,16,16)","ZZ" "(0,0,0,0)","1" "(0,1,0,1)","1-1/(q^2*t)" "(0,2,0,2)","1-t/q^2" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/hn.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/hn.csv index 1f0b31f..877d0c5 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/hn.csv +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/hn.csv @@ -1,4 +1,4 @@ -"(16,16)","LaurentPolynomial" +"(16,16)","ZZ" "(0,0)","1" "(1,1)","-1" "(2,2)","-1" diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/hp.csv b/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/hp.csv index 1f0b31f..877d0c5 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/hp.csv +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices/V4/hp.csv @@ -1,4 +1,4 @@ -"(16,16)","LaurentPolynomial" +"(16,16)","ZZ" "(0,0)","1" "(1,1)","-1" "(2,2)","-1" diff --git a/spherogram_src/links/tangles.py b/spherogram_src/links/tangles.py index 5632349..bd51b6c 100644 --- a/spherogram_src/links/tangles.py +++ b/spherogram_src/links/tangles.py @@ -656,8 +656,11 @@ def entry_crossing(k): def apply_reshetikhin_turaev_functor(self, tensors): return RTNetwork(tensors, T = self) - def contraction_width(self): - return RTNetwork(None, T = self).contraction_width() + def contraction_width(self, omit_idle_arcs = True): + return RTNetwork(None, T = self).contraction_width(omit_idle_arcs = omit_idle_arcs) + + def contraction_sequence(self, omit_idle_arcs = True): + return RTNetwork(None, T = self).contraction_sequence(omit_idle_arcs = omit_idle_arcs) def _component_starts_from_PD(self, code, labels, gluings, entry_dict): """ diff --git a/spherogram_src/version.py b/spherogram_src/version.py index dc4633d..2109f58 100644 --- a/spherogram_src/version.py +++ b/spherogram_src/version.py @@ -1 +1 @@ -version = '2.4.2b' +version = '2.4.3b' From a609b9f95da4d3de2ac87dc881328332247b0930 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Tue, 30 Jun 2026 19:57:41 -0500 Subject: [PATCH 31/53] Remove accidentally committed Vn_sql.py --- Vn_sql.py | 214 ------------------------------------------------------ 1 file changed, 214 deletions(-) delete mode 100644 Vn_sql.py diff --git a/Vn_sql.py b/Vn_sql.py deleted file mode 100644 index 2254598..0000000 --- a/Vn_sql.py +++ /dev/null @@ -1,214 +0,0 @@ - - -# This file was *autogenerated* from the file /Users/shana/Documents/Projects/read_databases/sage/Vn_sql.sage -from sage.all_cmdline import * # import sage library - -_sage_const_1 = Integer(1); _sage_const_5 = Integer(5); _sage_const_0 = Integer(0); _sage_const_8 = Integer(8); _sage_const_19 = Integer(19); _sage_const_9 = Integer(9); _sage_const_42 = Integer(42); _sage_const_10 = Integer(10); _sage_const_124 = Integer(124) -import sqlite3 -import re, ast - -# Edit path accordingly -sql_path = "/Users/shana/data-V/" - -# Names of databases and tables in them you want to access. -# Files should all be stored under the directory sql_path points to - - -schema = { - 'V-database_3-16c.db' : [f'V{i}' for i in range(_sage_const_1 ,_sage_const_5 )], - 'V-database_17c-loose.db' : ['V1', 'V2'], - 'V-database_18c-loose.db' : ['V1', 'V2'], - 'V1-database_17a.db' : ['V1'], - 'V1-database_18a.db' : ['V1'] -} - -""" -schema_16c = { - 'V-database_3-15c.db' : [f'V{i}' for i in range(1 ,5)], - 'V1-database_16c.db' : ['V1'], - 'V-database_V2-equiv_15-16c.db' : ['V3', 'V4'], -} | { - f'V2-database_16c_part-{i}.db' : ['V2'] for i in range(1,3) -} - -schema_loose = { - 'V-database_17c-loose.db' : ['V1', 'V2'], - 'V-database_18c-loose.db' : ['V1', 'V2'] -} - -schema_alt = { - 'V1-database_17a.db' : ['V1'] -} | { - f'V1-database_18a_part-{i}.db' : ['V1'] for i in range(1,5) -} - -schema = schema_16c | schema_alt -""" - -# The rest is automatic and you have nothing to worry about - -assert schema - -table_dict = dict() -alias_dict = dict() - -V_conn = sqlite3.connect(':memory:') -cursor = V_conn.cursor() - -for i, sql_name in enumerate(schema.keys(), start = _sage_const_1 ): - alias = f'db{i}' - - alias_dict.update({sql_name : alias}) - - for table_name in schema[sql_name]: - if table_name not in table_dict.keys(): - table_dict.update({table_name : [alias]}) - else: - table_dict.update({table_name : table_dict[table_name] + [alias]}) - - cursor.execute('ATTACH DATABASE ? AS ?', (sql_path + sql_name, alias)) - - -for table_name in table_dict.keys(): - select_statements = [] - for alias in table_dict[table_name]: - select_statements.append(f"SELECT * FROM {alias}.{table_name}") - - union_query = ' UNION ALL '.join(select_statements) - - view_name = f'all_{table_name}' - - create_view_sql = f"CREATE TEMPORARY VIEW {view_name} AS {union_query}" - - cursor.execute(create_view_sql) - -cursor.close() - -def laurent_poly_from_str(string, vars = ['t','q']): - F = PolynomialRing(ZZ, vars).fraction_field() - L = LaurentPolynomialRing(ZZ, vars) - - return L(F(string)) - -def laurent_poly_from_dict(dict, vars): - L = LaurentPolynomialRing(ZZ, vars) - return L(dict) - -def degree(laurent_poly, vars, var): - L = LaurentPolynomialRing(ZZ, vars) - t = L.gens()[vars.index(var)] - - return laurent_poly.degree(t) - laurent_poly.valuation(t) - -def t_degree(v_poly): - return degree(v_poly, ['t','q'], 't') - -def q_degree(v_poly): - return degree(v_poly, ['t','q'], 'q') - -def knot_info(n, snappy_name, conn = V_conn): - """ - Selects knot data from a specific table based on its SnapPy Name. - - Args: - conn: An active sqlite3 connection object. - n (int): The integer used to specify the table (Vn) and polynomial column. - snappy_name (str): The SnapPy name of the knot to find. - - Returns: - list: A list of tuples, where each tuple represents a row from the database. - Returns an empty list if no match is found. - """ - # Create a cursor object to execute SQL commands - cursor = conn.cursor() - - table_name = f"V{n}" - poly_column_name = f"V{n} Polynomial" - - header = ['KnotTheory Name', 'PD Code', 'Genus', 'Degree of Alexander Polynomial', 'Tightness', poly_column_name] - - query = f""" - SELECT - "KnotTheory Name", - "PD Code", - Genus, - "Degree of Alexander Polynomial", - Tightness, - "{poly_column_name}" - FROM - "all_{table_name}" - WHERE - "SnapPy Name" = ? - """ - - try: - cursor.execute(query, (snappy_name,)) - - results = cursor.fetchall() - return dict(zip(header, results[_sage_const_0 ])) - except sqlite3.Error as e: - print(f"An error occurred with sqlite3: {e}") - return dict() - except IndexError as e: - print(f"Knot name unknown") - return dict() - -def knot_table(n, cross_num, sort = True, conn = V_conn): - cursor = conn.cursor() - table_name = f"V{n}" - - query = f''' - SELECT "SnapPy Name" - FROM "all_{table_name}" - WHERE CAST( - SUBSTR("SnapPy Name", 1, LENGTH("SnapPy Name") - LENGTH(LTRIM("SnapPy Name", '0123456789'))) - AS INTEGER) = ? - ''' - params = [int(cross_num)] - - try: - cursor.execute(query, params) - filtered_knots = [row[_sage_const_0 ] for row in cursor.fetchall()] - except sqlite3.Error as e: - print(f"An error occurred: {e}") - return [] - - if sort: - key_func = lambda name: [re.sub(r'[0-9]', '', name)] + [int(part) for part in re.split(r'[a-zA-Z]*_|[an]', name)] - ans = sorted(filtered_knots, key=key_func) - else: - ans = filtered_knots - - return ans - -def genus(snappy_name, conn = V_conn): - return int(knot_info(_sage_const_1 , snappy_name, conn)['Genus']) -def alex_degree(snappy_name, conn = V_conn): - return int(knot_info(_sage_const_1 , snappy_name, conn)['Degree of Alexander Polynomial']) - -def is_tight(snappy_name, conn = V_conn): - return int(knot_info(_sage_const_1 , snappy_name, conn)['Tightness']) - -def PD(snappy_name, conn = V_conn): - return ast.literal_eval(knot_info(_sage_const_1 , snappy_name, conn)['PD Code']) - -def V(n, snappy_name, conn = V_conn): - return laurent_poly_from_str(knot_info(n, snappy_name, conn)[f"V{n} Polynomial"]) - -def V_str(n, snappy_name, conn = V_conn): - return knot_info(n, snappy_name, conn)[f"V{n} Polynomial"] - -def is_nonalt(snappy_name): - if len(snappy_name.split('n')) > _sage_const_1 : - return True - elif len(snappy_name.split('a')) > _sage_const_1 : - return False - else: - [cross, i] = snappy_name.split('_') - cross = int(cross) - i = int(i) - if (cross == _sage_const_8 and i >= _sage_const_19 ) or (cross == _sage_const_9 and i >= _sage_const_42 ) or (cross == _sage_const_10 and i >= _sage_const_124 ): - return True - else: - return False - From 61c5ea32518f6e01f1479c87c11cf4adacc44210 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Sun, 5 Jul 2026 00:01:50 -0500 Subject: [PATCH 32/53] is_planar implemented & update some names --- spherogram_src/__init__.py | 4 +- spherogram_src/links/invariants.py | 4 +- .../links/reshetikhin_turaev/R_matrices.py | 4 +- spherogram_src/links/tangles.py | 107 +++++++++++++++++- 4 files changed, 109 insertions(+), 10 deletions(-) diff --git a/spherogram_src/__init__.py b/spherogram_src/__init__.py index b75a395..7a06789 100644 --- a/spherogram_src/__init__.py +++ b/spherogram_src/__init__.py @@ -1,7 +1,7 @@ from .presentations import * from .links import * from .codecs import * -from .links.reshetikhin_turaev import DictLaurentPolynomial +from .links.reshetikhin_turaev import DictLaurentPolynomial, RMatrix, colored_links_gould_R_matrices, colored_jones_R_matrices, prefactor_colored_jones # Make the module version number easily accessible. from . import version as _version @@ -25,4 +25,4 @@ def version(): 'Tangle', 'CapTangle', 'CupTangle', 'RationalTangle', 'ZeroTangle', 'InfinityTangle', 'MinusOneTangle', 'OneTangle', 'IntegerTangle', 'IdentityBraid', 'BraidTangle', 'ComponentTangle', 'join_strands', - 'DictLaurentPolynomial'] + 'DictLaurentPolynomial', 'RMatrix', 'colored_links_gould_R_matrices', 'colored_jones_R_matrices', 'prefactor_colored_jones'] diff --git a/spherogram_src/links/invariants.py b/spherogram_src/links/invariants.py index 750f71c..d5fa9b8 100644 --- a/spherogram_src/links/invariants.py +++ b/spherogram_src/links/invariants.py @@ -340,7 +340,7 @@ def colored_links_gould_polynomial(self, n, sage_output = _within_sage, sage_pol """ from .reshetikhin_turaev import colored_links_gould_R_matrices, DictLaurentPolynomial - ans = self.min_long_diagram().apply_reshetikhin_turaev_functor(colored_links_gould_R_matrices(n, sage_polynomials=sage_polynomials)).evaluate(timed = timed) + ans = self.min_long_diagram().reshetikhin_turaev_network(colored_links_gould_R_matrices(n, sage_polynomials=sage_polynomials)).evaluate(timed = timed) if sage_output: if not sage_polynomials: @@ -363,7 +363,7 @@ def colored_jones_polynomial(self, n, sage_output = _within_sage, sage_polynomia """ from .reshetikhin_turaev import colored_jones_R_matrices, prefactor_colored_jones, DictLaurentPolynomial - ans = self.min_long_diagram().apply_reshetikhin_turaev_functor(colored_jones_R_matrices(n, sage_polynomials=sage_polynomials)).evaluate(timed = timed) + ans = self.min_long_diagram().reshetikhin_turaev_network(colored_jones_R_matrices(n, sage_polynomials=sage_polynomials)).evaluate(timed = timed) ans = (ans[0] * prefactor_colored_jones(n, self.writhe(), sage_polynomial=sage_polynomials), ans[1]) if sage_output: diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices.py b/spherogram_src/links/reshetikhin_turaev/R_matrices.py index 869593a..fec86c9 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices.py +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices.py @@ -74,7 +74,7 @@ def h(self, sign): return self._id @staticmethod - def laurent_R_from_directory(dir_path, vars = ['t', 'q'], compressed = False, sage_polynomials = False): + def from_directory(dir_path, vars = ['t', 'q'], compressed = False, sage_polynomials = False): names = [name + '.csv' + ('.bz2' if compressed else '') for name in ['Rp', 'Rn', 'hp', 'hn']] @@ -92,7 +92,7 @@ def colored_links_gould_R_matrices(n, sage_polynomials = False): if key in _cache.keys(): return _cache[key] else: - _cache[key] = RMatrix.laurent_R_from_directory(dir_path = os.path.join(dir_path, f'R_matrices/V{n}/'), + _cache[key] = RMatrix.from_directory(dir_path = os.path.join(dir_path, f'R_matrices/V{n}/'), vars = ['t', 'q'], compressed = False, sage_polynomials = sage_polynomials) diff --git a/spherogram_src/links/tangles.py b/spherogram_src/links/tangles.py index 705c2c9..9ccedd8 100644 --- a/spherogram_src/links/tangles.py +++ b/spherogram_src/links/tangles.py @@ -25,6 +25,7 @@ from .ordered_set import OrderedSet from .links import Crossing, Strand, Link, CrossingStrand, CrossingEntryPoint from .reshetikhin_turaev import RTNetwork +from .. import graphs class CyclicList(list): def __init__(self, iterable): @@ -107,7 +108,7 @@ def add(self, c): return component class Tangle: - def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, label=None, start_orientations = None, component_starts = None): + def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, label=None, start_orientations = None, component_starts = None, check_planarity = True): """ A tangle is a fragment of a Link with some number of boundary strands. Tangles can be composed in various ways along their boundary strands, @@ -222,6 +223,9 @@ def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, else: s.fuse() self.crossings.remove(s) + + if check_planarity and not self.is_planar(): + raise ValueError("Tangle isn't planar") def __getitem__(self, i): return (self, i % (self.boundary[0] + self.boundary[1])) @@ -650,7 +654,7 @@ def entry_crossing(k): return ans - def apply_reshetikhin_turaev_functor(self, tensors): + def reshetikhin_turaev_network(self, tensors): return RTNetwork(tensors, T = self) def contraction_width(self, omit_idle_arcs = True): @@ -1149,10 +1153,105 @@ def faces(self): face.append(next) return faces + + def digraph(self): + """ + The underlying directed graph for the tangle diagram. + """ + G = graphs.Digraph() + for component in self.components: + if isinstance(component[0].crossing, Tangle): + # Strip off the first element which is not an *entry* strand. + comp = component[1:] + else: + comp = component + for c in comp: + cs0 = CrossingStrand(c.crossing, c.strand_index) + cs1 = cs0.opposite() + + node0 = cs0.crossing if not isinstance(cs0.crossing, Tangle) else cs0 + node1 = cs1.crossing if not isinstance(cs1.crossing, Tangle) else cs1 + + G.add_edge(node0, node1) + + assert len(G.edges) == 2 * len(self.crossings) + (3 * len(self.boundary_strands) // 2) + + return G + + def split_tangle_diagram(self, destroy_original = False, check_planarity = False): + """ + Split the tangle diagram into its connected components. Returns a list of Tangles. + + If check_planarity is True, return in addition if the boundary strands of the components + are laid out in a planar manner with respect to each other. + """ + T = self.copy() if not destroy_original else self + components = T.digraph().weak_components() + + ans = [] + boundary_index = dict() + counterclock_bd_id = [i for i in range(T.boundary[0])] + list(reversed([T.boundary[0] + i for i in range(T.boundary[1])])) + + for i, component in enumerate(components): + boundaries = [] + crossings = [] # Strands included + for c in component: + if isinstance(c, CrossingStrand): + boundaries.append(c) + else: + assert isinstance(c, (Crossing, Strand)) + crossings.append(c) + + boundaries.sort(key = lambda cs: cs.strand_index) + + boundary = (len([cs for cs in boundaries if cs.strand_index < T.boundary[0]]), + len([cs for cs in boundaries if cs.strand_index >= T.boundary[0]])) + entry_points = [cs.opposite() for cs in boundaries] + start_orientations = [(cs.crossing, 1) for cs in entry_points] + + for cs in boundaries: + boundary_index.setdefault(i, []).append(cs.strand_index) + + ans.append(Tangle(boundary, crossings, entry_points, + label = f'{self.label}_component_{i}', + start_orientations = start_orientations, + check_planarity = False)) + + if not check_planarity: + return ans + else: + # Here we only check whether the boundary strands of components + # are laied out in a planar manner with respect to each other. + for i in range(len(ans)): + for j in range(len(ans)): + if i != j: + counterclock_i = [counterclock_bd_id[k] for k in boundary_index[i]] + i_min = min(counterclock_i) + i_max = max(counterclock_i) + + in_between = [i_min < counterclock_bd_id[k] < i_max for k in boundary_index[j]] + + if any(in_between): + if not all(in_between): + return (False, ans) + + return (True, ans) def is_planar(self): - # TODO - pass + G = self.digraph() + if not G.is_weakly_connected(): + boundary_planarity, components = self.split_tangle_diagram(destroy_original = False, check_planarity = True) + if not boundary_planarity: + return False + else: + return all([c.is_planar() for c in components]) + + v = len(self.crossings) + len(self.boundary_strands) + 1 + # view the boundary as a vertex at infinity + + euler = v - len(G.edges) + len(self.faces()) + + return euler == 2 or v == 1 def simplify(self, mode = 'basic', type_III_limit = 100): """ From 546426bbb00a80fe1d9ad4d36247106053ee9f0c Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Sun, 5 Jul 2026 23:39:17 -0500 Subject: [PATCH 33/53] Auto reordering of the tensor in contracted network. To implement flip (mutation) --- .../links/reshetikhin_turaev/RT_network.py | 27 ++++++++++++++++++- .../links/reshetikhin_turaev/sparse_array.py | 7 +++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/spherogram_src/links/reshetikhin_turaev/RT_network.py b/spherogram_src/links/reshetikhin_turaev/RT_network.py index f43f3a7..3643a9b 100644 --- a/spherogram_src/links/reshetikhin_turaev/RT_network.py +++ b/spherogram_src/links/reshetikhin_turaev/RT_network.py @@ -97,6 +97,12 @@ def __init__(self, tensors, T = None, network = None, rot_num = None, boundary = if e.index not in edge.keys(): edge[e.index] = e if e.sign == 1 else ~e + def __eq__(self, other): + if len(self.network) != 1 or len(other.network) != 1: + raise NotImplementedError('Equality is only implemented for contracted networks') + else: + return self.network[0][0] == other.network[0][0] + def optimal_contraction_sequence(self): try: import opt_einsum as oe @@ -344,7 +350,26 @@ def contract_sequence(self, seq, timed = False): def contract_all(self, timed = False): self._resolve_self_loops() - return self.contract_sequence(self.optimal_contraction_sequence(), timed = timed) + time = self.contract_sequence(self.optimal_contraction_sequence(), timed = timed) + + assert len(self.network) == 1 + + if self.boundary_labels: + desired_order = [] + for i in range(self.boundary[0]): + desired_order.append(~self.edge[self.boundary_labels[i]]) + for i in range(self.boundary[1]): + desired_order.append(self.edge[self.boundary_labels[self.boundary[0] + i]]) + + _, key = self.network[0] + reshape_indices = [] + for e in key: + reshape_indices.append(desired_order.index(e)) + + reshape_tensor = self.network[0][0].permute(reshape_indices) + self.network[0] = (reshape_tensor, tuple(desired_order)) + + return time def evaluate(self, timed = False): """ diff --git a/spherogram_src/links/reshetikhin_turaev/sparse_array.py b/spherogram_src/links/reshetikhin_turaev/sparse_array.py index 4139494..3c8ecf0 100644 --- a/spherogram_src/links/reshetikhin_turaev/sparse_array.py +++ b/spherogram_src/links/reshetikhin_turaev/sparse_array.py @@ -21,6 +21,13 @@ def __init__(self, shape, data=None, default=0): for k, v in data: self[k] = v + def __eq__(self, other): + if not isinstance(other, SparseArray): + return False + return (self._shape == other._shape and + self._default == other._default and + self._data == other._data) + def _key(self, index): if isinstance(index, tuple): return index From 760b7679e44e1640a1d90e09a6f03abc32d2d9d8 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Wed, 8 Jul 2026 17:00:55 -0500 Subject: [PATCH 34/53] Bug with rot_num fixed --- .../links/reshetikhin_turaev/RT_network.py | 19 ++++-- .../links/reshetikhin_turaev/sparse_array.py | 7 ++- spherogram_src/links/tangles.py | 62 ++++++++++++++++--- 3 files changed, 73 insertions(+), 15 deletions(-) diff --git a/spherogram_src/links/reshetikhin_turaev/RT_network.py b/spherogram_src/links/reshetikhin_turaev/RT_network.py index 3643a9b..bdad28c 100644 --- a/spherogram_src/links/reshetikhin_turaev/RT_network.py +++ b/spherogram_src/links/reshetikhin_turaev/RT_network.py @@ -47,6 +47,8 @@ def __init__(self, tensors, T = None, network = None, rot_num = None, boundary = self.boundary = T.boundary self.boundary_labels = T.strand_labels + self.idle_labels = set(self.boundary_labels) + self.edge = edge = dict() self.network = network = [] @@ -69,11 +71,17 @@ def __init__(self, tensors, T = None, network = None, rot_num = None, boundary = edge[labels[2]]) if tensors is not None: - network.append((tensors.R(c.sign), key)) + tensor = tensors.R(c.sign) + for i, e in enumerate(list(key[:2])): + if e.index in self.idle_labels and self.rot_num[e.index] != 0: + perm = list(range(i)) + [3] + list(range(i, 3)) + tensor = tensor.decorated_contract(tensors.h(0), {(i, 1): (1, tensors.h(self.rot_num[e.index]))}) + tensor = tensor.permute(perm) + + network.append((tensor, key)) else: network.append((None, key)) - self.idle_labels = set(self.boundary_labels) for arc in self.idle_labels: if arc not in edge.keys(): @@ -362,10 +370,9 @@ def contract_all(self, timed = False): desired_order.append(self.edge[self.boundary_labels[self.boundary[0] + i]]) _, key = self.network[0] - reshape_indices = [] - for e in key: - reshape_indices.append(desired_order.index(e)) - + key_pos = {e: i for i, e in enumerate(key)} + reshape_indices = [key_pos[e] for e in desired_order] + reshape_tensor = self.network[0][0].permute(reshape_indices) self.network[0] = (reshape_tensor, tuple(desired_order)) diff --git a/spherogram_src/links/reshetikhin_turaev/sparse_array.py b/spherogram_src/links/reshetikhin_turaev/sparse_array.py index 3c8ecf0..33c6e8e 100644 --- a/spherogram_src/links/reshetikhin_turaev/sparse_array.py +++ b/spherogram_src/links/reshetikhin_turaev/sparse_array.py @@ -398,9 +398,12 @@ def fixate(self, i, value): def permute(self, indices): """ - Permute self into the desired order. + Reorder axes using pull-style indices: indices[i] is the axis of self + that becomes axis i of the result. - A[i,j,k,l].permute([2,1,0,3]) -> A[k,j,i,l] + result[i0, i1, ...] = self[i_{indices[0]}, i_{indices[1]}, ...] + + Example: A.permute([2, 0, 1]) produces B where B[a,b,c] = A[b,c,a]. """ result_shape = [self._shape[i] for i in indices] result = SparseTensor(result_shape, default=self._default) diff --git a/spherogram_src/links/tangles.py b/spherogram_src/links/tangles.py index 9ccedd8..606d233 100644 --- a/spherogram_src/links/tangles.py +++ b/spherogram_src/links/tangles.py @@ -22,6 +22,7 @@ import pickle from collections import OrderedDict, Counter +from threading import local from .ordered_set import OrderedSet from .links import Crossing, Strand, Link, CrossingStrand, CrossingEntryPoint from .reshetikhin_turaev import RTNetwork @@ -504,7 +505,7 @@ def _crossings_from_PD_code(self, code, entry_points): if any(len(v) > 2 for v in gluings.values()): raise ValueError("PD code isn't consistent") - crossings = [Crossing(i) for i, d in enumerate(code)] + crossings = [Crossing(i) for i, _ in enumerate(code)] for item in gluings.values(): if len(item) > 1: @@ -601,20 +602,25 @@ def rot_num(self): n = len(self.crossings) ans = [0 for i in range(2 * n + self.boundary[0])] - front = [0] + front = [self.strand_labels[0]] + next_entry_id = 1 - entry_strands = set([cep.strand_label() for cep in self.entry_points()]) + #entry_strands = set([cep.strand_label() for cep in self.entry_points()]) exit_strands = set([cs.strand_label() for cs in self.exit_points()]) to_do = set([s for s in self.strand_labels] + [s for c in self.crossings for s in c.strand_labels]) - exit_strands def next_arc(): + nonlocal next_entry_id + inter = set(front) & to_do if inter: return min(inter) else: - arc = min(to_do) + assert next_entry_id < self.boundary[0] + arc = self.strand_labels[next_entry_id] front.append(arc) + next_entry_id += 1 return arc def entry_crossing(k): @@ -638,8 +644,8 @@ def entry_crossing(k): entry_arcs[0].rotate(2).strand_label(), \ ~entry_arcs[1].strand_label() else: - if left_label not in entry_strands: - ans[left_label] += 1 + #if left_label not in entry_strands: + ans[left_label] += 1 front[i:i+1] = ~left_label, \ entry_arcs[1].rotate(2).strand_label(), \ entry_arcs[0].rotate(2).strand_label() @@ -649,11 +655,53 @@ def entry_crossing(k): to_do.remove(k) - for s in self.strand_labels: + for s in exit_strands: assert ans[s] == 0 return ans + def flip(self): + """ + Given a Tangle, flip it over in 3D along the vertical axis. + + >>> RT = RationalTangle + >>> T = (RT(3, 4) + RT(1, 2)) * RT(-3, 2) + >>> E = (T + T).numerator_closure().exterior() + >>> F = (T + flip_tangle(T)).numerator_closure().exterior() + >>> E.isometry_signature(verified=True) == F.isometry_signature(verified=True) + False + + #TODO: update doctests + """ + cross_perm = (1, 0, 3, 2) + + boundary_perm = [self.boundary[0] - 1 - i for i in range(self.boundary[0])] + \ + [self.boundary[0] + self.boundary[1] - 1 - i for i in range(self.boundary[1])] + + crossings = [Crossing(i) for i, _ in enumerate(self.crossings)] + + old_to_index = {C: i for i, C in enumerate(self.crossings)} + + entry_points_dict = {} + + def old_to_new(crossing, strand): + C = crossings[old_to_index[crossing]] + return (C, cross_perm[strand]) + + # This glues everything twice, but... + for i, C_new in enumerate(crossings): + C_old = self.crossings[i] + for i in range(4): + D_old, j = C_old.adjacent[i] + + if isinstance(D_old, Strand): + _, k = D_old.adjacent[(j + 1) % 2] + entry_points_dict[boundary_perm[k]] = (C_new, cross_perm[i]) + else: + C_new[cross_perm[i]] = old_to_new(D_old, j) + + return Tangle(self.boundary, crossings, [entry_points_dict[i] for i in range(len(entry_points_dict))]) + def reshetikhin_turaev_network(self, tensors): return RTNetwork(tensors, T = self) From 2934f00cf01a6fc4c0a9bd26cccf4ba67bd230fb Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Thu, 9 Jul 2026 01:05:31 -0500 Subject: [PATCH 35/53] Fix bug with faces and strands in closed components --- spherogram_src/links/tangles.py | 48 +++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/spherogram_src/links/tangles.py b/spherogram_src/links/tangles.py index 606d233..fe89044 100644 --- a/spherogram_src/links/tangles.py +++ b/spherogram_src/links/tangles.py @@ -197,28 +197,29 @@ def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, self._build(start_orientations, component_starts) assert self.is_oriented(), 'Tangle is not oriented after build' - # Remove all Strands from crossings and components. + # Remove all Strands in crossings; erase them also from the components. # Note that this will not affect strands in boundary_strands for s in reversed(crossings): if isinstance(s, Strand): comp = self.components[s.strand_component] - - if isinstance(comp[0].crossing, Tangle): - for cep in reversed(comp): - if cep.crossing == s: - comp.remove(cep) - break - else: - raise RuntimeError(f"Component strand {s} not found in component {comp}") + for cep in reversed(comp): + if cep.crossing == s: + comp.remove(cep) + break + else: + raise RuntimeError(f"Component strand {s} not found in component {comp}") # Note that the components are always built following the orientation # hence below always insists that the comp_id is labeled on the entrance strand - if s.component_idx is not None: - comp_id = s.component_idx - if comp[1].crossing.component_idx is not None: - assert comp[1].crossing.component_idx == comp_id - else: - comp[1].crossing.component_idx = comp_id + if s.component_idx is not None: + assert isinstance(comp[0].crossing, Tangle), f'strands with component_idx should be in an unclosed component' + + comp_id = s.component_idx + if comp[1].crossing.component_idx is not None: + assert comp[1].crossing.component_idx == comp_id + else: + comp[1].crossing.component_idx = comp_id + if s.is_loop(): self.unlinked_unknot_components += 1 else: @@ -667,11 +668,9 @@ def flip(self): >>> RT = RationalTangle >>> T = (RT(3, 4) + RT(1, 2)) * RT(-3, 2) >>> E = (T + T).numerator_closure().exterior() - >>> F = (T + flip_tangle(T)).numerator_closure().exterior() + >>> F = (T + T.flip()).numerator_closure().exterior() >>> E.isometry_signature(verified=True) == F.isometry_signature(verified=True) False - - #TODO: update doctests """ cross_perm = (1, 0, 3, 2) @@ -1162,6 +1161,9 @@ def faces(self): similarly, if c is the tangle itself, it denots the corner as one stands at the j-th boundary entry and look *counterclockwisely*. + Boundary points of the tangle are seen as points with induced orientations + from the oriented strands of the tangle. + Alternatively, the sequence of CrossingStrands can be regarded as the *heads* of the oriented edges of the face. @@ -1182,14 +1184,20 @@ def faces(self): c, e = next.crossing, next.strand_index if isinstance(c, Tangle): if e == 0: - next = CrossingStrand(*c.adjacent[c.boundary[0]]) + if c.boundary[1] and c.boundary[0]: + next = CrossingStrand(*c.adjacent[c.boundary[0]]) + else: + next = CrossingStrand(*c.adjacent[c.boundary[0]-1]) elif e < c.boundary[0]: next = CrossingStrand(*c.adjacent[e-1]) elif e < c.boundary[0] + c.boundary[1] - 1: next = CrossingStrand(*c.adjacent[e+1]) else: assert e == c.boundary[0] + c.boundary[1] - 1 - next = CrossingStrand(*c.adjacent[c.boundary[0]-1]) + if c.boundary[0]: + next = CrossingStrand(*c.adjacent[c.boundary[0]-1]) + else: + next = CrossingStrand(*c.adjacent[c.boundary[0]]) else: next = next.next_corner() From aca0cfd7d1c8675eca66c48eef909f2fa96fdec2 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Thu, 9 Jul 2026 16:37:48 -0500 Subject: [PATCH 36/53] Make flip() preserve orientation and add doctests --- spherogram_src/links/invariants.py | 56 ++++++++++++++++++ spherogram_src/links/tangles.py | 94 ++++++++++++++++++++++++++---- 2 files changed, 138 insertions(+), 12 deletions(-) diff --git a/spherogram_src/links/invariants.py b/spherogram_src/links/invariants.py index d5fa9b8..fec6986 100644 --- a/spherogram_src/links/invariants.py +++ b/spherogram_src/links/invariants.py @@ -337,6 +337,44 @@ def colored_links_gould_polynomial(self, n, sage_output = _within_sage, sage_pol using DictLaurentPolynomial to reduce RAM consumption. The output, by default, follows whether in sage or not. + + >>> Link('3_1').colored_links_gould_polynomial(1) + t^2*q^2 - t*q^3 - t*q + 2*q^2 - t^-1*q^3 + 1 - t^-1*q + t^-2*q^2 + >>> Link('4_1').colored_links_gould_polynomial(1) + t^2 - 3*t*q + 2*q^2 - 3*t*q^-1 + 7 - 3*t^-1*q + 2*q^-2 - 3*t^-1*q^-1 + t^-2 + + Mirror image is equal to substituting q with q^-1: + + >>> Link('3_1').mirror().colored_links_gould_polynomial(1, sage_output = False).change_vars({'q': 'q^-1'}) == Link('3_1').colored_links_gould_polynomial(1, sage_output = False) + True + + The colored Links--Gould polynomial specializes to the square of the Alexander polynomial: + + >>> Link('3_1').colored_links_gould_polynomial(1, sage_output = False).change_vars({'q': '1'}) + t^2 - 2*t + 3 - 2*t^-1 + t^-2 + >>> Link('4_1).colored_links_gould_polynomial(1, sage_output = False).change_vars({'q': '1'}) + t^2 - 6*t + 11 - 6*t^-1 + t^-2 + + 1-colored Links--Gould polynomial is invariant under mutation: + + >>> Link('11n34').colored_links_gould_polynomial(1) == Link('11n42').colored_links_gould_polynomial(1) + True + + A mutation pair with the same 2-colored Links--Gould polynomial: + + >>> K1 = Link('12n364') + >>> K2 = Link('12n365').mirror() + >>> K1.colored_links_gould_polynomial(2) == K2.colored_links_gould_polynomial(2) + True + + Some higher colored values for the trefoil: + + >>> Link('3_1').colored_links_gould_polynomial(2) + -t^2*q^5 + t*q^6 + t^2*q^4 - t*q^5 + t*q^4 - 2*q^5 + t^-1*q^6 + t^2*q^2 - 2*t*q^3 + 2*q^4 - t^-1*q^5 + t^-1*q^4 - t^-2*q^5 - t*q + 2*q^2 - 2*t^-1*q^3 + t^-2*q^4 + 1 - t^-1*q + t^-2*q^2 + >>> Link('3_1').colored_links_gould_polynomial(3) + -t*q^27 + t^2*q^24 + t*q^25 - t^-1*q^27 - t^2*q^22 + t*q^23 + 2*q^24 + t^-1*q^25 - t^2*q^20 - 2*t*q^21 - 2*q^22 + t^-1*q^23 + t^-2*q^24 + t^2*q^18 + 2*t*q^19 - 2*q^20 - 2*t^-1*q^21 - t^-2*q^22 - t^2*q^16 + t*q^17 + 2*q^18 + 2*t^-1*q^19 - t^-2*q^20 - 2*t*q^15 - 2*q^16 + t^-1*q^17 + t^-2*q^18 + t^2*q^12 + t*q^13 - 2*t^-1*q^15 - t^-2*q^16 + 2*q^12 + t^-1*q^13 - 2*t*q^9 + t^-2*q^12 + t^2*q^6 - 2*t^-1*q^9 + 2*q^6 - t*q^3 + t^-2*q^6 - t^-1*q^3 + 1 + >>> Link('3_1').colored_links_gould_polynomial(4) + t*q^24 - t^2*q^22 - t*q^23 + t^2*q^21 - t*q^22 + t^-1*q^24 + t^2*q^20 - 2*q^22 - t^-1*q^23 + 2*t*q^20 + 2*q^21 - t^-1*q^22 - t^2*q^18 - t*q^19 + 2*q^20 - t^-2*q^22 - 2*t*q^18 + 2*t^-1*q^20 + t^-2*q^21 + t^2*q^16 + t*q^17 - 2*q^18 - t^-1*q^19 + t^-2*q^20 - t^2*q^15 + 2*t*q^16 - 2*t^-1*q^18 - t^2*q^14 + 2*q^16 + t^-1*q^17 - t^-2*q^18 - 2*t*q^14 - 2*q^15 + 2*t^-1*q^16 + t^2*q^12 + 2*t*q^13 - 2*q^14 + t^-2*q^16 - t^2*q^11 + t*q^12 - 2*t^-1*q^14 - t^-2*q^15 + 2*q^12 + 2*t^-1*q^13 - t^-2*q^14 - 2*t*q^10 - 2*q^11 + t^-1*q^12 + t^2*q^8 + t*q^9 + t^-2*q^12 - 2*t^-1*q^10 - t^-2*q^11 + 2*q^8 + t^-1*q^9 - 2*t*q^6 + t^2*q^4 + t^-2*q^8 - 2*t^-1*q^6 + 2*q^4 - t*q^2 + t^-2*q^4 - t^-1*q^2 + 1 """ from .reshetikhin_turaev import colored_links_gould_R_matrices, DictLaurentPolynomial @@ -360,6 +398,24 @@ def colored_jones_polynomial(self, n, sage_output = _within_sage, sage_polynomia """ Colored Jones polynomials are univariate, for whom sage's PuiseuxSeries has highly optimized multiplications, hence we default to use sage whenever possible. + + 1-colored Jones polynomial is equal to the usual Jones polynomial. + Here we follow the ordinary convention of variables for Jones polynomials, + instead of the squared q in jones_polynomial() + + >>> Link('3_1').colored_jones_polynomial(1) + -q^-4 + q^-3 + q^-1 + >>> Link('4_1').colored_jones_polynomial(1) + q^-2 - q^-1 + 1 - q + q^2 + + Some values of higher colored Jones polynomials for the trefoil: + + >>> Link('3_1').colored_jones_polynomial(2) + q^-11 - q^-10 - q^-9 + q^-8 - q^-7 + q^-5 + q^-2 + >>> Link('3_1').colored_jones_polynomial(3) + -q^-21 + q^-20 + q^-19 - q^-17 + q^-15 - q^-14 - q^-13 + q^-11 - q^-10 + q^-7 + q^-3 + >>> Link('3_1').colored_jones_polynomial(4) + q^-34 - q^-33 - q^-32 + 2*q^-29 - q^-28 + 2*q^-24 - q^-23 - q^-22 + q^-19 - q^-18 - q^-17 + q^-14 - q^-13 + q^-9 + q^-4 """ from .reshetikhin_turaev import colored_jones_R_matrices, prefactor_colored_jones, DictLaurentPolynomial diff --git a/spherogram_src/links/tangles.py b/spherogram_src/links/tangles.py index fe89044..c5e183a 100644 --- a/spherogram_src/links/tangles.py +++ b/spherogram_src/links/tangles.py @@ -596,6 +596,12 @@ def rot_num(self): >>> (T1|T2).rot_num() [0, 0, 0, 0, 1, -1, -1, 1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 1, 0] + + Entry strands may have nonzero rotation numbers: + + >>> T = Tangle((2, 2),[(9, 2, 10, 3), (1, 10, 2, 11), (6, 12, 7, 11), (12, 6, 13, 5), (3, 1, 4, 0), (4, 7, 5, 8)], [0, 9, 8, 13]) + >>> T.rot_num() + [0, 0, 0, -1, 0, 0, 0, -1, 0, 1, -1, 1, -1, 0] """ assert self.is_upward(), 'Tangle should be upward oriented in order to compute rotation numbers' assert self.boundary[0] == self.boundary[1] @@ -664,6 +670,7 @@ def entry_crossing(k): def flip(self): """ Given a Tangle, flip it over in 3D along the vertical axis. + Preserves the orientation of the original tangle. >>> RT = RationalTangle >>> T = (RT(3, 4) + RT(1, 2)) * RT(-3, 2) @@ -671,37 +678,76 @@ def flip(self): >>> F = (T + T.flip()).numerator_closure().exterior() >>> E.isometry_signature(verified=True) == F.isometry_signature(verified=True) False + >>> T.flip().flip().PD_code() == T.PD_code() + True + >>> rT = T.circulate_rotate(1) + >>> rT.boundary_signs + [-1, 1, -1, 1] + >>> rT.flip().boundary_signs + [1, -1, 1, -1] + + Some tests for corner cases: + + >>> T = Tangle((2,0), [], [0,0]) + >>> T.boundary_signs + [-1, 1] + >>> T.flip().boundary_signs + [1, -1] + >>> T = Tangle((0,2), [], [0,0]) + >>> T.boundary_signs + [-1, 1] + >>> T.flip().boundary_signs + [1, -1] """ cross_perm = (1, 0, 3, 2) boundary_perm = [self.boundary[0] - 1 - i for i in range(self.boundary[0])] + \ [self.boundary[0] + self.boundary[1] - 1 - i for i in range(self.boundary[1])] - - crossings = [Crossing(i) for i, _ in enumerate(self.crossings)] - old_to_index = {C: i for i, C in enumerate(self.crossings)} + crossings = [Crossing(i) for i, _ in enumerate(self.crossings)] + \ + [Strand(len(self.crossings) + i) for i, _ in enumerate(self.boundary_strands)] + + old_crossings = self.crossings + self.boundary_strands + old_to_index = {C: i for i, C in enumerate(old_crossings)} + + start_css = self._start_orientations() entry_points_dict = {} def old_to_new(crossing, strand): C = crossings[old_to_index[crossing]] - return (C, cross_perm[strand]) + if isinstance(crossing, Strand): + return (C, strand) + else: + return (C, cross_perm[strand]) # This glues everything twice, but... for i, C_new in enumerate(crossings): - C_old = self.crossings[i] - for i in range(4): + C_old = old_crossings[i] + for i in range(C_new._adjacent_len): D_old, j = C_old.adjacent[i] - if isinstance(D_old, Strand): - _, k = D_old.adjacent[(j + 1) % 2] - entry_points_dict[boundary_perm[k]] = (C_new, cross_perm[i]) + if isinstance(D_old, Tangle): + assert isinstance(C_new, Strand) + entry_points_dict[boundary_perm[j]] = (C_new, i) else: - C_new[cross_perm[i]] = old_to_new(D_old, j) + if isinstance(C_new, Strand): + C_new[i] = old_to_new(D_old, j) + else: + C_new[cross_perm[i]] = old_to_new(D_old, j) + + + new_start_css = [] + for css in start_css: + c, s = css + new_start_css.append(old_to_new(c, s)) - return Tangle(self.boundary, crossings, [entry_points_dict[i] for i in range(len(entry_points_dict))]) + return Tangle(self.boundary, crossings, [entry_points_dict[i] for i in range(len(entry_points_dict))], start_orientations=new_start_css) def reshetikhin_turaev_network(self, tensors): + """ + tensors should either be None or an instance of reshetikhin_turaev.RMatrix + """ return RTNetwork(tensors, T = self) def contraction_width(self, omit_idle_arcs = True): @@ -915,6 +961,7 @@ def copy(self): def rotate(self, s): """Rotate anticlockwise by s*90 degrees. This is only for (2,2) tangles. + Preserves orientation of the tangle. See ``Tangle.reshape()`` for a generalization to all tangle shapes.""" if self.boundary != (2, 2): @@ -945,6 +992,11 @@ def numerator_closure(self): A synonym for this is ``Tangle.bridge_closure()``. + >>> BraidTangle([2,-1,2],4).numerator_closure().colored_jones_polynomial(1) + -q^-4 + q^-3 + q^-1 + >>> BraidTangle([1,1,1]).rotate(1).numerator_closure().colored_jones_polynomial(1) + q + q^3 - q^4 + sage: BraidTangle([2,-1,2],4).numerator_closure().alexander_polynomial() t^2 - t + 1 sage: BraidTangle([1,1,1]).rotate(1).numerator_closure().alexander_polynomial() @@ -1012,6 +1064,7 @@ def reshape(self, boundary, displace=0): becomes the new lower-left strand). This is a generalization of ``Tangle.rotate()``. + Preserves the orientation of the tangle. >>> T = BraidTangle([1,2,1]) >>> T.PD_code() @@ -1052,6 +1105,7 @@ def reshape(self, boundary, displace=0): def circular_rotate(self, n): """ Rotate a tangle in a circular fashion clockwise, keeping the same boundary. + Preserves orientation of the tangle. This generalizes ``Tangle.rotate()``, and it is a mild specialization of ``Tangle.reshape()``. """ @@ -1240,6 +1294,18 @@ def split_tangle_diagram(self, destroy_original = False, check_planarity = False If check_planarity is True, return in addition if the boundary strands of the components are laid out in a planar manner with respect to each other. + + >>> RationalTangle(0,1).split_tangle_diagram() + [, + ] + + >>> Tangle(4, [(0, 2, 1, 3)], [0,2,4,5,3,1,4,5], label = 'C||').split_tangle_diagram() + [, + , + ] + + >>> Tangle(4, [(0, 2, 1, 3)], [0,4,2,5,3,1,4,5], check_planarity = False).split_tangle_diagram(check_planarity = True)[0] + False """ T = self.copy() if not destroy_original else self components = T.digraph().weak_components() @@ -1250,7 +1316,7 @@ def split_tangle_diagram(self, destroy_original = False, check_planarity = False for i, component in enumerate(components): boundaries = [] - crossings = [] # Strands included + crossings = [] # Strands will be included here for c in component: if isinstance(c, CrossingStrand): boundaries.append(c) @@ -1294,6 +1360,10 @@ def split_tangle_diagram(self, destroy_original = False, check_planarity = False return (True, ans) def is_planar(self): + """ + >>> Tangle(4, [(0, 2, 1, 3)], [2,0,4,5,3,1,4,5], check_planarity = False).is_planar() + False + """ G = self.digraph() if not G.is_weakly_connected(): boundary_planarity, components = self.split_tangle_diagram(destroy_original = False, check_planarity = True) From 97e48bf328c56599d7e4fc9ea17bffc91debf400 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Thu, 9 Jul 2026 16:55:55 -0500 Subject: [PATCH 37/53] Remove redundant import --- spherogram_src/links/tangles.py | 1 - 1 file changed, 1 deletion(-) diff --git a/spherogram_src/links/tangles.py b/spherogram_src/links/tangles.py index c5e183a..eeb1bde 100644 --- a/spherogram_src/links/tangles.py +++ b/spherogram_src/links/tangles.py @@ -22,7 +22,6 @@ import pickle from collections import OrderedDict, Counter -from threading import local from .ordered_set import OrderedSet from .links import Crossing, Strand, Link, CrossingStrand, CrossingEntryPoint from .reshetikhin_turaev import RTNetwork From 462a6e831862627d34174b3df636ac90a4bf7500 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Thu, 9 Jul 2026 17:09:17 -0500 Subject: [PATCH 38/53] Fix typo --- spherogram_src/links/tangles.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spherogram_src/links/tangles.py b/spherogram_src/links/tangles.py index eeb1bde..f7ad170 100644 --- a/spherogram_src/links/tangles.py +++ b/spherogram_src/links/tangles.py @@ -211,7 +211,7 @@ def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, # Note that the components are always built following the orientation # hence below always insists that the comp_id is labeled on the entrance strand if s.component_idx is not None: - assert isinstance(comp[0].crossing, Tangle), f'strands with component_idx should be in an unclosed component' + assert isinstance(comp[0].crossing, Tangle), f'Strands with component_idx should be in an unclosed component' comp_id = s.component_idx if comp[1].crossing.component_idx is not None: From de607a1e85be0ca0be788a8c95b28024746b9192 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Thu, 9 Jul 2026 17:36:54 -0500 Subject: [PATCH 39/53] Add caching for R-matrices of colored Jones polynomials --- spherogram_src/links/reshetikhin_turaev/R_matrices.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices.py b/spherogram_src/links/reshetikhin_turaev/R_matrices.py index fec86c9..b9c4ba6 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices.py +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices.py @@ -132,6 +132,10 @@ def colored_jones_R_matrices(n, sage_polynomials=False): if n < 0: raise NotImplementedError + key = (f'J{n}', sage_polynomials) + if key in _cache.keys(): + return _cache[key] + n = n + 1 q_actual = _q_pow(4) # q^1 @@ -192,7 +196,8 @@ def colored_jones_R_matrices(n, sage_polynomials=False): hp = SparseTensor((n, n), data={(i, i): _q_pow(4*i - 2*(n-1)).to_sage() for i in range(n)}) hn = SparseTensor((n, n), data={(i, i): _q_pow(2*(n-1) - 4*i).to_sage() for i in range(n)}) - return RMatrix(Rp, Rn, hp, hn) + _cache[key] = RMatrix(Rp, Rn, hp, hn) + return _cache[key] def prefactor_colored_jones(n, writhe, sage_polynomial = False): n = n + 1 From 518f497c11b5dc8b7ae5b254ca6da9bb5e038956 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Thu, 9 Jul 2026 17:54:06 -0500 Subject: [PATCH 40/53] Let contract_all return self. Add some docstrings. --- .../links/reshetikhin_turaev/RT_network.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/spherogram_src/links/reshetikhin_turaev/RT_network.py b/spherogram_src/links/reshetikhin_turaev/RT_network.py index bdad28c..b90f836 100644 --- a/spherogram_src/links/reshetikhin_turaev/RT_network.py +++ b/spherogram_src/links/reshetikhin_turaev/RT_network.py @@ -285,6 +285,9 @@ def contraction_sequence(self, omit_idle_arcs = True): return loops + contraction_seq, ans def contract_nodes(self, indices): + """ + This modifies self to avoid holding duplicate data in memory. + """ idx1, idx2 = indices tensor1, key1 = self.network[idx1] tensor2, key2 = self.network[idx2] @@ -357,6 +360,11 @@ def contract_sequence(self, seq, timed = False): return time_cost def contract_all(self, timed = False): + """ + Perform all possible contractions on self. + + Return modified self and time (None if not timed). + """ self._resolve_self_loops() time = self.contract_sequence(self.optimal_contraction_sequence(), timed = timed) @@ -376,12 +384,12 @@ def contract_all(self, timed = False): reshape_tensor = self.network[0][0].permute(reshape_indices) self.network[0] = (reshape_tensor, tuple(desired_order)) - return time + return (self, time) def evaluate(self, timed = False): """ - Fixate all idle labels at value 0, obtaining a new RTNework with (0,0) boundary, - contract all and return the product of all values of the resulting tensors. + Fixate all idle labels at value 0, obtaining a new RTNework with (0,0) boundary (without modifying self), + contract_all on the new RTNetwork and return the product of all values of the resulting tensors. """ assert self.boundary == (1,1) @@ -409,7 +417,7 @@ def evaluate(self, timed = False): boundary=(0, 0), boundary_labels=[] ) - time = reduced.contract_all(timed = timed) + time = reduced.contract_all(timed = timed)[1] result = prefactor for tensor, _ in reduced.network: From ebe58e310a3eb836e16acafa5bb1652e5ac5d650 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Thu, 9 Jul 2026 18:12:08 -0500 Subject: [PATCH 41/53] Add one more doctest for faces --- spherogram_src/links/tangles.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spherogram_src/links/tangles.py b/spherogram_src/links/tangles.py index f7ad170..bb3cf2d 100644 --- a/spherogram_src/links/tangles.py +++ b/spherogram_src/links/tangles.py @@ -1220,6 +1220,8 @@ def faces(self): Alternatively, the sequence of CrossingStrands can be regarded as the *heads* of the oriented edges of the face. + >>> len(snappy.Tangle((2,0),[],[0,0]).faces()) + 2 >>> len(IdentityBraid(2).faces()) 3 >>> len(BraidTangle([1,2,1]).faces()) From 4dd346c64bc8335e19b07334d9b9c368050645e3 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Thu, 9 Jul 2026 18:23:20 -0500 Subject: [PATCH 42/53] Fix doctests --- spherogram_src/links/invariants.py | 2 +- spherogram_src/links/tangles.py | 17 +++++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/spherogram_src/links/invariants.py b/spherogram_src/links/invariants.py index fec6986..a5b4334 100644 --- a/spherogram_src/links/invariants.py +++ b/spherogram_src/links/invariants.py @@ -352,7 +352,7 @@ def colored_links_gould_polynomial(self, n, sage_output = _within_sage, sage_pol >>> Link('3_1').colored_links_gould_polynomial(1, sage_output = False).change_vars({'q': '1'}) t^2 - 2*t + 3 - 2*t^-1 + t^-2 - >>> Link('4_1).colored_links_gould_polynomial(1, sage_output = False).change_vars({'q': '1'}) + >>> Link('4_1').colored_links_gould_polynomial(1, sage_output = False).change_vars({'q': '1'}) t^2 - 6*t + 11 - 6*t^-1 + t^-2 1-colored Links--Gould polynomial is invariant under mutation: diff --git a/spherogram_src/links/tangles.py b/spherogram_src/links/tangles.py index bb3cf2d..f04ea49 100644 --- a/spherogram_src/links/tangles.py +++ b/spherogram_src/links/tangles.py @@ -679,7 +679,7 @@ def flip(self): False >>> T.flip().flip().PD_code() == T.PD_code() True - >>> rT = T.circulate_rotate(1) + >>> rT = T.circular_rotate(1) >>> rT.boundary_signs [-1, 1, -1, 1] >>> rT.flip().boundary_signs @@ -691,7 +691,7 @@ def flip(self): >>> T.boundary_signs [-1, 1] >>> T.flip().boundary_signs - [1, -1] + [1, -1] >>> T = Tangle((0,2), [], [0,0]) >>> T.boundary_signs [-1, 1] @@ -1220,7 +1220,7 @@ def faces(self): Alternatively, the sequence of CrossingStrands can be regarded as the *heads* of the oriented edges of the face. - >>> len(snappy.Tangle((2,0),[],[0,0]).faces()) + >>> len(Tangle((2,0),[],[0,0]).faces()) 2 >>> len(IdentityBraid(2).faces()) 3 @@ -1296,14 +1296,11 @@ def split_tangle_diagram(self, destroy_original = False, check_planarity = False If check_planarity is True, return in addition if the boundary strands of the components are laid out in a planar manner with respect to each other. - >>> RationalTangle(0,1).split_tangle_diagram() - [, - ] + >>> len(RationalTangle(0,1).split_tangle_diagram()) + 2 - >>> Tangle(4, [(0, 2, 1, 3)], [0,2,4,5,3,1,4,5], label = 'C||').split_tangle_diagram() - [, - , - ] + >>> len(Tangle(4, [(0, 2, 1, 3)], [0,2,4,5,3,1,4,5], label = 'C||').split_tangle_diagram()) + 3 >>> Tangle(4, [(0, 2, 1, 3)], [0,4,2,5,3,1,4,5], check_planarity = False).split_tangle_diagram(check_planarity = True)[0] False From 9051046b67a8af4709e0aac2e0bc71a127801c12 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Thu, 9 Jul 2026 18:39:31 -0500 Subject: [PATCH 43/53] Add one more doctest for colored_jones_polynomial --- spherogram_src/links/invariants.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spherogram_src/links/invariants.py b/spherogram_src/links/invariants.py index a5b4334..a248b0f 100644 --- a/spherogram_src/links/invariants.py +++ b/spherogram_src/links/invariants.py @@ -407,6 +407,8 @@ def colored_jones_polynomial(self, n, sage_output = _within_sage, sage_polynomia -q^-4 + q^-3 + q^-1 >>> Link('4_1').colored_jones_polynomial(1) q^-2 - q^-1 + 1 - q + q^2 + >>> Link('L2a1').colored_jones_polynomial(1) + q^(-5/2) + q^(-1/2) Some values of higher colored Jones polynomials for the trefoil: From f131e59ee4a4667b1c305da809b54dd7d1818335 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Thu, 9 Jul 2026 18:46:40 -0500 Subject: [PATCH 44/53] Remove doctests dependent on snappy --- spherogram_src/links/tangles.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/spherogram_src/links/tangles.py b/spherogram_src/links/tangles.py index f04ea49..b29ba07 100644 --- a/spherogram_src/links/tangles.py +++ b/spherogram_src/links/tangles.py @@ -673,12 +673,14 @@ def flip(self): >>> RT = RationalTangle >>> T = (RT(3, 4) + RT(1, 2)) * RT(-3, 2) - >>> E = (T + T).numerator_closure().exterior() - >>> F = (T + T.flip()).numerator_closure().exterior() - >>> E.isometry_signature(verified=True) == F.isometry_signature(verified=True) - False + >>> T.PD_code() + ((2, 2), [(18, 8, 19, 7), (6, 16, 7, 15), (14, 6, 15, 5), (4, 14, 5, 19), (12, 17, 13, 18), (16, 11, 17, 12), (3, 1, 4, 0), (1, 10, 2, 11), (9, 2, 10, 3)], [0, 9, 8, 13]) + >>> fT = T.flip() + >>> fT.PD_code() + ((2, 2), [(12, 16, 13, 15), (18, 12, 19, 11), (10, 18, 11, 17), (16, 10, 17, 9), (14, 3, 15, 4), (2, 19, 3, 14), (5, 9, 6, 8), (1, 6, 2, 7), (7, 0, 8, 1)], [0, 5, 4, 13]) >>> T.flip().flip().PD_code() == T.PD_code() True + >>> rT = T.circular_rotate(1) >>> rT.boundary_signs [-1, 1, -1, 1] From 8433294d6fd1c94f066c1b3c02db2957ff8696c7 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Thu, 9 Jul 2026 18:52:59 -0500 Subject: [PATCH 45/53] Try fix issue with ZZ in pure python --- .../links/reshetikhin_turaev/dict_laurent_polynomial.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py index 10b4bb6..0c3e6b2 100644 --- a/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py +++ b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py @@ -4,12 +4,12 @@ from sage.all import PuiseuxSeriesRing, LaurentPolynomialRing, ZZ @sage_method -def laurent_poly_from_dict(dict, vars, F = ZZ): +def laurent_poly_from_dict(dict, vars, F): L = LaurentPolynomialRing(F, vars) return L(dict) @sage_method -def puiseux_series_from_dict(poly_dict, var, F=ZZ): +def puiseux_series_from_dict(poly_dict, var, F): """ Build a Sage Puiseux series from a poly_dict and a single LaurentVariable. Key k represents var^(k / var.denominator). @@ -121,9 +121,9 @@ def to_checked(self): @sage_method def to_sage(self): if len(self.vars) == 1: - return puiseux_series_from_dict(self.poly_dict, self.vars[0]) + return puiseux_series_from_dict(self.poly_dict, self.vars[0], F = ZZ) elif all(var.denominator == 1 for var in self.vars): - return laurent_poly_from_dict(self.poly_dict, [var.name for var in self.vars]) + return laurent_poly_from_dict(self.poly_dict, [var.name for var in self.vars], F = ZZ) else: raise NotImplementedError('Multi-variable Puiseux conversion to Sage is not supported.') From 0035161b93b0c582ec072741b7c244fedc764781 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Sun, 12 Jul 2026 23:32:37 -0400 Subject: [PATCH 46/53] Update modules for doctest --- spherogram_src/test.py | 1 + 1 file changed, 1 insertion(+) diff --git a/spherogram_src/test.py b/spherogram_src/test.py index 18c7d25..32b581d 100644 --- a/spherogram_src/test.py +++ b/spherogram_src/test.py @@ -43,6 +43,7 @@ spherogram.links.bands.core, spherogram.links.bands.search, spherogram.links.bands.regression, + spherogram.links.reshetikhin_turaev.dict_laurent_polynomial ] From 74bdfc091d380a14722868d3b56ae052811c3251 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Sun, 12 Jul 2026 23:35:46 -0400 Subject: [PATCH 47/53] Fix doctests for from_sage --- .../reshetikhin_turaev/dict_laurent_polynomial.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py index 0c3e6b2..ee14128 100644 --- a/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py +++ b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py @@ -139,14 +139,14 @@ def from_sage(cls, p, var_names=None): For PuiseuxSeries the conversion relies on the internal _l (Laurent series) and _e (ramification index) attributes of Sage's implementation. - >>> p = DictLaurentPolynomial.from_str('q^2 + 3 - q^-1', ['q']) - >>> DictLaurentPolynomial.from_sage(p.to_sage()) == p + sage: p = DictLaurentPolynomial.from_str('q^2 + 3 - q^-1', ['q']) + sage: DictLaurentPolynomial.from_sage(p.to_sage()) == p True - >>> r = DictLaurentPolynomial.from_str('q^2*t - 1', ['q', 't']) - >>> DictLaurentPolynomial.from_sage(r.to_sage()) == r + sage: r = DictLaurentPolynomial.from_str('q^2*t - 1', ['q', 't']) + sage: DictLaurentPolynomial.from_sage(r.to_sage()) == r True - >>> s = DictLaurentPolynomial.from_str('q^(1/2) + q^(-1/4)', ['q']) - >>> DictLaurentPolynomial.from_sage(s.to_sage()) == s + sage: s = DictLaurentPolynomial.from_str('q^(1/2) + q^(-1/4)', ['q']) + sage: DictLaurentPolynomial.from_sage(s.to_sage()) == s True """ from sage.rings.puiseux_series_ring_element import PuiseuxSeries From d06a6079983d7a7933c39d13545537aadd5196ac Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Sun, 19 Jul 2026 02:54:58 -0500 Subject: [PATCH 48/53] Formatting invariants.py using Black Formatter --- spherogram_src/links/invariants.py | 242 ++++++++++++++++++----------- 1 file changed, 152 insertions(+), 90 deletions(-) diff --git a/spherogram_src/links/invariants.py b/spherogram_src/links/invariants.py index a248b0f..94a998c 100644 --- a/spherogram_src/links/invariants.py +++ b/spherogram_src/links/invariants.py @@ -18,6 +18,7 @@ from sage.rings.rational_field import QQ from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing from sage.quadratic_forms.quadratic_form import QuadraticForm + try: from sage.knots.knot import Knot as SageKnot from sage.knots.link import Link as SageLink @@ -35,7 +36,7 @@ def normalize_alex_poly(p, t): polynomial. """ if len(t) == 1: - p = p * (t[0]**(-min(p.exponents()))) + p = p * (t[0] ** (-min(p.exponents()))) if p.coefficients()[-1] < 0: p = -p p, e = p.polynomial_construction() @@ -45,9 +46,9 @@ def normalize_alex_poly(p, t): max_degree = max(sum(x) for x in p.exponents()) highest_monomial_exps = [x for x in p.exponents() if sum(x) == max_degree] leading_exponents = max(highest_monomial_exps) - leading_monomial = functools.reduce(lambda x, y: x * y, - [t[i]**(leading_exponents[i]) - for i in range(len(t))]) + leading_monomial = functools.reduce( + lambda x, y: x * y, [t[i] ** (leading_exponents[i]) for i in range(len(t))] + ) l = p.monomial_coefficient(leading_monomial) if l < 0: @@ -55,7 +56,7 @@ def normalize_alex_poly(p, t): for i, ti in enumerate(t): min_exp = min(x[i] for x in p.exponents()) - p = p * (ti**(-min_exp)) + p = p * (ti ** (-min_exp)) R = p.parent() p = R.polynomial_ring()(p) @@ -88,10 +89,13 @@ def sage_braid_as_int_word(braid): see the documentation for the "sage_link" method for details. """ + class Link(links_base.Link): __doc__ = links_base.Link.__doc__ + extra_docstring - def __init__(self, crossings=None, braid_closure=None, check_planarity=True, build=True): + def __init__( + self, crossings=None, braid_closure=None, check_planarity=True, build=True + ): if _within_sage: if isinstance(crossings, Braid): assert braid_closure is None @@ -115,12 +119,13 @@ def linking_matrix(self): Returns a linking matrix, in which the (i,j)th component is the linking number of the ith and jth link components. """ - mat = [[0 for i in range(len(self.link_components))] - for j in range(len(self.link_components))] + mat = [ + [0 for i in range(len(self.link_components))] + for j in range(len(self.link_components)) + ] for n1, comp1 in enumerate(self.link_components): for n2, comp2 in enumerate(self.link_components): - tally = [[0 for m in range(len(self.crossings))] - for n in range(2)] + tally = [[0 for m in range(len(self.crossings))] for n in range(2)] if comp1 != comp2: for i, c in enumerate(self.crossings): for x1 in comp1: @@ -130,7 +135,7 @@ def linking_matrix(self): if x2[0] == c: tally[1][i] += 1 for k, c in enumerate(self.crossings): - if (tally[0][k] == 1 and tally[1][k] == 1): + if tally[0][k] == 1 and tally[1][k] == 1: mat[n1][n2] += 0.5 * (c.sign) mat[n1][n2] = int(mat[n1][n2]) return mat @@ -198,11 +203,11 @@ def alexander_matrix(self, mv=True): G = self.knot_group() num_gens = len(G.gens()) - L_g = LaurentPolynomialRing(QQ, [f'g{i+1}' for i in range(num_gens)]) + L_g = LaurentPolynomialRing(QQ, [f"g{i+1}" for i in range(num_gens)]) g = list(L_g.gens()) if mv: - L_t = LaurentPolynomialRing(QQ, [f't{i+1}' for i in range(comp)]) + L_t = LaurentPolynomialRing(QQ, [f"t{i+1}" for i in range(comp)]) t = list(L_t.gens()) # determine the component to which each variable corresponds @@ -211,7 +216,7 @@ def alexander_matrix(self, mv=True): g[i] = t[gci] else: - L_t = LaurentPolynomialRing(QQ, 't') + L_t = LaurentPolynomialRing(QQ, "t") t = L_t.gen() g = [t] * len(g) @@ -224,14 +229,17 @@ def alexander_poly(self, *args, **kwargs): """ Please use the "alexander_polynomial" method instead. """ - if 'alexander_poly' not in deprecation_warnings_issued: - deprecation_warnings_issued.add('alexander_poly') - print('Deprecation Warning: use "alexander_polynomial" instead of "alexander_poly".') + if "alexander_poly" not in deprecation_warnings_issued: + deprecation_warnings_issued.add("alexander_poly") + print( + 'Deprecation Warning: use "alexander_polynomial" instead of "alexander_poly".' + ) return self.alexander_polynomial(*args, **kwargs) @sage_method - def alexander_polynomial(self, multivar=True, v='no', method='default', - norm=True, factored=False): + def alexander_polynomial( + self, multivar=True, v="no", method="default", norm=True, factored=False + ): """ Calculates the Alexander polynomial of the link. @@ -259,22 +267,25 @@ def alexander_polynomial(self, multivar=True, v='no', method='default', # sign normalization still missing, but when "norm=True" the # leading coefficient with respect to the first variable is made # positive. - if method == 'snappy': + if method == "snappy": try: return self.exterior().alexander_polynomial() except ImportError: - raise RuntimeError('the method "snappy" for ' - 'alexander_polynomial requires SnapPy') + raise RuntimeError( + 'the method "snappy" for ' "alexander_polynomial requires SnapPy" + ) # We do any available Type I and II Reidemeister moves as the # functions we call assume that none are available. from . import simplify + if simplify.has_reidemeister_I_or_II(self): L = self.copy() - L.simplify('basic') - return L.alexander_polynomial(multivar=multivar, v=v, method=method, - norm=norm, factored=factored) + L.simplify("basic") + return L.alexander_polynomial( + multivar=multivar, v=v, method=method, norm=norm, factored=factored + ) # We have to deal with the special case of unknotted and # unlinked components. @@ -285,10 +296,10 @@ def alexander_polynomial(self, multivar=True, v='no', method='default', multivar = False if multivar: - L = LaurentPolynomialRing(QQ, [f't{i+1}' for i in range(comp + nugatory)]) + L = LaurentPolynomialRing(QQ, [f"t{i+1}" for i in range(comp + nugatory)]) t = list(L.gens()) else: - L = LaurentPolynomialRing(QQ, 't') + L = LaurentPolynomialRing(QQ, "t") t = [L.gen()] R = L.polynomial_ring() if norm else L @@ -297,10 +308,10 @@ def alexander_polynomial(self, multivar=True, v='no', method='default', return R(p) # If single variable, use the super-fast method of Bar-Natan. - if comp == 1 and method == 'default' and norm: + if comp == 1 and method == "default" and norm: p = alexander.alexander(self) else: # Use a simple method based on the Wirtinger presentation. - if method not in ['default', 'wirtinger']: + if method not in ["default", "wirtinger"]: raise ValueError("Available methods are 'default' and 'wirtinger'") M = self.alexander_matrix(mv=multivar) @@ -312,7 +323,7 @@ def alexander_polynomial(self, multivar=True, v='no', method='default', else: k = n - 1 - subMatrix = C[0: k, 0: k] + subMatrix = C[0:k, 0:k] p = subMatrix.determinant() if p == 0: return R(0) @@ -324,19 +335,21 @@ def alexander_polynomial(self, multivar=True, v='no', method='default', if norm: p = normalize_alex_poly(p, t) - if v != 'no': + if v != "no": return p(*v) if multivar and factored: # it's easier to view this way return p.factor() return p - - def colored_links_gould_polynomial(self, n, sage_output = _within_sage, sage_polynomials = False, timed = False): + + def colored_links_gould_polynomial( + self, n, sage_output=_within_sage, sage_polynomials=False, timed=False + ): """ Colored Links--Gould polynomials are bivariate, hence we default to - using DictLaurentPolynomial to reduce RAM consumption. - - The output, by default, follows whether in sage or not. + using DictLaurentPolynomial to reduce RAM consumption. + + The output, by default, follows whether in sage or not. >>> Link('3_1').colored_links_gould_polynomial(1) t^2*q^2 - t*q^3 - t*q + 2*q^2 - t^-1*q^3 + 1 - t^-1*q + t^-2*q^2 @@ -361,7 +374,7 @@ def colored_links_gould_polynomial(self, n, sage_output = _within_sage, sage_pol True A mutation pair with the same 2-colored Links--Gould polynomial: - + >>> K1 = Link('12n364') >>> K2 = Link('12n365').mirror() >>> K1.colored_links_gould_polynomial(2) == K2.colored_links_gould_polynomial(2) @@ -376,9 +389,18 @@ def colored_links_gould_polynomial(self, n, sage_output = _within_sage, sage_pol >>> Link('3_1').colored_links_gould_polynomial(4) t*q^24 - t^2*q^22 - t*q^23 + t^2*q^21 - t*q^22 + t^-1*q^24 + t^2*q^20 - 2*q^22 - t^-1*q^23 + 2*t*q^20 + 2*q^21 - t^-1*q^22 - t^2*q^18 - t*q^19 + 2*q^20 - t^-2*q^22 - 2*t*q^18 + 2*t^-1*q^20 + t^-2*q^21 + t^2*q^16 + t*q^17 - 2*q^18 - t^-1*q^19 + t^-2*q^20 - t^2*q^15 + 2*t*q^16 - 2*t^-1*q^18 - t^2*q^14 + 2*q^16 + t^-1*q^17 - t^-2*q^18 - 2*t*q^14 - 2*q^15 + 2*t^-1*q^16 + t^2*q^12 + 2*t*q^13 - 2*q^14 + t^-2*q^16 - t^2*q^11 + t*q^12 - 2*t^-1*q^14 - t^-2*q^15 + 2*q^12 + 2*t^-1*q^13 - t^-2*q^14 - 2*t*q^10 - 2*q^11 + t^-1*q^12 + t^2*q^8 + t*q^9 + t^-2*q^12 - 2*t^-1*q^10 - t^-2*q^11 + 2*q^8 + t^-1*q^9 - 2*t*q^6 + t^2*q^4 + t^-2*q^8 - 2*t^-1*q^6 + 2*q^4 - t*q^2 + t^-2*q^4 - t^-1*q^2 + 1 """ - from .reshetikhin_turaev import colored_links_gould_R_matrices, DictLaurentPolynomial - - ans = self.min_long_diagram().reshetikhin_turaev_network(colored_links_gould_R_matrices(n, sage_polynomials=sage_polynomials)).evaluate(timed = timed) + from .reshetikhin_turaev import ( + colored_links_gould_R_matrices, + DictLaurentPolynomial, + ) + + ans = ( + self.min_long_diagram() + .reshetikhin_turaev_network( + colored_links_gould_R_matrices(n, sage_polynomials=sage_polynomials) + ) + .evaluate(timed=timed) + ) if sage_output: if not sage_polynomials: @@ -394,7 +416,9 @@ def colored_links_gould_polynomial(self, n, sage_output = _within_sage, sage_pol else: return ans[0] - def colored_jones_polynomial(self, n, sage_output = _within_sage, sage_polynomials = _within_sage, timed = False): + def colored_jones_polynomial( + self, n, sage_output=_within_sage, sage_polynomials=_within_sage, timed=False + ): """ Colored Jones polynomials are univariate, for whom sage's PuiseuxSeries has highly optimized multiplications, hence we default to use sage whenever possible. @@ -419,10 +443,26 @@ def colored_jones_polynomial(self, n, sage_output = _within_sage, sage_polynomia >>> Link('3_1').colored_jones_polynomial(4) q^-34 - q^-33 - q^-32 + 2*q^-29 - q^-28 + 2*q^-24 - q^-23 - q^-22 + q^-19 - q^-18 - q^-17 + q^-14 - q^-13 + q^-9 + q^-4 """ - from .reshetikhin_turaev import colored_jones_R_matrices, prefactor_colored_jones, DictLaurentPolynomial - - ans = self.min_long_diagram().reshetikhin_turaev_network(colored_jones_R_matrices(n, sage_polynomials=sage_polynomials)).evaluate(timed = timed) - ans = (ans[0] * prefactor_colored_jones(n, self.writhe(), sage_polynomial=sage_polynomials), ans[1]) + from .reshetikhin_turaev import ( + colored_jones_R_matrices, + prefactor_colored_jones, + DictLaurentPolynomial, + ) + + ans = ( + self.min_long_diagram() + .reshetikhin_turaev_network( + colored_jones_R_matrices(n, sage_polynomials=sage_polynomials) + ) + .evaluate(timed=timed) + ) + ans = ( + ans[0] + * prefactor_colored_jones( + n, self.writhe(), sage_polynomial=sage_polynomials + ), + ans[1], + ) if sage_output: if not sage_polynomials: @@ -508,8 +548,9 @@ def knot_floer_homology(self, prime=2, complex=False): 1 """ import knot_floer_homology + if len(self.link_components) + self.unlinked_unknot_components > 1: - raise ValueError('Only works for knots, this has more components') + raise ValueError("Only works for knots, this has more components") if len(self.link_components) == 0 and self.unlinked_unknot_components == 1: return Link(braid_closure=[1, 1, -1]).knot_floer_homology() return knot_floer_homology.pd_to_hfk(self, prime=prime, complex=complex) @@ -550,8 +591,9 @@ def black_graph(self): for x in range(len(self.crossings)): total = {self.crossings[x][i] for i in range(4)} if total.issubset(s): - coords.append((tuple(faces[i]), tuple(faces[j]), - self.crossings[x])) # label by the crossing. + coords.append( + (tuple(faces[i]), tuple(faces[j]), self.crossings[x]) + ) # label by the crossing. G = graph.Graph(coords, multiedges=True) component = G.connected_components(sort=False)[1] @@ -595,24 +637,31 @@ def white_graph(self): expected way. """ # Map corners (i.e. CrossingStrands) to faces. - face_of = {corner: n for n, face in enumerate(self.faces()) - for corner in face} + face_of = {corner: n for n, face in enumerate(self.faces()) for corner in face} # Create the edges, labeled with crossing and sign. edges = [] for c in self.crossings: - edges.append((face_of[CrossingStrand(c, 0)], - face_of[CrossingStrand(c, 2)], - {'crossing': c, 'sign': 1})) - edges.append((face_of[CrossingStrand(c, 1)], - face_of[CrossingStrand(c, 3)], - {'crossing': c, 'sign': -1})) + edges.append( + ( + face_of[CrossingStrand(c, 0)], + face_of[CrossingStrand(c, 2)], + {"crossing": c, "sign": 1}, + ) + ) + edges.append( + ( + face_of[CrossingStrand(c, 1)], + face_of[CrossingStrand(c, 3)], + {"crossing": c, "sign": -1}, + ) + ) # Build the graph. G = graph.Graph(edges, multiedges=True) components = G.connected_components(sort=True) if len(components) > 2: - raise ValueError('The link diagram is split.') + raise ValueError("The link diagram is split.") return G.subgraph(components[1]) @sage_method @@ -632,7 +681,7 @@ def goeritz_matrix(self, return_graph=False): vertex = {v: n for n, v in enumerate(V)} for e in G.edges(sort=False): i, j = vertex[e[0]], vertex[e[1]] - m[(i, j)] = m[(j, i)] = m[(i, j)] + e[2]['sign'] + m[(i, j)] = m[(j, i)] = m[(i, j)] + e[2]["sign"] for i in range(N): m[(i, i)] = -sum(m.column(i)) m = m.delete_rows([0]).delete_columns([0]) @@ -669,8 +718,11 @@ def signature(self, new_convention=True): return sum([L.signature() for L in self.split_link_diagram()]) m, G = self.goeritz_matrix(return_graph=True) - correction = sum(e['sign'] for _, _, e in G.edges(sort=False) - if e['sign'] == e['crossing'].sign) + correction = sum( + e["sign"] + for _, _, e in G.edges(sort=False) + if e["sign"] == e["crossing"].sign + ) ans = QuadraticForm(QQ, m).signature() + correction if new_convention: ans = -ans @@ -694,7 +746,7 @@ def _colorability_matrix(self): return m @sage_method - def determinant(self, method='goeritz'): + def determinant(self, method="goeritz"): """ Returns the determinant of the link, a non-negative integer. @@ -706,7 +758,7 @@ def determinant(self, method='goeritz'): sage: K.determinant() 5 """ - if method == 'color': + if method == "color": M = self._colorability_matrix() size = len(self.crossings) - 1 N = matrix(size, size) @@ -714,12 +766,12 @@ def determinant(self, method='goeritz'): for j in range(size): N[(i, j)] = M[(i + 1, j + 1)] return abs(N.determinant()) - if method == 'goeritz': + if method == "goeritz": return abs(self.goeritz_matrix().determinant()) return abs(self.alexander_polynomial(multivar=False, v=[-1], norm=False)) @sage_method - def morse_number(self, solver='GLPK'): + def morse_number(self, solver="GLPK"): """ The *Morse number* of a planar link diagram D is @@ -738,6 +790,7 @@ def morse_number(self, solver='GLPK'): 3 """ from . import morse + return morse.morse_via_LP(self, solver)[0] @sage_method @@ -757,6 +810,7 @@ def morse_diagram(self): 64 """ from . import morse + return morse.MorseLinkDiagram(self) @sage_method @@ -805,7 +859,7 @@ def jones_polynomial(self, variable=None, new_convention=True): J = jones.jones_polynomial(self, normalized=True) R = J.parent() q = R.gen() - terms = [J[e] * q**(e // 2) for e in J.exponents()] + terms = [J[e] * q ** (e // 2) for e in J.exponents()] J = sum(terms, R(0)) if variable is not None: @@ -832,12 +886,13 @@ def seifert_matrix(self): after first making the link isotopic to a braid closure. """ from . import seifert + ans = seifert.seifert_matrix(self) if _within_sage: ans = matrix(ans) return ans - def bridge_upper_bound(self, method='plain sphere', return_meridians=False): + def bridge_upper_bound(self, method="plain sphere", return_meridians=False): """ Computes an upper bound on the bridge number of the given link. By default, it computes the plain sphere number rho(D) of the @@ -866,6 +921,7 @@ def bridge_upper_bound(self, method='plain sphere', return_meridians=False): https://dx.doi.org/10.4310/CAG.2020.v28.n2.a2 """ from . import bridge_bound + return bridge_bound.bridge_upper_bound(self, method, return_meridians) def braid_word(self, as_sage_braid=False): @@ -892,10 +948,11 @@ def braid_word(self, as_sage_braid=False): braids, a new algorithm". """ from . import seifert + word = seifert.braid_word(self) if as_sage_braid: if not _within_sage: - raise ValueError('Requested Sage braid outside of Sage.') + raise ValueError("Requested Sage braid outside of Sage.") n = max(abs(a) for a in word) + 1 word = BraidGroup(n)(word) return word @@ -934,7 +991,7 @@ def sage_link(self): """ if SageKnot is None: - raise ValueError('Your SageMath does not seem to have a native link type') + raise ValueError("Your SageMath does not seem to have a native link type") sage_type = SageKnot if len(self.link_components) == 1 else SageLink # Sage's PD_code lists strands *clockwise* not our # *anticlockwise* prior to Sage 10.1. @@ -955,14 +1012,16 @@ def _sage_(self): return self.sage_link() @sage_method - def ribbon_concordant_links(self, - max_bands=1, - max_twists=2, - max_band_len=None, - paths='shortest', - filter_for_plausibly_slice=True, - certificates=False, - print_progress=False): + def ribbon_concordant_links( + self, + max_bands=1, + max_twists=2, + max_band_len=None, + paths="shortest", + filter_for_plausibly_slice=True, + certificates=False, + print_progress=False, + ): """ Given a link L_0, generate ribbon concordant links L_i. Here, each L_i is obtained from L_0 by adding bands and deleting any @@ -1012,16 +1071,18 @@ def ribbon_concordant_links(self, """ from .bands.search import ribbon_concordant_links - return ribbon_concordant_links(self, - max_bands=max_bands, - max_twists=max_twists, - max_band_len=max_band_len, - paths=paths, - filter_for_plausibly_slice=filter_for_plausibly_slice, - certify=certificates, - print_progress=print_progress, - stop_at_unlink=filter_for_plausibly_slice, - use_ribbon_link_cache=filter_for_plausibly_slice) + return ribbon_concordant_links( + self, + max_bands=max_bands, + max_twists=max_twists, + max_band_len=max_band_len, + paths=paths, + filter_for_plausibly_slice=filter_for_plausibly_slice, + certify=certificates, + print_progress=print_progress, + stop_at_unlink=filter_for_plausibly_slice, + use_ribbon_link_cache=filter_for_plausibly_slice, + ) class ClosedBraid(Link): @@ -1040,15 +1101,16 @@ class ClosedBraid(Link): >>> B ClosedBraid(1, -2, 3, 1, -2, 3, 1, -2, 3) """ + def __init__(self, *args, **kwargs): - if args and 'braid_closure' not in kwargs: + if args and "braid_closure" not in kwargs: if len(args) == 1: - self.braid_word = kwargs['braid_closure'] = tuple(args[0]) + self.braid_word = kwargs["braid_closure"] = tuple(args[0]) args = () elif isinstance(args[0], int): - self.braid_word = kwargs['braid_closure'] = args + self.braid_word = kwargs["braid_closure"] = args args = () Link.__init__(self, *args, **kwargs) def __repr__(self): - return 'ClosedBraid%s' % str(self.braid_word) + return "ClosedBraid%s" % str(self.braid_word) From e96015f51a489b7b71054d22c67b3fa1a5880ac7 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Sun, 19 Jul 2026 20:00:31 -0700 Subject: [PATCH 49/53] Revert "Formatting invariants.py using Black Formatter" This reverts commit d06a6079983d7a7933c39d13545537aadd5196ac. --- spherogram_src/links/invariants.py | 242 +++++++++++------------------ 1 file changed, 90 insertions(+), 152 deletions(-) diff --git a/spherogram_src/links/invariants.py b/spherogram_src/links/invariants.py index 94a998c..a248b0f 100644 --- a/spherogram_src/links/invariants.py +++ b/spherogram_src/links/invariants.py @@ -18,7 +18,6 @@ from sage.rings.rational_field import QQ from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing from sage.quadratic_forms.quadratic_form import QuadraticForm - try: from sage.knots.knot import Knot as SageKnot from sage.knots.link import Link as SageLink @@ -36,7 +35,7 @@ def normalize_alex_poly(p, t): polynomial. """ if len(t) == 1: - p = p * (t[0] ** (-min(p.exponents()))) + p = p * (t[0]**(-min(p.exponents()))) if p.coefficients()[-1] < 0: p = -p p, e = p.polynomial_construction() @@ -46,9 +45,9 @@ def normalize_alex_poly(p, t): max_degree = max(sum(x) for x in p.exponents()) highest_monomial_exps = [x for x in p.exponents() if sum(x) == max_degree] leading_exponents = max(highest_monomial_exps) - leading_monomial = functools.reduce( - lambda x, y: x * y, [t[i] ** (leading_exponents[i]) for i in range(len(t))] - ) + leading_monomial = functools.reduce(lambda x, y: x * y, + [t[i]**(leading_exponents[i]) + for i in range(len(t))]) l = p.monomial_coefficient(leading_monomial) if l < 0: @@ -56,7 +55,7 @@ def normalize_alex_poly(p, t): for i, ti in enumerate(t): min_exp = min(x[i] for x in p.exponents()) - p = p * (ti ** (-min_exp)) + p = p * (ti**(-min_exp)) R = p.parent() p = R.polynomial_ring()(p) @@ -89,13 +88,10 @@ def sage_braid_as_int_word(braid): see the documentation for the "sage_link" method for details. """ - class Link(links_base.Link): __doc__ = links_base.Link.__doc__ + extra_docstring - def __init__( - self, crossings=None, braid_closure=None, check_planarity=True, build=True - ): + def __init__(self, crossings=None, braid_closure=None, check_planarity=True, build=True): if _within_sage: if isinstance(crossings, Braid): assert braid_closure is None @@ -119,13 +115,12 @@ def linking_matrix(self): Returns a linking matrix, in which the (i,j)th component is the linking number of the ith and jth link components. """ - mat = [ - [0 for i in range(len(self.link_components))] - for j in range(len(self.link_components)) - ] + mat = [[0 for i in range(len(self.link_components))] + for j in range(len(self.link_components))] for n1, comp1 in enumerate(self.link_components): for n2, comp2 in enumerate(self.link_components): - tally = [[0 for m in range(len(self.crossings))] for n in range(2)] + tally = [[0 for m in range(len(self.crossings))] + for n in range(2)] if comp1 != comp2: for i, c in enumerate(self.crossings): for x1 in comp1: @@ -135,7 +130,7 @@ def linking_matrix(self): if x2[0] == c: tally[1][i] += 1 for k, c in enumerate(self.crossings): - if tally[0][k] == 1 and tally[1][k] == 1: + if (tally[0][k] == 1 and tally[1][k] == 1): mat[n1][n2] += 0.5 * (c.sign) mat[n1][n2] = int(mat[n1][n2]) return mat @@ -203,11 +198,11 @@ def alexander_matrix(self, mv=True): G = self.knot_group() num_gens = len(G.gens()) - L_g = LaurentPolynomialRing(QQ, [f"g{i+1}" for i in range(num_gens)]) + L_g = LaurentPolynomialRing(QQ, [f'g{i+1}' for i in range(num_gens)]) g = list(L_g.gens()) if mv: - L_t = LaurentPolynomialRing(QQ, [f"t{i+1}" for i in range(comp)]) + L_t = LaurentPolynomialRing(QQ, [f't{i+1}' for i in range(comp)]) t = list(L_t.gens()) # determine the component to which each variable corresponds @@ -216,7 +211,7 @@ def alexander_matrix(self, mv=True): g[i] = t[gci] else: - L_t = LaurentPolynomialRing(QQ, "t") + L_t = LaurentPolynomialRing(QQ, 't') t = L_t.gen() g = [t] * len(g) @@ -229,17 +224,14 @@ def alexander_poly(self, *args, **kwargs): """ Please use the "alexander_polynomial" method instead. """ - if "alexander_poly" not in deprecation_warnings_issued: - deprecation_warnings_issued.add("alexander_poly") - print( - 'Deprecation Warning: use "alexander_polynomial" instead of "alexander_poly".' - ) + if 'alexander_poly' not in deprecation_warnings_issued: + deprecation_warnings_issued.add('alexander_poly') + print('Deprecation Warning: use "alexander_polynomial" instead of "alexander_poly".') return self.alexander_polynomial(*args, **kwargs) @sage_method - def alexander_polynomial( - self, multivar=True, v="no", method="default", norm=True, factored=False - ): + def alexander_polynomial(self, multivar=True, v='no', method='default', + norm=True, factored=False): """ Calculates the Alexander polynomial of the link. @@ -267,25 +259,22 @@ def alexander_polynomial( # sign normalization still missing, but when "norm=True" the # leading coefficient with respect to the first variable is made # positive. - if method == "snappy": + if method == 'snappy': try: return self.exterior().alexander_polynomial() except ImportError: - raise RuntimeError( - 'the method "snappy" for ' "alexander_polynomial requires SnapPy" - ) + raise RuntimeError('the method "snappy" for ' + 'alexander_polynomial requires SnapPy') # We do any available Type I and II Reidemeister moves as the # functions we call assume that none are available. from . import simplify - if simplify.has_reidemeister_I_or_II(self): L = self.copy() - L.simplify("basic") - return L.alexander_polynomial( - multivar=multivar, v=v, method=method, norm=norm, factored=factored - ) + L.simplify('basic') + return L.alexander_polynomial(multivar=multivar, v=v, method=method, + norm=norm, factored=factored) # We have to deal with the special case of unknotted and # unlinked components. @@ -296,10 +285,10 @@ def alexander_polynomial( multivar = False if multivar: - L = LaurentPolynomialRing(QQ, [f"t{i+1}" for i in range(comp + nugatory)]) + L = LaurentPolynomialRing(QQ, [f't{i+1}' for i in range(comp + nugatory)]) t = list(L.gens()) else: - L = LaurentPolynomialRing(QQ, "t") + L = LaurentPolynomialRing(QQ, 't') t = [L.gen()] R = L.polynomial_ring() if norm else L @@ -308,10 +297,10 @@ def alexander_polynomial( return R(p) # If single variable, use the super-fast method of Bar-Natan. - if comp == 1 and method == "default" and norm: + if comp == 1 and method == 'default' and norm: p = alexander.alexander(self) else: # Use a simple method based on the Wirtinger presentation. - if method not in ["default", "wirtinger"]: + if method not in ['default', 'wirtinger']: raise ValueError("Available methods are 'default' and 'wirtinger'") M = self.alexander_matrix(mv=multivar) @@ -323,7 +312,7 @@ def alexander_polynomial( else: k = n - 1 - subMatrix = C[0:k, 0:k] + subMatrix = C[0: k, 0: k] p = subMatrix.determinant() if p == 0: return R(0) @@ -335,21 +324,19 @@ def alexander_polynomial( if norm: p = normalize_alex_poly(p, t) - if v != "no": + if v != 'no': return p(*v) if multivar and factored: # it's easier to view this way return p.factor() return p - - def colored_links_gould_polynomial( - self, n, sage_output=_within_sage, sage_polynomials=False, timed=False - ): + + def colored_links_gould_polynomial(self, n, sage_output = _within_sage, sage_polynomials = False, timed = False): """ Colored Links--Gould polynomials are bivariate, hence we default to - using DictLaurentPolynomial to reduce RAM consumption. - - The output, by default, follows whether in sage or not. + using DictLaurentPolynomial to reduce RAM consumption. + + The output, by default, follows whether in sage or not. >>> Link('3_1').colored_links_gould_polynomial(1) t^2*q^2 - t*q^3 - t*q + 2*q^2 - t^-1*q^3 + 1 - t^-1*q + t^-2*q^2 @@ -374,7 +361,7 @@ def colored_links_gould_polynomial( True A mutation pair with the same 2-colored Links--Gould polynomial: - + >>> K1 = Link('12n364') >>> K2 = Link('12n365').mirror() >>> K1.colored_links_gould_polynomial(2) == K2.colored_links_gould_polynomial(2) @@ -389,18 +376,9 @@ def colored_links_gould_polynomial( >>> Link('3_1').colored_links_gould_polynomial(4) t*q^24 - t^2*q^22 - t*q^23 + t^2*q^21 - t*q^22 + t^-1*q^24 + t^2*q^20 - 2*q^22 - t^-1*q^23 + 2*t*q^20 + 2*q^21 - t^-1*q^22 - t^2*q^18 - t*q^19 + 2*q^20 - t^-2*q^22 - 2*t*q^18 + 2*t^-1*q^20 + t^-2*q^21 + t^2*q^16 + t*q^17 - 2*q^18 - t^-1*q^19 + t^-2*q^20 - t^2*q^15 + 2*t*q^16 - 2*t^-1*q^18 - t^2*q^14 + 2*q^16 + t^-1*q^17 - t^-2*q^18 - 2*t*q^14 - 2*q^15 + 2*t^-1*q^16 + t^2*q^12 + 2*t*q^13 - 2*q^14 + t^-2*q^16 - t^2*q^11 + t*q^12 - 2*t^-1*q^14 - t^-2*q^15 + 2*q^12 + 2*t^-1*q^13 - t^-2*q^14 - 2*t*q^10 - 2*q^11 + t^-1*q^12 + t^2*q^8 + t*q^9 + t^-2*q^12 - 2*t^-1*q^10 - t^-2*q^11 + 2*q^8 + t^-1*q^9 - 2*t*q^6 + t^2*q^4 + t^-2*q^8 - 2*t^-1*q^6 + 2*q^4 - t*q^2 + t^-2*q^4 - t^-1*q^2 + 1 """ - from .reshetikhin_turaev import ( - colored_links_gould_R_matrices, - DictLaurentPolynomial, - ) - - ans = ( - self.min_long_diagram() - .reshetikhin_turaev_network( - colored_links_gould_R_matrices(n, sage_polynomials=sage_polynomials) - ) - .evaluate(timed=timed) - ) + from .reshetikhin_turaev import colored_links_gould_R_matrices, DictLaurentPolynomial + + ans = self.min_long_diagram().reshetikhin_turaev_network(colored_links_gould_R_matrices(n, sage_polynomials=sage_polynomials)).evaluate(timed = timed) if sage_output: if not sage_polynomials: @@ -416,9 +394,7 @@ def colored_links_gould_polynomial( else: return ans[0] - def colored_jones_polynomial( - self, n, sage_output=_within_sage, sage_polynomials=_within_sage, timed=False - ): + def colored_jones_polynomial(self, n, sage_output = _within_sage, sage_polynomials = _within_sage, timed = False): """ Colored Jones polynomials are univariate, for whom sage's PuiseuxSeries has highly optimized multiplications, hence we default to use sage whenever possible. @@ -443,26 +419,10 @@ def colored_jones_polynomial( >>> Link('3_1').colored_jones_polynomial(4) q^-34 - q^-33 - q^-32 + 2*q^-29 - q^-28 + 2*q^-24 - q^-23 - q^-22 + q^-19 - q^-18 - q^-17 + q^-14 - q^-13 + q^-9 + q^-4 """ - from .reshetikhin_turaev import ( - colored_jones_R_matrices, - prefactor_colored_jones, - DictLaurentPolynomial, - ) - - ans = ( - self.min_long_diagram() - .reshetikhin_turaev_network( - colored_jones_R_matrices(n, sage_polynomials=sage_polynomials) - ) - .evaluate(timed=timed) - ) - ans = ( - ans[0] - * prefactor_colored_jones( - n, self.writhe(), sage_polynomial=sage_polynomials - ), - ans[1], - ) + from .reshetikhin_turaev import colored_jones_R_matrices, prefactor_colored_jones, DictLaurentPolynomial + + ans = self.min_long_diagram().reshetikhin_turaev_network(colored_jones_R_matrices(n, sage_polynomials=sage_polynomials)).evaluate(timed = timed) + ans = (ans[0] * prefactor_colored_jones(n, self.writhe(), sage_polynomial=sage_polynomials), ans[1]) if sage_output: if not sage_polynomials: @@ -548,9 +508,8 @@ def knot_floer_homology(self, prime=2, complex=False): 1 """ import knot_floer_homology - if len(self.link_components) + self.unlinked_unknot_components > 1: - raise ValueError("Only works for knots, this has more components") + raise ValueError('Only works for knots, this has more components') if len(self.link_components) == 0 and self.unlinked_unknot_components == 1: return Link(braid_closure=[1, 1, -1]).knot_floer_homology() return knot_floer_homology.pd_to_hfk(self, prime=prime, complex=complex) @@ -591,9 +550,8 @@ def black_graph(self): for x in range(len(self.crossings)): total = {self.crossings[x][i] for i in range(4)} if total.issubset(s): - coords.append( - (tuple(faces[i]), tuple(faces[j]), self.crossings[x]) - ) # label by the crossing. + coords.append((tuple(faces[i]), tuple(faces[j]), + self.crossings[x])) # label by the crossing. G = graph.Graph(coords, multiedges=True) component = G.connected_components(sort=False)[1] @@ -637,31 +595,24 @@ def white_graph(self): expected way. """ # Map corners (i.e. CrossingStrands) to faces. - face_of = {corner: n for n, face in enumerate(self.faces()) for corner in face} + face_of = {corner: n for n, face in enumerate(self.faces()) + for corner in face} # Create the edges, labeled with crossing and sign. edges = [] for c in self.crossings: - edges.append( - ( - face_of[CrossingStrand(c, 0)], - face_of[CrossingStrand(c, 2)], - {"crossing": c, "sign": 1}, - ) - ) - edges.append( - ( - face_of[CrossingStrand(c, 1)], - face_of[CrossingStrand(c, 3)], - {"crossing": c, "sign": -1}, - ) - ) + edges.append((face_of[CrossingStrand(c, 0)], + face_of[CrossingStrand(c, 2)], + {'crossing': c, 'sign': 1})) + edges.append((face_of[CrossingStrand(c, 1)], + face_of[CrossingStrand(c, 3)], + {'crossing': c, 'sign': -1})) # Build the graph. G = graph.Graph(edges, multiedges=True) components = G.connected_components(sort=True) if len(components) > 2: - raise ValueError("The link diagram is split.") + raise ValueError('The link diagram is split.') return G.subgraph(components[1]) @sage_method @@ -681,7 +632,7 @@ def goeritz_matrix(self, return_graph=False): vertex = {v: n for n, v in enumerate(V)} for e in G.edges(sort=False): i, j = vertex[e[0]], vertex[e[1]] - m[(i, j)] = m[(j, i)] = m[(i, j)] + e[2]["sign"] + m[(i, j)] = m[(j, i)] = m[(i, j)] + e[2]['sign'] for i in range(N): m[(i, i)] = -sum(m.column(i)) m = m.delete_rows([0]).delete_columns([0]) @@ -718,11 +669,8 @@ def signature(self, new_convention=True): return sum([L.signature() for L in self.split_link_diagram()]) m, G = self.goeritz_matrix(return_graph=True) - correction = sum( - e["sign"] - for _, _, e in G.edges(sort=False) - if e["sign"] == e["crossing"].sign - ) + correction = sum(e['sign'] for _, _, e in G.edges(sort=False) + if e['sign'] == e['crossing'].sign) ans = QuadraticForm(QQ, m).signature() + correction if new_convention: ans = -ans @@ -746,7 +694,7 @@ def _colorability_matrix(self): return m @sage_method - def determinant(self, method="goeritz"): + def determinant(self, method='goeritz'): """ Returns the determinant of the link, a non-negative integer. @@ -758,7 +706,7 @@ def determinant(self, method="goeritz"): sage: K.determinant() 5 """ - if method == "color": + if method == 'color': M = self._colorability_matrix() size = len(self.crossings) - 1 N = matrix(size, size) @@ -766,12 +714,12 @@ def determinant(self, method="goeritz"): for j in range(size): N[(i, j)] = M[(i + 1, j + 1)] return abs(N.determinant()) - if method == "goeritz": + if method == 'goeritz': return abs(self.goeritz_matrix().determinant()) return abs(self.alexander_polynomial(multivar=False, v=[-1], norm=False)) @sage_method - def morse_number(self, solver="GLPK"): + def morse_number(self, solver='GLPK'): """ The *Morse number* of a planar link diagram D is @@ -790,7 +738,6 @@ def morse_number(self, solver="GLPK"): 3 """ from . import morse - return morse.morse_via_LP(self, solver)[0] @sage_method @@ -810,7 +757,6 @@ def morse_diagram(self): 64 """ from . import morse - return morse.MorseLinkDiagram(self) @sage_method @@ -859,7 +805,7 @@ def jones_polynomial(self, variable=None, new_convention=True): J = jones.jones_polynomial(self, normalized=True) R = J.parent() q = R.gen() - terms = [J[e] * q ** (e // 2) for e in J.exponents()] + terms = [J[e] * q**(e // 2) for e in J.exponents()] J = sum(terms, R(0)) if variable is not None: @@ -886,13 +832,12 @@ def seifert_matrix(self): after first making the link isotopic to a braid closure. """ from . import seifert - ans = seifert.seifert_matrix(self) if _within_sage: ans = matrix(ans) return ans - def bridge_upper_bound(self, method="plain sphere", return_meridians=False): + def bridge_upper_bound(self, method='plain sphere', return_meridians=False): """ Computes an upper bound on the bridge number of the given link. By default, it computes the plain sphere number rho(D) of the @@ -921,7 +866,6 @@ def bridge_upper_bound(self, method="plain sphere", return_meridians=False): https://dx.doi.org/10.4310/CAG.2020.v28.n2.a2 """ from . import bridge_bound - return bridge_bound.bridge_upper_bound(self, method, return_meridians) def braid_word(self, as_sage_braid=False): @@ -948,11 +892,10 @@ def braid_word(self, as_sage_braid=False): braids, a new algorithm". """ from . import seifert - word = seifert.braid_word(self) if as_sage_braid: if not _within_sage: - raise ValueError("Requested Sage braid outside of Sage.") + raise ValueError('Requested Sage braid outside of Sage.') n = max(abs(a) for a in word) + 1 word = BraidGroup(n)(word) return word @@ -991,7 +934,7 @@ def sage_link(self): """ if SageKnot is None: - raise ValueError("Your SageMath does not seem to have a native link type") + raise ValueError('Your SageMath does not seem to have a native link type') sage_type = SageKnot if len(self.link_components) == 1 else SageLink # Sage's PD_code lists strands *clockwise* not our # *anticlockwise* prior to Sage 10.1. @@ -1012,16 +955,14 @@ def _sage_(self): return self.sage_link() @sage_method - def ribbon_concordant_links( - self, - max_bands=1, - max_twists=2, - max_band_len=None, - paths="shortest", - filter_for_plausibly_slice=True, - certificates=False, - print_progress=False, - ): + def ribbon_concordant_links(self, + max_bands=1, + max_twists=2, + max_band_len=None, + paths='shortest', + filter_for_plausibly_slice=True, + certificates=False, + print_progress=False): """ Given a link L_0, generate ribbon concordant links L_i. Here, each L_i is obtained from L_0 by adding bands and deleting any @@ -1071,18 +1012,16 @@ def ribbon_concordant_links( """ from .bands.search import ribbon_concordant_links - return ribbon_concordant_links( - self, - max_bands=max_bands, - max_twists=max_twists, - max_band_len=max_band_len, - paths=paths, - filter_for_plausibly_slice=filter_for_plausibly_slice, - certify=certificates, - print_progress=print_progress, - stop_at_unlink=filter_for_plausibly_slice, - use_ribbon_link_cache=filter_for_plausibly_slice, - ) + return ribbon_concordant_links(self, + max_bands=max_bands, + max_twists=max_twists, + max_band_len=max_band_len, + paths=paths, + filter_for_plausibly_slice=filter_for_plausibly_slice, + certify=certificates, + print_progress=print_progress, + stop_at_unlink=filter_for_plausibly_slice, + use_ribbon_link_cache=filter_for_plausibly_slice) class ClosedBraid(Link): @@ -1101,16 +1040,15 @@ class ClosedBraid(Link): >>> B ClosedBraid(1, -2, 3, 1, -2, 3, 1, -2, 3) """ - def __init__(self, *args, **kwargs): - if args and "braid_closure" not in kwargs: + if args and 'braid_closure' not in kwargs: if len(args) == 1: - self.braid_word = kwargs["braid_closure"] = tuple(args[0]) + self.braid_word = kwargs['braid_closure'] = tuple(args[0]) args = () elif isinstance(args[0], int): - self.braid_word = kwargs["braid_closure"] = args + self.braid_word = kwargs['braid_closure'] = args args = () Link.__init__(self, *args, **kwargs) def __repr__(self): - return "ClosedBraid%s" % str(self.braid_word) + return 'ClosedBraid%s' % str(self.braid_word) From 6154c05a7af1c12a58617bd42b9e5f42fef4f6c4 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Sun, 19 Jul 2026 20:09:11 -0700 Subject: [PATCH 50/53] Formatting new code with black and others manually --- spherogram_src/links/invariants.py | 15 +- spherogram_src/links/links_base.py | 2 +- .../links/reshetikhin_turaev/RT_network.py | 199 ++++++---- .../links/reshetikhin_turaev/R_matrices.py | 152 +++++--- .../links/reshetikhin_turaev/__init__.py | 18 +- .../dict_laurent_polynomial.py | 349 +++++++++++------- .../links/reshetikhin_turaev/sparse_array.py | 87 +++-- spherogram_src/links/tangles.py | 17 +- 8 files changed, 533 insertions(+), 306 deletions(-) diff --git a/spherogram_src/links/invariants.py b/spherogram_src/links/invariants.py index a248b0f..a06802b 100644 --- a/spherogram_src/links/invariants.py +++ b/spherogram_src/links/invariants.py @@ -331,7 +331,10 @@ def alexander_polynomial(self, multivar=True, v='no', method='default', return p.factor() return p - def colored_links_gould_polynomial(self, n, sage_output = _within_sage, sage_polynomials = False, timed = False): + def colored_links_gould_polynomial(self, + n, + sage_output=_within_sage, + sage_polynomials=False, timed=False): """ Colored Links--Gould polynomials are bivariate, hence we default to using DictLaurentPolynomial to reduce RAM consumption. @@ -378,7 +381,7 @@ def colored_links_gould_polynomial(self, n, sage_output = _within_sage, sage_pol """ from .reshetikhin_turaev import colored_links_gould_R_matrices, DictLaurentPolynomial - ans = self.min_long_diagram().reshetikhin_turaev_network(colored_links_gould_R_matrices(n, sage_polynomials=sage_polynomials)).evaluate(timed = timed) + ans = self.min_long_diagram().reshetikhin_turaev_network(colored_links_gould_R_matrices(n, sage_polynomials=sage_polynomials)).evaluate(timed=timed) if sage_output: if not sage_polynomials: @@ -394,7 +397,11 @@ def colored_links_gould_polynomial(self, n, sage_output = _within_sage, sage_pol else: return ans[0] - def colored_jones_polynomial(self, n, sage_output = _within_sage, sage_polynomials = _within_sage, timed = False): + def colored_jones_polynomial(self, + n, + sage_output=_within_sage, + sage_polynomials=_within_sage, + timed=False): """ Colored Jones polynomials are univariate, for whom sage's PuiseuxSeries has highly optimized multiplications, hence we default to use sage whenever possible. @@ -421,7 +428,7 @@ def colored_jones_polynomial(self, n, sage_output = _within_sage, sage_polynomia """ from .reshetikhin_turaev import colored_jones_R_matrices, prefactor_colored_jones, DictLaurentPolynomial - ans = self.min_long_diagram().reshetikhin_turaev_network(colored_jones_R_matrices(n, sage_polynomials=sage_polynomials)).evaluate(timed = timed) + ans = self.min_long_diagram().reshetikhin_turaev_network(colored_jones_R_matrices(n, sage_polynomials=sage_polynomials)).evaluate(timed=timed) ans = (ans[0] * prefactor_colored_jones(n, self.writhe(), sage_polynomial=sage_polynomials), ans[1]) if sage_output: diff --git a/spherogram_src/links/links_base.py b/spherogram_src/links/links_base.py index 4ac6d73..250eae4 100644 --- a/spherogram_src/links/links_base.py +++ b/spherogram_src/links/links_base.py @@ -1371,7 +1371,7 @@ def keep(C): return type(self)(final_crossings, check_planarity=False) - def long_diagram(self, cut_at = None): + def long_diagram(self, cut_at=None): """ Returns the long diagram of self obtained by cutting open the strand specified by cut_at. diff --git a/spherogram_src/links/reshetikhin_turaev/RT_network.py b/spherogram_src/links/reshetikhin_turaev/RT_network.py index b90f836..8d9683f 100644 --- a/spherogram_src/links/reshetikhin_turaev/RT_network.py +++ b/spherogram_src/links/reshetikhin_turaev/RT_network.py @@ -1,38 +1,47 @@ class DirectedEdge: - __slots__ = ['label', 'index', 'sign', 'reversed_edge'] + __slots__ = ["label", "index", "sign", "reversed_edge"] - def __init__(self, label, reversed_edge = None): + def __init__(self, label, reversed_edge=None): self.label = label self.index = max(label, ~label) self.sign = 1 if label == self.index else -1 - if reversed_edge is None: - reversed_edge = DirectedEdge(~self.label, self) + if reversed_edge is None: + reversed_edge = DirectedEdge(~self.label, self) self.reversed_edge = reversed_edge def __str__(self): - return ('' if self.sign == 1 else '~') + str(self.index) + return ("" if self.sign == 1 else "~") + str(self.index) def __repr__(self): return str(self) - + def __hash__(self): return hash(self.label) - + def __eq__(self, other): return self.label == other.label - + def __invert__(self): return self.reversed_edge + class RTNetwork: - - def __init__(self, tensors, T = None, network = None, rot_num = None, boundary = None, boundary_labels = None): + + def __init__( + self, + tensors, + T=None, + network=None, + rot_num=None, + boundary=None, + boundary_labels=None, + ): """ - Represent the tensor network obtained by applying - the Reshetikhin--Turaev functor determined by the + Represent the tensor network obtained by applying + the Reshetikhin--Turaev functor determined by the given RMatrix tensors to the tangle T. - + The network is represented as a list of pairs (tensor, legs) Requires numpy and opt_einsum modules for finding out the optimal contraction sequences @@ -40,7 +49,9 @@ def __init__(self, tensors, T = None, network = None, rot_num = None, boundary = self.tensors = tensors if T is not None: - assert T.is_upward(), 'Tangle should be upward for the Reshetikhin--Turaev functor to apply' + assert ( + T.is_upward() + ), "Tangle should be upward for the Reshetikhin--Turaev functor to apply" self.rot_num = T.rot_num() self.tangle = T.copy() @@ -57,32 +68,38 @@ def __init__(self, tensors, T = None, network = None, rot_num = None, boundary = for lab in labels: if lab not in edge.keys(): edge[lab] = DirectedEdge(lab) - + if c.sign == 1: - key = (~edge[labels[3]], - ~edge[labels[0]], - edge[labels[2]], - edge[labels[1]]) + key = ( + ~edge[labels[3]], + ~edge[labels[0]], + edge[labels[2]], + edge[labels[1]], + ) else: - assert c.sign == -1, f'Crossing {c} is not oriented' - key = (~edge[labels[0]], - ~edge[labels[1]], - edge[labels[3]], - edge[labels[2]]) + assert c.sign == -1, f"Crossing {c} is not oriented" + key = ( + ~edge[labels[0]], + ~edge[labels[1]], + edge[labels[3]], + edge[labels[2]], + ) if tensors is not None: tensor = tensors.R(c.sign) for i, e in enumerate(list(key[:2])): if e.index in self.idle_labels and self.rot_num[e.index] != 0: perm = list(range(i)) + [3] + list(range(i, 3)) - tensor = tensor.decorated_contract(tensors.h(0), {(i, 1): (1, tensors.h(self.rot_num[e.index]))}) + tensor = tensor.decorated_contract( + tensors.h(0), + {(i, 1): (1, tensors.h(self.rot_num[e.index]))}, + ) tensor = tensor.permute(perm) network.append((tensor, key)) else: network.append((None, key)) - for arc in self.idle_labels: if arc not in edge.keys(): edge[arc] = DirectedEdge(arc) @@ -92,7 +109,10 @@ def __init__(self, tensors, T = None, network = None, rot_num = None, boundary = else: network.append((None, (~edge[arc], edge[arc]))) else: - assert all(item is not None for item in (network, rot_num, boundary, boundary_labels)) + assert all( + item is not None + for item in (network, rot_num, boundary, boundary_labels) + ) self.network = network self.rot_num = rot_num self.boundary = boundary @@ -107,7 +127,9 @@ def __init__(self, tensors, T = None, network = None, rot_num = None, boundary = def __eq__(self, other): if len(self.network) != 1 or len(other.network) != 1: - raise NotImplementedError('Equality is only implemented for contracted networks') + raise NotImplementedError( + "Equality is only implemented for contracted networks" + ) else: return self.network[0][0] == other.network[0][0] @@ -115,10 +137,13 @@ def optimal_contraction_sequence(self): try: import opt_einsum as oe except ImportError: - raise ModuleNotFoundError('Module opt_einsum is required for computing the optimal contraction sequences') + raise ModuleNotFoundError( + "Module opt_einsum is required for computing the optimal contraction sequences" + ) class _ShapeOnly: - __slots__ = ['shape'] + __slots__ = ["shape"] + def __init__(self, shape): self.shape = shape @@ -129,7 +154,7 @@ def __init__(self, shape): try: idle.remove(key[0].index) except: - raise ValueError(f'key {key[0].index} not found in {idle}') + raise ValueError(f"key {key[0].index} not found in {idle}") else: shape = tensor.shape if tensor is not None else tuple(8 for _ in key) oe_network.append(_ShapeOnly(shape)) @@ -158,11 +183,14 @@ def local_contraction_width(abstract_network, indices): if idx1 == idx2: contracted_all = contracted1 | contracted2 - new_key = tuple(e for pos, e in enumerate(key1) if pos not in contracted_all) + new_key = tuple( + e for pos, e in enumerate(key1) if pos not in contracted_all + ) ans.pop(idx1) else: - new_key = (tuple(e for pos, e in enumerate(key1) if pos not in contracted1) + - tuple(e for pos, e in enumerate(key2) if pos not in contracted2)) + new_key = tuple( + e for pos, e in enumerate(key1) if pos not in contracted1 + ) + tuple(e for pos, e in enumerate(key2) if pos not in contracted2) hi, lo = max(idx1, idx2), min(idx1, idx2) ans.pop(hi) ans.pop(lo) @@ -176,9 +204,11 @@ def seq_contraction_width(self, seq): w = 0 m = 0 - + for indices in seq: - local_width, abstract_network = RTNetwork.local_contraction_width(abstract_network, indices) + local_width, abstract_network = RTNetwork.local_contraction_width( + abstract_network, indices + ) if local_width > w: w = local_width @@ -187,8 +217,8 @@ def seq_contraction_width(self, seq): m += 1 return (w, m), abstract_network - - def contraction_width(self, omit_idle_arcs = True): + + def contraction_width(self, omit_idle_arcs=True): abstract_network = [] for _, key in self.network: if omit_idle_arcs: @@ -197,11 +227,13 @@ def contraction_width(self, omit_idle_arcs = True): else: abstract_network.append((None, key)) - abstract_copy = RTNetwork(None, - network = abstract_network, - rot_num = self.rot_num, - boundary = (0,0) if omit_idle_arcs else self.boundary, - boundary_labels = [] if omit_idle_arcs else self.boundary_labels) + abstract_copy = RTNetwork( + None, + network=abstract_network, + rot_num=self.rot_num, + boundary=(0, 0) if omit_idle_arcs else self.boundary, + boundary_labels=[] if omit_idle_arcs else self.boundary_labels, + ) loops = abstract_copy._resolve_self_loops() if loops: @@ -212,11 +244,12 @@ def contraction_width(self, omit_idle_arcs = True): else: width = (0, 0) - - seq_width, ans = abstract_copy.seq_contraction_width(abstract_copy.optimal_contraction_sequence()) + seq_width, ans = abstract_copy.seq_contraction_width( + abstract_copy.optimal_contraction_sequence() + ) return max(width, seq_width), ans - + @staticmethod def local_contraction_seq(abstract_network, indices): idx1, idx2 = indices @@ -238,11 +271,14 @@ def local_contraction_seq(abstract_network, indices): if idx1 == idx2: contracted_all = contracted1 | contracted2 - new_key = tuple(e for pos, e in enumerate(key1) if pos not in contracted_all) + new_key = tuple( + e for pos, e in enumerate(key1) if pos not in contracted_all + ) ans.pop(idx1) else: - new_key = (tuple(e for pos, e in enumerate(key1) if pos not in contracted1) + - tuple(e for pos, e in enumerate(key2) if pos not in contracted2)) + new_key = tuple( + e for pos, e in enumerate(key1) if pos not in contracted1 + ) + tuple(e for pos, e in enumerate(key2) if pos not in contracted2) hi, lo = max(idx1, idx2), min(idx1, idx2) ans.pop(hi) ans.pop(lo) @@ -255,15 +291,17 @@ def seq_contraction_seq(self, seq): abstract_network = [key for _, key in self.network] ans = [] - + for indices in seq: - contracted_indices, abstract_network = RTNetwork.local_contraction_seq(abstract_network, indices) + contracted_indices, abstract_network = RTNetwork.local_contraction_seq( + abstract_network, indices + ) ans.append(contracted_indices) return ans, abstract_network - def contraction_sequence(self, omit_idle_arcs = True): + def contraction_sequence(self, omit_idle_arcs=True): abstract_network = [] for _, key in self.network: if omit_idle_arcs: @@ -272,21 +310,25 @@ def contraction_sequence(self, omit_idle_arcs = True): else: abstract_network.append((None, key)) - abstract_copy = RTNetwork(None, - network = abstract_network, - rot_num = self.rot_num, - boundary = (0,0) if omit_idle_arcs else self.boundary, - boundary_labels = [] if omit_idle_arcs else self.boundary_labels) + abstract_copy = RTNetwork( + None, + network=abstract_network, + rot_num=self.rot_num, + boundary=(0, 0) if omit_idle_arcs else self.boundary, + boundary_labels=[] if omit_idle_arcs else self.boundary_labels, + ) loops = abstract_copy._resolve_self_loops() - contraction_seq, ans = abstract_copy.seq_contraction_seq(abstract_copy.optimal_contraction_sequence()) + contraction_seq, ans = abstract_copy.seq_contraction_seq( + abstract_copy.optimal_contraction_sequence() + ) return loops + contraction_seq, ans def contract_nodes(self, indices): """ - This modifies self to avoid holding duplicate data in memory. + This modifies self to avoid holding duplicate data in memory. """ idx1, idx2 = indices tensor1, key1 = self.network[idx1] @@ -297,7 +339,10 @@ def contract_nodes(self, indices): for pos_j, ej in enumerate(key2): if ei.index == ej.index and ei.sign * ej.sign == -1: side = 0 if ei.sign == 1 else 1 - pairs[(pos_i, pos_j)] = (side, self.tensors.h(self.rot_num[ei.index])) + pairs[(pos_i, pos_j)] = ( + side, + self.tensors.h(self.rot_num[ei.index]), + ) if tensor1 is not None: result_tensor = tensor1.decorated_contract(tensor2, pairs) @@ -309,11 +354,14 @@ def contract_nodes(self, indices): if idx1 == idx2: contracted_all = contracted1 | contracted2 - new_key = tuple(e for pos, e in enumerate(key1) if pos not in contracted_all) + new_key = tuple( + e for pos, e in enumerate(key1) if pos not in contracted_all + ) self.network.pop(idx1) else: - new_key = (tuple(e for pos, e in enumerate(key1) if pos not in contracted1) + - tuple(e for pos, e in enumerate(key2) if pos not in contracted2)) + new_key = tuple( + e for pos, e in enumerate(key1) if pos not in contracted1 + ) + tuple(e for pos, e in enumerate(key2) if pos not in contracted2) hi, lo = max(idx1, idx2), min(idx1, idx2) self.network.pop(hi) self.network.pop(lo) @@ -337,7 +385,7 @@ def _resolve_self_loop_at(self, idx): if edge_indices: self.contract_nodes((idx, idx)) return edge_indices - + def _resolve_self_loops(self): ans = [] for i in range(len(self.network)): @@ -346,27 +394,28 @@ def _resolve_self_loops(self): ans.append(loop) return ans - def contract_sequence(self, seq, timed = False): + def contract_sequence(self, seq, timed=False): if timed: import time + start_time = time.time() for indices in seq: self.contract_nodes(indices) - + if timed: time_cost = time.time() - start_time return time_cost - def contract_all(self, timed = False): + def contract_all(self, timed=False): """ Perform all possible contractions on self. Return modified self and time (None if not timed). """ self._resolve_self_loops() - time = self.contract_sequence(self.optimal_contraction_sequence(), timed = timed) + time = self.contract_sequence(self.optimal_contraction_sequence(), timed=timed) assert len(self.network) == 1 @@ -375,7 +424,9 @@ def contract_all(self, timed = False): for i in range(self.boundary[0]): desired_order.append(~self.edge[self.boundary_labels[i]]) for i in range(self.boundary[1]): - desired_order.append(self.edge[self.boundary_labels[self.boundary[0] + i]]) + desired_order.append( + self.edge[self.boundary_labels[self.boundary[0] + i]] + ) _, key = self.network[0] key_pos = {e: i for i, e in enumerate(key)} @@ -386,12 +437,12 @@ def contract_all(self, timed = False): return (self, time) - def evaluate(self, timed = False): + def evaluate(self, timed=False): """ Fixate all idle labels at value 0, obtaining a new RTNework with (0,0) boundary (without modifying self), contract_all on the new RTNetwork and return the product of all values of the resulting tensors. """ - assert self.boundary == (1,1) + assert self.boundary == (1, 1) new_network = [] prefactor = 1 @@ -399,7 +450,7 @@ def evaluate(self, timed = False): for tensor, key in self.network: idle_positions = sorted( [pos for pos, e in enumerate(key) if e.index in self.idle_labels], - reverse=True + reverse=True, ) non_idle_key = tuple(e for e in key if e.index not in self.idle_labels) t = tensor @@ -415,9 +466,9 @@ def evaluate(self, timed = False): network=new_network, rot_num=self.rot_num, boundary=(0, 0), - boundary_labels=[] + boundary_labels=[], ) - time = reduced.contract_all(timed = timed)[1] + time = reduced.contract_all(timed=timed)[1] result = prefactor for tensor, _ in reduced.network: diff --git a/spherogram_src/links/reshetikhin_turaev/R_matrices.py b/spherogram_src/links/reshetikhin_turaev/R_matrices.py index b9c4ba6..75a8a5d 100644 --- a/spherogram_src/links/reshetikhin_turaev/R_matrices.py +++ b/spherogram_src/links/reshetikhin_turaev/R_matrices.py @@ -8,20 +8,21 @@ _cache = dict() -def laurent_sparse_tensor_from_file(file, vars = ['t', 'q'], sage_polynomials = False): + +def laurent_sparse_tensor_from_file(file, vars=["t", "q"], sage_polynomials=False): reader = csv.reader(file) header = next(reader) shape = ast.literal_eval(header[0]) - if header[1] != 'ZZ': + if header[1] != "ZZ": raise NotImplementedError data = dict() for line in reader: key, value = line key = tuple(ast.literal_eval(key)) - assert key not in data.keys(), f'{key} appeared multiple times in {file.name}' - data[key] = FastDictLaurentPolynomial.from_str(value, vars = vars) + assert key not in data.keys(), f"{key} appeared multiple times in {file.name}" + data[key] = FastDictLaurentPolynomial.from_str(value, vars=vars) # Unify variable denominators: compute LCM across all loaded polynomials so # every value shares the same vars tuple (enabling interning and consistent arithmetic). @@ -37,25 +38,34 @@ def laurent_sparse_tensor_from_file(file, vars = ['t', 'q'], sage_polynomials = if sage_polynomials: data = {key: poly.to_sage() for key, poly in data.items()} - return SparseTensor(shape = shape, data = data) + return SparseTensor(shape=shape, data=data) + -def laurent_sparse_tensor_from_path(path, vars = ['t', 'q'], compressed = False, sage_polynomials = False): +def laurent_sparse_tensor_from_path( + path, vars=["t", "q"], compressed=False, sage_polynomials=False +): if compressed: import bz2 - with bz2.open(path, 'rt') as f: - return laurent_sparse_tensor_from_file(f, vars = vars, sage_polynomials = sage_polynomials) + + with bz2.open(path, "rt") as f: + return laurent_sparse_tensor_from_file( + f, vars=vars, sage_polynomials=sage_polynomials + ) else: - with open(path, 'r') as f: - return laurent_sparse_tensor_from_file(f, vars = vars, sage_polynomials = sage_polynomials) + with open(path, "r") as f: + return laurent_sparse_tensor_from_file( + f, vars=vars, sage_polynomials=sage_polynomials + ) + class RMatrix: - __slots__ = ['_R', '_h', '_id'] + __slots__ = ["_R", "_h", "_id"] def __init__(self, Rp, Rm, hp, hm): self._R = (Rp, Rm) self._h = (hp, hm) - self._id = SparseTensor(hp.shape, data = {(i,i): 1 for i in range(hp.shape[0])}) + self._id = SparseTensor(hp.shape, data={(i, i): 1 for i in range(hp.shape[0])}) def R(self, sign): if sign == 1: @@ -63,7 +73,7 @@ def R(self, sign): else: assert sign == -1 return self._R[1].copy() - + def h(self, sign): if sign == 1: return self._h[0].copy() @@ -72,34 +82,46 @@ def h(self, sign): else: assert sign == 0 return self._id - + @staticmethod - def from_directory(dir_path, vars = ['t', 'q'], compressed = False, sage_polynomials = False): - names = [name + '.csv' + ('.bz2' if compressed else '') - for name in ['Rp', 'Rn', 'hp', 'hn']] - - tensors = [laurent_sparse_tensor_from_path(os.path.join(dir_path, name), - vars = vars, - compressed = compressed, - sage_polynomials = sage_polynomials) - for name in names] - + def from_directory( + dir_path, vars=["t", "q"], compressed=False, sage_polynomials=False + ): + names = [ + name + ".csv" + (".bz2" if compressed else "") + for name in ["Rp", "Rn", "hp", "hn"] + ] + + tensors = [ + laurent_sparse_tensor_from_path( + os.path.join(dir_path, name), + vars=vars, + compressed=compressed, + sage_polynomials=sage_polynomials, + ) + for name in names + ] + return RMatrix(*tensors) -def colored_links_gould_R_matrices(n, sage_polynomials = False): + +def colored_links_gould_R_matrices(n, sage_polynomials=False): if 0 < n <= 4: - key = (f'V{n}', sage_polynomials) + key = (f"V{n}", sage_polynomials) if key in _cache.keys(): return _cache[key] else: - _cache[key] = RMatrix.from_directory(dir_path = os.path.join(dir_path, f'R_matrices/V{n}/'), - vars = ['t', 'q'], - compressed = False, - sage_polynomials = sage_polynomials) + _cache[key] = RMatrix.from_directory( + dir_path=os.path.join(dir_path, f"R_matrices/V{n}/"), + vars=["t", "q"], + compressed=False, + sage_polynomials=sage_polynomials, + ) return _cache[key] else: raise NotImplementedError - + + def _q_binomial(n, k, q): if k < 0 or k > n: return 0 @@ -109,20 +131,23 @@ def _q_binomial(n, k, q): table[i][0] = 1 for i in range(1, n + 1): for j in range(1, min(i, k) + 1): - table[i][j] = table[i-1][j-1] + q**j * table[i-1][j] + table[i][j] = table[i - 1][j - 1] + q**j * table[i - 1][j] return table[n][k] + def _q_pochhammer(a, q, n): result = 1 for k in range(n): result = result * (1 - a * q**k) return result + def _q_pow(e): """ DictLaurentPolynomial representing q^(e/2). e must be an integer. """ - return FastDictLaurentPolynomial._make((LaurentVariable('q', 4),), {(e,): 1}) + return FastDictLaurentPolynomial._make((LaurentVariable("q", 4),), {(e,): 1}) + def colored_jones_R_matrices(n, sage_polynomials=False): """ @@ -132,16 +157,16 @@ def colored_jones_R_matrices(n, sage_polynomials=False): if n < 0: raise NotImplementedError - key = (f'J{n}', sage_polynomials) + key = (f"J{n}", sage_polynomials) if key in _cache.keys(): return _cache[key] - + n = n + 1 - q_actual = _q_pow(4) # q^1 - q_inv = _q_pow(-4) # q^(-1) + q_actual = _q_pow(4) # q^1 + q_inv = _q_pow(-4) # q^(-1) - shape = (n, n, n, n) + shape = (n, n, n, n) data_p = {} data_n = {} @@ -155,12 +180,12 @@ def colored_jones_R_matrices(n, sage_polynomials=False): # JRRp[i,j,k,l] = JRp[i, j, m_p, n] with m_p = j - k # = q^(-(n-1)^2/4) * q^(-(i+j-k)*k) * q^((n-1)*(i+k)/2) # * qbin[j, m_p] * qp[q^(n-1-i), q^-1, m_p] - m_p = j - k - e4_p = -(n-1)**2 - 4*(i+j-k)*k + 2*(n-1)*(i+k) + m_p = j - k + e4_p = -((n - 1) ** 2) - 4 * (i + j - k) * k + 2 * (n - 1) * (i + k) mono = _q_pow(e4_p) - qb = _q_binomial(j, m_p, q_actual) # 0 when m_p < 0 or m_p > j - qp = _q_pochhammer(_q_pow(4*(n-1-i)), q_inv, m_p) - val = mono * qb * qp + qb = _q_binomial(j, m_p, q_actual) # 0 when m_p < 0 or m_p > j + qp = _q_pochhammer(_q_pow(4 * (n - 1 - i)), q_inv, m_p) + val = mono * qb * qp if val: if not sage_polynomials: data_p[(i, j, k, l)] = val @@ -170,14 +195,18 @@ def colored_jones_R_matrices(n, sage_polynomials=False): # JRRn[i,j,k,l] = JRn[i, j, m_n, n] with m_n = k - j # = q^((n-1)^2/4) * (-1)^m_n * q^(i*j + m_n*(m_n-1)/2) * q^(-(n-1)*(i+k)/2) # * qbin[i, m_n] * qp[q^(n-1-j), q^-1, m_n] - m_n = k - j + m_n = k - j # m_n*(m_n-1) is always even (product of consecutive integers) - e4_n = (n-1)**2 + 4*(i*j + m_n*(m_n-1)//2) - 2*(n-1)*(i+k) - sign = (-1)**m_n + e4_n = ( + (n - 1) ** 2 + + 4 * (i * j + m_n * (m_n - 1) // 2) + - 2 * (n - 1) * (i + k) + ) + sign = (-1) ** m_n mono = _q_pow(e4_n) * sign - qb = _q_binomial(i, m_n, q_actual) # 0 when m_n < 0 or m_n > i - qp = _q_pochhammer(_q_pow(4*(n-1-j)), q_inv, m_n) - val = mono * qb * qp + qb = _q_binomial(i, m_n, q_actual) # 0 when m_n < 0 or m_n > i + qp = _q_pochhammer(_q_pow(4 * (n - 1 - j)), q_inv, m_n) + val = mono * qb * qp if val: if not sage_polynomials: data_n[(i, j, k, l)] = val @@ -190,19 +219,30 @@ def colored_jones_R_matrices(n, sage_polynomials=False): # hp[i,i] = q^(i + (1-n)/2) = q^(i - (n-1)/2), key e4 = 4*i - 2*(n-1) # hn[i,i] = 1 / hp[i,i] , key e4 = 2*(n-1) - 4*i if not sage_polynomials: - hp = SparseTensor((n, n), data={(i, i): _q_pow(4*i - 2*(n-1)) for i in range(n)}) - hn = SparseTensor((n, n), data={(i, i): _q_pow(2*(n-1) - 4*i) for i in range(n)}) + hp = SparseTensor( + (n, n), data={(i, i): _q_pow(4 * i - 2 * (n - 1)) for i in range(n)} + ) + hn = SparseTensor( + (n, n), data={(i, i): _q_pow(2 * (n - 1) - 4 * i) for i in range(n)} + ) else: - hp = SparseTensor((n, n), data={(i, i): _q_pow(4*i - 2*(n-1)).to_sage() for i in range(n)}) - hn = SparseTensor((n, n), data={(i, i): _q_pow(2*(n-1) - 4*i).to_sage() for i in range(n)}) + hp = SparseTensor( + (n, n), + data={(i, i): _q_pow(4 * i - 2 * (n - 1)).to_sage() for i in range(n)}, + ) + hn = SparseTensor( + (n, n), + data={(i, i): _q_pow(2 * (n - 1) - 4 * i).to_sage() for i in range(n)}, + ) _cache[key] = RMatrix(Rp, Rn, hp, hn) return _cache[key] -def prefactor_colored_jones(n, writhe, sage_polynomial = False): + +def prefactor_colored_jones(n, writhe, sage_polynomial=False): n = n + 1 if not sage_polynomial: - return _q_pow(writhe * ((n**2) -1)) + return _q_pow(writhe * ((n**2) - 1)) else: - return _q_pow(writhe * ((n**2) -1)).to_sage() + return _q_pow(writhe * ((n**2) - 1)).to_sage() diff --git a/spherogram_src/links/reshetikhin_turaev/__init__.py b/spherogram_src/links/reshetikhin_turaev/__init__.py index 48c677a..21ba508 100644 --- a/spherogram_src/links/reshetikhin_turaev/__init__.py +++ b/spherogram_src/links/reshetikhin_turaev/__init__.py @@ -1,6 +1,20 @@ from .RT_network import RTNetwork from .dict_laurent_polynomial import DictLaurentPolynomial -from .R_matrices import RMatrix, colored_links_gould_R_matrices, colored_jones_R_matrices, prefactor_colored_jones +from .R_matrices import ( + RMatrix, + colored_links_gould_R_matrices, + colored_jones_R_matrices, + prefactor_colored_jones, +) from .sparse_array import SparseArray, SparseTensor -__all__ = ['RTNetwork', 'RMatrix', 'DictLaurentPolynomial', 'SparseArray', 'SparseTensor','colored_links_gould_R_matrices', 'colored_jones_R_matrices', 'prefactor_colored_jones'] \ No newline at end of file +__all__ = [ + "RTNetwork", + "RMatrix", + "DictLaurentPolynomial", + "SparseArray", + "SparseTensor", + "colored_links_gould_R_matrices", + "colored_jones_R_matrices", + "prefactor_colored_jones", +] diff --git a/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py index ee14128..ede3eab 100644 --- a/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py +++ b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py @@ -3,11 +3,13 @@ if _within_sage: from sage.all import PuiseuxSeriesRing, LaurentPolynomialRing, ZZ + @sage_method def laurent_poly_from_dict(dict, vars, F): L = LaurentPolynomialRing(F, vars) return L(dict) + @sage_method def puiseux_series_from_dict(poly_dict, var, F): """ @@ -23,6 +25,7 @@ def puiseux_series_from_dict(poly_dict, var, F): result += coef * t ** (ZZ(k) / den) return result + class LaurentVariable: """ A named variable with an optional denominator. @@ -32,7 +35,8 @@ class LaurentVariable: LaurentVariable('q') # q^k for integer k LaurentVariable('q', 2) # q^(k/2), so key 1 means q^(1/2) """ - __slots__ = ['name', 'denominator'] + + __slots__ = ["name", "denominator"] def __init__(self, name, denominator=1): self.name = name @@ -49,7 +53,7 @@ def __hash__(self): def __repr__(self): if self.denominator == 1: return self.name - return f'{self.name}[1/{self.denominator}]' + return f"{self.name}[1/{self.denominator}]" def fmt_exp(self, k): """Format exponent k as a string for display.""" @@ -59,20 +63,25 @@ def fmt_exp(self, k): g = _gcd(abs(num), den) num, den = num // g, den // g if den == 1: - if num == 1: return '' - return f'^{num}' - return f'^({num}/{den})' + if num == 1: + return "" + return f"^{num}" + return f"^({num}/{den})" + def _gcd(a, b): while b: a, b = b, a % b return a + _vars_cache = {} + def _intern_vars(vars_tuple): return _vars_cache.setdefault(vars_tuple, vars_tuple) + class FastDictLaurentPolynomial: """ A sparse Laurent polynomial in arbitrarily many variables. @@ -84,7 +93,7 @@ class FastDictLaurentPolynomial: Warning: arithmetic operations (+, -, *, /, **) do NOT check that the two operands have compatible variables. It is the caller's responsibility to ensure both polynomials share the same vars tuple (same names, same order, - same denominators) before combining them. The denoiminators can be normalized + same denominators) before combining them. The denoiminators can be normalized via refactor_variables(new_vars) method. >>> p = DictLaurentPolynomial.from_str('q^2 - q^-1 + 3', ['q']) @@ -94,13 +103,20 @@ class FastDictLaurentPolynomial: >>> r q^2*t - 1 """ - __slots__ = ['vars', 'poly_dict'] + + __slots__ = ["vars", "poly_dict"] def __init__(self, vars, poly_dict): - self.vars = _intern_vars(tuple( - v if isinstance(v, LaurentVariable) else LaurentVariable(*((v,) if isinstance(v, str) else v)) - for v in vars - )) + self.vars = _intern_vars( + tuple( + ( + v + if isinstance(v, LaurentVariable) + else LaurentVariable(*((v,) if isinstance(v, str) else v)) + ) + for v in vars + ) + ) self.poly_dict = {k: v for k, v in poly_dict.items() if v != 0} def to_checked(self): @@ -121,11 +137,15 @@ def to_checked(self): @sage_method def to_sage(self): if len(self.vars) == 1: - return puiseux_series_from_dict(self.poly_dict, self.vars[0], F = ZZ) + return puiseux_series_from_dict(self.poly_dict, self.vars[0], F=ZZ) elif all(var.denominator == 1 for var in self.vars): - return laurent_poly_from_dict(self.poly_dict, [var.name for var in self.vars], F = ZZ) + return laurent_poly_from_dict( + self.poly_dict, [var.name for var in self.vars], F=ZZ + ) else: - raise NotImplementedError('Multi-variable Puiseux conversion to Sage is not supported.') + raise NotImplementedError( + "Multi-variable Puiseux conversion to Sage is not supported." + ) @classmethod @sage_method @@ -150,13 +170,17 @@ def from_sage(cls, p, var_names=None): True """ from sage.rings.puiseux_series_ring_element import PuiseuxSeries - + if isinstance(p, PuiseuxSeries): e = int(p.ramification_index()) name = var_names[0] if var_names else str(p.variable()) var = LaurentVariable(name, e) l = p.laurent_part() - poly_dict = {(int(k),): int(v) for k, v in zip(l.exponents(), l.coefficients()) if v != 0} + poly_dict = { + (int(k),): int(v) + for k, v in zip(l.exponents(), l.coefficients()) + if v != 0 + } return cls._make((var,), poly_dict) # LaurentPolynomial (univariate or multivariate, all denominators 1). @@ -177,7 +201,11 @@ def _make(cls, vars, poly_dict, _interned=False): """Construct without cleaning — caller guarantees no zero values. Pass _interned=True when vars is already a canonical interned tuple.""" obj = object.__new__(cls) - obj.vars = vars if _interned else _intern_vars(vars if isinstance(vars, tuple) else tuple(vars)) + obj.vars = ( + vars + if _interned + else _intern_vars(vars if isinstance(vars, tuple) else tuple(vars)) + ) obj.poly_dict = poly_dict return obj @@ -229,18 +257,25 @@ def __eq__(self, other): return False if any(v1.name != v2.name for v1, v2 in zip(self.vars, other.vars)): return False - def lcm(a, b): return a * b // _gcd(a, b) + + def lcm(a, b): + return a * b // _gcd(a, b) + common_vars = tuple( LaurentVariable(v1.name, lcm(v1.denominator, v2.denominator)) for v1, v2 in zip(self.vars, other.vars) ) - return (self.refactor_variables(common_vars).poly_dict == - other.refactor_variables(common_vars).poly_dict) + return ( + self.refactor_variables(common_vars).poly_dict + == other.refactor_variables(common_vars).poly_dict + ) if other == 0: return not self.poly_dict if other == 1: - return len(self.poly_dict) == 1 and \ - self.poly_dict.get((0,) * len(self.vars), 0) == 1 + return ( + len(self.poly_dict) == 1 + and self.poly_dict.get((0,) * len(self.vars), 0) == 1 + ) return NotImplemented __hash__ = None @@ -252,7 +287,8 @@ def __neg__(self): -2 - q """ return FastDictLaurentPolynomial._make( - self.vars, {k: -v for k, v in self.poly_dict.items()}, _interned=True) + self.vars, {k: -v for k, v in self.poly_dict.items()}, _interned=True + ) def __add__(self, other): """ @@ -280,9 +316,11 @@ def __add__(self, other): else: result[k] = v return FastDictLaurentPolynomial._make(self.vars, result, _interned=True) - + if other == 0: - return FastDictLaurentPolynomial._make(self.vars, dict(self.poly_dict), _interned=True) + return FastDictLaurentPolynomial._make( + self.vars, dict(self.poly_dict), _interned=True + ) zero_key = (0,) * len(self.vars) result = dict(self.poly_dict) @@ -335,7 +373,7 @@ def __mul__(self, other): result = {} if len(self.poly_dict) == 1: # monomial * polynomial: no cancellation possible, assign directly - (k1, v1), = self.poly_dict.items() + ((k1, v1),) = self.poly_dict.items() n = len(k1) if n == 1: for k2, v2 in other.poly_dict.items(): @@ -348,7 +386,7 @@ def __mul__(self, other): result[tuple(a + b for a, b in zip(k1, k2))] = v1 * v2 elif len(other.poly_dict) == 1: # polynomial * monomial: no cancellation possible, assign directly - (k2, v2), = other.poly_dict.items() + ((k2, v2),) = other.poly_dict.items() n = len(k2) if n == 1: for k1, v1 in self.poly_dict.items(): @@ -401,15 +439,18 @@ def __mul__(self, other): else: result[k] = prod return FastDictLaurentPolynomial._make(self.vars, result, _interned=True) - + if self == 0 or other == 0: return FastDictLaurentPolynomial._make(self.vars, {}, _interned=True) if other == 1: - return FastDictLaurentPolynomial._make(self.vars, dict(self.poly_dict), _interned=True) + return FastDictLaurentPolynomial._make( + self.vars, dict(self.poly_dict), _interned=True + ) return FastDictLaurentPolynomial._make( - self.vars, {k: v * other for k, v in self.poly_dict.items()}, _interned=True) + self.vars, {k: v * other for k, v in self.poly_dict.items()}, _interned=True + ) def __rmul__(self, other): return self.__mul__(other) @@ -437,17 +478,22 @@ def __truediv__(self, other): """ if isinstance(other, FastDictLaurentPolynomial): if len(other.poly_dict) != 1: - raise ValueError('DictLaurentPolynomial division: divisor must be a monomial') - return self * other ** -1 + raise ValueError( + "DictLaurentPolynomial division: divisor must be a monomial" + ) + return self * other**-1 if other == 0: - raise ZeroDivisionError('DictLaurentPolynomial division by zero') + raise ZeroDivisionError("DictLaurentPolynomial division by zero") if not all(c % other == 0 for c in self.poly_dict.values()): raise ValueError( - f'DictLaurentPolynomial division: scalar {other!r} does not ' - f'divide all coefficients') + f"DictLaurentPolynomial division: scalar {other!r} does not " + f"divide all coefficients" + ) return type(self)._make( - self.vars, {k: c // other for k, c in self.poly_dict.items()}, - _interned=True) + self.vars, + {k: c // other for k, c in self.poly_dict.items()}, + _interned=True, + ) def simplify_denominator(self): """ @@ -511,13 +557,15 @@ def refactor_variables(self, new_vars): for old_var, new_var in zip(self.vars, new_vars): if old_var.name != new_var.name: raise ValueError( - f'refactor_variables: variable name mismatch: ' - f'{old_var.name!r} vs {new_var.name!r}') + f"refactor_variables: variable name mismatch: " + f"{old_var.name!r} vs {new_var.name!r}" + ) if new_var.denominator % old_var.denominator != 0: raise ValueError( - f'refactor_variables: new denominator {new_var.denominator} ' - f'is not a multiple of old denominator {old_var.denominator} ' - f'for variable {old_var.name!r}') + f"refactor_variables: new denominator {new_var.denominator} " + f"is not a multiple of old denominator {old_var.denominator} " + f"for variable {old_var.name!r}" + ) scales.append(new_var.denominator // old_var.denominator) new_poly_dict = { tuple(k * s for k, s in zip(key, scales)): coef @@ -525,7 +573,7 @@ def refactor_variables(self, new_vars): } return FastDictLaurentPolynomial._make(new_vars, new_poly_dict) - def change_vars(self, rules, new_var_names = None): + def change_vars(self, rules, new_var_names=None): """ Return a new DictLaurentPolynomial with variables substituted by rules. @@ -592,7 +640,9 @@ def change_vars(self, rules, new_var_names = None): rules[var] = var.name # Parse string values. - var_names = new_var_names if new_var_names is not None else [v.name for v in self.vars] + var_names = ( + new_var_names if new_var_names is not None else [v.name for v in self.vars] + ) has_str_values = any(isinstance(v, str) for v in rules.values()) if has_str_values: parsed_rules = {} @@ -606,14 +656,17 @@ def change_vars(self, rules, new_var_names = None): # Only valid when image is a monomial (can be raised to 1/d power). if len(parsed.poly_dict) > 1: raise ValueError( - f'change_vars: image of {src_var!r} has denominator ' - f'{d} > 1 but the image is not a monomial') + f"change_vars: image of {src_var!r} has denominator " + f"{d} > 1 but the image is not a monomial" + ) # Scale image variable denominators by d; keys unchanged. new_img_vars = tuple( LaurentVariable(v.name, v.denominator * d) for v in parsed.vars ) - parsed = FastDictLaurentPolynomial._make(new_img_vars, parsed.poly_dict) + parsed = FastDictLaurentPolynomial._make( + new_img_vars, parsed.poly_dict + ) parsed_rules[src_var] = parsed else: parsed_rules[src_var] = img @@ -625,13 +678,18 @@ def change_vars(self, rules, new_var_names = None): if var in rules: full_rules[var] = rules[var] else: - full_rules[var] = FastDictLaurentPolynomial.generator(self.vars, index=i) + full_rules[var] = FastDictLaurentPolynomial.generator( + self.vars, index=i + ) # Unify all image DLPs to a common vars tuple when string values were used. if has_str_values and full_rules: all_imgs = list(full_rules.values()) ref_vars = all_imgs[0].vars - def _lcm2(a, b): return a * b // _gcd(a, b) + + def _lcm2(a, b): + return a * b // _gcd(a, b) + common_denoms = [v.denominator for v in ref_vars] for img in all_imgs[1:]: for i, v in enumerate(img.vars): @@ -656,7 +714,8 @@ def _lcm2(a, b): return a * b // _gcd(a, b) if term is None: zero_key = (0,) * len(next(iter(full_rules.values())).vars) term = FastDictLaurentPolynomial._make( - next(iter(full_rules.values())).vars, {zero_key: coef}) + next(iter(full_rules.values())).vars, {zero_key: coef} + ) else: term = term * coef @@ -696,19 +755,20 @@ def from_str(cls, s, vars): 3 """ if len(vars) != len(set(vars)): - raise ValueError(f'from_str: duplicate variable names in {vars!r}') + raise ValueError(f"from_str: duplicate variable names in {vars!r}") - s = s.replace(' ', '') + s = s.replace(" ", "") # --- tokeniser state (mutable via single-element list) --- pos = [0] def expect(ch): if pos[0] >= len(s) or s[pos[0]] != ch: - got = repr(s[pos[0]]) if pos[0] < len(s) else 'end of string' + got = repr(s[pos[0]]) if pos[0] < len(s) else "end of string" raise ValueError( - f'from_str: expected {ch!r}, got {got} ' - f'at position {pos[0]} in {s!r}') + f"from_str: expected {ch!r}, got {got} " + f"at position {pos[0]} in {s!r}" + ) pos[0] += 1 def parse_pos_int(): @@ -716,33 +776,35 @@ def parse_pos_int(): while pos[0] < len(s) and s[pos[0]].isdigit(): pos[0] += 1 if pos[0] == start: - got = repr(s[pos[0]]) if pos[0] < len(s) else 'end of string' + got = repr(s[pos[0]]) if pos[0] < len(s) else "end of string" raise ValueError( - f'from_str: expected integer at position {pos[0]} ' - f'in {s!r}, got {got}') - return int(s[start:pos[0]]) + f"from_str: expected integer at position {pos[0]} " + f"in {s!r}, got {got}" + ) + return int(s[start : pos[0]]) def parse_signed_int(): sign = 1 - if pos[0] < len(s) and s[pos[0]] in '+-': - if s[pos[0]] == '-': + if pos[0] < len(s) and s[pos[0]] in "+-": + if s[pos[0]] == "-": sign = -1 pos[0] += 1 return sign * parse_pos_int() def parse_exponent(): # Called after '^' has been consumed. - if pos[0] < len(s) and s[pos[0]] == '(': + if pos[0] < len(s) and s[pos[0]] == "(": pos[0] += 1 num = parse_signed_int() den = 1 - if pos[0] < len(s) and s[pos[0]] == '/': + if pos[0] < len(s) and s[pos[0]] == "/": pos[0] += 1 den = parse_pos_int() if den == 0: raise ValueError( - f'from_str: zero denominator in exponent in {s!r}') - expect(')') + f"from_str: zero denominator in exponent in {s!r}" + ) + expect(")") return num, den return parse_signed_int(), 1 @@ -756,65 +818,68 @@ def parse_exponent(): def parse_expr(): result = parse_term() - while pos[0] < len(s) and s[pos[0]] in '+-': - op = s[pos[0]]; pos[0] += 1 + while pos[0] < len(s) and s[pos[0]] in "+-": + op = s[pos[0]] + pos[0] += 1 result = (op, result, parse_term()) return result def parse_term(): result = parse_factor() - while pos[0] < len(s) and s[pos[0]] in '*/': - op = s[pos[0]]; pos[0] += 1 + while pos[0] < len(s) and s[pos[0]] in "*/": + op = s[pos[0]] + pos[0] += 1 result = (op, result, parse_factor()) return result def parse_factor(): sign = 1 - while pos[0] < len(s) and s[pos[0]] in '+-': - if s[pos[0]] == '-': + while pos[0] < len(s) and s[pos[0]] in "+-": + if s[pos[0]] == "-": sign = -sign pos[0] += 1 result = parse_atom() - return ('u-', result) if sign == -1 else result + return ("u-", result) if sign == -1 else result def parse_atom(): if pos[0] >= len(s): - raise ValueError( - f'from_str: unexpected end of expression in {s!r}') + raise ValueError(f"from_str: unexpected end of expression in {s!r}") c = s[pos[0]] - if c == '(': + if c == "(": pos[0] += 1 result = parse_expr() - expect(')') + expect(")") return result # Match a variable name (longest first to handle ambiguous prefixes). for var in sorted_vars: end = pos[0] + len(var) - if s[pos[0]:end] == var: + if s[pos[0] : end] == var: # Require a non-identifier character to follow (avoid prefix match). - if end < len(s) and (s[end].isalnum() or s[end] == '_'): + if end < len(s) and (s[end].isalnum() or s[end] == "_"): continue pos[0] = end num, den = 1, 1 - if pos[0] < len(s) and s[pos[0]] == '^': + if pos[0] < len(s) and s[pos[0]] == "^": pos[0] += 1 num, den = parse_exponent() - return ('v', var, num, den) + return ("v", var, num, den) # Must be an integer coefficient. if c.isdigit(): n = parse_pos_int() - if pos[0] < len(s) and s[pos[0]] == '.': + if pos[0] < len(s) and s[pos[0]] == ".": raise ValueError( - f'from_str: decimal numbers not supported ' - f'at position {pos[0]} in {s!r}') - return ('c', n) + f"from_str: decimal numbers not supported " + f"at position {pos[0]} in {s!r}" + ) + return ("c", n) raise ValueError( - f'from_str: unexpected character {c!r} ' - f'at position {pos[0]} in {s!r}; known variables: {vars!r}') + f"from_str: unexpected character {c!r} " + f"at position {pos[0]} in {s!r}; known variables: {vars!r}" + ) # --- parse --- @@ -822,8 +887,9 @@ def parse_atom(): if pos[0] != len(s): raise ValueError( - f'from_str: unexpected content {s[pos[0]:]!r} ' - f'at position {pos[0]} in {s!r}') + f"from_str: unexpected content {s[pos[0]:]!r} " + f"at position {pos[0]} in {s!r}" + ) # --- scan AST for per-variable denominator LCMs --- @@ -834,13 +900,13 @@ def lcm(a, b): def collect_denoms(node): tag = node[0] - if tag == 'v': + if tag == "v": _, name, num, den = node if num != 0: var_lcms[name] = lcm(var_lcms[name], den) - elif tag == 'u-': + elif tag == "u-": collect_denoms(node[1]) - elif tag != 'c': + elif tag != "c": collect_denoms(node[1]) collect_denoms(node[2]) @@ -859,33 +925,33 @@ def collect_denoms(node): def evaluate(node): tag = node[0] - if tag == 'c': + if tag == "c": return node[1] - if tag == 'v': + if tag == "v": _, name, num, den = node if num == 0: return 1 return generators[name] ** (num * var_lcms[name] // den) - if tag == 'u-': + if tag == "u-": return -evaluate(node[1]) - if tag == '+': + if tag == "+": return evaluate(node[1]) + evaluate(node[2]) - if tag == '-': + if tag == "-": return evaluate(node[1]) - evaluate(node[2]) - if tag == '*': + if tag == "*": return evaluate(node[1]) * evaluate(node[2]) # tag == '/' right_val = evaluate(node[2]) if isinstance(right_val, int): if abs(right_val) != 1: raise ValueError( - f'from_str: division by non-unit coefficient {right_val} ' - f'in {s!r}; write the coefficient in the numerator') + f"from_str: division by non-unit coefficient {right_val} " + f"in {s!r}; write the coefficient in the numerator" + ) return evaluate(node[1]) * right_val if len(right_val.poly_dict) != 1: - raise ValueError( - f'from_str: can only divide by a monomial in {s!r}') - return evaluate(node[1]) * (right_val ** -1) + raise ValueError(f"from_str: can only divide by a monomial in {s!r}") + return evaluate(node[1]) * (right_val**-1) result = evaluate(ast) @@ -907,20 +973,25 @@ def __pow__(self, n): try: n = n.__index__() except (AttributeError, TypeError): - raise ValueError(f'exponent must be an integer, got {n!r}') + raise ValueError(f"exponent must be an integer, got {n!r}") if n == 0: zero_key = (0,) * len(self.vars) - return FastDictLaurentPolynomial._make(self.vars, {zero_key: 1}, _interned=True) + return FastDictLaurentPolynomial._make( + self.vars, {zero_key: 1}, _interned=True + ) if n < 0: if len(self.poly_dict) != 1: - raise ValueError('negative powers only supported for monomials') - (key, coef), = self.poly_dict.items() + raise ValueError("negative powers only supported for monomials") + ((key, coef),) = self.poly_dict.items() if coef != 1 and coef != -1: raise ValueError( - f'negative powers require a ±1 leading coefficient, got {coef!r}') + f"negative powers require a ±1 leading coefficient, got {coef!r}" + ) inv_key = tuple(k * n for k in key) inv_coef = coef ** (-n) # -n > 0, so int**int stays int; (±1)^k = (±1)^{-k} - return FastDictLaurentPolynomial._make(self.vars, {inv_key: inv_coef}, _interned=True) + return FastDictLaurentPolynomial._make( + self.vars, {inv_key: inv_coef}, _interned=True + ) result = self base = self n -= 1 @@ -933,18 +1004,20 @@ def __pow__(self, n): def __repr__(self): if not self.poly_dict: - return '0' + return "0" # Sort by total degree descending (exact rational arithmetic), then lex descending. L = 1 for var in self.vars: L = L * var.denominator // _gcd(L, var.denominator) scales = tuple(L // var.denominator for var in self.vars) + def _sort_key(item): exp = item[0] total = sum(k * s for k, s in zip(exp, scales)) if len(self.vars) == 1: return total # ascending for univariate (matches PuiseuxSeries) return (-total, tuple(-k for k in exp)) # descending for multivariate + terms = [] for exp, coef in sorted(self.poly_dict.items(), key=_sort_key): parts = [] @@ -952,16 +1025,17 @@ def _sort_key(item): fmt = var.fmt_exp(k) if fmt is not None: parts.append(var.name + fmt) - monomial = '*'.join(parts) + monomial = "*".join(parts) if not monomial: terms.append(str(coef)) elif coef == 1: terms.append(monomial) elif coef == -1: - terms.append(f'-{monomial}') + terms.append(f"-{monomial}") else: - terms.append(f'{coef}*{monomial}') - return ' + '.join(terms).replace('+ -', '- ') + terms.append(f"{coef}*{monomial}") + return " + ".join(terms).replace("+ -", "- ") + class DictLaurentPolynomial(FastDictLaurentPolynomial): """ @@ -988,14 +1062,19 @@ def _match(self, other): if self.vars is other.vars: return self, other - def lcm(a, b): return a * b // _gcd(a, b) + def lcm(a, b): + return a * b // _gcd(a, b) self_by_name = {v.name: v for v in self.vars} other_by_name = {v.name: v for v in other.vars} union_list = [] for v in self.vars: - d = lcm(v.denominator, other_by_name[v.name].denominator) if v.name in other_by_name else v.denominator + d = ( + lcm(v.denominator, other_by_name[v.name].denominator) + if v.name in other_by_name + else v.denominator + ) union_list.append(LaurentVariable(v.name, d)) for v in other.vars: if v.name not in self_by_name: @@ -1016,26 +1095,46 @@ def expand(poly_obj): for old_key, coeff in poly_obj.poly_dict.items(): new_key = [] for slot in slots: - new_key.append(old_key[slot[0]] * slot[1] if slot is not None else 0) + new_key.append( + old_key[slot[0]] * slot[1] if slot is not None else 0 + ) new_dict[tuple(new_key)] = coeff return FastDictLaurentPolynomial._make(union_vars, new_dict, _interned=True) return expand(self), expand(other) def __add__(self, other): - lhs, rhs = self._match(other) if isinstance(other, FastDictLaurentPolynomial) else (self, other) + lhs, rhs = ( + self._match(other) + if isinstance(other, FastDictLaurentPolynomial) + else (self, other) + ) result = FastDictLaurentPolynomial.__add__(lhs, rhs) - return DictLaurentPolynomial._make(result.vars, result.poly_dict, _interned=True) + return DictLaurentPolynomial._make( + result.vars, result.poly_dict, _interned=True + ) def __sub__(self, other): - lhs, rhs = self._match(other) if isinstance(other, FastDictLaurentPolynomial) else (self, other) + lhs, rhs = ( + self._match(other) + if isinstance(other, FastDictLaurentPolynomial) + else (self, other) + ) result = FastDictLaurentPolynomial.__sub__(lhs, rhs) - return DictLaurentPolynomial._make(result.vars, result.poly_dict, _interned=True) + return DictLaurentPolynomial._make( + result.vars, result.poly_dict, _interned=True + ) def __mul__(self, other): - lhs, rhs = self._match(other) if isinstance(other, FastDictLaurentPolynomial) else (self, other) + lhs, rhs = ( + self._match(other) + if isinstance(other, FastDictLaurentPolynomial) + else (self, other) + ) result = FastDictLaurentPolynomial.__mul__(lhs, rhs) - return DictLaurentPolynomial._make(result.vars, result.poly_dict, _interned=True) + return DictLaurentPolynomial._make( + result.vars, result.poly_dict, _interned=True + ) def simplify_variables(self): """ @@ -1053,7 +1152,9 @@ def simplify_variables(self): """ if not self.poly_dict or not self.vars: return self - keep = [any(key[i] != 0 for key in self.poly_dict) for i in range(len(self.vars))] + keep = [ + any(key[i] != 0 for key in self.poly_dict) for i in range(len(self.vars)) + ] if all(keep): return self new_vars = _intern_vars(tuple(v for v, k in zip(self.vars, keep) if k)) @@ -1061,4 +1162,4 @@ def simplify_variables(self): tuple(exp for exp, k in zip(key, keep) if k): coeff for key, coeff in self.poly_dict.items() } - return DictLaurentPolynomial._make(new_vars, new_dict, _interned=True) \ No newline at end of file + return DictLaurentPolynomial._make(new_vars, new_dict, _interned=True) diff --git a/spherogram_src/links/reshetikhin_turaev/sparse_array.py b/spherogram_src/links/reshetikhin_turaev/sparse_array.py index 33c6e8e..ce60a4b 100644 --- a/spherogram_src/links/reshetikhin_turaev/sparse_array.py +++ b/spherogram_src/links/reshetikhin_turaev/sparse_array.py @@ -1,5 +1,6 @@ from itertools import product as cartesian_product + class SparseArray: """ A sparse array supporting arbitrary-dimensional indexing via tuples. @@ -7,7 +8,8 @@ class SparseArray: Internally stores only non-default entries in a dict keyed by index tuples. Indices can be integers or tuples of integers of any length. """ - __slots__ = ('_data', '_default', '_rank', '_shape') + + __slots__ = ("_data", "_default", "_rank", "_shape") def __init__(self, shape, data=None, default=0): self._data = {} @@ -24,9 +26,11 @@ def __init__(self, shape, data=None, default=0): def __eq__(self, other): if not isinstance(other, SparseArray): return False - return (self._shape == other._shape and - self._default == other._default and - self._data == other._data) + return ( + self._shape == other._shape + and self._default == other._default + and self._data == other._data + ) def _key(self, index): if isinstance(index, tuple): @@ -43,11 +47,13 @@ def __setitem__(self, index, value): else: if len(key) != self._rank: raise ValueError( - f'key length {len(key)} does not match rank {self._rank}') + f"key length {len(key)} does not match rank {self._rank}" + ) for i, (idx, dim) in enumerate(zip(key, self._shape)): if not (0 <= idx < dim): raise ValueError( - f'index {idx} at axis {i} is out of range [0, {dim})') + f"index {idx} at axis {i} is out of range [0, {dim})" + ) self._data[key] = value def __delitem__(self, index): @@ -66,7 +72,7 @@ def __iter__(self): return iter(self._data) def __repr__(self): - return f'SparseArray({self._data!r}, default={self._default!r})' + return f"SparseArray({self._data!r}, default={self._default!r})" def keys(self): return self._data.keys() @@ -96,7 +102,7 @@ def rank(self): @property def shape(self): return self._shape - + @property def default(self): return self._default @@ -126,7 +132,7 @@ class SparseTensor(SparseArray): """ def __repr__(self): - return f'SparseTensor({self._data!r}, default={self._default!r})' + return f"SparseTensor({self._data!r}, default={self._default!r})" def copy(self): return SparseTensor(self._shape, data=self._data.copy(), default=self._default) @@ -149,7 +155,7 @@ def _accumulate(self, key, value): else: self._data[key] = value - def contract(self, other: 'SparseTensor', pairs): + def contract(self, other: "SparseTensor", pairs): """ Contract self with other over the specified index pairs, returning a new SparseTensor whose axes are the free axes of self followed by the @@ -172,15 +178,17 @@ def contract(self, other: 'SparseTensor', pairs): for ai, bj in pairs: if self._shape[ai] != other._shape[bj]: raise ValueError( - f'axis {ai} of self (size {self._shape[ai]}) is incompatible ' - f'with axis {bj} of other (size {other._shape[bj]})') + f"axis {ai} of self (size {self._shape[ai]}) is incompatible " + f"with axis {bj} of other (size {other._shape[bj]})" + ) - self_contracted = {ai for ai, _ in pairs} - other_contracted = {bj for _, bj in pairs} - self_free = [i for i in range(self.rank) if i not in self_contracted] + self_contracted = {ai for ai, _ in pairs} + other_contracted = {bj for _, bj in pairs} + self_free = [i for i in range(self.rank) if i not in self_contracted] other_free = [i for i in range(other.rank) if i not in other_contracted] - result_shape = [self._shape[i] for i in self_free] + \ - [other._shape[i] for i in other_free] + result_shape = [self._shape[i] for i in self_free] + [ + other._shape[i] for i in other_free + ] if not self._data or not other._data: return SparseTensor(result_shape, default=self._default) @@ -190,7 +198,7 @@ def contract(self, other: 'SparseTensor', pairs): self_groups = {} for key, val in self.items(): c_key = tuple(key[ai] for ai, _ in pairs) - f_key = tuple(key[i] for i in self_free) + f_key = tuple(key[i] for i in self_free) self_groups.setdefault(c_key, []).append((f_key, val)) result = SparseTensor(result_shape, default=self._default) @@ -203,8 +211,8 @@ def contract(self, other: 'SparseTensor', pairs): for f_key_a, val_a in group: result._accumulate(f_key_a + f_key_b, val_a * val_b) return result - - def decorated_contract(self, other: 'SparseTensor', pairs): + + def decorated_contract(self, other: "SparseTensor", pairs): """ An enhanced version of contract, where: @@ -224,20 +232,23 @@ def decorated_contract(self, other: 'SparseTensor', pairs): h_self, h_other = (0, 1) if side == 0 else (1, 0) if self._shape[ai] != h._shape[h_self]: raise ValueError( - f'self axis {ai} (size {self._shape[ai]}) is incompatible ' - f'with h axis {h_self} (size {h._shape[h_self]})') + f"self axis {ai} (size {self._shape[ai]}) is incompatible " + f"with h axis {h_self} (size {h._shape[h_self]})" + ) if other._shape[bj] != h._shape[h_other]: raise ValueError( - f'other axis {bj} (size {other._shape[bj]}) is incompatible ' - f'with h axis {h_other} (size {h._shape[h_other]})') + f"other axis {bj} (size {other._shape[bj]}) is incompatible " + f"with h axis {h_other} (size {h._shape[h_other]})" + ) - pair_list = list(pairs.keys()) - self_contracted = {ai for ai, _ in pair_list} + pair_list = list(pairs.keys()) + self_contracted = {ai for ai, _ in pair_list} other_contracted = {bj for _, bj in pair_list} - self_free = [i for i in range(self.rank) if i not in self_contracted] + self_free = [i for i in range(self.rank) if i not in self_contracted] other_free = [i for i in range(other.rank) if i not in other_contracted] - result_shape = [self._shape[i] for i in self_free] + \ - [other._shape[i] for i in other_free] + result_shape = [self._shape[i] for i in self_free] + [ + other._shape[i] for i in other_free + ] if not self._data or not other._data: return SparseTensor(result_shape, default=self._default) @@ -246,14 +257,14 @@ def decorated_contract(self, other: 'SparseTensor', pairs): self_groups = {} for key, val in self._data.items(): c_key = tuple(key[ai] for ai, _ in pair_list) - f_key = tuple(key[i] for i in self_free) + f_key = tuple(key[i] for i in self_free) self_groups.setdefault(c_key, []).append((f_key, val)) # For each pair m, precompute: given k (other's contracted value), # which j values in self are reachable and with what h weight? # h_lookup[m][k] = [(j, h_val), ...] h_lookups = [] - for (ai, bj) in pair_list: + for ai, bj in pair_list: side, h = pairs[(ai, bj)] lookup = {} for hkey, hval in h._data.items(): @@ -315,7 +326,7 @@ def trace(self, i, j): free_key = tuple(v for idx, v in enumerate(key) if idx != i and idx != j) result._accumulate(free_key, value) return result - + def decorated_trace(self, i, j, decoration): """ Contract axes i and j of self with an edge tensor h inserted between @@ -352,12 +363,14 @@ def _decorated_trace_pairs(self, pairs): h_i, h_j = (0, 1) if side == 0 else (1, 0) if self._shape[i] != h._shape[h_i]: raise ValueError( - f'axis {i} of self (size {self._shape[i]}) is incompatible ' - f'with h axis {h_i} (size {h._shape[h_i]})') + f"axis {i} of self (size {self._shape[i]}) is incompatible " + f"with h axis {h_i} (size {h._shape[h_i]})" + ) if self._shape[j] != h._shape[h_j]: raise ValueError( - f'axis {j} of self (size {self._shape[j]}) is incompatible ' - f'with h axis {h_j} (size {h._shape[h_j]})') + f"axis {j} of self (size {self._shape[j]}) is incompatible " + f"with h axis {h_j} (size {h._shape[h_j]})" + ) free = [idx for idx in range(self.rank) if idx not in contracted] result_shape = [self._shape[idx] for idx in free] @@ -395,7 +408,7 @@ def fixate(self, i, value): free_key = tuple(v for idx, v in enumerate(key) if idx != i) result._data[free_key] = val return result - + def permute(self, indices): """ Reorder axes using pull-style indices: indices[i] is the axis of self diff --git a/spherogram_src/links/tangles.py b/spherogram_src/links/tangles.py index b29ba07..72bf051 100644 --- a/spherogram_src/links/tangles.py +++ b/spherogram_src/links/tangles.py @@ -108,7 +108,7 @@ def add(self, c): return component class Tangle: - def __init__(self, boundary=2, crossings=None, entry_points=None, build = True, label=None, start_orientations = None, component_starts = None, check_planarity = True): + def __init__(self, boundary=2, crossings=None, entry_points=None, build=True, label=None, start_orientations=None, component_starts=None, check_planarity=True): """ A tangle is a fragment of a Link with some number of boundary strands. Tangles can be composed in various ways along their boundary strands, @@ -537,7 +537,7 @@ def _crossings_from_PD_code(self, code, entry_points): return crossings, component_starts, entry_strands - def PD_code(self, KnotTheory=False, min_strand_index = 0): + def PD_code(self, KnotTheory=False, min_strand_index=0): """ The planar diagram code for the tangle. Unlike for links, it returns two extra fields, boundary and entry_info in addition to the PD code of crossings, in order to specify @@ -1291,7 +1291,7 @@ def digraph(self): return G - def split_tangle_diagram(self, destroy_original = False, check_planarity = False): + def split_tangle_diagram(self, destroy_original=False, check_planarity=False): """ Split the tangle diagram into its connected components. Returns a list of Tangles. @@ -1379,7 +1379,7 @@ def is_planar(self): return euler == 2 or v == 1 - def simplify(self, mode = 'basic', type_III_limit = 100): + def simplify(self, mode='basic', type_III_limit=100): """ Tries to simplify the tangle diagram. Returns whether it succeeded in reducing the number of crossings. Modifies the tangle in place, @@ -1432,7 +1432,8 @@ def simplify(self, mode = 'basic', type_III_limit = 100): raise NotImplementedError() def is_planar_isotopic(self, other, root=None, over_or_under=False) -> bool: - return self.isosig(root = root, over_or_under=over_or_under) == other.isosig(root = root, over_or_under = over_or_under) + return self.isosig(root=root, over_or_under=over_or_under) == \ + other.isosig(root=root, over_or_under=over_or_under) def __repr__(self): return "" % (self.label, len(self.components), len(self.crossings), self.boundary[0], self.boundary[1]) @@ -1493,7 +1494,7 @@ def ComponentTangle(component_idx): ValueError: Two Strand objects in different components have the same component_idx values """ s = Strand(component_idx=component_idx) - return Tangle((1, 1), [s], [(s, 0), (s, 1)], label = f'ComponentTangle({component_idx})') + return Tangle((1, 1), [s], [(s, 0), (s, 1)], label=f'ComponentTangle({component_idx})') def CapTangle(): @@ -1616,7 +1617,7 @@ def __init__(self, a, b=1): Tangle.__init__(self, 2, crossings, T.adjacent, - label = f"RationalTangle({a}, {b})") + label=f"RationalTangle({a}, {b})") # --------------------------------------------------- # @@ -1646,7 +1647,7 @@ def IdentityBraid(n): raise ValueError("Expecting non-negative int") entry_points = 2* [i for i in range(n)] return Tangle(n, [], entry_points, - label = f"IdentityBraid({n})") + label=f"IdentityBraid({n})") def BraidTangle(gens, n=None): From 14bf0e534430b7734b2f1e0a4f5b2538d660803b Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Sun, 19 Jul 2026 20:59:25 -0700 Subject: [PATCH 51/53] Fix formatting issues of docstrings --- spherogram_src/links/invariants.py | 80 ++++++++++++++----- spherogram_src/links/links_base.py | 12 ++- .../dict_laurent_polynomial.py | 4 +- spherogram_src/links/tangles.py | 78 ++++++++++++------ 4 files changed, 122 insertions(+), 52 deletions(-) diff --git a/spherogram_src/links/invariants.py b/spherogram_src/links/invariants.py index a06802b..99324b4 100644 --- a/spherogram_src/links/invariants.py +++ b/spherogram_src/links/invariants.py @@ -336,10 +336,12 @@ def colored_links_gould_polynomial(self, sage_output=_within_sage, sage_polynomials=False, timed=False): """ - Colored Links--Gould polynomials are bivariate, hence we default to - using DictLaurentPolynomial to reduce RAM consumption. - - The output, by default, follows whether in sage or not. + Computes the colored Links--Gould polynomial of a link. + The output is an instance of Sage's LaurentPolynomial if in sage, + otherwise a DictLaurentPolynomial. + + Colored Links--Gould polynomials are bivariate, for which we default to + using DictLaurentPolynomial during the procedure to reduce RAM consumption. >>> Link('3_1').colored_links_gould_polynomial(1) t^2*q^2 - t*q^3 - t*q + 2*q^2 - t^-1*q^3 + 1 - t^-1*q + t^-2*q^2 @@ -348,19 +350,26 @@ def colored_links_gould_polynomial(self, Mirror image is equal to substituting q with q^-1: - >>> Link('3_1').mirror().colored_links_gould_polynomial(1, sage_output = False).change_vars({'q': 'q^-1'}) == Link('3_1').colored_links_gould_polynomial(1, sage_output = False) - True + >>> mtref = Link('3_1').mirror() + >>> mtref_LG = mtref.colored_links_gould_polynomial(1, sage_output=False) + >>> mtref_LG.change_vars({'q': 'q^-1'}) + t^2*q^2 - t*q^3 - t*q + 2*q^2 - t^-1*q^3 + 1 - t^-1*q + t^-2*q^2 - The colored Links--Gould polynomial specializes to the square of the Alexander polynomial: + The colored Links--Gould polynomial specializes to the square of + the Alexander polynomial: - >>> Link('3_1').colored_links_gould_polynomial(1, sage_output = False).change_vars({'q': '1'}) + >>> tref_LG = Link('3_1').colored_links_gould_polynomial(1, sage_output=False) + >>> tref_LG.change_vars({'q': '1'}) t^2 - 2*t + 3 - 2*t^-1 + t^-2 - >>> Link('4_1').colored_links_gould_polynomial(1, sage_output = False).change_vars({'q': '1'}) + >>> fig8_LG = Link('4_1').colored_links_gould_polynomial(1, sage_output=False) + >>> fig8_LG.change_vars({'q': '1'}) t^2 - 6*t + 11 - 6*t^-1 + t^-2 1-colored Links--Gould polynomial is invariant under mutation: - >>> Link('11n34').colored_links_gould_polynomial(1) == Link('11n42').colored_links_gould_polynomial(1) + >>> conway_LG = Link('11n34').colored_links_gould_polynomial(1) + >>> KT_LG = Link('11n42').colored_links_gould_polynomial(1) + >>> conway_LG == KT_LG True A mutation pair with the same 2-colored Links--Gould polynomial: @@ -372,12 +381,32 @@ def colored_links_gould_polynomial(self, Some higher colored values for the trefoil: - >>> Link('3_1').colored_links_gould_polynomial(2) - -t^2*q^5 + t*q^6 + t^2*q^4 - t*q^5 + t*q^4 - 2*q^5 + t^-1*q^6 + t^2*q^2 - 2*t*q^3 + 2*q^4 - t^-1*q^5 + t^-1*q^4 - t^-2*q^5 - t*q + 2*q^2 - 2*t^-1*q^3 + t^-2*q^4 + 1 - t^-1*q + t^-2*q^2 - >>> Link('3_1').colored_links_gould_polynomial(3) - -t*q^27 + t^2*q^24 + t*q^25 - t^-1*q^27 - t^2*q^22 + t*q^23 + 2*q^24 + t^-1*q^25 - t^2*q^20 - 2*t*q^21 - 2*q^22 + t^-1*q^23 + t^-2*q^24 + t^2*q^18 + 2*t*q^19 - 2*q^20 - 2*t^-1*q^21 - t^-2*q^22 - t^2*q^16 + t*q^17 + 2*q^18 + 2*t^-1*q^19 - t^-2*q^20 - 2*t*q^15 - 2*q^16 + t^-1*q^17 + t^-2*q^18 + t^2*q^12 + t*q^13 - 2*t^-1*q^15 - t^-2*q^16 + 2*q^12 + t^-1*q^13 - 2*t*q^9 + t^-2*q^12 + t^2*q^6 - 2*t^-1*q^9 + 2*q^6 - t*q^3 + t^-2*q^6 - t^-1*q^3 + 1 - >>> Link('3_1').colored_links_gould_polynomial(4) - t*q^24 - t^2*q^22 - t*q^23 + t^2*q^21 - t*q^22 + t^-1*q^24 + t^2*q^20 - 2*q^22 - t^-1*q^23 + 2*t*q^20 + 2*q^21 - t^-1*q^22 - t^2*q^18 - t*q^19 + 2*q^20 - t^-2*q^22 - 2*t*q^18 + 2*t^-1*q^20 + t^-2*q^21 + t^2*q^16 + t*q^17 - 2*q^18 - t^-1*q^19 + t^-2*q^20 - t^2*q^15 + 2*t*q^16 - 2*t^-1*q^18 - t^2*q^14 + 2*q^16 + t^-1*q^17 - t^-2*q^18 - 2*t*q^14 - 2*q^15 + 2*t^-1*q^16 + t^2*q^12 + 2*t*q^13 - 2*q^14 + t^-2*q^16 - t^2*q^11 + t*q^12 - 2*t^-1*q^14 - t^-2*q^15 + 2*q^12 + 2*t^-1*q^13 - t^-2*q^14 - 2*t*q^10 - 2*q^11 + t^-1*q^12 + t^2*q^8 + t*q^9 + t^-2*q^12 - 2*t^-1*q^10 - t^-2*q^11 + 2*q^8 + t^-1*q^9 - 2*t*q^6 + t^2*q^4 + t^-2*q^8 - 2*t^-1*q^6 + 2*q^4 - t*q^2 + t^-2*q^4 - t^-1*q^2 + 1 + >>> K = Link('3_1') + >>> K.colored_links_gould_polynomial(2) # doctest: +NORMALIZE_WHITESPACE + -t^2*q^5 + t*q^6 + t^2*q^4 - t*q^5 + t*q^4 - 2*q^5 + t^-1*q^6 + t^2*q^2 + - 2*t*q^3 + 2*q^4 - t^-1*q^5 + t^-1*q^4 - t^-2*q^5 - t*q + 2*q^2 - + 2*t^-1*q^3 + t^-2*q^4 + 1 - t^-1*q + t^-2*q^2 + + >>> K.colored_links_gould_polynomial(3) # doctest: +NORMALIZE_WHITESPACE + -t*q^27 + t^2*q^24 + t*q^25 - t^-1*q^27 - t^2*q^22 + t*q^23 + 2*q^24 + + t^-1*q^25 - t^2*q^20 - 2*t*q^21 - 2*q^22 + t^-1*q^23 + t^-2*q^24 + + t^2*q^18 + 2*t*q^19 - 2*q^20 - 2*t^-1*q^21 - t^-2*q^22 - t^2*q^16 + + t*q^17 + 2*q^18 + 2*t^-1*q^19 - t^-2*q^20 - 2*t*q^15 - 2*q^16 + t^-1*q^17 + + t^-2*q^18 + t^2*q^12 + t*q^13 - 2*t^-1*q^15 - t^-2*q^16 + 2*q^12 + + t^-1*q^13 - 2*t*q^9 + t^-2*q^12 + t^2*q^6 - 2*t^-1*q^9 + 2*q^6 - t*q^3 + + t^-2*q^6 - t^-1*q^3 + 1 + + >>> K.colored_links_gould_polynomial(4) # doctest: +NORMALIZE_WHITESPACE + t*q^24 - t^2*q^22 - t*q^23 + t^2*q^21 - t*q^22 + t^-1*q^24 + t^2*q^20 - + 2*q^22 - t^-1*q^23 + 2*t*q^20 + 2*q^21 - t^-1*q^22 - t^2*q^18 - t*q^19 + + 2*q^20 - t^-2*q^22 - 2*t*q^18 + 2*t^-1*q^20 + t^-2*q^21 + t^2*q^16 + + t*q^17 - 2*q^18 - t^-1*q^19 + t^-2*q^20 - t^2*q^15 + 2*t*q^16 - 2*t^-1*q^18 + - t^2*q^14 + 2*q^16 + t^-1*q^17 - t^-2*q^18 - 2*t*q^14 - 2*q^15 + 2*t^-1*q^16 + + t^2*q^12 + 2*t*q^13 - 2*q^14 + t^-2*q^16 - t^2*q^11 + t*q^12 - 2*t^-1*q^14 + - t^-2*q^15 + 2*q^12 + 2*t^-1*q^13 - t^-2*q^14 - 2*t*q^10 - 2*q^11 + t^-1*q^12 + + t^2*q^8 + t*q^9 + t^-2*q^12 - 2*t^-1*q^10 - t^-2*q^11 + 2*q^8 + t^-1*q^9 - + 2*t*q^6 + t^2*q^4 + t^-2*q^8 - 2*t^-1*q^6 + 2*q^4 - t*q^2 + t^-2*q^4 - t^-1*q^2 + + 1 """ from .reshetikhin_turaev import colored_links_gould_R_matrices, DictLaurentPolynomial @@ -403,13 +432,18 @@ def colored_jones_polynomial(self, sage_polynomials=_within_sage, timed=False): """ - Colored Jones polynomials are univariate, for whom sage's PuiseuxSeries - has highly optimized multiplications, hence we default to use sage whenever possible. + Computes the colored Jones polynomial of a link. + The output is an instance of Sage's PuiseuxSeries if in sage, + otherwise a DictLaurentPolynomial. 1-colored Jones polynomial is equal to the usual Jones polynomial. Here we follow the ordinary convention of variables for Jones polynomials, instead of the squared q in jones_polynomial() + Colored Jones polynomials are univariate, for whom sage's PuiseuxSeries + has highly optimized multiplications, hence we default to use sage + during the procedure whenever possible. + >>> Link('3_1').colored_jones_polynomial(1) -q^-4 + q^-3 + q^-1 >>> Link('4_1').colored_jones_polynomial(1) @@ -421,10 +455,12 @@ def colored_jones_polynomial(self, >>> Link('3_1').colored_jones_polynomial(2) q^-11 - q^-10 - q^-9 + q^-8 - q^-7 + q^-5 + q^-2 - >>> Link('3_1').colored_jones_polynomial(3) - -q^-21 + q^-20 + q^-19 - q^-17 + q^-15 - q^-14 - q^-13 + q^-11 - q^-10 + q^-7 + q^-3 - >>> Link('3_1').colored_jones_polynomial(4) - q^-34 - q^-33 - q^-32 + 2*q^-29 - q^-28 + 2*q^-24 - q^-23 - q^-22 + q^-19 - q^-18 - q^-17 + q^-14 - q^-13 + q^-9 + q^-4 + >>> Link('3_1').colored_jones_polynomial(3) # doctest: +NORMALIZE_WHITESPACE + -q^-21 + q^-20 + q^-19 - q^-17 + q^-15 - q^-14 - q^-13 + q^-11 - q^-10 + q^-7 + + q^-3 + >>> Link('3_1').colored_jones_polynomial(4) # doctest: +NORMALIZE_WHITESPACE + q^-34 - q^-33 - q^-32 + 2*q^-29 - q^-28 + 2*q^-24 - q^-23 - q^-22 + q^-19 - + q^-18 - q^-17 + q^-14 - q^-13 + q^-9 + q^-4 """ from .reshetikhin_turaev import colored_jones_R_matrices, prefactor_colored_jones, DictLaurentPolynomial diff --git a/spherogram_src/links/links_base.py b/spherogram_src/links/links_base.py index 250eae4..8a9bd4b 100644 --- a/spherogram_src/links/links_base.py +++ b/spherogram_src/links/links_base.py @@ -893,7 +893,8 @@ def reverse_orientation(self, component_index): """ Reverse the orientation of components specified by component_index. - component_index: either a single index of component or a list of indices of components + component_index: either a single index of component + or a list of indices of components >>> L = Link([(4, 0, 5, 3), (0, 6, 1, 5), (6, 2, 7, 1), (2, 4, 3, 7)]) >>> L @@ -1373,10 +1374,13 @@ def keep(C): def long_diagram(self, cut_at=None): """ - Returns the long diagram of self obtained by cutting open the strand specified by cut_at. + Returns the long diagram of self obtained + by cutting open the strand specified by cut_at. - cut_at should be a pair of integers (i, j) representing the j-th strand of the i-th crossing. - If not specified, the first strand of the first crossing will be chosen by default. + cut_at should be a pair of integers (i, j) + representing the j-th strand of the i-th crossing. + If not specified, the first strand of the first + crossing will be chosen by default. >>> T = Link('4_1').long_diagram() >>> T.PD_code() diff --git a/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py index ede3eab..44640e2 100644 --- a/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py +++ b/spherogram_src/links/reshetikhin_turaev/dict_laurent_polynomial.py @@ -246,8 +246,8 @@ def __eq__(self, other): >>> DictLaurentPolynomial.from_str('1', ['q']) == 1 True >>> v4 = LaurentVariable('q', 4) - >>> p4 = DictLaurentPolynomial._make((v4,), {(4,): 1}) # q stored with denom 4 - >>> p4 == DictLaurentPolynomial.from_str('q', ['q']) # same value, different denom + >>> p4 = DictLaurentPolynomial._make((v4,), {(4,): 1}) + >>> p4 == DictLaurentPolynomial.from_str('q', ['q']) True """ if isinstance(other, FastDictLaurentPolynomial): diff --git a/spherogram_src/links/tangles.py b/spherogram_src/links/tangles.py index 72bf051..316fb6e 100644 --- a/spherogram_src/links/tangles.py +++ b/spherogram_src/links/tangles.py @@ -133,7 +133,8 @@ def __init__(self, boundary=2, crossings=None, entry_points=None, build=True, la Tangles now support creation from PD_code, for example: - >>> Tangle(3, [[0,4,1,5],[1,8,2,9],[2,7,3,6],[5,9,6,10]], [0,4,8,10,3,7], label = 'RIII') + >>> Tangle(3, [[0,4,1,5],[1,8,2,9],[2,7,3,6],[5,9,6,10]],\ + [0,4,8,10,3,7], label = 'RIII') see doc of ``PD_code`` for more details. @@ -399,7 +400,10 @@ def _build_components(self, component_starts=None): >>> len(Tangle(3, [[0,4,1,5],[1,8,2,9],[2,7,3,6],[5,9,6,10]], ... [0,4,8,10,3,7], label = 'RIII').components) 3 - >>> len(((RationalTangle(2,3)+IdentityBraid(1))|(RationalTangle(2,5)+ComponentTangle(-1))).components) + + >>> T1 = RationalTangle(2,3)+IdentityBraid(1) + >>> T2 = RationalTangle(2,5)+ComponentTangle(-1) + >>> len((T1|T2).components) 2 """ if component_starts is not None: @@ -539,11 +543,13 @@ def _crossings_from_PD_code(self, code, entry_points): def PD_code(self, KnotTheory=False, min_strand_index=0): """ - The planar diagram code for the tangle. Unlike for links, it returns two extra fields, - boundary and entry_info in addition to the PD code of crossings, in order to specify - how the boundary and entries of the tangle is arranged. The fields are ordered as follows: + The planar diagram code for the tangle. + + Unlike for links, it returns two extra fields, boundary and entry_info + in addition to the PD code of crossings, in order to specify how the boundary + and entries of the tangle is arranged. The fields are ordered as: - boundary, PD, entry_info + boundary, PD, entry_info so that they can be unpacked immediately for creating Tangles. @@ -598,7 +604,10 @@ def rot_num(self): Entry strands may have nonzero rotation numbers: - >>> T = Tangle((2, 2),[(9, 2, 10, 3), (1, 10, 2, 11), (6, 12, 7, 11), (12, 6, 13, 5), (3, 1, 4, 0), (4, 7, 5, 8)], [0, 9, 8, 13]) + >>> T = Tangle((2, 2),\ + [(9, 2, 10, 3), (1, 10, 2, 11), (6, 12, 7, 11), \ + (12, 6, 13, 5), (3, 1, 4, 0), (4, 7, 5, 8)], \ + [0, 9, 8, 13]) >>> T.rot_num() [0, 0, 0, -1, 0, 0, 0, -1, 0, 1, -1, 1, -1, 0] """ @@ -673,11 +682,16 @@ def flip(self): >>> RT = RationalTangle >>> T = (RT(3, 4) + RT(1, 2)) * RT(-3, 2) - >>> T.PD_code() - ((2, 2), [(18, 8, 19, 7), (6, 16, 7, 15), (14, 6, 15, 5), (4, 14, 5, 19), (12, 17, 13, 18), (16, 11, 17, 12), (3, 1, 4, 0), (1, 10, 2, 11), (9, 2, 10, 3)], [0, 9, 8, 13]) + >>> T.PD_code() # doctest: +NORMALIZE_WHITESPACE + ((2, 2), [(18, 8, 19, 7), (6, 16, 7, 15), (14, 6, 15, 5), + (4, 14, 5, 19), (12, 17, 13, 18), (16, 11, 17, 12), (3, 1, 4, 0), + (1, 10, 2, 11), (9, 2, 10, 3)], [0, 9, 8, 13]) + >>> fT = T.flip() - >>> fT.PD_code() - ((2, 2), [(12, 16, 13, 15), (18, 12, 19, 11), (10, 18, 11, 17), (16, 10, 17, 9), (14, 3, 15, 4), (2, 19, 3, 14), (5, 9, 6, 8), (1, 6, 2, 7), (7, 0, 8, 1)], [0, 5, 4, 13]) + >>> fT.PD_code() # doctest: +NORMALIZE_WHITESPACE + ((2, 2), [(12, 16, 13, 15), (18, 12, 19, 11), (10, 18, 11, 17), + (16, 10, 17, 9), (14, 3, 15, 4), (2, 19, 3, 14), (5, 9, 6, 8), + (1, 6, 2, 7), (7, 0, 8, 1)], [0, 5, 4, 13]) >>> T.flip().flip().PD_code() == T.PD_code() True @@ -845,8 +859,10 @@ def _component_starts_from_PD(self, code, labels, gluings, entry_dict): # The following operators always clear the current orientations on both tangles # and recreate an orientation with default behaviour. def __add__(self, other): - """Put self to left of other and fuse the top-right strand of self to the top-left - strand of other and the bottom-right strand of self to the bottom-left strand of other. + """ + Put self to left of other and fuse the top-right strand of self to + the top-left strand of other and the bottom-right strand of self to + the bottom-left strand of other. >>> (IdentityBraid(2) + BraidTangle([1])).describe() 'Tangle[{1,2}, {3,4}, X[2,4,5,5], P[1,3]]' @@ -925,7 +941,9 @@ def _start_orientations(self): def __or__(self, other): """ - Put self to left of other. This is like tangle addition but without the fusing of strands. + Put self to left of other. + This is like tangle addition but without the fusing of strands. + Preserves the orientations of both tangles, since no gluing happens. >>> (IdentityBraid(1) | CupTangle()).describe() @@ -961,7 +979,9 @@ def copy(self): return pickle.loads(pickle.dumps(self)) def rotate(self, s): - """Rotate anticlockwise by s*90 degrees. This is only for (2,2) tangles. + """ + Rotate anticlockwise by s*90 degrees. This is only for (2,2) tangles. + Preserves orientation of the tangle. See ``Tangle.reshape()`` for a generalization to all tangle shapes.""" @@ -995,7 +1015,8 @@ def numerator_closure(self): >>> BraidTangle([2,-1,2],4).numerator_closure().colored_jones_polynomial(1) -q^-4 + q^-3 + q^-1 - >>> BraidTangle([1,1,1]).rotate(1).numerator_closure().colored_jones_polynomial(1) + >>> K = BraidTangle([1,1,1]).rotate(1).numerator_closure() + >>> K.colored_jones_polynomial(1) q + q^3 - q^4 sage: BraidTangle([2,-1,2],4).numerator_closure().alexander_polynomial() @@ -1029,7 +1050,9 @@ def denominator_closure(self): t^2 - t + 1 sage: BraidTangle([1,-2,1,-2]).braid_closure().alexander_polynomial() t^2 - 3*t + 1 - >>> BraidTangle([1,-2,1,-2]).braid_closure().exterior().identify() # doctest: +SNAPPY + + >>> K = BraidTangle([1,-2,1,-2]).braid_closure() + >>> K.exterior().identify() # doctest: +SNAPPY [m004(0,0), 4_1(0,0), K2_1(0,0), K4a1(0,0), otet02_00001(0,0)] """ m, n = self.boundary @@ -1148,7 +1171,9 @@ def isosig(self, root=None, over_or_under=False): True >>> BraidTangle([1,1]).isosig() == BraidTangle([-1,-1]).isosig() True - >>> BraidTangle([1,1]).isosig(over_or_under=True) == BraidTangle([-1,-1]).isosig(over_or_under=True) + >>> iso1 = BraidTangle([1,1]).isosig(over_or_under=True) + >>> iso2 = BraidTangle([-1,-1]).isosig(over_or_under=True) + >>> iso1 == iso2 False """ @@ -1293,18 +1318,20 @@ def digraph(self): def split_tangle_diagram(self, destroy_original=False, check_planarity=False): """ - Split the tangle diagram into its connected components. Returns a list of Tangles. + Split the tangle diagram into its connected components. + Returns a list of Tangles. - If check_planarity is True, return in addition if the boundary strands of the components - are laid out in a planar manner with respect to each other. + If check_planarity is True, return in addition if the boundary strands + of the components are laid out in a planar manner with respect to each other. >>> len(RationalTangle(0,1).split_tangle_diagram()) 2 - >>> len(Tangle(4, [(0, 2, 1, 3)], [0,2,4,5,3,1,4,5], label = 'C||').split_tangle_diagram()) + >>> len(Tangle(4, [(0, 2, 1, 3)], [0,2,4,5,3,1,4,5]).split_tangle_diagram()) 3 - >>> Tangle(4, [(0, 2, 1, 3)], [0,4,2,5,3,1,4,5], check_planarity = False).split_tangle_diagram(check_planarity = True)[0] + >>> np_T = Tangle(4, [(0, 2, 1, 3)], [0,4,2,5,3,1,4,5], check_planarity=False) + >>> np_T.split_tangle_diagram(check_planarity=True)[0] False """ T = self.copy() if not destroy_original else self @@ -1361,7 +1388,10 @@ def split_tangle_diagram(self, destroy_original=False, check_planarity=False): def is_planar(self): """ - >>> Tangle(4, [(0, 2, 1, 3)], [2,0,4,5,3,1,4,5], check_planarity = False).is_planar() + Checks whether the tangle diagram can be planarly embedded into the disk. + + >>> np_T = Tangle(4, [(0, 2, 1, 3)], [2,0,4,5,3,1,4,5], check_planarity = False) + >>> np_T.is_planar() False """ G = self.digraph() From b997bc7704d0816c3ead7c80486e4c5dc37cfdfb Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Sun, 19 Jul 2026 21:00:57 -0700 Subject: [PATCH 52/53] fix tiny style issue --- spherogram_src/links/invariants.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spherogram_src/links/invariants.py b/spherogram_src/links/invariants.py index 99324b4..4abbad7 100644 --- a/spherogram_src/links/invariants.py +++ b/spherogram_src/links/invariants.py @@ -334,7 +334,8 @@ def alexander_polynomial(self, multivar=True, v='no', method='default', def colored_links_gould_polynomial(self, n, sage_output=_within_sage, - sage_polynomials=False, timed=False): + sage_polynomials=False, + timed=False): """ Computes the colored Links--Gould polynomial of a link. The output is an instance of Sage's LaurentPolynomial if in sage, From 34012d2f83d2aa0f117fe1cfbc0b1ed51fa666c7 Mon Sep 17 00:00:00 2001 From: Shana <903443276@qq.com> Date: Sun, 19 Jul 2026 21:13:17 -0700 Subject: [PATCH 53/53] Minor improve to docstrings --- spherogram_src/links/invariants.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spherogram_src/links/invariants.py b/spherogram_src/links/invariants.py index 4abbad7..0caa8f9 100644 --- a/spherogram_src/links/invariants.py +++ b/spherogram_src/links/invariants.py @@ -337,7 +337,7 @@ def colored_links_gould_polynomial(self, sage_polynomials=False, timed=False): """ - Computes the colored Links--Gould polynomial of a link. + Computes the n-colored Links--Gould polynomial of a link. The output is an instance of Sage's LaurentPolynomial if in sage, otherwise a DictLaurentPolynomial. @@ -433,11 +433,11 @@ def colored_jones_polynomial(self, sage_polynomials=_within_sage, timed=False): """ - Computes the colored Jones polynomial of a link. + Computes the n-colored Jones polynomial of a link. The output is an instance of Sage's PuiseuxSeries if in sage, otherwise a DictLaurentPolynomial. - 1-colored Jones polynomial is equal to the usual Jones polynomial. + The 1-colored Jones polynomial is equal to the usual Jones polynomial. Here we follow the ordinary convention of variables for Jones polynomials, instead of the squared q in jones_polynomial()