Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
r""" Induced Crystals
We construct a crystal structure on a set induced by a bijection `\Phi`.
AUTHORS:
- Travis Scrimshaw (2014-05-15): Initial implementation """
#***************************************************************************** # Copyright (C) 2014 Travis Scrimshaw <tscrim at ucdavis.edu> # # Distributed under the terms of the GNU General Public License (GPL) # # This code is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # The full text of the GPL is available at: # # http://www.gnu.org/licenses/ #****************************************************************************
from sage.structure.unique_representation import UniqueRepresentation from sage.structure.parent import Parent from sage.structure.element_wrapper import ElementWrapper
class InducedCrystal(UniqueRepresentation, Parent): r""" A crystal induced from an injection.
Let `X` be a set and let `C` be crystal and consider any injection `\Phi : X \to C`. We induce a crystal structure on `X` by considering `\Phi` to be a crystal morphism.
Alternatively we can induce a crystal structure on some (sub)set of `X` by considering an injection `\Phi : C \to X` considered as a crystal morphism. This form is also useful when the set `X` is not explicitly known.
INPUT:
- ``X`` -- the base set - ``phi`` -- the map `\Phi` - ``inverse`` -- (optional) the inverse map `\Phi^{-1}` - ``from_crystal`` -- (default: ``False``) if the induced structure is of the second type `\Phi : C \to X`
EXAMPLES:
We construct a crystal structure of Gelfand-Tsetlin patterns by going through their bijection with semistandard tableaux::
sage: D = crystals.Tableaux(['A',3], shapes=PartitionsInBox(4,3)) sage: G = GelfandTsetlinPatterns(4, 3) sage: phi = lambda x: D(x.to_tableau()) sage: phi_inv = lambda x: G(x.to_tableau()) sage: I = crystals.Induced(G, phi, phi_inv) sage: I.digraph().is_isomorphic(D.digraph(), edge_labels=True) True
Now we construct the above example but inducing the structure going the other way (from tableaux to Gelfand-Tsetlin patterns). This can also give us more information coming from the crystal. ::
sage: D2 = crystals.Tableaux(['A',3], shapes=PartitionsInBox(4,1)) sage: G2 = GelfandTsetlinPatterns(4, 1) sage: phi2 = lambda x: D2(x.to_tableau()) sage: phi2_inv = lambda x: G2(x.to_tableau()) sage: I2 = crystals.Induced(D2, phi2_inv, phi2, from_crystal=True) sage: I2.module_generators ([[0, 0, 0, 0], [0, 0, 0], [0, 0], [0]], [[1, 0, 0, 0], [1, 0, 0], [1, 0], [1]], [[1, 1, 0, 0], [1, 1, 0], [1, 1], [1]], [[1, 1, 1, 0], [1, 1, 1], [1, 1], [1]], [[1, 1, 1, 1], [1, 1, 1], [1, 1], [1]])
We check an example when the codomain is larger than the domain (although here the crystal structure is trivial)::
sage: P = Permutations(4) sage: D = crystals.Tableaux(['A',3], shapes=Partitions(4)) sage: T = crystals.TensorProduct(D, D) sage: phi = lambda p: T(D(RSK(p)[0]), D(RSK(p)[1])) sage: phi_inv = lambda d: RSK_inverse(d[0].to_tableau(), d[1].to_tableau(), output='permutation') sage: all(phi_inv(phi(p)) == p for p in P) # Check it really is the inverse True sage: I = crystals.Induced(P, phi, phi_inv) sage: I.digraph() Multi-digraph on 24 vertices
We construct an example without a specified inverse map::
sage: X = Words(2,4) sage: L = crystals.Letters(['A',1]) sage: T = crystals.TensorProduct(*[L]*4) sage: Phi = lambda x : T(*[L(i) for i in x]) sage: I = crystals.Induced(X, Phi) sage: I.digraph() Digraph on 16 vertices """ @staticmethod def __classcall_private__(cls, X, phi, inverse=None, from_crystal=False): """ Normalize input to ensure a unique representation.
TESTS::
sage: D = crystals.Tableaux(['A',3], shapes=PartitionsInBox(4,3)) sage: G = GelfandTsetlinPatterns(4, 3) sage: phi = lambda x: D(x.to_tableau()) sage: phi_inv = lambda x: G(x.to_tableau()) sage: I1 = crystals.Induced(G, phi, phi_inv) sage: I2 = crystals.Induced(G, phi, phi_inv) sage: I1 is I2 True """
def __init__(self, X, phi, inverse): """ Initialize ``self``.
TESTS:
Note that pickling only works when the input functions can be pickled::
sage: D = crystals.Tableaux(['A',3], shapes=PartitionsInBox(4,1)) sage: G = GelfandTsetlinPatterns(4, 1) sage: def phi(x): return D(x.to_tableau()) sage: def phi_inv(x): return G(x.to_tableau()) sage: import __main__ sage: __main__.phi = phi sage: __main__.phi_inv = phi_inv sage: I = crystals.Induced(G, phi, phi_inv) sage: TestSuite(I).run() """
raise ValueError("the inverse map must be defined for infinite sets") raise ValueError("the map is not injective")
def _repr_(self): """ Return a string representation of ``self``.
EXAMPLES::
sage: D = crystals.Tableaux(['A',3], shapes=PartitionsInBox(4,1)) sage: G = GelfandTsetlinPatterns(4, 1) sage: def phi(x): return D(x.to_tableau()) sage: def phi_inv(x): return G(x.to_tableau()) sage: crystals.Induced(G, phi, phi_inv) Crystal of Gelfand-Tsetlin patterns of width 4 and max value 1 induced by <function phi at 0x...> """
def _element_constructor_(self, x): """ Construct an element of ``self``.
EXAMPLES::
sage: D = crystals.Tableaux(['A',3], shapes=PartitionsInBox(4,1)) sage: G = GelfandTsetlinPatterns(4, 1) sage: def phi(x): return D(x.to_tableau()) sage: def phi_inv(x): return G(x.to_tableau()) sage: I = crystals.Induced(G, phi, phi_inv) sage: I([[1,1,0,0],[1,0,0],[1,0],[1]]) [[1, 1, 0, 0], [1, 0, 0], [1, 0], [1]] sage: I(D(3,2,1)) [[1, 1, 1, 0], [1, 1, 1], [1, 1], [1]] sage: I([[1,1,0,0],[1,0,0],[0,1],[1]]) Traceback (most recent call last): ... TypeError: unable to convert [[1, 1, 0, 0], [1, 0, 0], [0, 1], [1]] to Crystal of Gelfand-Tsetlin patterns of width 4 and max value 1 induced by <function phi at 0x...> """
def __contains__(self, x): """ Check if ``x`` is in ``self``.
EXAMPLES::
sage: D = crystals.Tableaux(['A',3], shapes=PartitionsInBox(4,1)) sage: G = GelfandTsetlinPatterns(4, 1) sage: def phi(x): return D(x.to_tableau()) sage: def phi_inv(x): return G(x.to_tableau()) sage: I = crystals.Induced(G, phi, phi_inv) sage: all(g in I for g in G) True sage: [[1,1,0,0],[1,0,0],[1,0],[1]] in I True sage: [[1,1,0,0],[1,0,0],[0,1],[1]] in I False """
def __iter__(self): """ Iterate over ``self``.
EXAMPLES::
sage: D = crystals.Tableaux(['A',3], shapes=PartitionsInBox(4,1)) sage: G = GelfandTsetlinPatterns(4, 1) sage: def phi(x): return D(x.to_tableau()) sage: def phi_inv(x): return G(x.to_tableau()) sage: I = crystals.Induced(G, phi, phi_inv) sage: sorted([x for x in I]) [[[0, 0, 0, 0], [0, 0, 0], [0, 0], [0]], [[1, 0, 0, 0], [0, 0, 0], [0, 0], [0]], [[1, 0, 0, 0], [1, 0, 0], [0, 0], [0]], [[1, 0, 0, 0], [1, 0, 0], [1, 0], [0]], [[1, 0, 0, 0], [1, 0, 0], [1, 0], [1]], [[1, 1, 0, 0], [1, 0, 0], [0, 0], [0]], [[1, 1, 0, 0], [1, 0, 0], [1, 0], [0]], [[1, 1, 0, 0], [1, 0, 0], [1, 0], [1]], [[1, 1, 0, 0], [1, 1, 0], [1, 0], [0]], [[1, 1, 0, 0], [1, 1, 0], [1, 0], [1]], [[1, 1, 0, 0], [1, 1, 0], [1, 1], [1]], [[1, 1, 1, 0], [1, 1, 0], [1, 0], [0]], [[1, 1, 1, 0], [1, 1, 0], [1, 0], [1]], [[1, 1, 1, 0], [1, 1, 0], [1, 1], [1]], [[1, 1, 1, 0], [1, 1, 1], [1, 1], [1]], [[1, 1, 1, 1], [1, 1, 1], [1, 1], [1]]] """
def cardinality(self): """ Return the cardinality of ``self``.
EXAMPLES::
sage: P = Permutations(4) sage: D = crystals.Tableaux(['A',3], shapes=Partitions(4)) sage: T = crystals.TensorProduct(D, D) sage: phi = lambda p: T(D(RSK(p)[0]), D(RSK(p)[1])) sage: phi_inv = lambda d: RSK_inverse(d[0].to_tableau(), d[1].to_tableau(), output='permutation') sage: I = crystals.Induced(P, phi, phi_inv) sage: I.cardinality() == factorial(4) True """
class Element(ElementWrapper): """ An element of an induced crystal. """ def e(self, i): """ Return `e_i` of ``self``.
EXAMPLES::
sage: D = crystals.Tableaux(['A',3], shapes=PartitionsInBox(4,3)) sage: G = GelfandTsetlinPatterns(4, 3) sage: phi = lambda x: D(x.to_tableau()) sage: phi_inv = lambda x: G(x.to_tableau()) sage: I = crystals.Induced(G, phi, phi_inv) sage: elt = I([[1, 1, 0, 0], [1, 1, 0], [1, 0], [1]]) sage: elt.e(1) sage: elt.e(2) [[1, 1, 0, 0], [1, 1, 0], [1, 1], [1]] sage: elt.e(3) """ except (ValueError, TypeError, AttributeError): return None
def f(self, i): """ Return `f_i` of ``self``.
EXAMPLES::
sage: D = crystals.Tableaux(['A',3], shapes=PartitionsInBox(4,3)) sage: G = GelfandTsetlinPatterns(4, 3) sage: phi = lambda x: D(x.to_tableau()) sage: phi_inv = lambda x: G(x.to_tableau()) sage: I = crystals.Induced(G, phi, phi_inv) sage: elt = I([[1, 1, 0, 0], [1, 1, 0], [1, 0], [1]]) sage: elt.f(1) [[1, 1, 0, 0], [1, 1, 0], [1, 0], [0]] sage: elt.f(2) sage: elt.f(3) [[1, 1, 0, 0], [1, 0, 0], [1, 0], [1]] """
def epsilon(self, i): r""" Return `\varepsilon_i` of ``self``.
EXAMPLES::
sage: D = crystals.Tableaux(['A',3], shapes=PartitionsInBox(4,3)) sage: G = GelfandTsetlinPatterns(4, 3) sage: phi = lambda x: D(x.to_tableau()) sage: phi_inv = lambda x: G(x.to_tableau()) sage: I = crystals.Induced(G, phi, phi_inv) sage: elt = I([[1, 1, 0, 0], [1, 1, 0], [1, 0], [1]]) sage: [elt.epsilon(i) for i in I.index_set()] [0, 1, 0] """
def phi(self, i): r""" Return `\varphi_i` of ``self``.
EXAMPLES::
sage: D = crystals.Tableaux(['A',3], shapes=PartitionsInBox(4,3)) sage: G = GelfandTsetlinPatterns(4, 3) sage: phi = lambda x: D(x.to_tableau()) sage: phi_inv = lambda x: G(x.to_tableau()) sage: I = crystals.Induced(G, phi, phi_inv) sage: elt = I([[1, 1, 0, 0], [1, 1, 0], [1, 0], [1]]) sage: [elt.phi(i) for i in I.index_set()] [1, 0, 1] """
def weight(self): """ Return the weight of ``self``.
EXAMPLES::
sage: D = crystals.Tableaux(['A',3], shapes=PartitionsInBox(4,3)) sage: G = GelfandTsetlinPatterns(4, 3) sage: phi = lambda x: D(x.to_tableau()) sage: phi_inv = lambda x: G(x.to_tableau()) sage: I = crystals.Induced(G, phi, phi_inv) sage: elt = I([[1, 1, 0, 0], [1, 1, 0], [1, 0], [1]]) sage: elt.weight() (1, 0, 1, 0) """
class InducedFromCrystal(UniqueRepresentation, Parent): r""" A crystal induced from an injection.
Alternatively we can induce a crystal structure on some (sub)set of `X` by considering an injection `\Phi : C \to X` considered as a crystal morphism.
.. SEEALSO::
:class:`InducedCrystal`
INPUT:
- ``X`` -- the base set - ``phi`` -- the map `\Phi` - ``inverse`` -- (optional) the inverse map `\Phi^{-1}`
EXAMPLES:
We construct a crystal structure on generalized permutations with a fixed first row by using RSK::
sage: C = crystals.Tableaux(['A',3], shape=[2,1]) sage: def psi(x): ....: ret = RSK_inverse(x.to_tableau(), Tableau([[1,1],[2]])) ....: return (tuple(ret[0]), tuple(ret[1])) sage: psi_inv = lambda x: C(RSK(*x)[0]) sage: I = crystals.Induced(C, psi, psi_inv, from_crystal=True) """ def __init__(self, X, phi, inverse): """ Initialize ``self``.
TESTS:
Note that pickling only works when the input functions can be pickled::
sage: D = crystals.Tableaux(['A',3], shapes=PartitionsInBox(4,1)) sage: G = GelfandTsetlinPatterns(4, 1) sage: def phi(x): return D(x.to_tableau()) sage: def phi_inv(x): return G(x.to_tableau()) sage: import __main__ sage: __main__.phi = phi sage: __main__.phi_inv = phi_inv sage: I = crystals.Induced(D, phi_inv, phi, from_crystal=True) sage: TestSuite(I).run() """
try: inverse = ~self._phi except (TypeError, ValueError): try: inverse = self._phi.section() except AttributeError: if X.cardinality() == float('inf'): raise ValueError("the inverse map must be defined for infinite sets") self._preimage = {} for x in X: y = phi(x) if y in self._preimage: raise ValueError("the map is not injective") self._preimage[y] = x inverse = self._preimage.__getitem__
for mg in X.module_generators)
def _repr_(self): """ Return a string representation of ``self``.
EXAMPLES::
sage: D = crystals.Tableaux(['A',3], shapes=PartitionsInBox(4,1)) sage: G = GelfandTsetlinPatterns(4, 1) sage: def phi(x): return D(x.to_tableau()) sage: def phi_inv(x): return G(x.to_tableau()) sage: crystals.Induced(D, phi_inv, phi, from_crystal=True) Crystal induced by <function phi_inv at 0x...> from The crystal of tableaux of type ['A', 3] and shape(s) [[], [1], [1, 1], [1, 1, 1], [1, 1, 1, 1]] """
def _element_constructor_(self, x): """ Construct an element of ``self``.
EXAMPLES::
sage: C = crystals.Tableaux(['A',3], shape=[2,1]) sage: def psi(x): ....: ret = RSK_inverse(x.to_tableau(), Tableau([[1,1],[2]])) ....: return (tuple(ret[0]), tuple(ret[1])) sage: psi_inv = lambda x: C(RSK(*x)[0]) sage: I = crystals.Induced(C, psi, psi_inv, from_crystal=True) sage: I([[1, 1, 2], [2, 2, 1]]) ((1, 1, 2), (2, 2, 1)) sage: I(C(2,1,3)) ((1, 1, 2), (2, 3, 1)) """
except (TypeError, ValueError, AttributeError): raise TypeError("unable to convert {!r} to {}".format(x, self))
def __contains__(self, x): """ Check if ``x`` is in ``self``.
EXAMPLES::
sage: C = crystals.Tableaux(['A',3], shape=[2,1]) sage: def psi(x): ....: ret = RSK_inverse(x.to_tableau(), Tableau([[1,1],[2]])) ....: return (tuple(ret[0]), tuple(ret[1])) sage: psi_inv = lambda x: C(RSK(*x)[0]) sage: I = crystals.Induced(C, psi, psi_inv, from_crystal=True) sage: ((1, 1, 2), (2, 2, 1)) in I True sage: ((1, 2, 2), (1, 1, 2)) in I False sage: ((1, 2, 3), (1, 2, 3)) in I False sage: ((1, 2, 2), (1, 3, 2)) in I False """
def __iter__(self): """ Iterate over ``self``.
EXAMPLES::
sage: C = crystals.Tableaux(['A',2], shape=[2,1]) sage: def psi(x): ....: ret = RSK_inverse(x.to_tableau(), Tableau([[1,1],[2]])) ....: return (tuple(ret[0]), tuple(ret[1])) sage: psi_inv = lambda x: C(RSK(*x)[0]) sage: I = crystals.Induced(C, psi, psi_inv, from_crystal=True) sage: sorted(x for x in I) [((1, 1, 2), (1, 2, 1)), ((1, 1, 2), (2, 2, 1)), ((1, 1, 2), (2, 3, 1)), ((1, 1, 2), (3, 3, 1)), ((1, 1, 2), (3, 3, 2)), ((1, 1, 2), (1, 3, 1)), ((1, 1, 2), (1, 3, 2)), ((1, 1, 2), (2, 3, 2))] """
def cardinality(self): """ Return the cardinality of ``self``.
EXAMPLES::
sage: C = crystals.Tableaux(['A',3], shape=[2,1]) sage: def psi(x): ....: ret = RSK_inverse(x.to_tableau(), Tableau([[1,1],[2]])) ....: return (tuple(ret[0]), tuple(ret[1])) sage: psi_inv = lambda x: C(RSK(*x)[0]) sage: I = crystals.Induced(C, psi, psi_inv, from_crystal=True) sage: I.cardinality() == C.cardinality() True """
class Element(ElementWrapper): """ An element of an induced crystal. """ def e(self, i): """ Return `e_i` of ``self``.
EXAMPLES::
sage: D = crystals.Tableaux(['A',3], shapes=PartitionsInBox(4,1)) sage: G = GelfandTsetlinPatterns(4, 1) sage: def phi(x): return G(x.to_tableau()) sage: def phi_inv(x): return D(G(x).to_tableau()) sage: I = crystals.Induced(D, phi, phi_inv, from_crystal=True) sage: elt = I([[1, 1, 0, 0], [1, 1, 0], [1, 0], [1]]) sage: elt.e(1) sage: elt.e(2) [[1, 1, 0, 0], [1, 1, 0], [1, 1], [1]] sage: elt.e(3) """
def f(self, i): """ Return `f_i` of ``self``.
EXAMPLES::
sage: D = crystals.Tableaux(['A',3], shapes=PartitionsInBox(4,1)) sage: G = GelfandTsetlinPatterns(4, 1) sage: def phi(x): return G(x.to_tableau()) sage: def phi_inv(x): return D(G(x).to_tableau()) sage: I = crystals.Induced(D, phi, phi_inv, from_crystal=True) sage: elt = I([[1, 1, 0, 0], [1, 1, 0], [1, 0], [1]]) sage: elt.f(1) [[1, 1, 0, 0], [1, 1, 0], [1, 0], [0]] sage: elt.f(2) sage: elt.f(3) [[1, 1, 0, 0], [1, 0, 0], [1, 0], [1]] """
def epsilon(self, i): r""" Return `\varepsilon_i` of ``self``.
EXAMPLES::
sage: D = crystals.Tableaux(['A',3], shapes=PartitionsInBox(4,1)) sage: G = GelfandTsetlinPatterns(4, 1) sage: def phi(x): return G(x.to_tableau()) sage: def phi_inv(x): return D(G(x).to_tableau()) sage: I = crystals.Induced(D, phi, phi_inv, from_crystal=True) sage: elt = I([[1, 1, 0, 0], [1, 1, 0], [1, 0], [1]]) sage: [elt.epsilon(i) for i in I.index_set()] [0, 1, 0] """
def phi(self, i): r""" Return `\varphi_i` of ``self``.
EXAMPLES::
sage: D = crystals.Tableaux(['A',3], shapes=PartitionsInBox(4,1)) sage: G = GelfandTsetlinPatterns(4, 1) sage: def phi(x): return G(x.to_tableau()) sage: def phi_inv(x): return D(G(x).to_tableau()) sage: I = crystals.Induced(D, phi, phi_inv, from_crystal=True) sage: elt = I([[1, 1, 0, 0], [1, 1, 0], [1, 0], [1]]) sage: [elt.epsilon(i) for i in I.index_set()] [0, 1, 0] """
def weight(self): """ Return the weight of ``self``.
EXAMPLES::
sage: D = crystals.Tableaux(['A',3], shapes=PartitionsInBox(4,1)) sage: G = GelfandTsetlinPatterns(4, 1) sage: def phi(x): return G(x.to_tableau()) sage: def phi_inv(x): return D(G(x).to_tableau()) sage: I = crystals.Induced(D, phi, phi_inv, from_crystal=True) sage: elt = I([[1, 1, 0, 0], [1, 1, 0], [1, 0], [1]]) sage: elt.weight() (1, 0, 1, 0) """
|