@@ -2035,22 +2035,60 @@ def dft(self, form=None, mult='l2r'):
2035
2035
(:meth:`antipode`) of the seminormal basis instead of
2036
2036
the seminormal basis.
2037
2037
2038
- EXAMPLES::
2038
+ - ``form`` -- string (default: ``"modular"`` if `p|n!` else ``"seminormal"``);
2039
+ one of the following:
2039
2040
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()
2042
2055
[ 1 1 1 1 1 1]
2043
2056
[ 1 1/2 -1 -1/2 -1/2 1/2]
2044
2057
[ 0 3/4 0 3/4 -3/4 -3/4]
2045
2058
[ 0 1 0 -1 1 -1]
2046
2059
[ 1 -1/2 1 -1/2 -1/2 -1/2]
2047
2060
[ 1 -1 -1 1 1 -1]
2048
2061
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
2050
2088
modular Fourier transform (:issue:`37751`)::
2051
2089
2052
- sage: GF2S3 = SymmetricGroupAlgebra(GF(2), 3)
2053
- sage: GF2S3 .dft()
2090
+ sage: GF2_S3 = SymmetricGroupAlgebra(GF(2), 3)
2091
+ sage: GF2_S3 .dft()
2054
2092
[1 0 0 0 1 0]
2055
2093
[0 1 0 0 0 1]
2056
2094
[0 0 1 0 0 1]
@@ -2066,8 +2104,109 @@ def dft(self, form=None, mult='l2r'):
2066
2104
return self ._dft_seminormal (mult = mult )
2067
2105
if form == "modular" :
2068
2106
return self ._dft_modular ()
2107
+ if form == "unitary" :
2108
+ return self ._dft_unitary ()
2069
2109
raise ValueError ("invalid form (= %s)" % form )
2070
2110
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
+
2071
2210
def _dft_seminormal (self , mult = 'l2r' ):
2072
2211
"""
2073
2212
Return the seminormal form of the discrete Fourier transform for ``self``.
0 commit comments