1919from  sage .misc .cachefunc  import  cached_method 
2020from  sage .sets .family  import  Family 
2121from  sage .rings .infinity  import  infinity 
22+ from  sage .rings .integer_ring  import  ZZ 
2223from  sage .categories .algebras  import  Algebras 
2324from  sage .combinat .free_module  import  CombinatorialFreeModule 
24- from  sage .monoids .free_abelian_monoid  import  FreeAbelianMonoid , FreeAbelianMonoid_class 
25+ from  sage .monoids .free_abelian_monoid  import  FreeAbelianMonoid 
26+ from  sage .matrix .constructor  import  matrix 
27+ from  sage .structure .element  import  Matrix 
2528
2629class  qCommutingPolynomials (CombinatorialFreeModule ):
2730    r""" 
2831    The algebra of `q`-commuting polynomials. 
2932
30-     Let `R` be a commutative ring, and fix an element `q \in R`. We say two 
31-     distinct variables `x` and `y` `q`-*commute* if they satisfy the relation 
33+     Let `R` be a commutative ring, and fix an element `q \in R`. Let 
34+     B = (B_{xy})_{x,y \in I}`  be a skew-symmetric bilinear form with 
35+     index set `I`. Let `R[I]_{q,B}` denote the polynomial ring in the variables 
36+     `I` such that we have the `q`-*commuting* relation for `x, y \in I`: 
3237
3338    .. MATH:: 
3439
35-         x y  = q \cdot y x . 
40+         y x  = q^{B_{xy}}  \cdot x y . 
3641
37-     These form a graded `R`-algebra with a natural basis given by monomials 
38-     written in increasing order. These then satisfy a `q`-analog of the 
39-     classical binomial coefficient theorem: 
42+     This is a graded `R`-algebra with a natural basis given by monomials 
43+     written in increasing order with respect to some total order on `I`. 
44+ 
45+     When `B_{xy} = 1` and `B_{yx} = -1` for all `x < y`, then we have 
46+     a `q`-analog of the classical binomial coefficient theorem: 
4047
4148    .. MATH:: 
4249
@@ -47,14 +54,38 @@ class qCommutingPolynomials(CombinatorialFreeModule):
4754        sage: q = ZZ['q'].fraction_field().gen() 
4855        sage: R.<x,y> = algebras.qCommutingPolynomials(q) 
4956
50-     We verify the `q`-binomial theorem:: 
57+     We verify a case of  the `q`-binomial theorem:: 
5158
5259        sage: f = (x + y)^10 
5360        sage: all(f[b] == q_binomial(10, b.list()[0]) for b in f.support()) 
5461        True 
62+ 
63+     We now do a computation with a non-standard `B` matrix:: 
64+ 
65+         sage: B = matrix([[0,1,2],[-1,0,3],[-2,-3,0]]) 
66+         sage: B 
67+         [ 0  1  2] 
68+         [-1  0  3] 
69+         [-2 -3  0] 
70+         sage: q = ZZ['q'].gen() 
71+         sage: R.<x,y,z> = algebras.qCommutingPolynomials(q, B) 
72+         sage: y * x 
73+         q*x*y 
74+         sage: z * x 
75+         q^2*x*z 
76+         sage: z * y 
77+         q^3*y*z 
78+ 
79+         sage: f = (x + z)^10 
80+         sage: all(f[b] == q_binomial(10, b.list()[0], q^2) for b in f.support()) 
81+         True 
82+ 
83+         sage: f = (y + z)^10 
84+         sage: all(f[b] == q_binomial(10, b.list()[1], q^3) for b in f.support()) 
85+         True 
5586    """ 
5687    @staticmethod  
57-     def  __classcall_private__ (cls , q , n = None , base_ring = None , names = None ):
88+     def  __classcall_private__ (cls , q , n = None , B = None ,  base_ring = None , names = None ):
5889        r""" 
5990        Normalize input to ensure a unique representation. 
6091
@@ -70,13 +101,34 @@ def __classcall_private__(cls, q, n=None, base_ring=None, names=None):
70101        if  base_ring  is  not None :
71102            q  =  base_ring (q )
72103
73-         if  isinstance (n , FreeAbelianMonoid_class ):
74-             indices  =  n 
104+         if  B  is  None  and  isinstance (n , Matrix ):
105+             n , B  =  B , n 
106+ 
107+         if  names  is  None :
108+             raise  ValueError ("the names of the variables must be given" )
109+         from  sage .structure .category_object  import  normalize_names 
110+         if  n  is  None :
111+             if  isinstance (names , str ):
112+                 n  =  names .count (',' ) +  1 
113+             else :
114+                 n  =  len (names )
115+         names  =  normalize_names (n , names )
116+         n  =  len (names )
117+         if  B  is  None :
118+             B  =  matrix .zero (ZZ , n )
119+             for  i  in  range (n ):
120+                 for  j  in  range (i + 1 , n ):
121+                     B [i ,j ] =  1 
122+                     B [j ,i ] =  - 1 
123+             B .set_immutable ()
75124        else :
76-             indices  =  FreeAbelianMonoid (n , names )
77-         return  super ().__classcall__ (cls , q , indices )
125+             if  not  B .is_skew_symmetric ():
126+                 raise  ValueError ("the matrix must be skew symmetric" )
127+             B  =  B .change_ring (ZZ )
128+             B .set_immutable ()
129+         return  super ().__classcall__ (cls , q = q , B = B , names = names )
78130
79-     def  __init__ (self , q , indices ):
131+     def  __init__ (self , q , B ,  names ):
80132        r""" 
81133        Initialize ``self``. 
82134
@@ -87,7 +139,9 @@ def __init__(self, q, indices):
87139            sage: TestSuite(R).run() 
88140        """ 
89141        self ._q  =  q 
142+         self ._B  =  B 
90143        base_ring  =  q .parent ()
144+         indices  =  FreeAbelianMonoid (len (names ), names )
91145        category  =  Algebras (base_ring ).WithBasis ().Graded ()
92146        CombinatorialFreeModule .__init__ (self , base_ring , indices ,
93147                                         bracket = False , prefix = '' ,
@@ -104,10 +158,13 @@ def _repr_(self):
104158            sage: R.<x,y,z> = algebras.qCommutingPolynomials(q) 
105159            sage: R 
106160            q-commuting polynomial ring in x, y, z over Fraction Field of 
107-              Univariate Polynomial Ring in q over Integer Ring 
161+              Univariate Polynomial Ring in q over Integer Ring with matrix: 
162+             [ 0  1  1] 
163+             [-1  0  1] 
164+             [-1 -1  0] 
108165        """ 
109166        names  =  ", " .join (self .variable_names ())
110-         return  "{}-commuting polynomial ring in {} over {}" .format (self ._q , names , self .base_ring ())
167+         return  "{}-commuting polynomial ring in {} over {} with matrix: \n {} " .format (self ._q , names , self .base_ring (),  self . _B )
111168
112169    def  _latex_ (self ):
113170        r""" 
@@ -142,7 +199,7 @@ def _term_key(x):
142199        return  (sum (L ), L )
143200
144201    def  gen (self , i ):
145-         """ 
202+         r """
146203        Return the ``i``-generator of ``self``. 
147204
148205        EXAMPLES:: 
@@ -260,6 +317,24 @@ def product_on_basis(self, x, y):
260317            x^3 + (q^2+q+1)*x^2*y + (q^2+q+1)*x*y^2 + y^3 
261318            sage: (x + y)^4 
262319            x^4 + (q^3+q^2+q+1)*x^3*y + (q^4+q^3+2*q^2+q+1)*x^2*y^2 + (q^3+q^2+q+1)*x*y^3 + y^4 
320+ 
321+         With a non-standard `B` matrix:: 
322+ 
323+             sage: B = matrix([[0,1,2],[-1,0,3],[-2,-3,0]]) 
324+             sage: q = ZZ['q'].fraction_field().gen() 
325+             sage: R.<x,y,z> = algebras.qCommutingPolynomials(q, B=B) 
326+             sage: x * y 
327+             x*y 
328+             sage: y * x^2 
329+             q^2*x^2*y 
330+             sage: z^2 * x 
331+             q^4*x*z^2 
332+             sage: z^2 * x^3 
333+             q^12*x^3*z^2 
334+             sage: z^2 * y 
335+             q^6*y*z^2 
336+             sage: z^2 * y^3 
337+             q^18*y^3*z^2 
263338        """ 
264339        # Special case for multiplying by 1 
265340        if  x  ==  self .one_basis ():
@@ -271,6 +346,6 @@ def product_on_basis(self, x, y):
271346        Ly  =  y .list ()
272347
273348        # This could be made more efficient 
274-         qpow  =  sum (exp  *  sum (Ly [:i ]) for  i ,exp  in  enumerate (Lx ))
349+         qpow  =  sum (exp  *  sum (self . _B [ j , i ]  *   val   for   j ,  val   in   enumerate ( Ly [:i ]) ) for  i ,exp  in  enumerate (Lx ))
275350        return  self .term (x  *  y , self ._q  **  qpow )
276351
0 commit comments