def _latex_(self, **options): r""" Returns the crystal graph as a latex string. This can be exported to a file with self.latex_file('filename'). EXAMPLES:: sage: T = CrystalOfTableaux(['A',2],shape=[1]) sage: T._latex_() #optional - dot2tex ... sage: view(T, pdflatex = True, tightpage = True) #optional - dot2tex graphviz One can for example also color the edges using the following options:: sage: T = CrystalOfTableaux(['A',2],shape=[1]) sage: T._latex_(color_by_label = {0:"black", 1:"red", 2:"blue"}) #optional - dot2tex graphviz ... """ if not have_dot2tex(): print "dot2tex not available. Install after running \'sage -sh\'" return G=self.digraph() G.set_latex_options(format="dot2tex", edge_labels = True, edge_options = lambda (u,v,label): ({"backward":label ==0}), **options) return G._latex_()
def digraph(self, subset=None, index_set=None): r""" Return the :class:`DiGraph` associated to ``self``. INPUT: - ``subset`` -- (optional) a subset of vertices for which the digraph should be constructed - ``index_set`` -- (optional) the index set to draw arrows .. SEEALSO:: :meth:`sage.categories.crystals.Crystals.ParentMethods.digraph` EXAMPLES:: sage: C = crystals.KirillovReshetikhin(['D',4,1], 2, 1) sage: G = C.digraph() sage: G.latex_options() # optional - dot2tex LaTeX options for Digraph on 29 vertices: {...'edge_options': <function <lambda> at 0x...>,...} sage: view(G, tightpage=True) # optional - dot2tex graphviz, not tested (opens external window) """ G = Crystals().parent_class.digraph(self, subset, index_set) if have_dot2tex(): f = lambda u_v_label: ({"backward": u_v_label[2] == 0}) G.set_latex_options(edge_options=f) return G
def digraph(self, subset=None, index_set=None): r""" Return the :class:`DiGraph` associated to ``self``. INPUT: - ``subset`` -- (optional) a subset of vertices for which the digraph should be constructed - ``index_set`` -- (optional) the index set to draw arrows .. SEEALSO:: :meth:`sage.categories.crystals.Crystals.ParentMethods.digraph` EXAMPLES:: sage: C = crystals.KirillovReshetikhin(['D',4,1], 2, 1) sage: G = C.digraph() sage: G.latex_options() # optional - dot2tex LaTeX options for Digraph on 29 vertices: {...'edge_options': <function ... at ...>...} sage: view(G, tightpage=True) # optional - dot2tex graphviz, not tested (opens external window) """ G = Crystals().parent_class.digraph(self, subset, index_set) if have_dot2tex(): f = lambda u_v_label: ({"backward": u_v_label[2] == 0}) G.set_latex_options(edge_options=f) return G
def digraph(self): """ Returns the DiGraph associated to self. EXAMPLES:: sage: C = Crystals().example(5) sage: C.digraph() Digraph on 6 vertices The edges of the crystal graph are by default colored using blue for edge 1, red for edge 2, and green for edge 3:: sage: C = Crystals().example(3) sage: G = C.digraph() sage: view(G, pdflatex=True, tightpage=True) #optional - dot2tex graphviz One may also overwrite the colors:: sage: C = Crystals().example(3) sage: G = C.digraph() sage: G.set_latex_options(color_by_label = {1:"red", 2:"purple", 3:"blue"}) sage: view(G, pdflatex=True, tightpage=True) #optional - dot2tex graphviz Or one may add colors to yet unspecified edges:: sage: C = Crystals().example(4) sage: G = C.digraph() sage: C.cartan_type()._index_set_coloring[4]="purple" sage: view(G, pdflatex=True, tightpage=True) #optional - dot2tex graphviz TODO: add more tests """ from sage.graphs.all import DiGraph d = {} for x in self: d[x] = {} for i in self.index_set(): child = x.f(i) if child is None: continue d[x][child]=i G = DiGraph(d) if have_dot2tex(): G.set_latex_options(format="dot2tex", edge_labels = True, color_by_label = self.cartan_type()._index_set_coloring, edge_options = lambda (u,v,label): ({"backward":label ==0})) return G
def digraph(self, subset=None, index_set=None): """ Return the DiGraph associated with ``self``. See :meth:`~sage.categories.crystals.ParentMethods.digraph()` for more information. EXAMPLES:: sage: A = crystals.KirillovReshetikhin(['A',2,1], 2, 2).affinization() sage: S = A.subcrystal(max_depth=3) sage: G = A.digraph(subset=S) """ G = super(AffinizationOfCrystal, self).digraph(subset, index_set) from sage.graphs.dot2tex_utils import have_dot2tex if have_dot2tex(): G.set_latex_options(edge_options = lambda (u,v,label): ({})) return G
def digraph(self, subset=None, index_set=None): """ Return the DiGraph associated with ``self``. See :meth:`~sage.categories.crystals.ParentMethods.digraph()` for more information. EXAMPLES:: sage: A = crystals.KirillovReshetikhin(['A',2,1], 2, 2).affinization() sage: S = A.subcrystal(max_depth=3) sage: G = A.digraph(subset=S) """ G = super(AffinizationOfCrystal, self).digraph(subset, index_set) from sage.graphs.dot2tex_utils import have_dot2tex if have_dot2tex(): G.set_latex_options(edge_options=lambda (u, v, label): ({})) return G
def _latex_(self, **options): r""" Return a latex representation of this Kleber tree. EXAMPLES:: sage: KT = KleberTree(['D', 3], [[2,1], [2,1]]) sage: KT._latex_() #optional - dot2tex ... sage: view(KT, pdflatex=True, tightpage=True) #optional - dot2tex graphviz """ if not have_dot2tex(): print "dot2tex not available. Install after running \'sage -sh\'" return G = self.digraph() G.set_latex_options(format="dot2tex", edge_labels=True, **options) return G._latex_()
def digraph(self): r""" Return a DiGraph representation of this Kleber tree. EXAMPLES:: sage: KT = KleberTree(['D', 4], [[2, 2]]) sage: KT.digraph() Digraph on 3 vertices """ d = {} for x in self: d[x] = {} if x.parent_node is None: continue d[x][x.parent_node] = tuple(x.up_root.to_vector()) G = DiGraph(d) if have_dot2tex(): G.set_latex_options(format="dot2tex", edge_labels=True) # edge_options = lambda (u,v,label): ({"backward":label ==0})) return G
def _latex_(self, **options): r""" Returns the crystal graph as a latex string. This can be exported to a file with self.latex_file('filename'). EXAMPLES:: sage: T = crystals.Tableaux(['A',2],shape=[1]) sage: T._latex_() #optional - dot2tex '...tikzpicture...' sage: view(T, pdflatex = True, tightpage = True) #optional - dot2tex graphviz One can for example also color the edges using the following options:: sage: T = crystals.Tableaux(['A',2],shape=[1]) sage: T._latex_(color_by_label = {0:"black", 1:"red", 2:"blue"}) #optional - dot2tex graphviz '...tikzpicture...' """ if not have_dot2tex(): print "dot2tex not available. Install after running \'sage -sh\'" return G=self.digraph() G.set_latex_options(**options) return G._latex_()
def digraph(self, subset=None, index_set=None, depth=None): """ Return the DiGraph associated to ``self``. INPUT: - ``subset`` -- (optional) a subset of vertices for which the digraph should be constructed - ``index_set`` -- (optional) the index set to draw arrows - ``depth`` -- the depth to draw; optional only for finite crystals EXAMPLES:: sage: T = crystals.Tableaux(['A',2], shape=[2,1]) sage: T.digraph() Digraph on 8 vertices sage: S = T.subcrystal(max_depth=2) sage: len(S) 5 sage: G = T.digraph(subset=list(S)) sage: G.is_isomorphic(T.digraph(depth=2), edge_labels=True) True TESTS: The following example demonstrates the speed improvement. The speedup in non-affine types is small however:: sage: depth = 5 sage: C = crystals.AlcovePaths(['A',2,1], [1,1,0]) sage: general_digraph = Crystals().parent_class.digraph sage: S = C.subcrystal(max_depth=depth, direction='lower') sage: %timeit C.digraph(depth=depth) # not tested 10 loops, best of 3: 48.9 ms per loop sage: %timeit general_digraph(C, subset=S) # not tested 10 loops, best of 3: 96.5 ms per loop sage: G1 = C.digraph(depth=depth) sage: G2 = general_digraph(C, subset=S) sage: G1.is_isomorphic(G2, edge_labels=True) True """ if subset is not None: return Crystals().parent_class.digraph(self, subset, index_set) if self not in Crystals().Finite() and depth is None: raise NotImplementedError("crystals not known to be finite must" " specify either the subset or depth") from sage.graphs.all import DiGraph if index_set is None: index_set = self.index_set() rank = 0 d = {g: {} for g in self.module_generators} visited = set(d.keys()) while depth is None or rank < depth: recently_visited = set() for x in visited: d.setdefault(x, {}) # does nothing if there's a default for i in index_set: xfi = x.f(i) if xfi is not None: d[x][xfi] = i recently_visited.add(xfi) if not recently_visited: # No new nodes, nothing more to do break rank += 1 visited = recently_visited G = DiGraph(d) if have_dot2tex(): G.set_latex_options(format="dot2tex", edge_labels=True, color_by_label=self.cartan_type()._index_set_coloring) return G
def run(self, verbose=False, build_graph=False): """ Run the bijection from rigged configurations to tensor product of KR tableaux. INPUT: - ``verbose`` -- (default: ``False``) display each step in the bijection - ``build_graph`` -- (default: ``False``) build the graph of each step of the bijection EXAMPLES:: sage: RC = RiggedConfigurations(['A', 4, 1], [[2, 1]]) sage: x = RC(partition_list=[[1],[1],[1],[1]]) sage: from sage.combinat.rigged_configurations.bij_type_A import RCToKRTBijectionTypeA sage: RCToKRTBijectionTypeA(x).run() [[2], [5]] sage: bij = RCToKRTBijectionTypeA(x) sage: bij.run(build_graph=True) [[2], [5]] sage: bij._graph Digraph on 3 vertices """ from sage.combinat.crystals.letters import CrystalOfLetters letters = CrystalOfLetters( self.rigged_con.parent()._cartan_type.classical()) # This is technically bad, but because the first thing we do is append # an empty list to ret_crystal_path, we correct this. We do it this # way so that we do not have to remove an empty list after the # bijection has been performed. ret_crystal_path = [] for dim in self.rigged_con.parent().dims: ret_crystal_path.append([]) # Iterate over each column for dummy_var in range(dim[1]): # Split off a new column if necessary if self.cur_dims[0][1] > 1: if verbose: print("====================") print( repr(self.rigged_con.parent()( *self.cur_partitions, use_vacancy_numbers=True))) print("--------------------") print(ret_crystal_path) print("--------------------\n") print("Applying column split") self.cur_dims[0][1] -= 1 self.cur_dims.insert(0, [dim[0], 1]) # Perform the corresponding splitting map on rigged configurations # All it does is update the vacancy numbers on the RC side for a in range(self.n): self._update_vacancy_numbers(a) if build_graph: y = self.rigged_con.parent()( *[x._clone() for x in self.cur_partitions], use_vacancy_numbers=True) self._graph.append( [self._graph[-1][1], (y, len(self._graph)), 'ls']) while self.cur_dims[0][0] > 0: if verbose: print("====================") print( repr(self.rigged_con.parent()( *self.cur_partitions, use_vacancy_numbers=True))) print("--------------------") print(ret_crystal_path) print("--------------------\n") self.cur_dims[0][0] -= 1 # This takes care of the indexing b = self.next_state(self.cur_dims[0][0]) # Make sure we have a crystal letter ret_crystal_path[-1].append(letters(b)) # Append the rank if build_graph: y = self.rigged_con.parent()( *[x._clone() for x in self.cur_partitions], use_vacancy_numbers=True) self._graph.append([ self._graph[-1][1], (y, len(self._graph)), letters(b) ]) self.cur_dims.pop(0) # Pop off the leading column if build_graph: self._graph.pop(0) # Remove the dummy at the start from sage.graphs.digraph import DiGraph from sage.graphs.dot2tex_utils import have_dot2tex self._graph = DiGraph(self._graph) if have_dot2tex(): self._graph.set_latex_options(format="dot2tex", edge_labels=True) # Basic check to make sure we end with the empty configuration #tot_len = sum([len(rp) for rp in self.cur_partitions]) #if tot_len != 0: # print "Invalid bijection end for:" # print self.rigged_con # print "-----------------------" # print self.cur_partitions # raise ValueError("Invalid bijection end") return self.KRT(pathlist=ret_crystal_path)
def digraph(self, subset=None, index_set=None, depth=None): """ Return the DiGraph associated to ``self``. INPUT: - ``subset`` -- (optional) a subset of vertices for which the digraph should be constructed - ``index_set`` -- (optional) the index set to draw arrows - ``depth`` -- the depth to draw; optional only for finite crystals EXAMPLES:: sage: T = crystals.Tableaux(['A',2], shape=[2,1]) sage: T.digraph() Digraph on 8 vertices sage: S = T.subcrystal(max_depth=2) sage: len(S) 5 sage: G = T.digraph(subset=list(S)) sage: G.is_isomorphic(T.digraph(depth=2), edge_labels=True) True TESTS: The following example demonstrates the speed improvement. The speedup in non-affine types is small however:: sage: depth = 5 sage: C = crystals.AlcovePaths(['A',2,1], [1,1,0]) sage: general_digraph = Crystals().parent_class.digraph sage: S = C.subcrystal(max_depth=depth, direction='lower') sage: %timeit C.digraph(depth=depth) # not tested 10 loops, best of 3: 48.9 ms per loop sage: %timeit general_digraph(C, subset=S) # not tested 10 loops, best of 3: 96.5 ms per loop sage: G1 = C.digraph(depth=depth) sage: G2 = general_digraph(C, subset=S) sage: G1.is_isomorphic(G2, edge_labels=True) True """ if subset is not None: return Crystals().parent_class.digraph(self, subset, index_set) if self not in Crystals().Finite() and depth is None: raise NotImplementedError( "crystals not known to be finite must" " specify either the subset or depth") from sage.graphs.all import DiGraph if index_set is None: index_set = self.index_set() rank = 0 d = {g: {} for g in self.module_generators} visited = set(d.keys()) while depth is None or rank < depth: recently_visited = set() for x in visited: d.setdefault(x, {}) # does nothing if there's a default for i in index_set: xfi = x.f(i) if xfi is not None: d[x][xfi] = i recently_visited.add(xfi) if not recently_visited: # No new nodes, nothing more to do break rank += 1 visited = recently_visited G = DiGraph(d) if have_dot2tex(): G.set_latex_options( format="dot2tex", edge_labels=True, color_by_label=self.cartan_type()._index_set_coloring) return G
def dual_equivalence_graph(self, X=None, index_set=None, directed=True): r""" Return the dual equivalence graph indexed by ``index_set`` on the subset ``X`` of ``self``. Let `b \in B` be an element of weight `0`, so `\varepsilon_j(b) = \varphi_j(b)` for all `j \in I`, where `I` is the indexing set. We say `b'` is an `i`-elementary dual equivalence transformation of `b` (where `i \in I`) if * `\varepsilon_i(b) = 1` and `\varepsilon_{i-1}(b) = 0`, and * `b' = f_{i-1} f_i e_{i-1} e_i b`. We can do the inverse procedure by interchanging `i` and `i-1` above. .. NOTE:: If the index set is not an ordered interval, we let `i - 1` mean the index appearing before `i` in `I`. This definition comes from [Assaf08]_ Section 4 (where our `\varphi_j(b)` and `\varepsilon_j(b)` are denoted by `\epsilon(b, j)` and `-\delta(b, j)`, respectively). The dual equivalence graph of `B` is defined to be the colored graph whose vertices are the elements of `B` of weight `0`, and whose edges of color `i` (for `i \in I`) connect pairs `\{ b, b' \}` such that `b'` is an `i`-elementary dual equivalence transformation of `b`. .. NOTE:: This dual equivalence graph is a generalization of `\mathcal{G}\left(\mathcal{X}\right)` in [Assaf08]_ Section 4 except we do not require `\varepsilon_i(b) = 0, 1` for all `i`. This definition can be generalized by choosing a subset `X` of the set of all vertices of `B` of weight `0`, and restricting the dual equivalence graph to the vertex set `X`. INPUT: - ``X`` -- (optional) the vertex set `X` (default: the whole set of vertices of ``self`` of weight `0`) - ``index_set`` -- (optional) the index set `I` (default: the whole index set of ``self``); this has to be a subset of the index set of ``self`` (as a list or tuple) - ``directed`` -- (default: ``True``) whether to have the dual equivalence graph be directed, where the head of an edge `b - b'` is `b` and the tail is `b' = f_{i-1} f_i e_{i-1} e_i b`) .. SEEALSO:: :meth:`sage.combinat.partition.Partition.dual_equivalence_graph` REFERENCES: .. [Assaf08] Sami Assaf. *A combinatorial realization of Schur-Weyl duality via crystal graphs and dual equivalence graphs*. FPSAC 2008, 141-152, Discrete Math. Theor. Comput. Sci. Proc., AJ, Assoc. Discrete Math. Theor. Comput. Sci., (2008). :arxiv:`0804.1587v1` EXAMPLES:: sage: T = crystals.Tableaux(['A',3], shape=[2,2]) sage: G = T.dual_equivalence_graph() sage: sorted(G.edges()) [([[1, 3], [2, 4]], [[1, 2], [3, 4]], 2), ([[1, 2], [3, 4]], [[1, 3], [2, 4]], 3)] sage: T = crystals.Tableaux(['A',4], shape=[3,2]) sage: G = T.dual_equivalence_graph() sage: sorted(G.edges()) [([[1, 3, 5], [2, 4]], [[1, 3, 4], [2, 5]], 4), ([[1, 3, 5], [2, 4]], [[1, 2, 5], [3, 4]], 2), ([[1, 3, 4], [2, 5]], [[1, 2, 4], [3, 5]], 2), ([[1, 2, 5], [3, 4]], [[1, 3, 5], [2, 4]], 3), ([[1, 2, 4], [3, 5]], [[1, 2, 3], [4, 5]], 3), ([[1, 2, 3], [4, 5]], [[1, 2, 4], [3, 5]], 4)] sage: T = crystals.Tableaux(['A',4], shape=[3,1]) sage: G = T.dual_equivalence_graph(index_set=[1,2,3]) sage: G.vertices() [[[1, 3, 4], [2]], [[1, 2, 4], [3]], [[1, 2, 3], [4]]] sage: G.edges() [([[1, 3, 4], [2]], [[1, 2, 4], [3]], 2), ([[1, 2, 4], [3]], [[1, 2, 3], [4]], 3)] TESTS:: sage: T = crystals.Tableaux(['A',4], shape=[3,1]) sage: G = T.dual_equivalence_graph(index_set=[2,3]) sage: sorted(G.edges()) [([[1, 2, 4], [3]], [[1, 2, 3], [4]], 3), ([[2, 4, 5], [3]], [[2, 3, 5], [4]], 3)] sage: sorted(G.vertices()) [[[1, 3, 4], [2]], [[1, 2, 4], [3]], [[2, 4, 5], [3]], [[1, 2, 3], [4]], [[2, 3, 5], [4]], [[1, 1, 1], [5]], [[1, 1, 5], [5]], [[1, 5, 5], [5]], [[2, 3, 4], [5]]] """ if index_set is None: index_set = self.index_set() def wt_zero(x): for i in index_set: if x.epsilon(i) != x.phi(i): return False return True if X is None: X = [x for x in self if wt_zero(x)] checker = lambda x: True elif any(not wt_zero(x) for x in X): raise ValueError("the elements are not all weight 0") else: checker = lambda x: x in X edges = [] for x in X: for k, i in enumerate(index_set[1:]): im = index_set[k] if x.epsilon(i) == 1 and x.epsilon(im) == 0: y = x.e(i).e(im).f(i).f(im) if checker(y): edges.append([x, y, i]) from sage.graphs.all import DiGraph G = DiGraph([X, edges], format="vertices_and_edges", immutable=True) if have_dot2tex(): G.set_latex_options(format="dot2tex", edge_labels=True, color_by_label=self.cartan_type()._index_set_coloring) return G
def dual_equivalence_class(self, index_set=None): r""" Return the dual equivalence class indexed by ``index_set`` of ``self``. The dual equivalence class of an element `b \in B` is the set of all elements of `B` reachable from `b` via sequences of `i`-elementary dual equivalence relations (i.e., `i`-elementary dual equivalence transformations and their inverses) for `i` in the index set of `B`. For this to be well-defined, the element `b` has to be of weight `0` with respect to `I`; that is, we need to have `\varepsilon_j(b) = \varphi_j(b)` for all `j \in I`. See [Assaf08]_. See also :meth:`dual_equivalence_graph` for a definition of `i`-elementary dual equivalence transformations. INPUT: - ``index_set`` -- (optional) the index set `I` (default: the whole index set of the crystal); this has to be a subset of the index set of the crystal (as a list or tuple) OUTPUT: The dual equivalence class of ``self`` indexed by the subset ``index_set``. This class is returned as an undirected edge-colored multigraph. The color of an edge is the index `i` of the dual equivalence relation it encodes. .. SEEALSO:: - :meth:`~sage.categories.regular_crystals.RegularCrystals.ParentMethods.dual_equivalence_graph` - :meth:`sage.combinat.partition.Partition.dual_equivalence_graph` EXAMPLES:: sage: T = crystals.Tableaux(['A',3], shape=[2,2]) sage: G = T(2,1,4,3).dual_equivalence_class() sage: sorted(G.edges()) [([[1, 3], [2, 4]], [[1, 2], [3, 4]], 2), ([[1, 3], [2, 4]], [[1, 2], [3, 4]], 3)] sage: T = crystals.Tableaux(['A',4], shape=[3,2]) sage: G = T(2,1,4,3,5).dual_equivalence_class() sage: sorted(G.edges()) [([[1, 3, 5], [2, 4]], [[1, 3, 4], [2, 5]], 4), ([[1, 3, 5], [2, 4]], [[1, 2, 5], [3, 4]], 2), ([[1, 3, 5], [2, 4]], [[1, 2, 5], [3, 4]], 3), ([[1, 3, 4], [2, 5]], [[1, 2, 4], [3, 5]], 2), ([[1, 2, 4], [3, 5]], [[1, 2, 3], [4, 5]], 3), ([[1, 2, 4], [3, 5]], [[1, 2, 3], [4, 5]], 4)] """ if index_set is None: index_set = self.index_set() for i in index_set: if self.epsilon(i) != self.phi(i): raise ValueError("the element is not weight 0") visited = set([]) todo = set([self]) edges = [] while todo: x = todo.pop() visited.add(x) for k, i in enumerate(index_set[1:]): im = index_set[k] if x.epsilon(i) == 1 and x.epsilon(im) == 0: y = x.e(i).e(im).f(i).f(im) if [y, x, i] not in edges: edges.append([x, y, i]) if y not in visited: todo.add(y) if x.epsilon(i) == 0 and x.epsilon(im) == 1: y = x.e(im).e(i).f(im).f(i) if [y, x, i] not in edges: edges.append([x, y, i]) if y not in visited: todo.add(y) from sage.graphs.graph import Graph G = Graph([visited, edges], format="vertices_and_edges", immutable=True, multiedges=True) if have_dot2tex(): G.set_latex_options(format="dot2tex", edge_labels=True, color_by_label=self.cartan_type()._index_set_coloring) return G
def dual_equivalence_graph(self, X=None, index_set=None, directed=True): r""" Return the dual equivalence graph indexed by ``index_set`` on the subset ``X`` of ``self``. Let `b \in B` be an element of weight `0`, so `\varepsilon_j(b) = \varphi_j(b)` for all `j \in I`, where `I` is the indexing set. We say `b'` is an `i`-elementary dual equivalence transformation of `b` (where `i \in I`) if * `\varepsilon_i(b) = 1` and `\varepsilon_{i-1}(b) = 0`, and * `b' = f_{i-1} f_i e_{i-1} e_i b`. We can do the inverse procedure by interchanging `i` and `i-1` above. .. NOTE:: If the index set is not an ordered interval, we let `i - 1` mean the index appearing before `i` in `I`. This definition comes from [As2008]_ Section 4 (where our `\varphi_j(b)` and `\varepsilon_j(b)` are denoted by `\epsilon(b, j)` and `-\delta(b, j)`, respectively). The dual equivalence graph of `B` is defined to be the colored graph whose vertices are the elements of `B` of weight `0`, and whose edges of color `i` (for `i \in I`) connect pairs `\{ b, b' \}` such that `b'` is an `i`-elementary dual equivalence transformation of `b`. .. NOTE:: This dual equivalence graph is a generalization of `\mathcal{G}\left(\mathcal{X}\right)` in [As2008]_ Section 4 except we do not require `\varepsilon_i(b) = 0, 1` for all `i`. This definition can be generalized by choosing a subset `X` of the set of all vertices of `B` of weight `0`, and restricting the dual equivalence graph to the vertex set `X`. INPUT: - ``X`` -- (optional) the vertex set `X` (default: the whole set of vertices of ``self`` of weight `0`) - ``index_set`` -- (optional) the index set `I` (default: the whole index set of ``self``); this has to be a subset of the index set of ``self`` (as a list or tuple) - ``directed`` -- (default: ``True``) whether to have the dual equivalence graph be directed, where the head of an edge `b - b'` is `b` and the tail is `b' = f_{i-1} f_i e_{i-1} e_i b`) .. SEEALSO:: :meth:`sage.combinat.partition.Partition.dual_equivalence_graph` EXAMPLES:: sage: T = crystals.Tableaux(['A',3], shape=[2,2]) sage: G = T.dual_equivalence_graph() sage: sorted(G.edges()) [([[1, 3], [2, 4]], [[1, 2], [3, 4]], 2), ([[1, 2], [3, 4]], [[1, 3], [2, 4]], 3)] sage: T = crystals.Tableaux(['A',4], shape=[3,2]) sage: G = T.dual_equivalence_graph() sage: sorted(G.edges()) [([[1, 3, 5], [2, 4]], [[1, 3, 4], [2, 5]], 4), ([[1, 3, 5], [2, 4]], [[1, 2, 5], [3, 4]], 2), ([[1, 3, 4], [2, 5]], [[1, 2, 4], [3, 5]], 2), ([[1, 2, 5], [3, 4]], [[1, 3, 5], [2, 4]], 3), ([[1, 2, 4], [3, 5]], [[1, 2, 3], [4, 5]], 3), ([[1, 2, 3], [4, 5]], [[1, 2, 4], [3, 5]], 4)] sage: T = crystals.Tableaux(['A',4], shape=[3,1]) sage: G = T.dual_equivalence_graph(index_set=[1,2,3]) sage: G.vertices() [[[1, 3, 4], [2]], [[1, 2, 4], [3]], [[1, 2, 3], [4]]] sage: G.edges() [([[1, 3, 4], [2]], [[1, 2, 4], [3]], 2), ([[1, 2, 4], [3]], [[1, 2, 3], [4]], 3)] TESTS:: sage: T = crystals.Tableaux(['A',4], shape=[3,1]) sage: G = T.dual_equivalence_graph(index_set=[2,3]) sage: sorted(G.edges()) [([[1, 2, 4], [3]], [[1, 2, 3], [4]], 3), ([[2, 4, 5], [3]], [[2, 3, 5], [4]], 3)] sage: sorted(G.vertices()) [[[1, 3, 4], [2]], [[1, 2, 4], [3]], [[2, 4, 5], [3]], [[1, 2, 3], [4]], [[2, 3, 5], [4]], [[1, 1, 1], [5]], [[1, 1, 5], [5]], [[1, 5, 5], [5]], [[2, 3, 4], [5]]] """ if index_set is None: index_set = self.index_set() def wt_zero(x): for i in index_set: if x.epsilon(i) != x.phi(i): return False return True if X is None: X = [x for x in self if wt_zero(x)] checker = lambda x: True elif any(not wt_zero(x) for x in X): raise ValueError("the elements are not all weight 0") else: checker = lambda x: x in X edges = [] for x in X: for k, i in enumerate(index_set[1:]): im = index_set[k] if x.epsilon(i) == 1 and x.epsilon(im) == 0: y = x.e(i).e(im).f(i).f(im) if checker(y): edges.append([x, y, i]) from sage.graphs.all import DiGraph G = DiGraph([X, edges], format="vertices_and_edges", immutable=True) if have_dot2tex(): G.set_latex_options( format="dot2tex", edge_labels=True, color_by_label=self.cartan_type()._index_set_coloring) return G
def dual_equivalence_class(self, index_set=None): r""" Return the dual equivalence class indexed by ``index_set`` of ``self``. The dual equivalence class of an element `b \in B` is the set of all elements of `B` reachable from `b` via sequences of `i`-elementary dual equivalence relations (i.e., `i`-elementary dual equivalence transformations and their inverses) for `i` in the index set of `B`. For this to be well-defined, the element `b` has to be of weight `0` with respect to `I`; that is, we need to have `\varepsilon_j(b) = \varphi_j(b)` for all `j \in I`. See [As2008]_. See also :meth:`dual_equivalence_graph` for a definition of `i`-elementary dual equivalence transformations. INPUT: - ``index_set`` -- (optional) the index set `I` (default: the whole index set of the crystal); this has to be a subset of the index set of the crystal (as a list or tuple) OUTPUT: The dual equivalence class of ``self`` indexed by the subset ``index_set``. This class is returned as an undirected edge-colored multigraph. The color of an edge is the index `i` of the dual equivalence relation it encodes. .. SEEALSO:: - :meth:`~sage.categories.regular_crystals.RegularCrystals.ParentMethods.dual_equivalence_graph` - :meth:`sage.combinat.partition.Partition.dual_equivalence_graph` EXAMPLES:: sage: T = crystals.Tableaux(['A',3], shape=[2,2]) sage: G = T(2,1,4,3).dual_equivalence_class() sage: sorted(G.edges()) [([[1, 3], [2, 4]], [[1, 2], [3, 4]], 2), ([[1, 3], [2, 4]], [[1, 2], [3, 4]], 3)] sage: T = crystals.Tableaux(['A',4], shape=[3,2]) sage: G = T(2,1,4,3,5).dual_equivalence_class() sage: sorted(G.edges()) [([[1, 3, 5], [2, 4]], [[1, 3, 4], [2, 5]], 4), ([[1, 3, 5], [2, 4]], [[1, 2, 5], [3, 4]], 2), ([[1, 3, 5], [2, 4]], [[1, 2, 5], [3, 4]], 3), ([[1, 3, 4], [2, 5]], [[1, 2, 4], [3, 5]], 2), ([[1, 2, 4], [3, 5]], [[1, 2, 3], [4, 5]], 3), ([[1, 2, 4], [3, 5]], [[1, 2, 3], [4, 5]], 4)] """ if index_set is None: index_set = self.index_set() for i in index_set: if self.epsilon(i) != self.phi(i): raise ValueError("the element is not weight 0") visited = set([]) todo = set([self]) edges = [] while todo: x = todo.pop() visited.add(x) for k, i in enumerate(index_set[1:]): im = index_set[k] if x.epsilon(i) == 1 and x.epsilon(im) == 0: y = x.e(i).e(im).f(i).f(im) if [y, x, i] not in edges: edges.append([x, y, i]) if y not in visited: todo.add(y) if x.epsilon(i) == 0 and x.epsilon(im) == 1: y = x.e(im).e(i).f(im).f(i) if [y, x, i] not in edges: edges.append([x, y, i]) if y not in visited: todo.add(y) from sage.graphs.graph import Graph G = Graph([visited, edges], format="vertices_and_edges", immutable=True, multiedges=True) if have_dot2tex(): G.set_latex_options( format="dot2tex", edge_labels=True, color_by_label=self.cartan_type()._index_set_coloring) return G
def markov_chain_digraph(self, action = 'promotion', labeling = 'identity'): r""" Returns the digraph of the action of generalized promotion or tau on ``self`` INPUT: - ``action`` -- 'promotion' or 'tau' (default: 'promotion') - ``labeling`` -- 'identity' or 'source' (default: 'identity') .. todo:: - generalize this feature by accepting a family of operators as input - move up in some appropriate category This method creates a graph with vertices being the linear extensions of a given finite poset and an edge from `\pi` to `\pi'` if `\pi' = \pi \partial_i` where `\partial_i` is the promotion operator (see :meth:`promotion`) if ``action`` is set to ``promotion`` and `\tau_i` (see :meth:`tau`) if ``action`` is set to ``tau``. The label of the edge is `i` (resp. `\pi_i`) if ``labeling`` is set to ``identity`` (resp. ``source``). EXAMPLES:: sage: P = Poset(([1,2,3,4], [[1,3],[1,4],[2,3]]), linear_extension = True) sage: L = P.linear_extensions() sage: G = L.markov_chain_digraph(); G Looped multi-digraph on 5 vertices sage: sorted(G.vertices(), key = repr) [[1, 2, 3, 4], [1, 2, 4, 3], [1, 4, 2, 3], [2, 1, 3, 4], [2, 1, 4, 3]] sage: sorted(G.edges(), key = repr) [([1, 2, 3, 4], [1, 2, 3, 4], 4), ([1, 2, 3, 4], [1, 2, 4, 3], 2), ([1, 2, 3, 4], [1, 2, 4, 3], 3), ([1, 2, 3, 4], [2, 1, 4, 3], 1), ([1, 2, 4, 3], [1, 2, 3, 4], 3), ([1, 2, 4, 3], [1, 2, 4, 3], 4), ([1, 2, 4, 3], [1, 4, 2, 3], 2), ([1, 2, 4, 3], [2, 1, 3, 4], 1), ([1, 4, 2, 3], [1, 2, 3, 4], 1), ([1, 4, 2, 3], [1, 2, 3, 4], 2), ([1, 4, 2, 3], [1, 4, 2, 3], 3), ([1, 4, 2, 3], [1, 4, 2, 3], 4), ([2, 1, 3, 4], [1, 2, 4, 3], 1), ([2, 1, 3, 4], [2, 1, 3, 4], 4), ([2, 1, 3, 4], [2, 1, 4, 3], 2), ([2, 1, 3, 4], [2, 1, 4, 3], 3), ([2, 1, 4, 3], [1, 4, 2, 3], 1), ([2, 1, 4, 3], [2, 1, 3, 4], 2), ([2, 1, 4, 3], [2, 1, 3, 4], 3), ([2, 1, 4, 3], [2, 1, 4, 3], 4)] sage: G = L.markov_chain_digraph(labeling = 'source') sage: sorted(G.vertices(), key = repr) [[1, 2, 3, 4], [1, 2, 4, 3], [1, 4, 2, 3], [2, 1, 3, 4], [2, 1, 4, 3]] sage: sorted(G.edges(), key = repr) [([1, 2, 3, 4], [1, 2, 3, 4], 4), ([1, 2, 3, 4], [1, 2, 4, 3], 2), ([1, 2, 3, 4], [1, 2, 4, 3], 3), ([1, 2, 3, 4], [2, 1, 4, 3], 1), ([1, 2, 4, 3], [1, 2, 3, 4], 4), ([1, 2, 4, 3], [1, 2, 4, 3], 3), ([1, 2, 4, 3], [1, 4, 2, 3], 2), ([1, 2, 4, 3], [2, 1, 3, 4], 1), ([1, 4, 2, 3], [1, 2, 3, 4], 1), ([1, 4, 2, 3], [1, 2, 3, 4], 4), ([1, 4, 2, 3], [1, 4, 2, 3], 2), ([1, 4, 2, 3], [1, 4, 2, 3], 3), ([2, 1, 3, 4], [1, 2, 4, 3], 2), ([2, 1, 3, 4], [2, 1, 3, 4], 4), ([2, 1, 3, 4], [2, 1, 4, 3], 1), ([2, 1, 3, 4], [2, 1, 4, 3], 3), ([2, 1, 4, 3], [1, 4, 2, 3], 2), ([2, 1, 4, 3], [2, 1, 3, 4], 1), ([2, 1, 4, 3], [2, 1, 3, 4], 4), ([2, 1, 4, 3], [2, 1, 4, 3], 3)] The edges of the graph are by default colored using blue for edge 1, red for edge 2, green for edge 3, and yellow for edge 4:: sage: view(G) #optional - dot2tex graphviz Alternatively, one may get the graph of the action of the ``tau`` operator:: sage: G = L.markov_chain_digraph(action='tau'); G Looped multi-digraph on 5 vertices sage: sorted(G.vertices(), key = repr) [[1, 2, 3, 4], [1, 2, 4, 3], [1, 4, 2, 3], [2, 1, 3, 4], [2, 1, 4, 3]] sage: sorted(G.edges(), key = repr) [([1, 2, 3, 4], [1, 2, 3, 4], 2), ([1, 2, 3, 4], [1, 2, 4, 3], 3), ([1, 2, 3, 4], [2, 1, 3, 4], 1), ([1, 2, 4, 3], [1, 2, 3, 4], 3), ([1, 2, 4, 3], [1, 4, 2, 3], 2), ([1, 2, 4, 3], [2, 1, 4, 3], 1), ([1, 4, 2, 3], [1, 2, 4, 3], 2), ([1, 4, 2, 3], [1, 4, 2, 3], 1), ([1, 4, 2, 3], [1, 4, 2, 3], 3), ([2, 1, 3, 4], [1, 2, 3, 4], 1), ([2, 1, 3, 4], [2, 1, 3, 4], 2), ([2, 1, 3, 4], [2, 1, 4, 3], 3), ([2, 1, 4, 3], [1, 2, 4, 3], 1), ([2, 1, 4, 3], [2, 1, 3, 4], 3), ([2, 1, 4, 3], [2, 1, 4, 3], 2)] sage: view(G) #optional - dot2tex graphviz .. seealso:: :meth:`markov_chain_transition_matrix`, :meth:`promotion`, :meth:`tau` TESTS:: sage: P = Poset(([1,2,3,4], [[1,3],[1,4],[2,3]]), linear_extension = True, facade = True) sage: L = P.linear_extensions() sage: G = L.markov_chain_digraph(labeling = 'source'); G Looped multi-digraph on 5 vertices """ d = dict([x,dict([y,[]] for y in self)] for x in self) if action == 'promotion': R = range(self.poset().cardinality()) else: R = range(self.poset().cardinality()-1) if labeling == 'source': for x in self: for i in R: child = getattr(x, action)(i+1) d[x][child]+=[self.poset().unwrap(x[i])] else: for x in self: for i in R: child = getattr(x, action)(i+1) d[x][child]+=[i+1] G = DiGraph(d) if have_dot2tex(): G.set_latex_options(format="dot2tex", edge_labels = True, color_by_label = {1:"blue", 2:"red", 3:"green", 4:"yellow"}) #G.set_latex_options(format="dot2tex", edge_labels = True, color_by_label = {1:"green", 2:"blue", 3:"brown", 4:"red"}) return G
def markov_chain_digraph(self, action='promotion', labeling='identity'): r""" Returns the digraph of the action of generalized promotion or tau on ``self`` INPUT: - ``action`` -- 'promotion' or 'tau' (default: 'promotion') - ``labeling`` -- 'identity' or 'source' (default: 'identity') .. todo:: - generalize this feature by accepting a family of operators as input - move up in some appropriate category This method creates a graph with vertices being the linear extensions of a given finite poset and an edge from `\pi` to `\pi'` if `\pi' = \pi \partial_i` where `\partial_i` is the promotion operator (see :meth:`promotion`) if ``action`` is set to ``promotion`` and `\tau_i` (see :meth:`tau`) if ``action`` is set to ``tau``. The label of the edge is `i` (resp. `\pi_i`) if ``labeling`` is set to ``identity`` (resp. ``source``). EXAMPLES:: sage: P = Poset(([1,2,3,4], [[1,3],[1,4],[2,3]]), linear_extension = True) sage: L = P.linear_extensions() sage: G = L.markov_chain_digraph(); G Looped multi-digraph on 5 vertices sage: sorted(G.vertices(), key = repr) [[1, 2, 3, 4], [1, 2, 4, 3], [1, 4, 2, 3], [2, 1, 3, 4], [2, 1, 4, 3]] sage: sorted(G.edges(), key = repr) [([1, 2, 3, 4], [1, 2, 3, 4], 4), ([1, 2, 3, 4], [1, 2, 4, 3], 2), ([1, 2, 3, 4], [1, 2, 4, 3], 3), ([1, 2, 3, 4], [2, 1, 4, 3], 1), ([1, 2, 4, 3], [1, 2, 3, 4], 3), ([1, 2, 4, 3], [1, 2, 4, 3], 4), ([1, 2, 4, 3], [1, 4, 2, 3], 2), ([1, 2, 4, 3], [2, 1, 3, 4], 1), ([1, 4, 2, 3], [1, 2, 3, 4], 1), ([1, 4, 2, 3], [1, 2, 3, 4], 2), ([1, 4, 2, 3], [1, 4, 2, 3], 3), ([1, 4, 2, 3], [1, 4, 2, 3], 4), ([2, 1, 3, 4], [1, 2, 4, 3], 1), ([2, 1, 3, 4], [2, 1, 3, 4], 4), ([2, 1, 3, 4], [2, 1, 4, 3], 2), ([2, 1, 3, 4], [2, 1, 4, 3], 3), ([2, 1, 4, 3], [1, 4, 2, 3], 1), ([2, 1, 4, 3], [2, 1, 3, 4], 2), ([2, 1, 4, 3], [2, 1, 3, 4], 3), ([2, 1, 4, 3], [2, 1, 4, 3], 4)] sage: G = L.markov_chain_digraph(labeling = 'source') sage: sorted(G.vertices(), key = repr) [[1, 2, 3, 4], [1, 2, 4, 3], [1, 4, 2, 3], [2, 1, 3, 4], [2, 1, 4, 3]] sage: sorted(G.edges(), key = repr) [([1, 2, 3, 4], [1, 2, 3, 4], 4), ([1, 2, 3, 4], [1, 2, 4, 3], 2), ([1, 2, 3, 4], [1, 2, 4, 3], 3), ([1, 2, 3, 4], [2, 1, 4, 3], 1), ([1, 2, 4, 3], [1, 2, 3, 4], 4), ([1, 2, 4, 3], [1, 2, 4, 3], 3), ([1, 2, 4, 3], [1, 4, 2, 3], 2), ([1, 2, 4, 3], [2, 1, 3, 4], 1), ([1, 4, 2, 3], [1, 2, 3, 4], 1), ([1, 4, 2, 3], [1, 2, 3, 4], 4), ([1, 4, 2, 3], [1, 4, 2, 3], 2), ([1, 4, 2, 3], [1, 4, 2, 3], 3), ([2, 1, 3, 4], [1, 2, 4, 3], 2), ([2, 1, 3, 4], [2, 1, 3, 4], 4), ([2, 1, 3, 4], [2, 1, 4, 3], 1), ([2, 1, 3, 4], [2, 1, 4, 3], 3), ([2, 1, 4, 3], [1, 4, 2, 3], 2), ([2, 1, 4, 3], [2, 1, 3, 4], 1), ([2, 1, 4, 3], [2, 1, 3, 4], 4), ([2, 1, 4, 3], [2, 1, 4, 3], 3)] The edges of the graph are by default colored using blue for edge 1, red for edge 2, green for edge 3, and yellow for edge 4:: sage: view(G) # optional - dot2tex graphviz, not tested (opens external window) Alternatively, one may get the graph of the action of the ``tau`` operator:: sage: G = L.markov_chain_digraph(action='tau'); G Looped multi-digraph on 5 vertices sage: sorted(G.vertices(), key = repr) [[1, 2, 3, 4], [1, 2, 4, 3], [1, 4, 2, 3], [2, 1, 3, 4], [2, 1, 4, 3]] sage: sorted(G.edges(), key = repr) [([1, 2, 3, 4], [1, 2, 3, 4], 2), ([1, 2, 3, 4], [1, 2, 4, 3], 3), ([1, 2, 3, 4], [2, 1, 3, 4], 1), ([1, 2, 4, 3], [1, 2, 3, 4], 3), ([1, 2, 4, 3], [1, 4, 2, 3], 2), ([1, 2, 4, 3], [2, 1, 4, 3], 1), ([1, 4, 2, 3], [1, 2, 4, 3], 2), ([1, 4, 2, 3], [1, 4, 2, 3], 1), ([1, 4, 2, 3], [1, 4, 2, 3], 3), ([2, 1, 3, 4], [1, 2, 3, 4], 1), ([2, 1, 3, 4], [2, 1, 3, 4], 2), ([2, 1, 3, 4], [2, 1, 4, 3], 3), ([2, 1, 4, 3], [1, 2, 4, 3], 1), ([2, 1, 4, 3], [2, 1, 3, 4], 3), ([2, 1, 4, 3], [2, 1, 4, 3], 2)] sage: view(G) # optional - dot2tex graphviz, not tested (opens external window) .. SEEALSO:: :meth:`markov_chain_transition_matrix`, :meth:`promotion`, :meth:`tau` TESTS:: sage: P = Poset(([1,2,3,4], [[1,3],[1,4],[2,3]]), linear_extension = True, facade = True) sage: L = P.linear_extensions() sage: G = L.markov_chain_digraph(labeling = 'source'); G Looped multi-digraph on 5 vertices """ d = dict([x, dict([y, []] for y in self)] for x in self) if action == 'promotion': R = list(range(self.poset().cardinality())) else: R = list(range(self.poset().cardinality() - 1)) if labeling == 'source': for x in self: for i in R: child = getattr(x, action)(i + 1) d[x][child] += [self.poset().unwrap(x[i])] else: for x in self: for i in R: child = getattr(x, action)(i + 1) d[x][child] += [i + 1] G = DiGraph(d, format="dict_of_dicts") if have_dot2tex(): G.set_latex_options(format="dot2tex", edge_labels=True, color_by_label={ 1: "blue", 2: "red", 3: "green", 4: "yellow" }) #G.set_latex_options(format="dot2tex", edge_labels = True, color_by_label = {1:"green", 2:"blue", 3:"brown", 4:"red"}) return G
def run(self, verbose=False, build_graph=False): """ Run the bijection from rigged configurations to tensor product of KR tableaux for type `B_n^{(1)}`. INPUT: - ``verbose`` -- (default: ``False``) display each step in the bijection - ``build_graph`` -- (default: ``False``) build the graph of each step of the bijection EXAMPLES:: sage: RC = RiggedConfigurations(['B', 3, 1], [[2, 1]]) sage: from sage.combinat.rigged_configurations.bij_type_B import RCToKRTBijectionTypeB sage: RCToKRTBijectionTypeB(RC(partition_list=[[1],[1,1],[1]])).run() [[3], [0]] sage: RC = RiggedConfigurations(['B', 3, 1], [[3, 1]]) sage: x = RC(partition_list=[[],[1],[1]]) sage: RCToKRTBijectionTypeB(x).run() [[1], [3], [-2]] sage: bij = RCToKRTBijectionTypeB(x) sage: bij.run(build_graph=True) [[1], [3], [-2]] sage: bij._graph Digraph on 6 vertices """ from sage.combinat.crystals.letters import CrystalOfLetters letters = CrystalOfLetters(self.rigged_con.parent()._cartan_type.classical()) # This is technically bad, but because the first thing we do is append # an empty list to ret_crystal_path, we correct this. We do it this # way so that we do not have to remove an empty list after the # bijection has been performed. ret_crystal_path = [] for dim in self.rigged_con.parent().dims: ret_crystal_path.append([]) # Check to see if we are a spinor if dim[0] == self.n: # Perform the spinor bijection by converting to type A_{2n-1}^{(2)} # doing the bijection there and pulling back from sage.combinat.rigged_configurations.bij_type_A2_odd import RCToKRTBijectionTypeA2Odd from sage.combinat.rigged_configurations.rigged_configurations import RiggedConfigurations from sage.combinat.rigged_configurations.rigged_partition import RiggedPartition, RiggedPartitionTypeB # Convert to a type A_{2n-1}^{(2)} RC RC = RiggedConfigurations(['A', 2*self.n-1, 2], self.cur_dims) if verbose: print("====================") print(repr(RC(*self.cur_partitions, use_vacancy_numbers=True))) print("--------------------") print(ret_crystal_path) print("--------------------\n") print("Applying doubling map\n") # Convert the n-th partition into a regular rigged partition self.cur_partitions[-1] = RiggedPartition(self.cur_partitions[-1]._list, self.cur_partitions[-1].rigging, self.cur_partitions[-1].vacancy_numbers) bij = RCToKRTBijectionTypeA2Odd(RC(*self.cur_partitions, use_vacancy_numbers=True)) for i in range(len(self.cur_dims)): if bij.cur_dims[i][0] != self.n: bij.cur_dims[i][1] *= 2 for i in range(self.n-1): for j in range(len(bij.cur_partitions[i])): bij.cur_partitions[i]._list[j] *= 2 bij.cur_partitions[i].rigging[j] *= 2 bij.cur_partitions[i].vacancy_numbers[j] *= 2 if build_graph: y = self.rigged_con.parent()(*[x._clone() for x in self.cur_partitions], use_vacancy_numbers=True) self._graph.append([self._graph[-1][1], (y, len(self._graph)), '2x']) # Perform the type A_{2n-1}^{(2)} bijection # Iterate over each column for dummy_var in range(dim[1]): # Split off a new column if necessary if bij.cur_dims[0][1] > 1: bij.cur_dims[0][1] -= 1 bij.cur_dims.insert(0, [dim[0], 1]) # Perform the corresponding splitting map on rigged configurations # All it does is update the vacancy numbers on the RC side for a in range(self.n): bij._update_vacancy_numbers(a) if build_graph: y = self.rigged_con.parent()(*[x._clone() for x in self.cur_partitions], use_vacancy_numbers=True) self._graph.append([self._graph[-1][1], (y, len(self._graph)), 'ls']) while bij.cur_dims[0][0]: # > 0: if verbose: print("====================") print(repr(RC(*bij.cur_partitions, use_vacancy_numbers=True))) print("--------------------") print(ret_crystal_path) print("--------------------\n") ht = bij.cur_dims[0][0] bij.cur_dims[0][0] = bij._next_index(ht) b = bij.next_state(ht) # Make sure we have a crystal letter ret_crystal_path[-1].append(letters(b)) # Append the rank if build_graph: y = self.rigged_con.parent()(*[x._clone() for x in self.cur_partitions], use_vacancy_numbers=True) self._graph.append([self._graph[-1][1], (y, len(self._graph)), letters(b)]) bij.cur_dims.pop(0) # Pop off the leading column self.cur_dims.pop(0) # Pop off the spin rectangle self.cur_partitions = bij.cur_partitions # Convert the n-th partition back into the special type B one self.cur_partitions[-1] = RiggedPartitionTypeB(self.cur_partitions[-1]) # Convert back to a type B_n^{(1)} if verbose: print("====================") print(repr(self.rigged_con.parent()(*bij.cur_partitions, use_vacancy_numbers=True))) print("--------------------") print(ret_crystal_path) print("--------------------\n") print("Applying halving map\n") for i in range(self.n-1): for j in range(len(self.cur_partitions[i])): self.cur_partitions[i]._list[j] //= 2 self.cur_partitions[i].rigging[j] //= 2 self.cur_partitions[i].vacancy_numbers[j] //= 2 if build_graph: y = self.rigged_con.parent()(*[x._clone() for x in self.cur_partitions], use_vacancy_numbers=True) self._graph.append([self._graph[-1][1], (y, len(self._graph)), '1/2x']) else: # Perform the regular type B_n^{(1)} bijection # Iterate over each column for dummy_var in range(dim[1]): # Split off a new column if necessary if self.cur_dims[0][1] > 1: if verbose: print("====================") print(repr(self.rigged_con.parent()(*self.cur_partitions, use_vacancy_numbers=True))) print("--------------------") print(ret_crystal_path) print("--------------------\n") print("Applying column split") self.cur_dims[0][1] -= 1 self.cur_dims.insert(0, [dim[0], 1]) # Perform the corresponding splitting map on rigged configurations # All it does is update the vacancy numbers on the RC side for a in range(self.n): self._update_vacancy_numbers(a) if build_graph: y = self.rigged_con.parent()(*[x._clone() for x in self.cur_partitions], use_vacancy_numbers=True) self._graph.append([self._graph[-1][1], (y, len(self._graph)), '2x']) while self.cur_dims[0][0]: #> 0: if verbose: print("====================") print(repr(self.rigged_con.parent()(*self.cur_partitions, use_vacancy_numbers=True))) print("--------------------") print(ret_crystal_path) print("--------------------\n") self.cur_dims[0][0] -= 1 # This takes care of the indexing b = self.next_state(self.cur_dims[0][0]) # Make sure we have a crystal letter ret_crystal_path[-1].append(letters(b)) # Append the rank if build_graph: y = self.rigged_con.parent()(*[x._clone() for x in self.cur_partitions], use_vacancy_numbers=True) self._graph.append([self._graph[-1][1], (y, len(self._graph)), letters(b)]) self.cur_dims.pop(0) # Pop off the leading column if build_graph: self._graph.pop(0) # Remove the dummy at the start from sage.graphs.digraph import DiGraph from sage.graphs.dot2tex_utils import have_dot2tex self._graph = DiGraph(self._graph) if have_dot2tex(): self._graph.set_latex_options(format="dot2tex", edge_labels=True) return self.KRT(pathlist=ret_crystal_path)
def digraph(self, subset=None, index_set=None): """ Returns the DiGraph associated to ``self``. INPUT: - ``subset`` -- (Optional) A subset of vertices for which the digraph should be constructed - ``index_set`` -- (Optional) The index set to draw arrows EXAMPLES:: sage: C = Crystals().example(5) sage: C.digraph() Digraph on 6 vertices The edges of the crystal graph are by default colored using blue for edge 1, red for edge 2, and green for edge 3:: sage: C = Crystals().example(3) sage: G = C.digraph() sage: view(G, pdflatex=True, tightpage=True) #optional - dot2tex graphviz One may also overwrite the colors:: sage: C = Crystals().example(3) sage: G = C.digraph() sage: G.set_latex_options(color_by_label = {1:"red", 2:"purple", 3:"blue"}) sage: view(G, pdflatex=True, tightpage=True) #optional - dot2tex graphviz Or one may add colors to yet unspecified edges:: sage: C = Crystals().example(4) sage: G = C.digraph() sage: C.cartan_type()._index_set_coloring[4]="purple" sage: view(G, pdflatex=True, tightpage=True) #optional - dot2tex graphviz Here is an example of how to take the top part up to a given depth of an infinite dimensional crystal:: sage: C = CartanType(['C',2,1]) sage: La = C.root_system().weight_lattice().fundamental_weights() sage: T = HighestWeightCrystal(La[0]) sage: S = T.subcrystal(max_depth=3) sage: G = T.digraph(subset=S); G Digraph on 5 vertices sage: G.vertices() [(1/2*Lambda[0] + Lambda[1] - Lambda[2] - 1/2*delta, -1/2*Lambda[0] + Lambda[1] - 1/2*delta), (-Lambda[0] + 2*Lambda[1] - delta,), (Lambda[0] - 2*Lambda[1] + 2*Lambda[2] - delta,), (1/2*Lambda[0] - Lambda[1] + Lambda[2] - 1/2*delta, -1/2*Lambda[0] + Lambda[1] - 1/2*delta), (Lambda[0],)] Here is a way to construct a picture of a Demazure crystal using the ``subset`` option:: sage: B = CrystalOfTableaux(['A',2], shape=[2,1]) sage: C = CombinatorialFreeModule(QQ,B) sage: t = B.highest_weight_vector() sage: b = C(t) sage: D = B.demazure_operator(b,[2,1]); D B[[[1, 1], [2]]] + B[[[1, 2], [2]]] + B[[[1, 3], [2]]] + B[[[1, 1], [3]]] + B[[[1, 3], [3]]] sage: G = B.digraph(subset=D.support()) sage: G.vertices() [[[1, 1], [2]], [[1, 2], [2]], [[1, 3], [2]], [[1, 1], [3]], [[1, 3], [3]]] sage: view(G, pdflatex=True, tightpage=True) #optional - dot2tex graphviz We can also choose to display particular arrows using the ``index_set`` option:: sage: C = KirillovReshetikhinCrystal(['D',4,1], 2, 1) sage: G = C.digraph(index_set=[1,3]) sage: len(G.edges()) 20 sage: view(G, pdflatex=True, tightpage=True) #optional - dot2tex graphviz TODO: add more tests """ from sage.graphs.all import DiGraph from sage.categories.highest_weight_crystals import HighestWeightCrystals d = {} if self in HighestWeightCrystals: f = lambda (u,v,label): ({}) else: f = lambda (u,v,label): ({"backward":label ==0}) # Parse optional arguments if subset is None: subset = self if index_set is None: index_set = self.index_set() for x in subset: d[x] = {} for i in index_set: child = x.f(i) if child is None or child not in subset: continue d[x][child]=i G = DiGraph(d) if have_dot2tex(): G.set_latex_options(format="dot2tex", edge_labels = True, color_by_label = self.cartan_type()._index_set_coloring, edge_options = f) return G
def run(self, verbose=False, build_graph=False): """ Run the bijection from rigged configurations to tensor product of KR tableaux. INPUT: - ``verbose`` -- (default: ``False``) display each step in the bijection - ``build_graph`` -- (default: ``False``) build the graph of each step of the bijection EXAMPLES:: sage: RC = RiggedConfigurations(['A', 4, 1], [[2, 1]]) sage: x = RC(partition_list=[[1],[1],[1],[1]]) sage: from sage.combinat.rigged_configurations.bij_type_A import RCToKRTBijectionTypeA sage: RCToKRTBijectionTypeA(x).run() [[2], [5]] sage: bij = RCToKRTBijectionTypeA(x) sage: bij.run(build_graph=True) [[2], [5]] sage: bij._graph Digraph on 3 vertices """ from sage.combinat.crystals.letters import CrystalOfLetters letters = CrystalOfLetters(self.rigged_con.parent()._cartan_type.classical()) # This is technically bad, but because the first thing we do is append # an empty list to ret_crystal_path, we correct this. We do it this # way so that we do not have to remove an empty list after the # bijection has been performed. ret_crystal_path = [] for dim in self.rigged_con.parent().dims: ret_crystal_path.append([]) # Iterate over each column for dummy_var in range(dim[1]): # Split off a new column if necessary if self.cur_dims[0][1] > 1: if verbose: print("====================") print(repr(self.rigged_con.parent()(*self.cur_partitions, use_vacancy_numbers=True))) print("--------------------") print(ret_crystal_path) print("--------------------\n") print("Applying column split") self.cur_dims[0][1] -= 1 self.cur_dims.insert(0, [dim[0], 1]) # Perform the corresponding splitting map on rigged configurations # All it does is update the vacancy numbers on the RC side for a in range(self.n): self._update_vacancy_numbers(a) if build_graph: y = self.rigged_con.parent()(*[x._clone() for x in self.cur_partitions], use_vacancy_numbers=True) self._graph.append([self._graph[-1][1], (y, len(self._graph)), 'ls']) while self.cur_dims[0][0] > 0: if verbose: print("====================") print(repr(self.rigged_con.parent()(*self.cur_partitions, use_vacancy_numbers=True))) print("--------------------") print(ret_crystal_path) print("--------------------\n") self.cur_dims[0][0] -= 1 # This takes care of the indexing b = self.next_state(self.cur_dims[0][0]) # Make sure we have a crystal letter ret_crystal_path[-1].append(letters(b)) # Append the rank if build_graph: y = self.rigged_con.parent()(*[x._clone() for x in self.cur_partitions], use_vacancy_numbers=True) self._graph.append([self._graph[-1][1], (y, len(self._graph)), letters(b)]) self.cur_dims.pop(0) # Pop off the leading column if build_graph: self._graph.pop(0) # Remove the dummy at the start from sage.graphs.digraph import DiGraph from sage.graphs.dot2tex_utils import have_dot2tex self._graph = DiGraph(self._graph) if have_dot2tex(): self._graph.set_latex_options(format="dot2tex", edge_labels=True) # Basic check to make sure we end with the empty configuration #tot_len = sum([len(rp) for rp in self.cur_partitions]) #if tot_len != 0: # print "Invalid bijection end for:" # print self.rigged_con # print "-----------------------" # print self.cur_partitions # raise ValueError("Invalid bijection end") return self.KRT(pathlist=ret_crystal_path)
def run(self, verbose=False, build_graph=False): """ Run the bijection from rigged configurations to tensor product of KR tableaux for type `D_n^{(1)}`. INPUT: - ``verbose`` -- (default: ``False``) display each step in the bijection - ``build_graph`` -- (default: ``False``) build the graph of each step of the bijection EXAMPLES:: sage: RC = RiggedConfigurations(['D', 4, 1], [[2, 1]]) sage: x = RC(partition_list=[[1],[1],[1],[1]]) sage: from sage.combinat.rigged_configurations.bij_type_D import RCToKRTBijectionTypeD sage: RCToKRTBijectionTypeD(x).run() [[2], [-3]] sage: bij = RCToKRTBijectionTypeD(x) sage: bij.run(build_graph=True) [[2], [-3]] sage: bij._graph Digraph on 3 vertices """ from sage.combinat.crystals.letters import CrystalOfLetters letters = CrystalOfLetters(self.rigged_con.parent()._cartan_type.classical()) # This is technically bad, but because the first thing we do is append # an empty list to ret_crystal_path, we correct this. We do it this # way so that we do not have to remove an empty list after the # bijection has been performed. ret_crystal_path = [] for dim in self.rigged_con.parent().dims: ret_crystal_path.append([]) # Iterate over each column for dummy_var in range(dim[1]): # Split off a new column if necessary if self.cur_dims[0][1] > 1: self.cur_dims[0][1] -= 1 self.cur_dims.insert(0, [dim[0], 1]) # Perform the corresponding splitting map on rigged configurations # All it does is update the vacancy numbers on the RC side for a in range(self.n): self._update_vacancy_numbers(a) if build_graph: y = self.rigged_con.parent()(*[x._clone() for x in self.cur_partitions], use_vacancy_numbers=True) self._graph.append([self._graph[-1][1], (y, len(self._graph)), 'ls']) # Check to see if we are a spinor if dim[0] >= self.n - 1: if verbose: print("====================") print(repr(self.rigged_con.parent()(*self.cur_partitions, use_vacancy_numbers=True))) print("--------------------") print(ret_crystal_path) print("--------------------\n") print("Applying doubling map") self.doubling_map() if build_graph: y = self.rigged_con.parent()(*[x._clone() for x in self.cur_partitions], use_vacancy_numbers=True) self._graph.append([self._graph[-1][1], (y, len(self._graph)), '2x']) if dim[0] == self.n - 1: if verbose: print("====================") print(repr(self.rigged_con.parent()(*self.cur_partitions, use_vacancy_numbers=True))) print("--------------------") print(ret_crystal_path) print("--------------------\n") b = self.next_state(self.n) if b == self.n: b = -self.n ret_crystal_path[-1].append(letters(b)) # Append the rank if build_graph: y = self.rigged_con.parent()(*[x._clone() for x in self.cur_partitions], use_vacancy_numbers=True) self._graph.append([self._graph[-1][1], (y, len(self._graph)), letters(b)]) while self.cur_dims[0][0] > 0: if verbose: print("====================") print(repr(self.rigged_con.parent()(*self.cur_partitions, use_vacancy_numbers=True))) print("--------------------") print(ret_crystal_path) print("--------------------\n") self.cur_dims[0][0] -= 1 # This takes care of the indexing b = self.next_state(self.cur_dims[0][0]) # Corrections for spinor if dim[0] == self.n and b == -self.n \ and self.cur_dims[0][0] == self.n - 1: b = -(self.n-1) # Make sure we have a crystal letter ret_crystal_path[-1].append(letters(b)) # Append the rank if build_graph: y = self.rigged_con.parent()(*[x._clone() for x in self.cur_partitions], use_vacancy_numbers=True) self._graph.append([self._graph[-1][1], (y, len(self._graph)), letters(b)]) self.cur_dims.pop(0) # Pop off the leading column # Check to see if we were a spinor if dim[0] >= self.n-1: if verbose: print("====================") print(repr(self.rigged_con.parent()(*self.cur_partitions, use_vacancy_numbers=True))) print("--------------------") print(ret_crystal_path) print("--------------------\n") print("Applying halving map") self.halving_map() if build_graph: y = self.rigged_con.parent()(*[x._clone() for x in self.cur_partitions], use_vacancy_numbers=True) self._graph.append([self._graph[-1][1], (y, len(self._graph)), '1/2x']) if build_graph: self._graph.pop(0) # Remove the dummy at the start from sage.graphs.digraph import DiGraph from sage.graphs.dot2tex_utils import have_dot2tex self._graph = DiGraph(self._graph, format="list_of_edges") if have_dot2tex(): self._graph.set_latex_options(format="dot2tex", edge_labels=True) return self.KRT(pathlist=ret_crystal_path)
def run(self, verbose=False, build_graph=False): """ Run the bijection from rigged configurations to tensor product of KR tableaux for type `D_n^{(1)}`. INPUT: - ``verbose`` -- (default: ``False``) display each step in the bijection - ``build_graph`` -- (default: ``False``) build the graph of each step of the bijection EXAMPLES:: sage: RC = RiggedConfigurations(['D', 4, 1], [[2, 1]]) sage: x = RC(partition_list=[[1],[1],[1],[1]]) sage: from sage.combinat.rigged_configurations.bij_type_D import RCToKRTBijectionTypeD sage: RCToKRTBijectionTypeD(x).run() [[2], [-3]] sage: bij = RCToKRTBijectionTypeD(x) sage: bij.run(build_graph=True) [[2], [-3]] sage: bij._graph Digraph on 3 vertices """ from sage.combinat.crystals.letters import CrystalOfLetters letters = CrystalOfLetters( self.rigged_con.parent()._cartan_type.classical()) # This is technically bad, but because the first thing we do is append # an empty list to ret_crystal_path, we correct this. We do it this # way so that we do not have to remove an empty list after the # bijection has been performed. ret_crystal_path = [] for dim in self.rigged_con.parent().dims: ret_crystal_path.append([]) # Iterate over each column for dummy_var in range(dim[1]): # Split off a new column if necessary if self.cur_dims[0][1] > 1: self.cur_dims[0][1] -= 1 self.cur_dims.insert(0, [dim[0], 1]) # Perform the corresponding splitting map on rigged configurations # All it does is update the vacancy numbers on the RC side for a in range(self.n): self._update_vacancy_numbers(a) if build_graph: y = self.rigged_con.parent()( *[x._clone() for x in self.cur_partitions], use_vacancy_numbers=True) self._graph.append( [self._graph[-1][1], (y, len(self._graph)), 'ls']) # Check to see if we are a spinor if dim[0] >= self.n - 1: if verbose: print("====================") print( repr(self.rigged_con.parent()( *self.cur_partitions, use_vacancy_numbers=True))) print("--------------------") print(ret_crystal_path) print("--------------------\n") print("Applying doubling map") self.doubling_map() if build_graph: y = self.rigged_con.parent()( *[x._clone() for x in self.cur_partitions], use_vacancy_numbers=True) self._graph.append( [self._graph[-1][1], (y, len(self._graph)), '2x']) if dim[0] == self.n - 1: if verbose: print("====================") print( repr(self.rigged_con.parent()( *self.cur_partitions, use_vacancy_numbers=True))) print("--------------------") print(ret_crystal_path) print("--------------------\n") b = self.next_state(self.n) if b == self.n: b = -self.n ret_crystal_path[-1].append( letters(b)) # Append the rank if build_graph: y = self.rigged_con.parent()( *[x._clone() for x in self.cur_partitions], use_vacancy_numbers=True) self._graph.append([ self._graph[-1][1], (y, len(self._graph)), letters(b) ]) while self.cur_dims[0][0] > 0: if verbose: print("====================") print( repr(self.rigged_con.parent()( *self.cur_partitions, use_vacancy_numbers=True))) print("--------------------") print(ret_crystal_path) print("--------------------\n") self.cur_dims[0][0] -= 1 # This takes care of the indexing b = self.next_state(self.cur_dims[0][0]) # Corrections for spinor if dim[0] == self.n and b == -self.n \ and self.cur_dims[0][0] == self.n - 1: b = -(self.n - 1) # Make sure we have a crystal letter ret_crystal_path[-1].append(letters(b)) # Append the rank if build_graph: y = self.rigged_con.parent()( *[x._clone() for x in self.cur_partitions], use_vacancy_numbers=True) self._graph.append([ self._graph[-1][1], (y, len(self._graph)), letters(b) ]) self.cur_dims.pop(0) # Pop off the leading column # Check to see if we were a spinor if dim[0] >= self.n - 1: if verbose: print("====================") print( repr(self.rigged_con.parent()( *self.cur_partitions, use_vacancy_numbers=True))) print("--------------------") print(ret_crystal_path) print("--------------------\n") print("Applying halving map") self.halving_map() if build_graph: y = self.rigged_con.parent()( *[x._clone() for x in self.cur_partitions], use_vacancy_numbers=True) self._graph.append([ self._graph[-1][1], (y, len(self._graph)), '1/2x' ]) if build_graph: self._graph.pop(0) # Remove the dummy at the start from sage.graphs.digraph import DiGraph from sage.graphs.dot2tex_utils import have_dot2tex self._graph = DiGraph(self._graph, format="list_of_edges") if have_dot2tex(): self._graph.set_latex_options(format="dot2tex", edge_labels=True) return self.KRT(pathlist=ret_crystal_path)
def digraph(self, subset=None, index_set=None): """ Returns the DiGraph associated to ``self``. INPUT: - ``subset`` -- (Optional) A subset of vertices for which the digraph should be constructed - ``index_set`` -- (Optional) The index set to draw arrows EXAMPLES:: sage: C = Crystals().example(5) sage: C.digraph() Digraph on 6 vertices The edges of the crystal graph are by default colored using blue for edge 1, red for edge 2, and green for edge 3:: sage: C = Crystals().example(3) sage: G = C.digraph() sage: view(G, pdflatex=True, tightpage=True) #optional - dot2tex graphviz One may also overwrite the colors:: sage: C = Crystals().example(3) sage: G = C.digraph() sage: G.set_latex_options(color_by_label = {1:"red", 2:"purple", 3:"blue"}) sage: view(G, pdflatex=True, tightpage=True) #optional - dot2tex graphviz Or one may add colors to yet unspecified edges:: sage: C = Crystals().example(4) sage: G = C.digraph() sage: C.cartan_type()._index_set_coloring[4]="purple" sage: view(G, pdflatex=True, tightpage=True) #optional - dot2tex graphviz Here is an example of how to take the top part up to a given depth of an infinite dimensional crystal:: sage: C = CartanType(['C',2,1]) sage: La = C.root_system().weight_lattice().fundamental_weights() sage: T = crystals.HighestWeight(La[0]) sage: S = T.subcrystal(max_depth=3) sage: G = T.digraph(subset=S); G Digraph on 5 vertices sage: sorted(G.vertices(), key=str) [(-Lambda[0] + 2*Lambda[1] - delta,), (1/2*Lambda[0] + Lambda[1] - Lambda[2] - 1/2*delta, -1/2*Lambda[0] + Lambda[1] - 1/2*delta), (1/2*Lambda[0] - Lambda[1] + Lambda[2] - 1/2*delta, -1/2*Lambda[0] + Lambda[1] - 1/2*delta), (Lambda[0] - 2*Lambda[1] + 2*Lambda[2] - delta,), (Lambda[0],)] Here is a way to construct a picture of a Demazure crystal using the ``subset`` option:: sage: B = crystals.Tableaux(['A',2], shape=[2,1]) sage: C = CombinatorialFreeModule(QQ,B) sage: t = B.highest_weight_vector() sage: b = C(t) sage: D = B.demazure_operator(b,[2,1]); D B[[[1, 1], [2]]] + B[[[1, 2], [2]]] + B[[[1, 3], [2]]] + B[[[1, 1], [3]]] + B[[[1, 3], [3]]] sage: G = B.digraph(subset=D.support()) sage: G.vertices() [[[1, 1], [2]], [[1, 2], [2]], [[1, 3], [2]], [[1, 1], [3]], [[1, 3], [3]]] sage: view(G, pdflatex=True, tightpage=True) #optional - dot2tex graphviz We can also choose to display particular arrows using the ``index_set`` option:: sage: C = crystals.KirillovReshetikhin(['D',4,1], 2, 1) sage: G = C.digraph(index_set=[1,3]) sage: len(G.edges()) 20 sage: view(G, pdflatex=True, tightpage=True) #optional - dot2tex graphviz TODO: add more tests """ from sage.graphs.all import DiGraph from sage.categories.highest_weight_crystals import HighestWeightCrystals d = {} if self in HighestWeightCrystals: f = lambda u_v_label: ({}) else: f = lambda u_v_label: ({"backward": u_v_label[2] == 0}) # Parse optional arguments if subset is None: subset = self if index_set is None: index_set = self.index_set() for x in subset: d[x] = {} for i in index_set: child = x.f(i) if child is None or child not in subset: continue d[x][child]=i G = DiGraph(d) if have_dot2tex(): G.set_latex_options(format="dot2tex", edge_labels = True, color_by_label = self.cartan_type()._index_set_coloring, edge_options = f) return G