@@ -2035,22 +2035,60 @@ def dft(self, form=None, mult='l2r'):
20352035 (:meth:`antipode`) of the seminormal basis instead of
20362036 the seminormal basis.
20372037
2038- EXAMPLES::
2038+ - ``form`` -- string (default: ``"modular"`` if `p|n!` else ``"seminormal"``);
2039+ one of the following:
20392040
2040- sage: QS3 = SymmetricGroupAlgebra(QQ, 3)
2041- sage: QS3.dft()
2041+ * ``"seminormal"`` -- use the seminormal basis
2042+ * ``"modular"`` -- use the modular DFT, which uses the Peirce decomposition
2043+ of the group algebra into blocks with central orthogonal idempotents
2044+ * ``"unitary"`` -- use the unitary DFT, which computes the extended
2045+ Cholesky decomposition of an `S_n`-invariant symmetric bilinear form as
2046+ a change-of-basis matrix (for positive characteristics, it must be
2047+ a finite field of square order)
2048+
2049+ EXAMPLES:
2050+
2051+ The default is the seminormal DFT when the characteristic does not divide the group order::
2052+
2053+ sage: QQ_S3 = SymmetricGroupAlgebra(QQ, 3)
2054+ sage: QQ_S3.dft()
20422055 [ 1 1 1 1 1 1]
20432056 [ 1 1/2 -1 -1/2 -1/2 1/2]
20442057 [ 0 3/4 0 3/4 -3/4 -3/4]
20452058 [ 0 1 0 -1 1 -1]
20462059 [ 1 -1/2 1 -1/2 -1/2 -1/2]
20472060 [ 1 -1 -1 1 1 -1]
20482061
2049- Over fields of characteristic `p > 0` such that `p \mid n!`, we use the
2062+ The unitary form works in characteristic zero::
2063+
2064+ sage: U = QQ_S3.dft(form='unitary'); U
2065+ [-1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2]
2066+ [ 1/3*sqrt3 1/6*sqrt3 -1/3*sqrt3 -1/6*sqrt3 -1/6*sqrt3 1/6*sqrt3]
2067+ [ 0 1/2 0 1/2 -1/2 -1/2]
2068+ [ 0 1/2 0 -1/2 1/2 -1/2]
2069+ [ 1/3*sqrt3 -1/6*sqrt3 1/3*sqrt3 -1/6*sqrt3 -1/6*sqrt3 -1/6*sqrt3]
2070+ [-1/6*sqrt3*sqrt2 1/6*sqrt3*sqrt2 1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2 1/6*sqrt3*sqrt2]
2071+ sage: U*U.H == 1
2072+ True
2073+
2074+ Over finite fields of square order with characteristic `p > n`, we can perform the unitary DFT::
2075+
2076+ sage: GF25_S3 = SymmetricGroupAlgebra(GF(5**2), 3)
2077+ sage: U = GF25_S3.dft(form='unitary'); U
2078+ [ 1 1 1 1 1 1]
2079+ [2*z2 + 4 z2 + 2 3*z2 + 1 4*z2 + 3 4*z2 + 3 z2 + 2]
2080+ [ 0 2 0 2 3 3]
2081+ [ 0 z2 + 1 0 4*z2 + 4 z2 + 1 4*z2 + 4]
2082+ [2*z2 + 4 4*z2 + 3 2*z2 + 4 4*z2 + 3 4*z2 + 3 4*z2 + 3]
2083+ [ 1 4 4 1 1 4]
2084+ sage: U*U.H == 1
2085+ True
2086+
2087+ Over fields of characteristic `p > 0` such that `p|n!`, we use the
20502088 modular Fourier transform (:issue:`37751`)::
20512089
2052- sage: GF2S3 = SymmetricGroupAlgebra(GF(2), 3)
2053- sage: GF2S3 .dft()
2090+ sage: GF2_S3 = SymmetricGroupAlgebra(GF(2), 3)
2091+ sage: GF2_S3 .dft()
20542092 [1 0 0 0 1 0]
20552093 [0 1 0 0 0 1]
20562094 [0 0 1 0 0 1]
@@ -2066,8 +2104,109 @@ def dft(self, form=None, mult='l2r'):
20662104 return self ._dft_seminormal (mult = mult )
20672105 if form == "modular" :
20682106 return self ._dft_modular ()
2107+ if form == "unitary" :
2108+ return self ._dft_unitary ()
20692109 raise ValueError ("invalid form (= %s)" % form )
20702110
2111+ def _dft_unitary (self ):
2112+ """
2113+ Return the unitary form of the discrete Fourier transform for ``self``.
2114+
2115+ EXAMPLES::
2116+
2117+ sage: QQ_S3 = SymmetricGroupAlgebra(QQ, 3)
2118+ sage: QQ_S3._dft_unitary()
2119+ [-1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2]
2120+ [ 1/3*sqrt3 1/6*sqrt3 -1/3*sqrt3 -1/6*sqrt3 -1/6*sqrt3 1/6*sqrt3]
2121+ [ 0 1/2 0 1/2 -1/2 -1/2]
2122+ [ 0 1/2 0 -1/2 1/2 -1/2]
2123+ [ 1/3*sqrt3 -1/6*sqrt3 1/3*sqrt3 -1/6*sqrt3 -1/6*sqrt3 -1/6*sqrt3]
2124+ [-1/6*sqrt3*sqrt2 1/6*sqrt3*sqrt2 1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2 -1/6*sqrt3*sqrt2 1/6*sqrt3*sqrt2]
2125+ sage: GF49_S3 = SymmetricGroupAlgebra(GF(7**2), 3)
2126+ sage: GF49_S3._dft_unitary()
2127+ [5*z2 + 5 5*z2 + 5 5*z2 + 5 5*z2 + 5 5*z2 + 5 5*z2 + 5]
2128+ [2*z2 + 5 z2 + 6 5*z2 + 2 6*z2 + 1 6*z2 + 1 z2 + 6]
2129+ [ 0 4*z2 + 5 0 4*z2 + 5 3*z2 + 2 3*z2 + 2]
2130+ [ 0 3*z2 + 2 0 4*z2 + 5 3*z2 + 2 4*z2 + 5]
2131+ [2*z2 + 5 6*z2 + 1 2*z2 + 5 6*z2 + 1 6*z2 + 1 6*z2 + 1]
2132+ [5*z2 + 5 2*z2 + 2 2*z2 + 2 5*z2 + 5 5*z2 + 5 2*z2 + 2]
2133+
2134+ TESTS::
2135+
2136+ sage: QQ_S3 = SymmetricGroupAlgebra(QQ, 3)
2137+ sage: U = QQ_S3._dft_unitary()
2138+ sage: U*U.H == 1
2139+ True
2140+ sage: GF25_S3 = SymmetricGroupAlgebra(GF(5**2), 3)
2141+ sage: U = GF25_S3._dft_unitary()
2142+ sage: U*U.H == 1
2143+ True
2144+ sage: GF5_S3 = SymmetricGroupAlgebra(GF(5), 3)
2145+ sage: U = GF5_S3._dft_unitary()
2146+ Traceback (most recent call last):
2147+ ...
2148+ ValueError: the base ring must be a finite field of square order
2149+ sage: Z5_S3 = SymmetricGroupAlgebra(Integers(5), 3)
2150+ sage: U = Z5_S3._dft_unitary()
2151+ Traceback (most recent call last):
2152+ ...
2153+ ValueError: the base ring must be a finite field of square order
2154+ sage: F.<x> = FunctionField(GF(3))
2155+ sage: GF3_x_S3 = SymmetricGroupAlgebra(F, 5)
2156+ sage: U = GF3_x_S3._dft_unitary()
2157+ Traceback (most recent call last):
2158+ ...
2159+ ValueError: the base ring must be a finite field of square order
2160+ sage: GF9_S3 = SymmetricGroupAlgebra(GF(3**2), 3)
2161+ sage: U = GF9_S3._dft_unitary()
2162+ Traceback (most recent call last):
2163+ ...
2164+ NotImplementedError: not implemented when p|n!; dimension of invariant forms may be greater than one
2165+ """
2166+ from sage .matrix .special import diagonal_matrix
2167+ F = self .base_ring ()
2168+ G = self .group ()
2169+
2170+ if F .characteristic () == 0 :
2171+ from sage .misc .functional import sqrt
2172+ from sage .rings .number_field .number_field import NumberField
2173+ dft_matrix = self .dft ()
2174+ n = dft_matrix .nrows ()
2175+ diag = [sum (dft_matrix [i , j ] * dft_matrix [i , j ].conjugate () for j in range (n ))
2176+ for i in range (n )]
2177+ primes_needed = {factor for d in diag for factor , _ in d .squarefree_part ().factor ()}
2178+ names = [f"sqrt{ factor } " for factor in primes_needed ]
2179+ x = PolynomialRing (QQ , 'x' ).gen ()
2180+ K = NumberField ([x ** 2 - d for d in primes_needed ], names = names )
2181+ dft_matrix = dft_matrix .change_ring (K )
2182+ for i , d in enumerate (diag ):
2183+ dft_matrix [i ] *= ~ sqrt (K (d ))
2184+ return dft_matrix
2185+
2186+ # positive characteristic case
2187+ assert F .characteristic () > 0 , "F must have positive characteristic"
2188+ if not (F .is_field () and F .is_finite () and F .order ().is_square ()):
2189+ raise ValueError ("the base ring must be a finite field of square order" )
2190+ if F .characteristic ().divides (G .cardinality ()):
2191+ raise NotImplementedError ("not implemented when p|n!; dimension of invariant forms may be greater than one" )
2192+ q = F .order ().sqrt ()
2193+
2194+ def conj_square_root (u ):
2195+ if not u :
2196+ return F .zero ()
2197+ z = F .multiplicative_generator ()
2198+ k = u .log (z )
2199+ if k % (q + 1 ) != 0 :
2200+ raise ValueError (f"unable to factor as { u } is not in base field GF({ q } )" )
2201+ return z ** ((k // (q + 1 )) % (q - 1 ))
2202+
2203+ dft_matrix = self .dft ()
2204+ n = dft_matrix .nrows ()
2205+ for i in range (n ):
2206+ d = sum (dft_matrix [i , j ] * dft_matrix [i , j ].conjugate () for j in range (n ))
2207+ dft_matrix [i ] *= ~ conj_square_root (d )
2208+ return dft_matrix
2209+
20712210 def _dft_seminormal (self , mult = 'l2r' ):
20722211 """
20732212 Return the seminormal form of the discrete Fourier transform for ``self``.
0 commit comments